]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
sys: attempt to placate Microsoft's compiler
[xonotic/darkplaces.git] / sys_shared.c
1 #ifdef WIN32
2 # ifndef DONT_USE_SETDLLDIRECTORY
3 #  define _WIN32_WINNT 0x0502
4 # endif
5 #endif
6
7 #define SUPPORTDLL
8
9 #ifdef WIN32
10 # include <windows.h>
11 # include <mmsystem.h> // timeGetTime
12 # include <time.h> // localtime
13 # include <conio.h> // _kbhit, _getch, _putch
14 # include <io.h> // write; Include this BEFORE darkplaces.h because it uses strncpy which trips DP_STATIC_ASSERT
15 #ifdef _MSC_VER
16 #pragma comment(lib, "winmm.lib")
17 #endif
18 #else
19 # ifdef __FreeBSD__
20 #  include <sys/sysctl.h>
21 # endif
22 # ifdef __ANDROID__
23 #  include <android/log.h>
24 # endif
25 # include <unistd.h>
26 # include <fcntl.h>
27 # include <sys/time.h>
28 # include <time.h>
29 # ifdef SUPPORTDLL
30 #  include <dlfcn.h>
31 # endif
32 #endif
33
34 #include <signal.h>
35
36 #include "quakedef.h"
37 #include "taskqueue.h"
38 #include "thread.h"
39 #include "libcurl.h"
40
41 #ifdef WIN32
42         // Microsoft's compiler complains about portable code
43         #pragma warning(disable : 4996)
44 #endif
45
46 sys_t sys;
47
48 static char sys_timestring[128];
49 char *Sys_TimeString(const char *timeformat)
50 {
51         time_t mytime = time(NULL);
52 #if _MSC_VER >= 1400
53         struct tm mytm;
54         localtime_s(&mytm, &mytime);
55         strftime(sys_timestring, sizeof(sys_timestring), timeformat, &mytm);
56 #else
57         strftime(sys_timestring, sizeof(sys_timestring), timeformat, localtime(&mytime));
58 #endif
59         return sys_timestring;
60 }
61
62
63 void Sys_Quit (int returnvalue)
64 {
65         // Unlock mutexes because the quit command may jump directly here, causing a deadlock
66         if ((cmd_local)->cbuf->lock)
67                 Cbuf_Unlock((cmd_local)->cbuf);
68         SV_UnlockThreadMutex();
69         TaskQueue_Frame(true);
70
71         if (Sys_CheckParm("-profilegameonly"))
72                 Sys_AllowProfiling(false);
73         host.state = host_shutdown;
74         Host_Shutdown();
75
76 #ifdef __ANDROID__
77         Sys_AllowProfiling(false);
78 #endif
79
80 #ifndef WIN32
81         fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) & ~O_NONBLOCK);
82         fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) & ~O_NONBLOCK);
83 #endif
84         fflush(stdout);
85         fflush(stderr);
86
87         exit(returnvalue);
88 }
89
90 #ifdef __cplusplus
91 extern "C"
92 #endif
93 void Sys_AllowProfiling(qbool enable)
94 {
95 #ifdef __ANDROID__
96 #ifdef USE_PROFILER
97         extern void monstartup(const char *libname);
98         extern void moncleanup(void);
99         if (enable)
100                 monstartup("libmain.so");
101         else
102                 moncleanup();
103 #endif
104 #elif (defined(__linux__) && (defined(__GLIBC__) || defined(__GNU_LIBRARY__))) || defined(__FreeBSD__)
105         extern int moncontrol(int);
106         moncontrol(enable);
107 #endif
108 }
109
110
111 /*
112 ===============================================================================
113
114 DLL MANAGEMENT
115
116 ===============================================================================
117 */
118
119 static qbool Sys_LoadDependencyFunctions(dllhandle_t dllhandle, const dllfunction_t *fcts, qbool complain, qbool has_next)
120 {
121         const dllfunction_t *func;
122         if(dllhandle)
123         {
124                 for (func = fcts; func && func->name != NULL; func++)
125                         if (!(*func->funcvariable = (void *) Sys_GetProcAddress (dllhandle, func->name)))
126                         {
127                                 if(complain)
128                                 {
129                                         Con_DPrintf (" - missing function \"%s\" - broken library!", func->name);
130                                         if(has_next)
131                                                 Con_DPrintf("\nContinuing with");
132                                 }
133                                 goto notfound;
134                         }
135                 return true;
136
137         notfound:
138                 for (func = fcts; func && func->name != NULL; func++)
139                         *func->funcvariable = NULL;
140         }
141         return false;
142 }
143
144 qbool Sys_LoadSelf(dllhandle_t *handle)
145 {
146         dllhandle_t dllhandle = 0;
147
148         if (handle == NULL)
149                 return false;
150 #ifdef WIN32
151         dllhandle = LoadLibrary (NULL);
152 #else
153         dllhandle = dlopen (NULL, RTLD_NOW | RTLD_GLOBAL);
154 #endif
155         *handle = dllhandle;
156         return true;
157 }
158
159 qbool Sys_LoadDependency (const char** dllnames, dllhandle_t* handle, const dllfunction_t *fcts)
160 {
161 #ifdef SUPPORTDLL
162         const dllfunction_t *func;
163         dllhandle_t dllhandle = 0;
164         unsigned int i;
165
166         if (handle == NULL)
167                 return false;
168
169 #ifndef WIN32
170 #ifdef PREFER_PRELOAD
171         dllhandle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
172         if(Sys_LoadDependencyFunctions(dllhandle, fcts, false, false))
173         {
174                 Con_DPrintf ("All of %s's functions were already linked in! Not loading dynamically...\n", dllnames[0]);
175                 *handle = dllhandle;
176                 return true;
177         }
178         else
179                 Sys_FreeLibrary(&dllhandle);
180 notfound:
181 #endif
182 #endif
183
184         // Initializations
185         for (func = fcts; func && func->name != NULL; func++)
186                 *func->funcvariable = NULL;
187
188         // Try every possible name
189         Con_DPrintf ("Trying to load library...");
190         for (i = 0; dllnames[i] != NULL; i++)
191         {
192                 Con_DPrintf (" \"%s\"", dllnames[i]);
193 #ifdef WIN32
194 # ifndef DONT_USE_SETDLLDIRECTORY
195 #  ifdef _WIN64
196                 SetDllDirectory("bin64");
197 #  else
198                 SetDllDirectory("bin32");
199 #  endif
200 # endif
201 #endif
202                 if(Sys_LoadLibrary(dllnames[i], &dllhandle))
203                 {
204                         if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
205                                 break;
206                         else
207                                 Sys_FreeLibrary (&dllhandle);
208                 }
209         }
210
211         // see if the names can be loaded relative to the executable path
212         // (this is for Mac OSX which does not check next to the executable)
213         if (!dllhandle && strrchr(sys.argv[0], '/'))
214         {
215                 char path[MAX_OSPATH];
216                 strlcpy(path, sys.argv[0], sizeof(path));
217                 strrchr(path, '/')[1] = 0;
218                 for (i = 0; dllnames[i] != NULL; i++)
219                 {
220                         char temp[MAX_OSPATH];
221                         strlcpy(temp, path, sizeof(temp));
222                         strlcat(temp, dllnames[i], sizeof(temp));
223                         Con_DPrintf (" \"%s\"", temp);
224
225                         if(Sys_LoadLibrary(temp, &dllhandle))
226                         {
227                                 if (Sys_LoadDependencyFunctions(dllhandle, fcts, true, (dllnames[i+1] != NULL) || (strrchr(sys.argv[0], '/'))))
228                                         break;
229                                 else
230                                         Sys_FreeLibrary (&dllhandle);
231                         }
232                 }
233         }
234
235         // No DLL found
236         if (! dllhandle)
237         {
238                 Con_DPrintf(" - failed.\n");
239                 return false;
240         }
241
242         Con_DPrintf(" - loaded.\n");
243         Con_Printf("Loaded library \"%s\"\n", dllnames[i]);
244
245         *handle = dllhandle;
246         return true;
247 #else
248         return false;
249 #endif
250 }
251
252 qbool Sys_LoadLibrary(const char *name, dllhandle_t *handle)
253 {
254         dllhandle_t dllhandle = 0;
255
256         if(handle == NULL)
257                 return false;
258
259 #ifdef SUPPORTDLL
260 # ifdef WIN32
261         dllhandle = LoadLibrary (name);
262 # else
263         dllhandle = dlopen (name, RTLD_LAZY | RTLD_GLOBAL);
264 # endif
265 #endif
266         if(!dllhandle)
267                 return false;
268
269         *handle = dllhandle;
270         return true;
271 }
272
273 void Sys_FreeLibrary (dllhandle_t* handle)
274 {
275 #ifdef SUPPORTDLL
276         if (handle == NULL || *handle == NULL)
277                 return;
278
279 #ifdef WIN32
280         FreeLibrary (*handle);
281 #else
282         dlclose (*handle);
283 #endif
284
285         *handle = NULL;
286 #endif
287 }
288
289 void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
290 {
291 #ifdef SUPPORTDLL
292 #ifdef WIN32
293         return (void *)GetProcAddress (handle, name);
294 #else
295         return (void *)dlsym (handle, name);
296 #endif
297 #else
298         return NULL;
299 #endif
300 }
301
302 #ifdef WIN32
303 # define HAVE_TIMEGETTIME 1
304 # define HAVE_QUERYPERFORMANCECOUNTER 1
305 # define HAVE_Sleep 1
306 #endif
307
308 #ifndef WIN32
309 #if defined(CLOCK_MONOTONIC) || defined(CLOCK_HIRES)
310 # define HAVE_CLOCKGETTIME 1
311 #endif
312 // FIXME improve this check, manpage hints to DST_NONE
313 # define HAVE_GETTIMEOFDAY 1
314 #endif
315
316 #ifdef FD_SET
317 # define HAVE_SELECT 1
318 #endif
319
320 #ifndef WIN32
321 // FIXME improve this check
322 # define HAVE_USLEEP 1
323 #endif
324
325 // these are referenced elsewhere
326 cvar_t sys_usenoclockbutbenchmark = {CF_SHARED, "sys_usenoclockbutbenchmark", "0", "don't use ANY real timing, and simulate a clock (for benchmarking); the game then runs as fast as possible. Run a QC mod with bots that does some stuff, then does a quit at the end, to benchmark a server. NEVER do this on a public server."};
327 cvar_t sys_libdir = {CF_READONLY | CF_SHARED, "sys_libdir", "", "Default engine library directory"};
328
329 // these are not
330 static cvar_t sys_debugsleep = {CF_SHARED, "sys_debugsleep", "0", "write requested and attained sleep times to standard output, to be used with gnuplot"};
331 static cvar_t sys_usesdlgetticks = {CF_SHARED, "sys_usesdlgetticks", "0", "use SDL_GetTicks() timer (less accurate, for debugging)"};
332 static cvar_t sys_usesdldelay = {CF_SHARED, "sys_usesdldelay", "0", "use SDL_Delay() (less accurate, for debugging)"};
333 #if HAVE_QUERYPERFORMANCECOUNTER
334 static cvar_t sys_usequeryperformancecounter = {CF_SHARED | CF_ARCHIVE, "sys_usequeryperformancecounter", "0", "use windows QueryPerformanceCounter timer (which has issues on multicore/multiprocessor machines and processors which are designed to conserve power) for timing rather than timeGetTime function (which has issues on some motherboards)"};
335 #endif
336 #if HAVE_CLOCKGETTIME
337 static cvar_t sys_useclockgettime = {CF_SHARED | CF_ARCHIVE, "sys_useclockgettime", "1", "use POSIX clock_gettime function (not adjusted by NTP on some older Linux kernels) for timing rather than gettimeofday (which has issues if the system time is stepped by ntpdate, or apparently on some Xen installations)"};
338 #endif
339
340 static cvar_t sys_stdout = {CF_SHARED, "sys_stdout", "1", "0: nothing is written to stdout (-nostdout cmdline option sets this), 1: normal messages are written to stdout, 2: normal messages are written to stderr (-stderr cmdline option sets this)"};
341 #ifndef WIN32
342 static cvar_t sys_stdout_blocks = {CF_SHARED, "sys_stdout_blocks", "0", "1: writes to stdout and stderr streams will block (causing a stutter or complete halt) if the buffer is full, ensuring no messages are lost at a price"};
343 #endif
344
345 static double benchmark_time; // actually always contains an integer amount of milliseconds, will eventually "overflow"
346
347 /*
348 ================
349 Sys_CheckParm
350
351 Returns the position (1 to argc-1) in the program's argument list
352 where the given parameter apears, or 0 if not present
353 ================
354 */
355 int Sys_CheckParm (const char *parm)
356 {
357         int i;
358
359         for (i=1 ; i<sys.argc ; i++)
360         {
361                 if (!sys.argv[i])
362                         continue;               // NEXTSTEP sometimes clears appkit vars.
363                 if (!strcmp (parm,sys.argv[i]))
364                         return i;
365         }
366
367         return 0;
368 }
369
370 static void Sys_UpdateOutFD_c(cvar_t *var)
371 {
372         switch (sys_stdout.integer)
373         {
374                 case 0: sys.outfd = -1; break;
375                 default:
376                 case 1: sys.outfd = fileno(stdout); break;
377                 case 2: sys.outfd = fileno(stderr); break;
378         }
379 }
380
381 void Sys_Init_Commands (void)
382 {
383         Cvar_RegisterVariable(&sys_debugsleep);
384         Cvar_RegisterVariable(&sys_usenoclockbutbenchmark);
385         Cvar_RegisterVariable(&sys_libdir);
386 #if HAVE_TIMEGETTIME || HAVE_QUERYPERFORMANCECOUNTER || HAVE_CLOCKGETTIME || HAVE_GETTIMEOFDAY
387         if(sys_supportsdlgetticks)
388         {
389                 Cvar_RegisterVariable(&sys_usesdlgetticks);
390                 Cvar_RegisterVariable(&sys_usesdldelay);
391         }
392 #endif
393 #if HAVE_QUERYPERFORMANCECOUNTER
394         Cvar_RegisterVariable(&sys_usequeryperformancecounter);
395 #endif
396 #if HAVE_CLOCKGETTIME
397         Cvar_RegisterVariable(&sys_useclockgettime);
398 #endif
399         Cvar_RegisterVariable(&sys_stdout);
400         Cvar_RegisterCallback(&sys_stdout, Sys_UpdateOutFD_c);
401 #ifndef WIN32
402         Cvar_RegisterVariable(&sys_stdout_blocks);
403 #endif
404 }
405
406 double Sys_DirtyTime(void)
407 {
408         // first all the OPTIONAL timers
409
410         // benchmark timer (fake clock)
411         if(sys_usenoclockbutbenchmark.integer)
412         {
413                 double old_benchmark_time = benchmark_time;
414                 benchmark_time += 1;
415                 if(benchmark_time == old_benchmark_time)
416                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
417                 return benchmark_time * 0.000001;
418         }
419 #if HAVE_QUERYPERFORMANCECOUNTER
420         if (sys_usequeryperformancecounter.integer)
421         {
422                 // LadyHavoc: note to people modifying this code, DWORD is specifically defined as an unsigned 32bit number, therefore the 65536.0 * 65536.0 is fine.
423                 // QueryPerformanceCounter
424                 // platform:
425                 // Windows 95/98/ME/NT/2000/XP
426                 // features:
427                 // very accurate (CPU cycles)
428                 // known issues:
429                 // does not necessarily match realtime too well (tends to get faster and faster in win98)
430                 // wraps around occasionally on some platforms (depends on CPU speed and probably other unknown factors)
431                 double timescale;
432                 LARGE_INTEGER PerformanceFreq;
433                 LARGE_INTEGER PerformanceCount;
434
435                 if (QueryPerformanceFrequency (&PerformanceFreq))
436                 {
437                         QueryPerformanceCounter (&PerformanceCount);
438         
439                         timescale = 1.0 / ((double) PerformanceFreq.LowPart + (double) PerformanceFreq.HighPart * 65536.0 * 65536.0);
440                         return ((double) PerformanceCount.LowPart + (double) PerformanceCount.HighPart * 65536.0 * 65536.0) * timescale;
441                 }
442                 else
443                 {
444                         Con_Printf("No hardware timer available\n");
445                         // fall back to other clock sources
446                         Cvar_SetValueQuick(&sys_usequeryperformancecounter, false);
447                 }
448         }
449 #endif
450
451 #if HAVE_CLOCKGETTIME
452         if (sys_useclockgettime.integer)
453         {
454                 struct timespec ts;
455 #  ifdef CLOCK_MONOTONIC
456                 // linux
457                 clock_gettime(CLOCK_MONOTONIC, &ts);
458 #  else
459                 // sunos
460                 clock_gettime(CLOCK_HIGHRES, &ts);
461 #  endif
462                 return (double) ts.tv_sec + ts.tv_nsec / 1000000000.0;
463         }
464 #endif
465
466         // now all the FALLBACK timers
467         if(sys_supportsdlgetticks && sys_usesdlgetticks.integer)
468                 return (double) Sys_SDL_GetTicks() / 1000.0;
469 #if HAVE_GETTIMEOFDAY
470         {
471                 struct timeval tp;
472                 gettimeofday(&tp, NULL);
473                 return (double) tp.tv_sec + tp.tv_usec / 1000000.0;
474         }
475 #elif HAVE_TIMEGETTIME
476         {
477                 static int firsttimegettime = true;
478                 // timeGetTime
479                 // platform:
480                 // Windows 95/98/ME/NT/2000/XP
481                 // features:
482                 // reasonable accuracy (millisecond)
483                 // issues:
484                 // wraps around every 47 days or so (but this is non-fatal to us, odd times are rejected, only causes a one frame stutter)
485
486                 // make sure the timer is high precision, otherwise different versions of windows have varying accuracy
487                 if (firsttimegettime)
488                 {
489                         timeBeginPeriod(1);
490                         firsttimegettime = false;
491                 }
492
493                 return (double) timeGetTime() / 1000.0;
494         }
495 #else
496         // fallback for using the SDL timer if no other timer is available
497         // this calls Sys_Error() if not linking against SDL
498         return (double) Sys_SDL_GetTicks() / 1000.0;
499 #endif
500 }
501
502 extern cvar_t host_maxwait;
503 double Sys_Sleep(double time)
504 {
505         double dt;
506         uint32_t microseconds;
507
508         // convert to microseconds
509         time *= 1000000.0;
510
511         if(host_maxwait.value <= 0)
512                 time = min(time, 1000000.0);
513         else
514                 time = min(time, host_maxwait.value * 1000.0);
515
516         if (time < 1 || host.restless)
517                 return 0; // not sleeping this frame
518
519         microseconds = time; // post-validation to prevent overflow
520
521         if(sys_usenoclockbutbenchmark.integer)
522         {
523                 double old_benchmark_time = benchmark_time;
524                 benchmark_time += microseconds;
525                 if(benchmark_time == old_benchmark_time)
526                         Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
527                 return 0;
528         }
529
530         if(sys_debugsleep.integer)
531                 Con_Printf("sys_debugsleep: requesting %u ", microseconds);
532         dt = Sys_DirtyTime();
533
534         // less important on newer libcurl so no need to disturb dedicated servers
535         if (cls.state != ca_dedicated && Curl_Select(microseconds))
536         {
537                 // a transfer is ready or we finished sleeping
538         }
539         else if(sys_supportsdlgetticks && sys_usesdldelay.integer)
540                 Sys_SDL_Delay(microseconds / 1000);
541 #if HAVE_SELECT
542         else
543         {
544                 struct timeval tv;
545                 lhnetsocket_t *s;
546                 fd_set fdreadset;
547                 int lastfd = -1;
548
549                 FD_ZERO(&fdreadset);
550                 if (cls.state == ca_dedicated && sv_checkforpacketsduringsleep.integer)
551                 {
552                         List_For_Each_Entry(s, &lhnet_socketlist.list, lhnetsocket_t, list)
553                         {
554                                 if (s->address.addresstype == LHNETADDRESSTYPE_INET4 || s->address.addresstype == LHNETADDRESSTYPE_INET6)
555                                 {
556                                         if (lastfd < s->inetsocket)
557                                                 lastfd = s->inetsocket;
558         #if defined(WIN32) && !defined(_MSC_VER)
559                                         FD_SET((int)s->inetsocket, &fdreadset);
560         #else
561                                         FD_SET((unsigned int)s->inetsocket, &fdreadset);
562         #endif
563                                 }
564                         }
565                 }
566                 tv.tv_sec = microseconds / 1000000;
567                 tv.tv_usec = microseconds % 1000000;
568                 // on Win32, select() cannot be used with all three FD list args being NULL according to MSDN
569                 // (so much for POSIX...)
570                 // bones_was_here: but a zeroed fd_set seems to be tolerated (tested on Win 7)
571                 select(lastfd + 1, &fdreadset, NULL, NULL, &tv);
572         }
573 #elif HAVE_USLEEP
574         else
575                 usleep(microseconds);
576 #elif HAVE_Sleep
577         else
578                 Sleep(microseconds / 1000);
579 #else
580         else
581                 Sys_SDL_Delay(microseconds / 1000);
582 #endif
583
584         dt = Sys_DirtyTime() - dt;
585         if(sys_debugsleep.integer)
586                 Con_Printf(" got %u oversleep %d\n", (unsigned int)(dt * 1000000), (unsigned int)(dt * 1000000) - microseconds);
587         return (dt < 0 || dt >= 1800) ? 0 : dt;
588 }
589
590
591 /*
592 ===============================================================================
593
594 STDIO
595
596 ===============================================================================
597 */
598
599 void Sys_Print(const char *text)
600 {
601 #ifdef __ANDROID__
602         if (developer.integer > 0)
603         {
604                 __android_log_write(ANDROID_LOG_DEBUG, sys.argv[0], text);
605         }
606 #else
607         if(sys.outfd < 0)
608                 return;
609   #ifndef WIN32
610         // BUG: for some reason, NDELAY also affects stdout (1) when used on stdin (0).
611         // this is because both go to /dev/tty by default!
612         {
613                 int origflags = fcntl(sys.outfd, F_GETFL, 0);
614                 if (sys_stdout_blocks.integer)
615                         fcntl(sys.outfd, F_SETFL, origflags & ~O_NONBLOCK);
616   #else
617     #define write _write
618   #endif
619                 while(*text)
620                 {
621                         fs_offset_t written = (fs_offset_t)write(sys.outfd, text, (int)strlen(text));
622                         if(written <= 0)
623                                 break; // sorry, I cannot do anything about this error - without an output
624                         text += written;
625                 }
626   #ifndef WIN32
627                 if (sys_stdout_blocks.integer)
628                         fcntl(sys.outfd, F_SETFL, origflags);
629         }
630   #endif
631         //fprintf(stdout, "%s", text);
632 #endif
633 }
634
635 /// for the console to report failures inside Con_Printf()
636 void Sys_Printf(const char *fmt, ...)
637 {
638         va_list argptr;
639         char msg[MAX_INPUTLINE];
640
641         va_start(argptr,fmt);
642         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
643         va_end(argptr);
644
645         Sys_Print(msg);
646 }
647
648 /// Reads a line from POSIX stdin or the Windows console
649 char *Sys_ConsoleInput(void)
650 {
651         static char text[MAX_INPUTLINE];
652 #ifdef WIN32
653         static unsigned int len = 0;
654         int c;
655
656         // read a line out
657         while (_kbhit ())
658         {
659                 c = _getch ();
660                 if (c == '\r')
661                 {
662                         text[len] = '\0';
663                         _putch ('\n');
664                         len = 0;
665                         return text;
666                 }
667                 if (c == '\b')
668                 {
669                         if (len)
670                         {
671                                 _putch (c);
672                                 _putch (' ');
673                                 _putch (c);
674                                 len--;
675                         }
676                         continue;
677                 }
678                 if (len < sizeof (text) - 1)
679                 {
680                         _putch (c);
681                         text[len] = c;
682                         len++;
683                 }
684         }
685 #else
686         fd_set fdset;
687         struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
688
689         FD_ZERO(&fdset);
690         FD_SET(fileno(stdin), &fdset);
691         if (select(1, &fdset, NULL, NULL, &timeout) != -1 && FD_ISSET(fileno(stdin), &fdset))
692                 return fgets(text, sizeof(text), stdin);
693 #endif
694         return NULL;
695 }
696
697
698 /*
699 ===============================================================================
700
701 Startup and Shutdown
702
703 ===============================================================================
704 */
705
706 void Sys_Error (const char *error, ...)
707 {
708         va_list argptr;
709         char string[MAX_INPUTLINE];
710
711         // set output to blocking stderr
712         sys.outfd = fileno(stderr);
713 #ifndef WIN32
714         fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
715 #endif
716
717         va_start (argptr,error);
718         dpvsnprintf (string, sizeof (string), error, argptr);
719         va_end (argptr);
720
721         Con_Printf(CON_ERROR "Engine Error: %s\n", string);
722
723         // don't want a dead window left blocking the OS UI or the crash dialog
724         Host_Shutdown();
725
726         Sys_SDL_Dialog("Engine Error", string);
727
728         fflush(stderr);
729
730         exit (1);
731 }
732
733 #ifndef WIN32
734 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
735 {
736         const char *p = PATH;
737         const char *q;
738         if(p && name)
739         {
740                 while((q = strchr(p, ':')))
741                 {
742                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
743                         if(FS_SysFileExists(buf))
744                                 return buf;
745                         p = q + 1;
746                 }
747                 if(!q) // none found - try the last item
748                 {
749                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
750                         if(FS_SysFileExists(buf))
751                                 return buf;
752                 }
753         }
754         return name;
755 }
756 #endif
757
758 static const char *Sys_FindExecutableName(void)
759 {
760 #if defined(WIN32)
761         return sys.argv[0];
762 #else
763         static char exenamebuf[MAX_OSPATH+1];
764         ssize_t n = -1;
765 #if defined(__FreeBSD__)
766         int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
767         size_t exenamebuflen = sizeof(exenamebuf)-1;
768         if (sysctl(mib, 4, exenamebuf, &exenamebuflen, NULL, 0) == 0)
769         {
770                 n = exenamebuflen;
771         }
772 #elif defined(__linux__)
773         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
774 #endif
775         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
776         {
777                 exenamebuf[n] = 0;
778                 return exenamebuf;
779         }
780         if(strchr(sys.argv[0], '/'))
781                 return sys.argv[0]; // possibly a relative path
782         else
783                 return Sys_FindInPATH(sys.argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
784 #endif
785 }
786
787 void Sys_ProvideSelfFD(void)
788 {
789         if(sys.selffd != -1)
790                 return;
791         sys.selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
792 }
793
794 // for x86 cpus only...  (x64 has SSE2_PRESENT)
795 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
796 // code from SDL, shortened as we can expect CPUID to work
797 static int CPUID_Features(void)
798 {
799         int features = 0;
800 # if (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)) && defined(__i386__)
801         __asm__ (
802 "        movl    %%ebx,%%edi\n"
803 "        xorl    %%eax,%%eax                                           \n"
804 "        incl    %%eax                                                 \n"
805 "        cpuid                       # Get family/model/stepping/features\n"
806 "        movl    %%edx,%0                                              \n"
807 "        movl    %%edi,%%ebx\n"
808         : "=m" (features)
809         :
810         : "%eax", "%ecx", "%edx", "%edi"
811         );
812 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
813         __asm {
814         xor     eax, eax
815         inc     eax
816         cpuid                       ; Get family/model/stepping/features
817         mov     features, edx
818         }
819 # else
820 #  error SSE_POSSIBLE set but no CPUID implementation
821 # endif
822         return features;
823 }
824 #endif
825
826 #ifdef SSE_POSSIBLE
827 qbool Sys_HaveSSE(void)
828 {
829         // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
830         if(Sys_CheckParm("-nosse"))
831                 return false;
832 #ifdef SSE_PRESENT
833         return true;
834 #else
835         // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
836         if(Sys_CheckParm("-forcesse") || Sys_CheckParm("-forcesse2"))
837                 return true;
838         if(CPUID_Features() & (1 << 25))
839                 return true;
840         return false;
841 #endif
842 }
843
844 qbool Sys_HaveSSE2(void)
845 {
846         // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
847         if(Sys_CheckParm("-nosse") || Sys_CheckParm("-nosse2"))
848                 return false;
849 #ifdef SSE2_PRESENT
850         return true;
851 #else
852         // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
853         if(Sys_CheckParm("-forcesse2"))
854                 return true;
855         if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
856                 return true;
857         return false;
858 #endif
859 }
860 #endif
861
862 /// called to set process priority for dedicated servers
863 #if defined(__linux__)
864 #include <sys/resource.h>
865 #include <errno.h>
866
867 void Sys_InitProcessNice (void)
868 {
869         struct rlimit lim;
870         sys.nicepossible = false;
871         if(Sys_CheckParm("-nonice"))
872                 return;
873         errno = 0;
874         sys.nicelevel = getpriority(PRIO_PROCESS, 0);
875         if(errno)
876         {
877                 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
878                 return;
879         }
880         if(getrlimit(RLIMIT_NICE, &lim))
881         {
882                 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
883                 return;
884         }
885         if(lim.rlim_cur != RLIM_INFINITY && sys.nicelevel < (int) (20 - lim.rlim_cur))
886         {
887                 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
888                 return;
889         }
890         sys.nicepossible = true;
891         sys.isnice = false;
892 }
893 void Sys_MakeProcessNice (void)
894 {
895         if(!sys.nicepossible)
896                 return;
897         if(sys.isnice)
898                 return;
899         Con_DPrintf("Process is becoming 'nice'...\n");
900         if(setpriority(PRIO_PROCESS, 0, 19))
901                 Con_Printf(CON_ERROR "Failed to raise nice level to %d\n", 19);
902         sys.isnice = true;
903 }
904 void Sys_MakeProcessMean (void)
905 {
906         if(!sys.nicepossible)
907                 return;
908         if(!sys.isnice)
909                 return;
910         Con_DPrintf("Process is becoming 'mean'...\n");
911         if(setpriority(PRIO_PROCESS, 0, sys.nicelevel))
912                 Con_Printf(CON_ERROR "Failed to lower nice level to %d\n", sys.nicelevel);
913         sys.isnice = false;
914 }
915 #else
916 void Sys_InitProcessNice (void)
917 {
918 }
919 void Sys_MakeProcessNice (void)
920 {
921 }
922 void Sys_MakeProcessMean (void)
923 {
924 }
925 #endif
926
927 /** Halt and try not to catch fire.
928  * Writing to any file could corrupt it,
929  * any uneccessary code could crash while we crash.
930  * No malloc() (libgcc should be loaded already) or Con_Printf() allowed here.
931  */
932 static void Sys_HandleCrash(int sig)
933 {
934 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
935         // Before doing anything else grab the stack frame addresses
936         #include <execinfo.h>
937         void *stackframes[32];
938         int framecount = backtrace(stackframes, 32);
939 #endif
940
941         // Windows doesn't have strsignal()
942         const char *sigdesc;
943         switch (sig)
944         {
945 #ifndef WIN32 // or SIGBUS
946                 case SIGBUS:  sigdesc = "Bus error"; break;
947 #endif
948                 case SIGILL:  sigdesc = "Illegal instruction"; break;
949                 case SIGABRT: sigdesc = "Aborted"; break;
950                 case SIGFPE:  sigdesc = "Floating point exception"; break;
951                 case SIGSEGV: sigdesc = "Segmentation fault"; break;
952                 default:      sigdesc = "Yo dawg, we hit a bug while hitting a bug";
953         }
954
955         // set output to blocking stderr
956         sys.outfd = fileno(stderr);
957 #ifndef WIN32
958         fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
959 #endif
960
961         fprintf(stderr, "\n\n\e[1;37;41m    Engine Crash: %s (%d)    \e[m\n", sigdesc, sig);
962 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
963         // the first two addresses will be in this function and in signal() in libc
964         backtrace_symbols_fd(stackframes + 2, framecount - 2, sys.outfd);
965 #endif
966         fprintf(stderr, "\e[1m%s\e[m\n", engineversion);
967
968         // DP8 TODO: send a disconnect message indicating we crashed, see CL_DisconnectEx()
969
970         // don't want a dead window left blocking the OS UI or the crash dialog
971         VID_Shutdown();
972         S_StopAllSounds();
973
974         Sys_SDL_Dialog("Engine Crash", sigdesc);
975
976         fflush(stderr);
977
978         exit (sig);
979 }
980
981 static void Sys_HandleSignal(int sig)
982 {
983 #ifdef WIN32
984         // Windows users will likely never see this so no point replicating strsignal()
985         Con_Printf("\nReceived signal %d, exiting...\n", sig);
986 #else
987         Con_Printf("\nReceived %s signal (%d), exiting...\n", strsignal(sig), sig);
988 #endif
989         host.state = host_shutdown;
990 }
991
992 /// SDL2 only handles SIGINT and SIGTERM by default and doesn't log anything
993 static void Sys_InitSignals(void)
994 {
995 // Windows docs say its signal() only accepts these ones
996         signal(SIGABRT, Sys_HandleCrash);
997         signal(SIGFPE,  Sys_HandleCrash);
998         signal(SIGILL,  Sys_HandleCrash);
999         signal(SIGINT,  Sys_HandleSignal);
1000         signal(SIGSEGV, Sys_HandleCrash);
1001         signal(SIGTERM, Sys_HandleSignal);
1002 #ifndef WIN32
1003         signal(SIGHUP,  Sys_HandleSignal);
1004         signal(SIGQUIT, Sys_HandleSignal);
1005         signal(SIGBUS,  Sys_HandleCrash);
1006         signal(SIGPIPE, Sys_HandleSignal);
1007 #endif
1008 }
1009
1010 int main (int argc, char **argv)
1011 {
1012         sys.argc = argc;
1013         sys.argv = (const char **)argv;
1014
1015         // COMMANDLINEOPTION: Console: -nostdout disables text output to the terminal the game was launched from
1016         // COMMANDLINEOPTION: -noterminal disables console output on stdout
1017         if(Sys_CheckParm("-noterminal") || Sys_CheckParm("-nostdout"))
1018                 sys_stdout.string = "0";
1019         // COMMANDLINEOPTION: -stderr moves console output to stderr
1020         else if(Sys_CheckParm("-stderr"))
1021                 sys_stdout.string = "2";
1022         // too early for Cvar_SetQuick
1023         sys_stdout.value = sys_stdout.integer = atoi(sys_stdout.string);
1024         Sys_UpdateOutFD_c(&sys_stdout);
1025 #ifndef WIN32
1026         fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, 0) | O_NONBLOCK);
1027         // stdout/stderr will be set to blocking in Sys_Print() if so configured, or during a fatal error.
1028         fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) | O_NONBLOCK);
1029         fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) | O_NONBLOCK);
1030 #endif
1031
1032         sys.selffd = -1;
1033         Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
1034
1035 #ifdef __ANDROID__
1036         Sys_AllowProfiling(true);
1037 #endif
1038
1039         Sys_InitSignals();
1040
1041         Host_Main();
1042
1043         Sys_Quit(0);
1044
1045         return 0;
1046 }