]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sys_shared.c
sys: optimise printing to stdout
[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                 dp_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                         dp_strlcpy(temp, path, sizeof(temp));
222                         dp_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, size_t textlen)
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, textlen);
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 void Sys_Printf(const char *fmt, ...)
636 {
637         va_list argptr;
638         char msg[MAX_INPUTLINE];
639         int msglen;
640
641         va_start(argptr,fmt);
642         msglen = dpvsnprintf(msg, sizeof(msg), fmt, argptr);
643         va_end(argptr);
644
645         if (msglen >= 0)
646                 Sys_Print(msg, msglen);
647 }
648
649 /// Reads a line from POSIX stdin or the Windows console
650 char *Sys_ConsoleInput(void)
651 {
652         static char text[MAX_INPUTLINE];
653 #ifdef WIN32
654         static unsigned int len = 0;
655         int c;
656
657         // read a line out
658         while (_kbhit ())
659         {
660                 c = _getch ();
661                 if (c == '\r')
662                 {
663                         text[len] = '\0';
664                         _putch ('\n');
665                         len = 0;
666                         return text;
667                 }
668                 if (c == '\b')
669                 {
670                         if (len)
671                         {
672                                 _putch (c);
673                                 _putch (' ');
674                                 _putch (c);
675                                 len--;
676                         }
677                         continue;
678                 }
679                 if (len < sizeof (text) - 1)
680                 {
681                         _putch (c);
682                         text[len] = c;
683                         len++;
684                 }
685         }
686 #else
687         fd_set fdset;
688         struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 };
689
690         FD_ZERO(&fdset);
691         FD_SET(fileno(stdin), &fdset);
692         if (select(1, &fdset, NULL, NULL, &timeout) != -1 && FD_ISSET(fileno(stdin), &fdset))
693                 return fgets(text, sizeof(text), stdin);
694 #endif
695         return NULL;
696 }
697
698
699 /*
700 ===============================================================================
701
702 Startup and Shutdown
703
704 ===============================================================================
705 */
706
707 void Sys_Error (const char *error, ...)
708 {
709         va_list argptr;
710         char string[MAX_INPUTLINE];
711
712         // set output to blocking stderr
713         sys.outfd = fileno(stderr);
714 #ifndef WIN32
715         fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
716 #endif
717
718         va_start (argptr,error);
719         dpvsnprintf (string, sizeof (string), error, argptr);
720         va_end (argptr);
721
722         Con_Printf(CON_ERROR "Engine Error: %s\n", string);
723
724         // don't want a dead window left blocking the OS UI or the crash dialog
725         Host_Shutdown();
726
727         Sys_SDL_Dialog("Engine Error", string);
728
729         fflush(stderr);
730
731         exit (1);
732 }
733
734 #ifndef WIN32
735 static const char *Sys_FindInPATH(const char *name, char namesep, const char *PATH, char pathsep, char *buf, size_t bufsize)
736 {
737         const char *p = PATH;
738         const char *q;
739         if(p && name)
740         {
741                 while((q = strchr(p, ':')))
742                 {
743                         dpsnprintf(buf, bufsize, "%.*s%c%s", (int)(q-p), p, namesep, name);
744                         if(FS_SysFileExists(buf))
745                                 return buf;
746                         p = q + 1;
747                 }
748                 if(!q) // none found - try the last item
749                 {
750                         dpsnprintf(buf, bufsize, "%s%c%s", p, namesep, name);
751                         if(FS_SysFileExists(buf))
752                                 return buf;
753                 }
754         }
755         return name;
756 }
757 #endif
758
759 static const char *Sys_FindExecutableName(void)
760 {
761 #if defined(WIN32)
762         return sys.argv[0];
763 #else
764         static char exenamebuf[MAX_OSPATH+1];
765         ssize_t n = -1;
766 #if defined(__FreeBSD__)
767         int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
768         size_t exenamebuflen = sizeof(exenamebuf)-1;
769         if (sysctl(mib, 4, exenamebuf, &exenamebuflen, NULL, 0) == 0)
770         {
771                 n = exenamebuflen;
772         }
773 #elif defined(__linux__)
774         n = readlink("/proc/self/exe", exenamebuf, sizeof(exenamebuf)-1);
775 #endif
776         if(n > 0 && (size_t)(n) < sizeof(exenamebuf))
777         {
778                 exenamebuf[n] = 0;
779                 return exenamebuf;
780         }
781         if(strchr(sys.argv[0], '/'))
782                 return sys.argv[0]; // possibly a relative path
783         else
784                 return Sys_FindInPATH(sys.argv[0], '/', getenv("PATH"), ':', exenamebuf, sizeof(exenamebuf));
785 #endif
786 }
787
788 void Sys_ProvideSelfFD(void)
789 {
790         if(sys.selffd != -1)
791                 return;
792         sys.selffd = FS_SysOpenFD(Sys_FindExecutableName(), "rb", false);
793 }
794
795 // for x86 cpus only...  (x64 has SSE2_PRESENT)
796 #if defined(SSE_POSSIBLE) && !defined(SSE2_PRESENT)
797 // code from SDL, shortened as we can expect CPUID to work
798 static int CPUID_Features(void)
799 {
800         int features = 0;
801 # if (defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)) && defined(__i386__)
802         __asm__ (
803 "        movl    %%ebx,%%edi\n"
804 "        xorl    %%eax,%%eax                                           \n"
805 "        incl    %%eax                                                 \n"
806 "        cpuid                       # Get family/model/stepping/features\n"
807 "        movl    %%edx,%0                                              \n"
808 "        movl    %%edi,%%ebx\n"
809         : "=m" (features)
810         :
811         : "%eax", "%ecx", "%edx", "%edi"
812         );
813 # elif (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
814         __asm {
815         xor     eax, eax
816         inc     eax
817         cpuid                       ; Get family/model/stepping/features
818         mov     features, edx
819         }
820 # else
821 #  error SSE_POSSIBLE set but no CPUID implementation
822 # endif
823         return features;
824 }
825 #endif
826
827 #ifdef SSE_POSSIBLE
828 qbool Sys_HaveSSE(void)
829 {
830         // COMMANDLINEOPTION: SSE: -nosse disables SSE support and detection
831         if(Sys_CheckParm("-nosse"))
832                 return false;
833 #ifdef SSE_PRESENT
834         return true;
835 #else
836         // COMMANDLINEOPTION: SSE: -forcesse enables SSE support and disables detection
837         if(Sys_CheckParm("-forcesse") || Sys_CheckParm("-forcesse2"))
838                 return true;
839         if(CPUID_Features() & (1 << 25))
840                 return true;
841         return false;
842 #endif
843 }
844
845 qbool Sys_HaveSSE2(void)
846 {
847         // COMMANDLINEOPTION: SSE2: -nosse2 disables SSE2 support and detection
848         if(Sys_CheckParm("-nosse") || Sys_CheckParm("-nosse2"))
849                 return false;
850 #ifdef SSE2_PRESENT
851         return true;
852 #else
853         // COMMANDLINEOPTION: SSE2: -forcesse2 enables SSE2 support and disables detection
854         if(Sys_CheckParm("-forcesse2"))
855                 return true;
856         if((CPUID_Features() & (3 << 25)) == (3 << 25)) // SSE is 1<<25, SSE2 is 1<<26
857                 return true;
858         return false;
859 #endif
860 }
861 #endif
862
863 /// called to set process priority for dedicated servers
864 #if defined(__linux__)
865 #include <sys/resource.h>
866 #include <errno.h>
867
868 void Sys_InitProcessNice (void)
869 {
870         struct rlimit lim;
871         sys.nicepossible = false;
872         if(Sys_CheckParm("-nonice"))
873                 return;
874         errno = 0;
875         sys.nicelevel = getpriority(PRIO_PROCESS, 0);
876         if(errno)
877         {
878                 Con_Printf("Kernel does not support reading process priority - cannot use niceness\n");
879                 return;
880         }
881         if(getrlimit(RLIMIT_NICE, &lim))
882         {
883                 Con_Printf("Kernel does not support lowering nice level again - cannot use niceness\n");
884                 return;
885         }
886         if(lim.rlim_cur != RLIM_INFINITY && sys.nicelevel < (int) (20 - lim.rlim_cur))
887         {
888                 Con_Printf("Current nice level is below the soft limit - cannot use niceness\n");
889                 return;
890         }
891         sys.nicepossible = true;
892         sys.isnice = false;
893 }
894 void Sys_MakeProcessNice (void)
895 {
896         if(!sys.nicepossible)
897                 return;
898         if(sys.isnice)
899                 return;
900         Con_DPrintf("Process is becoming 'nice'...\n");
901         if(setpriority(PRIO_PROCESS, 0, 19))
902                 Con_Printf(CON_ERROR "Failed to raise nice level to %d\n", 19);
903         sys.isnice = true;
904 }
905 void Sys_MakeProcessMean (void)
906 {
907         if(!sys.nicepossible)
908                 return;
909         if(!sys.isnice)
910                 return;
911         Con_DPrintf("Process is becoming 'mean'...\n");
912         if(setpriority(PRIO_PROCESS, 0, sys.nicelevel))
913                 Con_Printf(CON_ERROR "Failed to lower nice level to %d\n", sys.nicelevel);
914         sys.isnice = false;
915 }
916 #else
917 void Sys_InitProcessNice (void)
918 {
919 }
920 void Sys_MakeProcessNice (void)
921 {
922 }
923 void Sys_MakeProcessMean (void)
924 {
925 }
926 #endif
927
928 /** Halt and try not to catch fire.
929  * Writing to any file could corrupt it,
930  * any uneccessary code could crash while we crash.
931  * No malloc() (libgcc should be loaded already) or Con_Printf() allowed here.
932  */
933 static void Sys_HandleCrash(int sig)
934 {
935 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
936         // Before doing anything else grab the stack frame addresses
937         #include <execinfo.h>
938         void *stackframes[32];
939         int framecount = backtrace(stackframes, 32);
940 #endif
941
942         // Windows doesn't have strsignal()
943         const char *sigdesc;
944         switch (sig)
945         {
946 #ifndef WIN32 // or SIGBUS
947                 case SIGBUS:  sigdesc = "Bus error"; break;
948 #endif
949                 case SIGILL:  sigdesc = "Illegal instruction"; break;
950                 case SIGABRT: sigdesc = "Aborted"; break;
951                 case SIGFPE:  sigdesc = "Floating point exception"; break;
952                 case SIGSEGV: sigdesc = "Segmentation fault"; break;
953                 default:      sigdesc = "Yo dawg, we hit a bug while hitting a bug";
954         }
955
956         // set output to blocking stderr
957         sys.outfd = fileno(stderr);
958 #ifndef WIN32
959         fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
960 #endif
961
962         fprintf(stderr, "\n\n\e[1;37;41m    Engine Crash: %s (%d)    \e[m\n", sigdesc, sig);
963 #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
964         // the first two addresses will be in this function and in signal() in libc
965         backtrace_symbols_fd(stackframes + 2, framecount - 2, sys.outfd);
966 #endif
967         fprintf(stderr, "\e[1m%s\e[m\n", engineversion);
968
969         // DP8 TODO: send a disconnect message indicating we crashed, see CL_DisconnectEx()
970
971         // don't want a dead window left blocking the OS UI or the crash dialog
972         VID_Shutdown();
973         S_StopAllSounds();
974
975         Sys_SDL_Dialog("Engine Crash", sigdesc);
976
977         fflush(stderr);
978
979         exit (sig);
980 }
981
982 static void Sys_HandleSignal(int sig)
983 {
984 #ifdef WIN32
985         // Windows users will likely never see this so no point replicating strsignal()
986         Con_Printf("\nReceived signal %d, exiting...\n", sig);
987 #else
988         Con_Printf("\nReceived %s signal (%d), exiting...\n", strsignal(sig), sig);
989 #endif
990         host.state = host_shutdown;
991 }
992
993 /// SDL2 only handles SIGINT and SIGTERM by default and doesn't log anything
994 static void Sys_InitSignals(void)
995 {
996 // Windows docs say its signal() only accepts these ones
997         signal(SIGABRT, Sys_HandleCrash);
998         signal(SIGFPE,  Sys_HandleCrash);
999         signal(SIGILL,  Sys_HandleCrash);
1000         signal(SIGINT,  Sys_HandleSignal);
1001         signal(SIGSEGV, Sys_HandleCrash);
1002         signal(SIGTERM, Sys_HandleSignal);
1003 #ifndef WIN32
1004         signal(SIGHUP,  Sys_HandleSignal);
1005         signal(SIGQUIT, Sys_HandleSignal);
1006         signal(SIGBUS,  Sys_HandleCrash);
1007         signal(SIGPIPE, Sys_HandleSignal);
1008 #endif
1009 }
1010
1011 int main (int argc, char **argv)
1012 {
1013         sys.argc = argc;
1014         sys.argv = (const char **)argv;
1015
1016         // COMMANDLINEOPTION: Console: -nostdout disables text output to the terminal the game was launched from
1017         // COMMANDLINEOPTION: -noterminal disables console output on stdout
1018         if(Sys_CheckParm("-noterminal") || Sys_CheckParm("-nostdout"))
1019                 sys_stdout.string = "0";
1020         // COMMANDLINEOPTION: -stderr moves console output to stderr
1021         else if(Sys_CheckParm("-stderr"))
1022                 sys_stdout.string = "2";
1023         // too early for Cvar_SetQuick
1024         sys_stdout.value = sys_stdout.integer = atoi(sys_stdout.string);
1025         Sys_UpdateOutFD_c(&sys_stdout);
1026 #ifndef WIN32
1027         fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, 0) | O_NONBLOCK);
1028         // stdout/stderr will be set to blocking in Sys_Print() if so configured, or during a fatal error.
1029         fcntl(fileno(stdout), F_SETFL, fcntl(fileno(stdout), F_GETFL, 0) | O_NONBLOCK);
1030         fcntl(fileno(stderr), F_SETFL, fcntl(fileno(stderr), F_GETFL, 0) | O_NONBLOCK);
1031 #endif
1032
1033         sys.selffd = -1;
1034         Sys_ProvideSelfFD(); // may call Con_Printf() so must be after sys.outfd is set
1035
1036 #ifdef __ANDROID__
1037         Sys_AllowProfiling(true);
1038 #endif
1039
1040         Sys_InitSignals();
1041
1042         Host_Main();
1043
1044         Sys_Quit(0);
1045
1046         return 0;
1047 }