1 /*
2 	sys.c
3 
4 	virtual filesystem functions
5 
6 	Copyright (C) 1996-1997  Id Software, Inc.
7 
8 	This program is free software; you can redistribute it and/or
9 	modify it under the terms of the GNU General Public License
10 	as published by the Free Software Foundation; either version 2
11 	of the License, or (at your option) any later version.
12 
13 	This program is distributed in the hope that it will be useful,
14 	but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 
17 	See the GNU General Public License for more details.
18 
19 	You should have received a copy of the GNU General Public License
20 	along with this program; if not, write to:
21 
22 		Free Software Foundation, Inc.
23 		59 Temple Place - Suite 330
24 		Boston, MA  02111-1307, USA
25 
26 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 
31 #ifdef HAVE_STRING_H
32 # include <string.h>
33 #endif
34 #ifdef HAVE_STRINGS_H
35 # include <strings.h>
36 #endif
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40 #ifdef HAVE_LIMITS_H
41 # include <limits.h>
42 #endif
43 #ifdef HAVE_CONIO_H
44 # include <conio.h>
45 #endif
46 #ifdef HAVE_IO_H
47 # include <io.h>
48 #endif
49 #ifdef HAVE_WINDOWS_H
50 # include "winquake.h"
51 # include "shlobj.h"
52 #endif
53 #ifdef HAVE_SYS_MMAN_H
54 # include <sys/mman.h>
55 #endif
56 #ifdef HAVE_SYS_SELECT_H
57 # include <sys/select.h>
58 #endif
59 #ifdef HAVE_PWD_H
60 # include <pwd.h>
61 #endif
62 
63 #include <signal.h>
64 #include <setjmp.h>
65 #include <errno.h>
66 #include <fcntl.h>
67 #include <stdarg.h>
68 #include <time.h>
69 #include <sys/stat.h>
70 #ifdef HAVE_SYS_TIME_H
71 #include <sys/time.h>
72 #endif
73 #include <sys/types.h>
74 
75 #ifdef HAVE_DIRECT_H
76 #include <direct.h>
77 #endif
78 
79 #include "qfalloca.h"
80 
81 #include "QF/cmd.h"
82 #include "QF/cvar.h"
83 #include "QF/dstring.h"
84 #include "QF/mathlib.h"
85 #include "QF/sys.h"
86 #include "QF/quakefs.h"
87 #include "QF/va.h"
88 
89 #include "compat.h"
90 
91 static void Sys_StdPrintf (const char *fmt, va_list args);
92 static void Sys_ErrPrintf (const char *fmt, va_list args);
93 
94 VISIBLE cvar_t *sys_nostdout;
95 VISIBLE cvar_t *sys_extrasleep;
96 cvar_t     *sys_dead_sleep;
97 cvar_t     *sys_sleep;
98 
99 int         sys_checksum;
100 
101 static sys_printf_t sys_std_printf_function = Sys_StdPrintf;
102 static sys_printf_t sys_err_printf_function = Sys_ErrPrintf;
103 
104 typedef struct shutdown_list_s {
105 	struct shutdown_list_s *next;
106 	void (*func)(void);
107 } shutdown_list_t;
108 
109 static shutdown_list_t *shutdown_list;
110 
111 #ifndef _WIN32
112 static int  do_stdin = 1;
113 qboolean    stdin_ready;
114 #endif
115 
116 /* The translation table between the graphical font and plain ASCII  --KB */
117 VISIBLE const char sys_char_map[256] = {
118 	'\0', '#', '#', '#', '#', '.', '#', '#',
119 	'#', 9, 10, '#', ' ', 13, '.', '.',
120 	'[', ']', '0', '1', '2', '3', '4', '5',
121 	'6', '7', '8', '9', '.', '<', '=', '>',
122 	' ', '!', '"', '#', '$', '%', '&', '\'',
123 	'(', ')', '*', '+', ',', '-', '.', '/',
124 	'0', '1', '2', '3', '4', '5', '6', '7',
125 	'8', '9', ':', ';', '<', '=', '>', '?',
126 	'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
127 	'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
128 	'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
129 	'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
130 	'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
131 	'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
132 	'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
133 	'x', 'y', 'z', '{', '|', '}', '~', '<',
134 
135 	'<', '=', '>', '#', '#', '.', '#', '#',
136 	'#', '#', ' ', '#', ' ', '>', '.', '.',
137 	'[', ']', '0', '1', '2', '3', '4', '5',
138 	'6', '7', '8', '9', '.', '<', '=', '>',
139 	' ', '!', '"', '#', '$', '%', '&', '\'',
140 	'(', ')', '*', '+', ',', '-', '.', '/',
141 	'0', '1', '2', '3', '4', '5', '6', '7',
142 	'8', '9', ':', ';', '<', '=', '>', '?',
143 	'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
144 	'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
145 	'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
146 	'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
147 	'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
148 	'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
149 	'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
150 	'x', 'y', 'z', '{', '|', '}', '~', '<'
151 };
152 
153 #define MAXPRINTMSG 4096
154 
155 
156 #ifndef USE_INTEL_ASM
157 void
Sys_MaskFPUExceptions(void)158 Sys_MaskFPUExceptions (void)
159 {
160 }
161 #endif
162 
163 int
Sys_mkdir(const char * path)164 Sys_mkdir (const char *path)
165 {
166 #ifdef HAVE_MKDIR
167 # ifdef _WIN32
168 	if (mkdir (path) == 0)
169 		return 0;
170 # else
171 	if (mkdir (path, 0777) == 0)
172 		return 0;
173 # endif
174 #else
175 # ifdef HAVE__MKDIR
176 	if (_mkdir (path) == 0)
177 		return 0;
178 # else
179 #  error do not know how to make directories
180 # endif
181 #endif
182 	if (errno != EEXIST)
183 		return -1;
184 	return 0;
185 }
186 
187 VISIBLE int
Sys_FileTime(const char * path)188 Sys_FileTime (const char *path)
189 {
190 #ifdef HAVE_ACCESS
191 	if (access (path, R_OK) == 0)
192 		return 0;
193 #else
194 # ifdef HAVE__ACCESS
195 	if (_access (path, R_OK) == 0)
196 		return 0;
197 # else
198 	int         fd;
199 
200 	if ((fd = open (path, O_RDONLY)) >= 0) {
201 		close (fd);
202 		return 0;
203 	}
204 # endif
205 #endif
206 	return -1;
207 }
208 
209 /*
210 	Sys_SetPrintf
211 
212 	for want of a better name, but it sets the function pointer for the
213 	actual implementation of Sys_Printf.
214 */
215 VISIBLE void
Sys_SetStdPrintf(sys_printf_t func)216 Sys_SetStdPrintf (sys_printf_t func)
217 {
218 	sys_std_printf_function = func;
219 }
220 
221 VISIBLE void
Sys_SetErrPrintf(sys_printf_t func)222 Sys_SetErrPrintf (sys_printf_t func)
223 {
224 	sys_err_printf_function = func;
225 }
226 
227 void
Sys_Print(FILE * stream,const char * fmt,va_list args)228 Sys_Print (FILE *stream, const char *fmt, va_list args)
229 {
230 	static dstring_t *msg;
231 	unsigned char *p;
232 
233 	if (!msg)
234 		msg = dstring_new ();
235 
236 	dvsprintf (msg, fmt, args);
237 
238 	if (stream == stderr) {
239 #ifdef _WIN32
240 		MessageBox (NULL, msg->str, "Fatal Error", 0 /* MB_OK */ );
241 #endif
242 		fputs ("Fatal Error: ", stream);
243 	}
244 
245 	/* translate to ASCII instead of printing [xx]  --KB */
246 	for (p = (unsigned char *) msg->str; *p; p++)
247 		putc (sys_char_map[*p], stream);
248 
249 	if (stream == stderr) {
250 		fputs ("\n", stream);
251 	}
252 	fflush (stream);
253 }
254 
255 static void
Sys_StdPrintf(const char * fmt,va_list args)256 Sys_StdPrintf (const char *fmt, va_list args)
257 {
258 	if (sys_nostdout && sys_nostdout->int_val)
259 		return;
260 	Sys_Print (stdout, fmt, args);
261 }
262 
263 static void
Sys_ErrPrintf(const char * fmt,va_list args)264 Sys_ErrPrintf (const char *fmt, va_list args)
265 {
266 	Sys_Print (stderr, fmt, args);
267 }
268 
269 VISIBLE void
Sys_Printf(const char * fmt,...)270 Sys_Printf (const char *fmt, ...)
271 {
272 	va_list     args;
273 	va_start (args, fmt);
274 	sys_std_printf_function (fmt, args);
275 	va_end (args);
276 }
277 
278 VISIBLE void
Sys_MaskPrintf(int mask,const char * fmt,...)279 Sys_MaskPrintf (int mask, const char *fmt, ...)
280 {
281 	va_list     args;
282 
283 	if (!developer || !(developer->int_val & mask))
284 		return;
285 	va_start (args, fmt);
286 	sys_std_printf_function (fmt, args);
287 	va_end (args);
288 }
289 
290 VISIBLE double
Sys_DoubleTime(void)291 Sys_DoubleTime (void)
292 {
293 	static qboolean first = true;
294 #ifdef _WIN32
295 # if 0
296 	static DWORD starttime;
297 	DWORD       now;
298 
299 	now = timeGetTime ();
300 
301 	if (first) {
302 		first = false;
303 		starttime = now;
304 		return 0.0;
305 	}
306 
307 	if (now < starttime)				// wrapped?
308 		return (now / 1000.0) + (LONG_MAX - starttime / 1000.0);
309 
310 	if (now - starttime == 0)
311 		return 0.0;
312 
313 	return (now - starttime) / 1000.0;
314 # else
315 	// MH's solution combining timeGetTime for stability and
316 	// QueryPerformanceCounter for precision.
317 	static __int64 qpcfreq = 0;
318 	static __int64 currqpccount = 0;
319 	static __int64 lastqpccount = 0;
320 	static double qpcfudge = 0;
321 	DWORD currtime = 0;
322 	static DWORD lasttime = 0;
323 	static DWORD starttime = 0;
324 
325 	if (first) {
326 		timeBeginPeriod (1);
327 		starttime = lasttime = timeGetTime ();
328 		QueryPerformanceFrequency ((LARGE_INTEGER *) &qpcfreq);
329 		QueryPerformanceCounter ((LARGE_INTEGER *) &lastqpccount);
330 		first = false;
331 		return 0;
332 	}
333 
334 	// get the current time from both counters
335 	currtime = timeGetTime ();
336     QueryPerformanceCounter ((LARGE_INTEGER *) &currqpccount);
337 
338 	if (currtime != lasttime)  {
339 		// requery the frequency in case it changes (which it can on multicore
340 		// machines)
341 		QueryPerformanceFrequency ((LARGE_INTEGER *) &qpcfreq);
342 
343 		// store back times and calc a fudge factor as timeGetTime can
344 		// overshoot on a sub-millisecond scale
345 		qpcfudge = (((double) (currqpccount - lastqpccount)
346 					/ (double) qpcfreq))
347 				- ((double) (currtime - lasttime) * 0.001);
348 		lastqpccount = currqpccount;
349 		lasttime = currtime;
350 	} else {
351 		qpcfudge = 0;
352 	}
353 
354 	// the final time is the base from timeGetTime plus an addition from QPC
355 	return ((double) (currtime - starttime) * 0.001)
356 		+ ((double) (currqpccount - lastqpccount) / (double) qpcfreq)
357 		+ qpcfudge;
358 # endif
359 #else
360 	struct timeval tp;
361 	struct timezone tzp;
362 	double now;
363 	static double start_time;
364 
365 	gettimeofday (&tp, &tzp);
366 
367 	now = tp.tv_sec + tp.tv_usec / 1e6;
368 
369 	if (first) {
370 		first = false;
371 		start_time = now;
372 	}
373 
374 	return now - start_time;
375 #endif
376 }
377 
378 VISIBLE void
Sys_TimeOfDay(date_t * date)379 Sys_TimeOfDay (date_t *date)
380 {
381 	struct tm  *newtime;
382 	time_t      long_time;
383 
384 	time (&long_time);
385 	newtime = localtime (&long_time);
386 
387 	date->day = newtime->tm_mday;
388 	date->mon = newtime->tm_mon;
389 	date->year = newtime->tm_year + 1900;
390 	date->hour = newtime->tm_hour;
391 	date->min = newtime->tm_min;
392 	date->sec = newtime->tm_sec;
393 	strftime (date->str, 128, "%a %b %d, %H:%M %Y", newtime);
394 }
395 
396 VISIBLE void
Sys_MakeCodeWriteable(uintptr_t startaddr,size_t length)397 Sys_MakeCodeWriteable (uintptr_t startaddr, size_t length)
398 {
399 #ifdef _WIN32
400 	DWORD       flOldProtect;
401 
402 	if (!VirtualProtect
403 		((LPVOID) startaddr, length, PAGE_READWRITE, &flOldProtect))
404 		Sys_Error ("Protection change failed");
405 #else
406 # ifdef HAVE_MPROTECT
407 	int         r;
408 	unsigned long endaddr = startaddr + length;
409 #  ifdef HAVE__SC_PAGESIZE
410 	long		psize = sysconf (_SC_PAGESIZE);
411 
412 	startaddr &= ~(psize - 1);
413 	endaddr = (endaddr + psize - 1) & ~(psize -1);
414 #  else
415 #   ifdef HAVE_GETPAGESIZE
416 	int         psize = getpagesize ();
417 
418 	startaddr &= ~(psize - 1);
419 	endaddr = (endaddr + psize - 1) & ~(psize - 1);
420 #   endif
421 #  endif
422 	// systems with mprotect but not getpagesize (or similar) probably don't
423 	// need to page align the arguments to mprotect (eg, QNX)
424 	r = mprotect ((char *) startaddr, endaddr - startaddr,
425 				  PROT_EXEC | PROT_READ | PROT_WRITE);
426 
427 	if (r < 0)
428 		Sys_Error ("Protection change failed");
429 # endif
430 #endif
431 }
432 
433 VISIBLE void
Sys_Init_Cvars(void)434 Sys_Init_Cvars (void)
435 {
436 	sys_nostdout = Cvar_Get ("sys_nostdout", "0", CVAR_NONE, NULL,
437 							 "Set to disable std out");
438 	sys_extrasleep = Cvar_Get ("sys_extrasleep", "0", CVAR_NONE, NULL,
439 							   "Set to cause whatever amount delay in "
440 							   "microseconds you want. Mostly "
441 							   "useful to generate simulated bad "
442 							   "connections.");
443 	sys_dead_sleep = Cvar_Get ("sys_dead_sleep", "0", CVAR_NONE, NULL,
444 							   "When set, the server gets NO cpu if no "
445 							   "clients are connected and there's no other "
446 							   "activity. *MIGHT* cause problems with some "
447 							   "mods.");
448 	sys_sleep = Cvar_Get ("sys_sleep", "8", CVAR_NONE, NULL, "Sleep how long "
449 						  "in seconds between checking for connections. "
450 						  "Minimum is 0, maximum is 13");
451 }
452 
453 void
Sys_Shutdown(void)454 Sys_Shutdown (void)
455 {
456 	shutdown_list_t *t;
457 
458 	while (shutdown_list) {
459 		shutdown_list->func ();
460 		t = shutdown_list;
461 		shutdown_list = shutdown_list->next;
462 		free (t);
463 	}
464 }
465 
466 VISIBLE void
Sys_Quit(void)467 Sys_Quit (void)
468 {
469 	Sys_Shutdown ();
470 
471 	exit (0);
472 }
473 
474 #if defined (HAVE_VA_COPY)
475 # define VA_COPY(a,b) va_copy (a, b)
476 #elif defined (HAVE__VA_COPY)
477 # define VA_COPY(a,b) __va_copy (a, b)
478 #else
479 # define VA_COPY(a,b) memcpy (a, b, sizeof (a))
480 #endif
481 
482 VISIBLE void
Sys_Error(const char * error,...)483 Sys_Error (const char *error, ...)
484 {
485 	va_list     args;
486 #ifdef VA_LIST_IS_ARRAY
487 	va_list     tmp_args;
488 #endif
489 	static int  in_sys_error = 0;
490 
491 	if (in_sys_error) {
492 		ssize_t     cnt;
493 		const char *msg = "\nSys_Error: recursive error condition\n";
494 		cnt = write (2, msg, strlen (msg));
495 		if (cnt < 0)
496 			perror ("write failed");
497 		abort ();
498 	}
499 	in_sys_error = 1;
500 
501 	va_start (args, error);
502 #ifdef VA_LIST_IS_ARRAY
503 	VA_COPY (tmp_args, args);
504 #endif
505 	sys_err_printf_function (error, args);
506 	va_end (args);
507 
508 	Sys_Shutdown ();
509 
510 	if (sys_err_printf_function != Sys_ErrPrintf) {
511 		// print the message again using the default error printer to increase
512 		// the chances of the error being seen.
513 #ifdef VA_LIST_IS_ARRAY
514 		VA_COPY (args, tmp_args);
515 #endif
516 		Sys_ErrPrintf (error, args);
517 	}
518 
519 	exit (1);
520 }
521 
522 VISIBLE void
Sys_RegisterShutdown(void (* func)(void))523 Sys_RegisterShutdown (void (*func) (void))
524 {
525 	shutdown_list_t *p;
526 	if (!func)
527 		return;
528 	p = malloc (sizeof (*p));
529 	if (!p)
530 		Sys_Error ("Sys_RegisterShutdown: insufficient memory");
531 	p->func = func;
532 	p->next = shutdown_list;
533 	shutdown_list = p;
534 }
535 
536 VISIBLE int
Sys_TimeID(void)537 Sys_TimeID (void) //FIXME I need a new name, one that doesn't make me feel 3 feet thick
538 {
539 	int val;
540 #ifdef HAVE_GETUID
541 	val = ((int) (getpid () + getuid () * 1000) * time (NULL)) & 0xffff;
542 #else
543 	val = ((int) (Sys_DoubleTime () * 1000) * time (NULL)) & 0xffff;
544 #endif
545 	return val;
546 }
547 
548 VISIBLE void
Sys_PageIn(void * ptr,int size)549 Sys_PageIn (void *ptr, int size)
550 {
551 //may or may not be useful in linux #ifdef _WIN32
552 	byte       *x;
553 	int         m, n;
554 
555 	// touch all the memory to make sure it's there. The 16-page skip is to
556 	// keep Win 95 from thinking we're trying to page ourselves in (we are
557 	// doing that, of course, but there's no reason we shouldn't)
558 	x = (byte *) ptr;
559 
560 	for (n = 0; n < 4; n++) {
561 		for (m = 0; m < (size - 16 * 0x1000); m += 4) {
562 			sys_checksum += *(int *) &x[m];
563 			sys_checksum += *(int *) &x[m + 16 * 0x1000];
564 		}
565 	}
566 //#endif
567 }
568 
569 VISIBLE void
Sys_DebugLog(const char * file,const char * fmt,...)570 Sys_DebugLog (const char *file, const char *fmt, ...)
571 {
572 	va_list     args;
573 	static dstring_t *data;
574 	int         fd;
575 
576 	if (!data)
577 		data = dstring_newstr ();
578 
579 	va_start (args, fmt);
580 	dvsprintf (data, fmt, args);
581 	va_end (args);
582 	if ((fd = open (file, O_WRONLY | O_CREAT | O_APPEND, 0644)) >= 0) {
583 		if (write (fd, data->str, data->size - 1) != (ssize_t) (data->size - 1))
584 			Sys_Printf ("Error writing %s: %s\n", file, strerror(errno));
585 		close (fd);
586 	}
587 }
588 
589 VISIBLE int
Sys_CheckInput(int idle,int net_socket)590 Sys_CheckInput (int idle, int net_socket)
591 {
592 	fd_set      fdset;
593 	int         res;
594 	struct timeval _timeout;
595 	struct timeval *timeout = 0;
596 
597 #ifdef _WIN32
598 	int         sleep_msec;
599 	// Now we want to give some processing time to other applications,
600 	// such as qw_client, running on this machine.
601 	sleep_msec = sys_sleep->int_val;
602 	if (sleep_msec > 0) {
603 		if (sleep_msec > 13)
604 			sleep_msec = 13;
605 		Sleep (sleep_msec);
606 	}
607 
608 	_timeout.tv_sec = 0;
609 	_timeout.tv_usec = net_socket < 0 ? 0 : 20;
610 #else
611 	_timeout.tv_sec = 0;
612 	_timeout.tv_usec = net_socket < 0 ? 0 : 2000;
613 #endif
614 	// select on the net socket and stdin
615 	// the only reason we have a timeout at all is so that if the last
616 	// connected client times out, the message would not otherwise
617 	// be printed until the next event.
618 	FD_ZERO (&fdset);
619 #ifndef _WIN32
620 	if (do_stdin)
621 		FD_SET (0, &fdset);
622 #endif
623 	if (net_socket >= 0)
624 		FD_SET (((unsigned) net_socket), &fdset);	// cast needed for windows
625 
626 	if (!idle || !sys_dead_sleep->int_val)
627 		timeout = &_timeout;
628 
629 	res = select (max (net_socket, 0) + 1, &fdset, NULL, NULL, timeout);
630 	if (res == 0 || res == -1)
631 		return 0;
632 #ifndef _WIN32
633 	stdin_ready = FD_ISSET (0, &fdset);
634 #endif
635 	return 1;
636 }
637 
638 /*
639 	Sys_ConsoleInput
640 
641 	Checks for a complete line of text typed in at the console, then forwards
642 	it to the host command processor
643 */
644 VISIBLE const char *
Sys_ConsoleInput(void)645 Sys_ConsoleInput (void)
646 {
647 	static char text[256];
648 	static int len = 0;
649 
650 #ifdef _WIN32
651 	int         c;
652 
653 	// read a line out
654 	while (_kbhit ()) {
655 		c = _getch ();
656 		putch (c);
657 		if (c == '\r') {
658 			text[len] = 0;
659 			putch ('\n');
660 			len = 0;
661 			return text;
662 		}
663 		if (c == 8) {
664 			if (len) {
665 				putch (' ');
666 				putch (c);
667 				len--;
668 				text[len] = 0;
669 			}
670 			continue;
671 		}
672 		text[len] = c;
673 		len++;
674 		if (len < (int) sizeof (text))
675 			text[len] = 0;
676 		else {
677 		// buffer is full
678 			len = 0;
679 			text[sizeof (text) - 1] = 0;
680 			return text;
681 		}
682 	}
683 
684 	return NULL;
685 #else
686 	if (!stdin_ready || !do_stdin)
687 		return NULL;					// the select didn't say it was ready
688 	stdin_ready = false;
689 
690 	len = read (0, text, sizeof (text));
691 	if (len == 0) {
692 		// end of file
693 		do_stdin = 0;
694 		return NULL;
695 	}
696 	if (len < 1)
697 		return NULL;
698 	text[len - 1] = 0;					// rip off the \n and terminate
699 
700 	return text;
701 #endif
702 }
703 
704 static jmp_buf  aiee_abort;
705 
706 typedef struct sh_stack_s {
707 	struct sh_stack_s *next;
708 	int (*signal_hook)(int,void*);
709 	void *data;
710 } sh_stack_t;
711 
712 static sh_stack_t *sh_stack;
713 static sh_stack_t *free_sh;
714 static int (*signal_hook)(int,void*);
715 static void *signal_hook_data;
716 
717 VISIBLE void
Sys_PushSignalHook(int (* hook)(int,void *),void * data)718 Sys_PushSignalHook (int (*hook)(int, void *), void *data)
719 {
720 	sh_stack_t *s;
721 
722 	if (free_sh) {
723 		s = free_sh;
724 	} else {
725 		s = malloc (sizeof (sh_stack_t));
726 		s->next = 0;
727 	}
728 	s->signal_hook = signal_hook;
729 	s->data = signal_hook_data;
730 	signal_hook = hook;
731 	signal_hook_data = data;
732 
733 	free_sh = s->next;
734 	s->next = sh_stack;
735 	sh_stack = s;
736 }
737 
738 VISIBLE void
Sys_PopSignalHook(void)739 Sys_PopSignalHook (void)
740 {
741 	if (sh_stack) {
742 		sh_stack_t *s;
743 
744 		signal_hook = sh_stack->signal_hook;
745 		signal_hook_data = sh_stack->data;
746 
747 		s = sh_stack->next;
748 		sh_stack->next = free_sh;
749 		free_sh = sh_stack;
750 		sh_stack = s;
751 	}
752 }
753 
754 static void
aiee(int sig)755 aiee (int sig)
756 {
757 	printf ("AIEE, signal %d in shutdown code, giving up\n", sig);
758 	longjmp (aiee_abort, 1);
759 }
760 
761 static void
signal_handler(int sig)762 signal_handler (int sig)
763 {
764 	int volatile recover = 0;	// volatile for longjump
765 
766 	printf ("Received signal %d, exiting...\n", sig);
767 
768 	switch (sig) {
769 		case SIGINT:
770 		case SIGTERM:
771 #ifndef _WIN32
772 		case SIGHUP:
773 			signal (SIGHUP, SIG_DFL);
774 #endif
775 			signal (SIGINT, SIG_DFL);
776 			signal (SIGTERM, SIG_DFL);
777 			Sys_Quit ();
778 		default:
779 #ifndef _WIN32
780 			signal (SIGQUIT, aiee);
781 			signal (SIGTRAP, aiee);
782 			signal (SIGIOT, aiee);
783 			signal (SIGBUS, aiee);
784 #endif
785 			signal (SIGILL, aiee);
786 			signal (SIGSEGV, aiee);
787 			signal (SIGFPE, aiee);
788 
789 			if (!setjmp (aiee_abort)) {
790 				if (signal_hook)
791 					recover = signal_hook (sig, signal_hook_data);
792 				Sys_Shutdown ();
793 			}
794 
795 			if (recover) {
796 #ifndef _WIN32
797 				signal (SIGQUIT, signal_handler);
798 				signal (SIGTRAP, signal_handler);
799 				signal (SIGIOT, signal_handler);
800 				signal (SIGBUS, signal_handler);
801 #endif
802 				signal (SIGILL, signal_handler);
803 				signal (SIGSEGV, signal_handler);
804 				signal (SIGFPE, signal_handler);
805 			} else {
806 #ifndef _WIN32
807 				signal (SIGQUIT, SIG_DFL);
808 				signal (SIGTRAP, SIG_DFL);
809 				signal (SIGIOT, SIG_DFL);
810 				signal (SIGBUS, SIG_DFL);
811 #endif
812 				signal (SIGILL, SIG_DFL);
813 				signal (SIGSEGV, SIG_DFL);
814 				signal (SIGFPE, SIG_DFL);
815 			}
816 	}
817 }
818 
819 VISIBLE void
Sys_Init(void)820 Sys_Init (void)
821 {
822 	// catch signals
823 #ifndef _WIN32
824 	signal (SIGHUP, signal_handler);
825 	signal (SIGQUIT, signal_handler);
826 	signal (SIGTRAP, signal_handler);
827 	signal (SIGIOT, signal_handler);
828 	signal (SIGBUS, signal_handler);
829 #endif
830 	signal (SIGINT, signal_handler);
831 	signal (SIGILL, signal_handler);
832 	signal (SIGSEGV, signal_handler);
833 	signal (SIGTERM, signal_handler);
834 	signal (SIGFPE, signal_handler);
835 
836 	Cvar_Init_Hash ();
837 	Cmd_Init_Hash ();
838 	Cvar_Init ();
839 	Sys_Init_Cvars ();
840 
841 	Cmd_Init ();
842 }
843 
844 VISIBLE int
Sys_CreatePath(const char * path)845 Sys_CreatePath (const char *path)
846 {
847 	char       *ofs;
848 	char       *e_path = alloca (strlen (path) + 1);
849 
850 	strcpy (e_path, path);
851 	for (ofs = e_path + 1; *ofs; ofs++) {
852 		if (*ofs == '/') {				// create the directory
853 			*ofs = 0;
854 			if (Sys_mkdir (e_path) == -1)
855 				return -1;
856 			*ofs = '/';
857 		}
858 	}
859 	return 0;
860 }
861 
862 char *
Sys_ExpandSquiggle(const char * path)863 Sys_ExpandSquiggle (const char *path)
864 {
865 	const char *home = 0;
866 #ifdef _WIN32
867 	char        userpath[MAX_PATH];	// sigh, why can't windows give the size?
868 #else
869 # ifdef HAVE_GETUID
870 	struct passwd *pwd_ent;
871 # endif
872 #endif
873 
874 	if (strncmp (path, "~/", 2) != 0) {
875 		return strdup (path);
876 	}
877 
878 #ifdef _WIN32
879 	if (SHGetFolderPathA (0, CSIDL_LOCAL_APPDATA, 0, 0, userpath) == S_OK) {
880 		home = userpath;
881 	}
882 	// LordHavoc: first check HOME to duplicate previous version behavior
883 	// (also handy if someone wants it elsewhere than their windows directory)
884 	if (!home)
885 		home = getenv ("HOME");
886 	if (!home || !home[0])
887 		home = getenv ("USERPROFILE");
888 	if (!home || !home[0])
889 		home = 0;
890 #else
891 # ifdef HAVE_GETUID
892 	if ((pwd_ent = getpwuid (getuid ()))) {
893 		home = pwd_ent->pw_dir;
894 	} else
895 		home = getenv ("HOME");
896 # endif
897 #endif
898 
899 	if (!home)
900 		home = ".";
901 	return nva ("%s%s", home, path + 1);	// skip leading ~
902 }
903