xref: /netbsd-src/external/bsd/less/dist/os.c (revision 0c995ed233527eb317227bbcd15c7a49f6473eac)
1*0c995ed2Ssimonb /*	$NetBSD: os.c,v 1.6 2023/10/06 07:31:30 simonb Exp $	*/
220006a0bStron 
320006a0bStron /*
4838f5788Ssimonb  * Copyright (C) 1984-2023  Mark Nudelman
520006a0bStron  *
620006a0bStron  * You may distribute under the terms of either the GNU General Public
720006a0bStron  * License or the Less License, as specified in the README file.
820006a0bStron  *
9ec18bca0Stron  * For more information, see the README file.
1020006a0bStron  */
1120006a0bStron 
1220006a0bStron 
1320006a0bStron /*
1420006a0bStron  * Operating system dependent routines.
1520006a0bStron  *
1620006a0bStron  * Most of the stuff in here is based on Unix, but an attempt
1720006a0bStron  * has been made to make things work on other operating systems.
1820006a0bStron  * This will sometimes result in a loss of functionality, unless
1920006a0bStron  * someone rewrites code specifically for the new operating system.
2020006a0bStron  *
2120006a0bStron  * The makefile provides defines to decide whether various
2220006a0bStron  * Unix features are present.
2320006a0bStron  */
2420006a0bStron 
2520006a0bStron #include "less.h"
2620006a0bStron #include <signal.h>
2720006a0bStron #include <setjmp.h>
28838f5788Ssimonb #if MSDOS_COMPILER==WIN32C
29838f5788Ssimonb #include <windows.h>
30838f5788Ssimonb #endif
3120006a0bStron #if HAVE_TIME_H
3220006a0bStron #include <time.h>
3320006a0bStron #endif
3420006a0bStron #if HAVE_ERRNO_H
3520006a0bStron #include <errno.h>
3620006a0bStron #endif
3720006a0bStron #if HAVE_VALUES_H
3820006a0bStron #include <values.h>
3920006a0bStron #endif
4020006a0bStron 
41838f5788Ssimonb #if defined(__APPLE__)
42838f5788Ssimonb #include <sys/utsname.h>
43838f5788Ssimonb #endif
44838f5788Ssimonb 
45838f5788Ssimonb #if HAVE_POLL && !MSDOS_COMPILER
46838f5788Ssimonb #define USE_POLL 1
47838f5788Ssimonb static int use_poll = TRUE;
4820006a0bStron #else
49838f5788Ssimonb #define USE_POLL 0
50838f5788Ssimonb #endif
51838f5788Ssimonb #if USE_POLL
52838f5788Ssimonb #include <poll.h>
53838f5788Ssimonb static int any_data = FALSE;
5420006a0bStron #endif
5520006a0bStron 
5620006a0bStron /*
5720006a0bStron  * BSD setjmp() saves (and longjmp() restores) the signal mask.
5820006a0bStron  * This costs a system call or two per setjmp(), so if possible we clear the
5920006a0bStron  * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead.
6020006a0bStron  * On other systems, setjmp() doesn't affect the signal mask and so
6120006a0bStron  * _setjmp() does not exist; we just use setjmp().
6220006a0bStron  */
6320006a0bStron #if HAVE__SETJMP && HAVE_SIGSETMASK
6420006a0bStron #define SET_JUMP        _setjmp
6520006a0bStron #define LONG_JUMP       _longjmp
6620006a0bStron #else
6720006a0bStron #define SET_JUMP        setjmp
6820006a0bStron #define LONG_JUMP       longjmp
6920006a0bStron #endif
7020006a0bStron 
7120006a0bStron public int reading;
72838f5788Ssimonb public int waiting_for_data;
73838f5788Ssimonb public int consecutive_nulls = 0;
7420006a0bStron 
75838f5788Ssimonb /* Milliseconds to wait for data before displaying "waiting for data" message. */
76838f5788Ssimonb static int waiting_for_data_delay = 4000;
7720006a0bStron static jmp_buf read_label;
7820006a0bStron 
7920006a0bStron extern int sigs;
80838f5788Ssimonb extern int ignore_eoi;
81838f5788Ssimonb extern int exit_F_on_close;
82838f5788Ssimonb extern int follow_mode;
83838f5788Ssimonb extern int scanning_eof;
84838f5788Ssimonb extern char intr_char;
85838f5788Ssimonb #if !MSDOS_COMPILER
86838f5788Ssimonb extern int tty;
87838f5788Ssimonb #endif
88838f5788Ssimonb #if LESSTEST
89838f5788Ssimonb extern char *ttyin_name;
90838f5788Ssimonb #endif /*LESSTEST*/
91838f5788Ssimonb 
init_poll(void)92838f5788Ssimonb public void init_poll(void)
93838f5788Ssimonb {
94838f5788Ssimonb 	char *delay = lgetenv("LESS_DATA_DELAY");
95838f5788Ssimonb 	int idelay = (delay == NULL) ? 0 : atoi(delay);
96838f5788Ssimonb 	if (idelay > 0)
97838f5788Ssimonb 		waiting_for_data_delay = idelay;
98838f5788Ssimonb #if USE_POLL
99838f5788Ssimonb #if defined(__APPLE__)
100838f5788Ssimonb 	/* In old versions of MacOS, poll() does not work with /dev/tty. */
101838f5788Ssimonb 	struct utsname uts;
102838f5788Ssimonb 	if (uname(&uts) < 0 || lstrtoi(uts.release, NULL, 10) < 20)
103838f5788Ssimonb 		use_poll = FALSE;
104838f5788Ssimonb #endif
105838f5788Ssimonb #endif
106838f5788Ssimonb }
107838f5788Ssimonb 
108838f5788Ssimonb #if USE_POLL
109838f5788Ssimonb /*
110838f5788Ssimonb  * Check whether data is available, either from a file/pipe or from the tty.
111838f5788Ssimonb  * Return READ_AGAIN if no data currently available, but caller should retry later.
112838f5788Ssimonb  * Return READ_INTR to abort F command (forw_loop).
113838f5788Ssimonb  * Return 0 if safe to read from fd.
114838f5788Ssimonb  */
check_poll(int fd,int tty)115838f5788Ssimonb static int check_poll(int fd, int tty)
116838f5788Ssimonb {
117838f5788Ssimonb 	struct pollfd poller[2] = { { fd, POLLIN, 0 }, { tty, POLLIN, 0 } };
118838f5788Ssimonb 	int timeout = (waiting_for_data && !(scanning_eof && follow_mode == FOLLOW_NAME)) ? -1 : waiting_for_data_delay;
119838f5788Ssimonb 	if (!any_data)
120838f5788Ssimonb 	{
121838f5788Ssimonb 		/*
122838f5788Ssimonb 		 * Don't do polling if no data has yet been received,
123838f5788Ssimonb 		 * to allow a program piping data into less to have temporary
124838f5788Ssimonb 		 * access to the tty (like sudo asking for a password).
125838f5788Ssimonb 		 */
126838f5788Ssimonb 		return (0);
127838f5788Ssimonb 	}
128838f5788Ssimonb 	poll(poller, 2, timeout);
129838f5788Ssimonb #if LESSTEST
130838f5788Ssimonb 	if (ttyin_name == NULL) /* Check for ^X only on a real tty. */
131838f5788Ssimonb #endif /*LESSTEST*/
132838f5788Ssimonb 	{
133838f5788Ssimonb 		if (poller[1].revents & POLLIN)
134838f5788Ssimonb 		{
135838f5788Ssimonb 			LWCHAR ch = getchr();
136838f5788Ssimonb 			if (ch == intr_char)
137838f5788Ssimonb 				/* Break out of "waiting for data". */
138838f5788Ssimonb 				return (READ_INTR);
139838f5788Ssimonb 			ungetcc_back(ch);
140838f5788Ssimonb 		}
141838f5788Ssimonb 	}
142838f5788Ssimonb 	if (ignore_eoi && exit_F_on_close && (poller[0].revents & (POLLHUP|POLLIN)) == POLLHUP)
143838f5788Ssimonb 		/* Break out of F loop on HUP due to --exit-follow-on-close. */
144838f5788Ssimonb 		return (READ_INTR);
145838f5788Ssimonb 	if ((poller[0].revents & (POLLIN|POLLHUP|POLLERR)) == 0)
146838f5788Ssimonb 		/* No data available; let caller take action, then try again. */
147838f5788Ssimonb 		return (READ_AGAIN);
148838f5788Ssimonb 	/* There is data (or HUP/ERR) available. Safe to call read() without blocking. */
149838f5788Ssimonb 	return (0);
150838f5788Ssimonb }
151838f5788Ssimonb #endif /* USE_POLL */
152838f5788Ssimonb 
supports_ctrl_x(void)153838f5788Ssimonb public int supports_ctrl_x(void)
154838f5788Ssimonb {
155838f5788Ssimonb #if USE_POLL
156838f5788Ssimonb 	return (use_poll);
157838f5788Ssimonb #else
158838f5788Ssimonb 	return (FALSE);
159838f5788Ssimonb #endif /* USE_POLL */
160838f5788Ssimonb }
16120006a0bStron 
16220006a0bStron /*
16320006a0bStron  * Like read() system call, but is deliberately interruptible.
16420006a0bStron  * A call to intread() from a signal handler will interrupt
16520006a0bStron  * any pending iread().
16620006a0bStron  */
iread(int fd,unsigned char * buf,unsigned int len)167838f5788Ssimonb public int iread(int fd, unsigned char *buf, unsigned int len)
16820006a0bStron {
169838f5788Ssimonb 	int n;
17020006a0bStron 
17120006a0bStron start:
17220006a0bStron #if MSDOS_COMPILER==WIN32C
17320006a0bStron 	if (ABORT_SIGS())
17420006a0bStron 		return (READ_INTR);
17520006a0bStron #else
17620006a0bStron #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
17720006a0bStron 	if (kbhit())
17820006a0bStron 	{
17920006a0bStron 		int c;
18020006a0bStron 
18120006a0bStron 		c = getch();
18220006a0bStron 		if (c == '\003')
18320006a0bStron 			return (READ_INTR);
18420006a0bStron 		ungetch(c);
18520006a0bStron 	}
18620006a0bStron #endif
18720006a0bStron #endif
188838f5788Ssimonb 	if (!reading && SET_JUMP(read_label))
18920006a0bStron 	{
19020006a0bStron 		/*
19120006a0bStron 		 * We jumped here from intread.
19220006a0bStron 		 */
19320006a0bStron 		reading = 0;
19420006a0bStron #if HAVE_SIGPROCMASK
19520006a0bStron 		{
19620006a0bStron 		  sigset_t mask;
19720006a0bStron 		  sigemptyset(&mask);
19820006a0bStron 		  sigprocmask(SIG_SETMASK, &mask, NULL);
19920006a0bStron 		}
20020006a0bStron #else
20120006a0bStron #if HAVE_SIGSETMASK
20220006a0bStron 		sigsetmask(0);
20320006a0bStron #else
20420006a0bStron #ifdef _OSK
20520006a0bStron 		sigmask(~0);
20620006a0bStron #endif
20720006a0bStron #endif
20820006a0bStron #endif
209838f5788Ssimonb #if !MSDOS_COMPILER
210838f5788Ssimonb 		if (fd != tty && !ABORT_SIGS())
211838f5788Ssimonb 			/* Non-interrupt signal like SIGWINCH. */
212838f5788Ssimonb 			return (READ_AGAIN);
213838f5788Ssimonb #endif
21420006a0bStron 		return (READ_INTR);
21520006a0bStron 	}
21620006a0bStron 
21720006a0bStron 	flush();
21820006a0bStron 	reading = 1;
21920006a0bStron #if MSDOS_COMPILER==DJGPPC
22020006a0bStron 	if (isatty(fd))
22120006a0bStron 	{
22220006a0bStron 		/*
22320006a0bStron 		 * Don't try reading from a TTY until a character is
22420006a0bStron 		 * available, because that makes some background programs
22520006a0bStron 		 * believe DOS is busy in a way that prevents those
22620006a0bStron 		 * programs from working while "less" waits.
227838f5788Ssimonb 		 * {{ This code was added 12 Jan 2007; still needed? }}
22820006a0bStron 		 */
22920006a0bStron 		fd_set readfds;
23020006a0bStron 
23120006a0bStron 		FD_ZERO(&readfds);
23220006a0bStron 		FD_SET(fd, &readfds);
23320006a0bStron 		if (select(fd+1, &readfds, 0, 0, 0) == -1)
234838f5788Ssimonb 		{
235838f5788Ssimonb 			reading = 0;
236838f5788Ssimonb 			return (READ_ERR);
237838f5788Ssimonb 		}
23820006a0bStron 	}
23920006a0bStron #endif
240838f5788Ssimonb #if USE_POLL
241838f5788Ssimonb 	if (fd != tty && use_poll)
242838f5788Ssimonb 	{
243838f5788Ssimonb 		int ret = check_poll(fd, tty);
244838f5788Ssimonb 		if (ret != 0)
245838f5788Ssimonb 		{
246838f5788Ssimonb 			if (ret == READ_INTR)
247838f5788Ssimonb 				sigs |= S_INTERRUPT;
248838f5788Ssimonb 			reading = 0;
249838f5788Ssimonb 			return (ret);
250838f5788Ssimonb 		}
251838f5788Ssimonb 	}
252838f5788Ssimonb #else
253838f5788Ssimonb #if MSDOS_COMPILER==WIN32C
254838f5788Ssimonb 	if (win32_kbhit())
255838f5788Ssimonb 	{
256838f5788Ssimonb 		int c;
257838f5788Ssimonb 
258838f5788Ssimonb 		c = WIN32getch();
259838f5788Ssimonb 		if (c == intr_char)
260838f5788Ssimonb 		{
261838f5788Ssimonb 			sigs |= S_INTERRUPT;
262838f5788Ssimonb 			reading = 0;
263838f5788Ssimonb 			return (READ_INTR);
264838f5788Ssimonb 		}
265838f5788Ssimonb 		WIN32ungetch(c);
266838f5788Ssimonb 	}
267838f5788Ssimonb #endif
268838f5788Ssimonb #endif
26920006a0bStron 	n = read(fd, buf, len);
270838f5788Ssimonb 	reading = 0;
27120006a0bStron #if 1
27220006a0bStron 	/*
27320006a0bStron 	 * This is a kludge to workaround a problem on some systems
27420006a0bStron 	 * where terminating a remote tty connection causes read() to
27520006a0bStron 	 * start returning 0 forever, instead of -1.
27620006a0bStron 	 */
27720006a0bStron 	{
27820006a0bStron 		if (!ignore_eoi)
27920006a0bStron 		{
28020006a0bStron 			if (n == 0)
28120006a0bStron 				consecutive_nulls++;
28220006a0bStron 			else
28320006a0bStron 				consecutive_nulls = 0;
28420006a0bStron 			if (consecutive_nulls > 20)
28520006a0bStron 				quit(QUIT_ERROR);
28620006a0bStron 		}
28720006a0bStron 	}
28820006a0bStron #endif
28920006a0bStron 	if (n < 0)
29020006a0bStron 	{
29120006a0bStron #if HAVE_ERRNO
29220006a0bStron 		/*
29320006a0bStron 		 * Certain values of errno indicate we should just retry the read.
29420006a0bStron 		 */
29520006a0bStron #if MUST_DEFINE_ERRNO
29620006a0bStron 		extern int errno;
29720006a0bStron #endif
29820006a0bStron #ifdef EINTR
29920006a0bStron 		if (errno == EINTR)
30020006a0bStron 			goto start;
30120006a0bStron #endif
30220006a0bStron #ifdef EAGAIN
30320006a0bStron 		if (errno == EAGAIN)
30420006a0bStron 			goto start;
30520006a0bStron #endif
30620006a0bStron #endif
307838f5788Ssimonb 		return (READ_ERR);
30820006a0bStron 	}
309838f5788Ssimonb #if USE_POLL
310838f5788Ssimonb 	if (fd != tty && n > 0)
311838f5788Ssimonb 		any_data = TRUE;
312838f5788Ssimonb #endif
31320006a0bStron 	return (n);
31420006a0bStron }
31520006a0bStron 
31620006a0bStron /*
31720006a0bStron  * Interrupt a pending iread().
31820006a0bStron  */
intread(void)319838f5788Ssimonb public void intread(void)
32020006a0bStron {
32120006a0bStron 	LONG_JUMP(read_label, 1);
32220006a0bStron }
32320006a0bStron 
32420006a0bStron /*
32520006a0bStron  * Return the current time.
32620006a0bStron  */
32720006a0bStron #if HAVE_TIME
get_time(void)328838f5788Ssimonb public time_type get_time(void)
32920006a0bStron {
33020006a0bStron 	time_type t;
33120006a0bStron 
33220006a0bStron 	time(&t);
33320006a0bStron 	return (t);
33420006a0bStron }
33520006a0bStron #endif
33620006a0bStron 
33720006a0bStron 
33820006a0bStron #if !HAVE_STRERROR
33920006a0bStron /*
34020006a0bStron  * Local version of strerror, if not available from the system.
34120006a0bStron  */
strerror(int err)342838f5788Ssimonb static char * strerror(int err)
34320006a0bStron {
344838f5788Ssimonb 	static char buf[INT_STRLEN_BOUND(int)+12];
34520006a0bStron #if HAVE_SYS_ERRLIST
34620006a0bStron 	extern char *sys_errlist[];
34720006a0bStron 	extern int sys_nerr;
34820006a0bStron 
34920006a0bStron 	if (err < sys_nerr)
35020006a0bStron 		return sys_errlist[err];
351838f5788Ssimonb #endif
35220006a0bStron 	sprintf(buf, "Error %d", err);
35320006a0bStron 	return buf;
35420006a0bStron }
35520006a0bStron #endif
35620006a0bStron 
35720006a0bStron /*
35820006a0bStron  * errno_message: Return an error message based on the value of "errno".
35920006a0bStron  */
errno_message(char * filename)360838f5788Ssimonb public char * errno_message(char *filename)
36120006a0bStron {
362838f5788Ssimonb 	char *p;
363838f5788Ssimonb 	char *m;
36420006a0bStron 	int len;
36520006a0bStron #if HAVE_ERRNO
36620006a0bStron #if MUST_DEFINE_ERRNO
36720006a0bStron 	extern int errno;
36820006a0bStron #endif
36920006a0bStron 	p = strerror(errno);
37020006a0bStron #else
37120006a0bStron 	p = "cannot open";
37220006a0bStron #endif
373838f5788Ssimonb 	len = (int) (strlen(filename) + strlen(p) + 3);
37420006a0bStron 	m = (char *) ecalloc(len, sizeof(char));
37520006a0bStron 	SNPRINTF2(m, len, "%s: %s", filename, p);
37620006a0bStron 	return (m);
37720006a0bStron }
37820006a0bStron 
379838f5788Ssimonb /*
380838f5788Ssimonb  * Return a description of a signal.
381838f5788Ssimonb  * The return value is good until the next call to this function.
382838f5788Ssimonb  */
signal_message(int sig)383838f5788Ssimonb public char * signal_message(int sig)
38420006a0bStron {
385838f5788Ssimonb 	static char sigbuf[sizeof("Signal ") + INT_STRLEN_BOUND(sig) + 1];
386838f5788Ssimonb #if HAVE_STRSIGNAL
387838f5788Ssimonb 	char *description = strsignal(sig);
388838f5788Ssimonb 	if (description)
389838f5788Ssimonb 		return description;
39020006a0bStron #endif
391838f5788Ssimonb 	sprintf(sigbuf, "Signal %d", sig);
392838f5788Ssimonb 	return sigbuf;
393838f5788Ssimonb }
394838f5788Ssimonb 
395838f5788Ssimonb /*
396838f5788Ssimonb  * Return (VAL * NUM) / DEN, where DEN is positive
397838f5788Ssimonb  * and min(VAL, NUM) <= DEN so the result cannot overflow.
398838f5788Ssimonb  * Round to the nearest integer, breaking ties by rounding to even.
399838f5788Ssimonb  */
muldiv(uintmax val,uintmax num,uintmax den)400838f5788Ssimonb public uintmax muldiv(uintmax val, uintmax num, uintmax den)
401838f5788Ssimonb {
402838f5788Ssimonb 	/*
403838f5788Ssimonb 	 * Like round(val * (double) num / den), but without rounding error.
404838f5788Ssimonb 	 * Overflow cannot occur, so there is no need for floating point.
405838f5788Ssimonb 	 */
406838f5788Ssimonb 	uintmax q = val / den;
407838f5788Ssimonb 	uintmax r = val % den;
408838f5788Ssimonb 	uintmax qnum = q * num;
409838f5788Ssimonb 	uintmax rnum = r * num;
410838f5788Ssimonb 	uintmax quot = qnum + rnum / den;
411838f5788Ssimonb 	uintmax rem = rnum % den;
412838f5788Ssimonb 	return quot + (den / 2 < rem + (quot & ~den & 1));
41320006a0bStron }
41420006a0bStron 
41520006a0bStron /*
41620006a0bStron  * Return the ratio of two POSITIONS, as a percentage.
41720006a0bStron  * {{ Assumes a POSITION is a long int. }}
41820006a0bStron  */
percentage(POSITION num,POSITION den)419838f5788Ssimonb public int percentage(POSITION num, POSITION den)
42020006a0bStron {
42120006a0bStron 	return (int) muldiv(num,  (POSITION) 100, den);
42220006a0bStron }
42320006a0bStron 
42420006a0bStron /*
42520006a0bStron  * Return the specified percentage of a POSITION.
426838f5788Ssimonb  * Assume (0 <= POS && 0 <= PERCENT <= 100
427838f5788Ssimonb  *	   && 0 <= FRACTION < (PERCENT == 100 ? 1 : NUM_FRAC_DENOM)),
428838f5788Ssimonb  * so the result cannot overflow.  Round to even.
42920006a0bStron  */
percent_pos(POSITION pos,int percent,long fraction)430838f5788Ssimonb public POSITION percent_pos(POSITION pos, int percent, long fraction)
43120006a0bStron {
432838f5788Ssimonb 	/*
433838f5788Ssimonb 	 * Change from percent (parts per 100)
434838f5788Ssimonb 	 * to pctden (parts per 100 * NUM_FRAC_DENOM).
435838f5788Ssimonb 	 */
436838f5788Ssimonb 	POSITION pctden = (percent * NUM_FRAC_DENOM) + fraction;
43720006a0bStron 
438838f5788Ssimonb 	return (POSITION) muldiv(pos, pctden, 100 * (POSITION) NUM_FRAC_DENOM);
43920006a0bStron }
44020006a0bStron 
44120006a0bStron #if !HAVE_STRCHR
44220006a0bStron /*
44320006a0bStron  * strchr is used by regexp.c.
44420006a0bStron  */
strchr(char * s,char c)445838f5788Ssimonb char * strchr(char *s, char c)
44620006a0bStron {
44720006a0bStron 	for ( ;  *s != '\0';  s++)
44820006a0bStron 		if (*s == c)
44920006a0bStron 			return (s);
45020006a0bStron 	if (c == '\0')
45120006a0bStron 		return (s);
45220006a0bStron 	return (NULL);
45320006a0bStron }
45420006a0bStron #endif
45520006a0bStron 
45620006a0bStron #if !HAVE_MEMCPY
memcpy(void * dst,void * src,int len)457838f5788Ssimonb void * memcpy(void *dst, void *src, int len)
45820006a0bStron {
45920006a0bStron 	char *dstp = (char *) dst;
46020006a0bStron 	char *srcp = (char *) src;
46120006a0bStron 	int i;
46220006a0bStron 
46320006a0bStron 	for (i = 0;  i < len;  i++)
46420006a0bStron 		dstp[i] = srcp[i];
46520006a0bStron 	return (dst);
46620006a0bStron }
46720006a0bStron #endif
46820006a0bStron 
46920006a0bStron #ifdef _OSK_MWC32
47020006a0bStron 
47120006a0bStron /*
47220006a0bStron  * This implements an ANSI-style intercept setup for Microware C 3.2
47320006a0bStron  */
os9_signal(int type,RETSIGTYPE (* handler)())474838f5788Ssimonb public int os9_signal(int type, RETSIGTYPE (*handler)())
47520006a0bStron {
47620006a0bStron 	intercept(handler);
47720006a0bStron }
47820006a0bStron 
47920006a0bStron #include <sgstat.h>
48020006a0bStron 
isatty(int f)481838f5788Ssimonb int isatty(int f)
48220006a0bStron {
48320006a0bStron 	struct sgbuf sgbuf;
48420006a0bStron 
48520006a0bStron 	if (_gs_opt(f, &sgbuf) < 0)
48620006a0bStron 		return -1;
48720006a0bStron 	return (sgbuf.sg_class == 0);
48820006a0bStron }
48920006a0bStron 
49020006a0bStron #endif
491838f5788Ssimonb 
sleep_ms(int ms)492838f5788Ssimonb public void sleep_ms(int ms)
493838f5788Ssimonb {
494838f5788Ssimonb #if MSDOS_COMPILER==WIN32C
495838f5788Ssimonb 	Sleep(ms);
496838f5788Ssimonb #else
497838f5788Ssimonb #if HAVE_NANOSLEEP
498838f5788Ssimonb 	int sec = ms / 1000;
499838f5788Ssimonb 	struct timespec t = { sec, (ms - sec*1000) * 1000000 };
500838f5788Ssimonb 	nanosleep(&t, NULL);
501838f5788Ssimonb #else
502838f5788Ssimonb #if HAVE_USLEEP
503838f5788Ssimonb 	usleep(ms);
504838f5788Ssimonb #else
505838f5788Ssimonb 	sleep(ms / 1000 + (ms % 1000 != 0));
506838f5788Ssimonb #endif
507838f5788Ssimonb #endif
508838f5788Ssimonb #endif
509838f5788Ssimonb }
510