1238730Sdelphij/*
2369759Sgit2svn * Copyright (C) 1984-2021  Mark Nudelman
3238730Sdelphij *
4238730Sdelphij * You may distribute under the terms of either the GNU General Public
5238730Sdelphij * License or the Less License, as specified in the README file.
6238730Sdelphij *
7238730Sdelphij * For more information, see the README file.
8238730Sdelphij */
960786Sps
1060786Sps
1160786Sps/*
1260786Sps * Operating system dependent routines.
1360786Sps *
1460786Sps * Most of the stuff in here is based on Unix, but an attempt
1560786Sps * has been made to make things work on other operating systems.
1660786Sps * This will sometimes result in a loss of functionality, unless
1760786Sps * someone rewrites code specifically for the new operating system.
1860786Sps *
1960786Sps * The makefile provides defines to decide whether various
2060786Sps * Unix features are present.
2160786Sps */
2260786Sps
2360786Sps#include "less.h"
2460786Sps#include <signal.h>
2560786Sps#include <setjmp.h>
26369759Sgit2svn#if MSDOS_COMPILER==WIN32C
27369759Sgit2svn#include <windows.h>
28369759Sgit2svn#endif
2960786Sps#if HAVE_TIME_H
3060786Sps#include <time.h>
3160786Sps#endif
3260786Sps#if HAVE_ERRNO_H
3360786Sps#include <errno.h>
3460786Sps#endif
3560786Sps#if HAVE_VALUES_H
3660786Sps#include <values.h>
3760786Sps#endif
3860786Sps
39369759Sgit2svn#if HAVE_POLL && !MSDOS_COMPILER && !defined(__APPLE__)
40369759Sgit2svn#define USE_POLL 1
41369759Sgit2svn#else
42369759Sgit2svn#define USE_POLL 0
43369759Sgit2svn#endif
44369759Sgit2svn#if USE_POLL
45369759Sgit2svn#include <poll.h>
46369759Sgit2svn#endif
47369759Sgit2svn
4860786Sps/*
4960786Sps * BSD setjmp() saves (and longjmp() restores) the signal mask.
5060786Sps * This costs a system call or two per setjmp(), so if possible we clear the
5160786Sps * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead.
5260786Sps * On other systems, setjmp() doesn't affect the signal mask and so
5360786Sps * _setjmp() does not exist; we just use setjmp().
5460786Sps */
5560786Sps#if HAVE__SETJMP && HAVE_SIGSETMASK
56369759Sgit2svn#define SET_JUMP        _setjmp
57369759Sgit2svn#define LONG_JUMP       _longjmp
5860786Sps#else
59369759Sgit2svn#define SET_JUMP        setjmp
60369759Sgit2svn#define LONG_JUMP       longjmp
6160786Sps#endif
6260786Sps
6360786Spspublic int reading;
6460786Sps
6560786Spsstatic jmp_buf read_label;
6660786Sps
6760786Spsextern int sigs;
68369759Sgit2svnextern int ignore_eoi;
69369759Sgit2svn#if !MSDOS_COMPILER
70369759Sgit2svnextern int tty;
71369759Sgit2svn#endif
7260786Sps
73369759Sgit2svn#if USE_POLL
7460786Sps/*
75369759Sgit2svn * Return true if one of the events has occurred on the specified file.
76369759Sgit2svn */
77369759Sgit2svn	static int
78369759Sgit2svnpoll_events(fd, events)
79369759Sgit2svn	int fd;
80369759Sgit2svn	int events;
81369759Sgit2svn{
82369759Sgit2svn	struct pollfd poller = { fd, events, 0 };
83369759Sgit2svn	int n = poll(&poller, 1, 0);
84369759Sgit2svn	if (n <= 0)
85369759Sgit2svn		return 0;
86369759Sgit2svn	return (poller.revents & events);
87369759Sgit2svn}
88369759Sgit2svn#endif
89369759Sgit2svn
90369759Sgit2svn/*
9160786Sps * Like read() system call, but is deliberately interruptible.
9260786Sps * A call to intread() from a signal handler will interrupt
9360786Sps * any pending iread().
9460786Sps */
9560786Sps	public int
9660786Spsiread(fd, buf, len)
9760786Sps	int fd;
98330570Sdelphij	unsigned char *buf;
9960786Sps	unsigned int len;
10060786Sps{
101330570Sdelphij	int n;
10260786Sps
103170256Sdelphijstart:
10460786Sps#if MSDOS_COMPILER==WIN32C
10560786Sps	if (ABORT_SIGS())
10660786Sps		return (READ_INTR);
10760786Sps#else
10860786Sps#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
10960786Sps	if (kbhit())
11060786Sps	{
11160786Sps		int c;
11260786Sps
11360786Sps		c = getch();
11460786Sps		if (c == '\003')
11560786Sps			return (READ_INTR);
11660786Sps		ungetch(c);
11760786Sps	}
11860786Sps#endif
11960786Sps#endif
12060786Sps	if (SET_JUMP(read_label))
12160786Sps	{
12260786Sps		/*
12360786Sps		 * We jumped here from intread.
12460786Sps		 */
12560786Sps		reading = 0;
12663128Sps#if HAVE_SIGPROCMASK
12763128Sps		{
12863128Sps		  sigset_t mask;
12963128Sps		  sigemptyset(&mask);
13063128Sps		  sigprocmask(SIG_SETMASK, &mask, NULL);
13163128Sps		}
13263128Sps#else
13360786Sps#if HAVE_SIGSETMASK
13460786Sps		sigsetmask(0);
13560786Sps#else
13660786Sps#ifdef _OSK
13760786Sps		sigmask(~0);
13860786Sps#endif
13960786Sps#endif
14063128Sps#endif
14160786Sps		return (READ_INTR);
14260786Sps	}
14360786Sps
14460786Sps	flush();
14560786Sps	reading = 1;
14660786Sps#if MSDOS_COMPILER==DJGPPC
14760786Sps	if (isatty(fd))
14860786Sps	{
14960786Sps		/*
15060786Sps		 * Don't try reading from a TTY until a character is
15160786Sps		 * available, because that makes some background programs
15260786Sps		 * believe DOS is busy in a way that prevents those
15360786Sps		 * programs from working while "less" waits.
15460786Sps		 */
15560786Sps		fd_set readfds;
15660786Sps
15760786Sps		FD_ZERO(&readfds);
15860786Sps		FD_SET(fd, &readfds);
15960786Sps		if (select(fd+1, &readfds, 0, 0, 0) == -1)
16060786Sps			return (-1);
16160786Sps	}
16260786Sps#endif
163369759Sgit2svn#if USE_POLL
164369759Sgit2svn	if (ignore_eoi && fd != tty)
165369759Sgit2svn	{
166369759Sgit2svn		if (poll_events(tty, POLLIN) && getchr() == CONTROL('X'))
167369759Sgit2svn		{
168369759Sgit2svn			sigs |= S_INTERRUPT;
169369759Sgit2svn			return (READ_INTR);
170369759Sgit2svn		}
171369759Sgit2svn		if (poll_events(fd, POLLERR|POLLHUP))
172369759Sgit2svn		{
173369759Sgit2svn			sigs |= S_INTERRUPT;
174369759Sgit2svn			return (READ_INTR);
175369759Sgit2svn		}
176369759Sgit2svn	}
177369759Sgit2svn#else
178369759Sgit2svn#if MSDOS_COMPILER==WIN32C
179369759Sgit2svn	if (win32_kbhit() && WIN32getch() == CONTROL('X'))
180369759Sgit2svn	{
181369759Sgit2svn		sigs |= S_INTERRUPT;
182369759Sgit2svn		return (READ_INTR);
183369759Sgit2svn	}
184369759Sgit2svn#endif
185369759Sgit2svn#endif
18660786Sps	n = read(fd, buf, len);
18760786Sps#if 1
18860786Sps	/*
18960786Sps	 * This is a kludge to workaround a problem on some systems
19060786Sps	 * where terminating a remote tty connection causes read() to
19160786Sps	 * start returning 0 forever, instead of -1.
19260786Sps	 */
19360786Sps	{
19460786Sps		if (!ignore_eoi)
19560786Sps		{
19660786Sps			static int consecutive_nulls = 0;
19760786Sps			if (n == 0)
19860786Sps				consecutive_nulls++;
19960786Sps			else
20060786Sps				consecutive_nulls = 0;
20160786Sps			if (consecutive_nulls > 20)
20260786Sps				quit(QUIT_ERROR);
20360786Sps		}
20460786Sps	}
20560786Sps#endif
20660786Sps	reading = 0;
20760786Sps	if (n < 0)
208170256Sdelphij	{
209170256Sdelphij#if HAVE_ERRNO
210170256Sdelphij		/*
211170256Sdelphij		 * Certain values of errno indicate we should just retry the read.
212170256Sdelphij		 */
213170256Sdelphij#if MUST_DEFINE_ERRNO
214170256Sdelphij		extern int errno;
215170256Sdelphij#endif
216170256Sdelphij#ifdef EINTR
217170256Sdelphij		if (errno == EINTR)
218170256Sdelphij			goto start;
219170256Sdelphij#endif
220170256Sdelphij#ifdef EAGAIN
221170256Sdelphij		if (errno == EAGAIN)
222170256Sdelphij			goto start;
223170256Sdelphij#endif
224170256Sdelphij#endif
22560786Sps		return (-1);
226170256Sdelphij	}
22760786Sps	return (n);
22860786Sps}
22960786Sps
23060786Sps/*
23160786Sps * Interrupt a pending iread().
23260786Sps */
23360786Sps	public void
234355504Sdelphijintread(VOID_PARAM)
23560786Sps{
23660786Sps	LONG_JUMP(read_label, 1);
23760786Sps}
23860786Sps
23960786Sps/*
24060786Sps * Return the current time.
24160786Sps */
24260786Sps#if HAVE_TIME
243293190Sdelphij	public time_type
244355504Sdelphijget_time(VOID_PARAM)
24560786Sps{
24660786Sps	time_type t;
24760786Sps
24860786Sps	time(&t);
24960786Sps	return (t);
25060786Sps}
25160786Sps#endif
25260786Sps
25360786Sps
25460786Sps#if !HAVE_STRERROR
25560786Sps/*
25660786Sps * Local version of strerror, if not available from the system.
25760786Sps */
25860786Sps	static char *
25960786Spsstrerror(err)
26060786Sps	int err;
26160786Sps{
262369759Sgit2svn	static char buf[16];
26360786Sps#if HAVE_SYS_ERRLIST
26460786Sps	extern char *sys_errlist[];
26560786Sps	extern int sys_nerr;
26660786Sps
26760786Sps	if (err < sys_nerr)
26860786Sps		return sys_errlist[err];
269369759Sgit2svn#endif
27060786Sps	sprintf(buf, "Error %d", err);
27160786Sps	return buf;
27260786Sps}
27360786Sps#endif
27460786Sps
27560786Sps/*
27660786Sps * errno_message: Return an error message based on the value of "errno".
27760786Sps */
27860786Sps	public char *
27960786Spserrno_message(filename)
28060786Sps	char *filename;
28160786Sps{
282330570Sdelphij	char *p;
283330570Sdelphij	char *m;
284161475Sdelphij	int len;
28560786Sps#if HAVE_ERRNO
28660786Sps#if MUST_DEFINE_ERRNO
28760786Sps	extern int errno;
28860786Sps#endif
28960786Sps	p = strerror(errno);
29060786Sps#else
29160786Sps	p = "cannot open";
29260786Sps#endif
293293190Sdelphij	len = (int) (strlen(filename) + strlen(p) + 3);
294161475Sdelphij	m = (char *) ecalloc(len, sizeof(char));
295161475Sdelphij	SNPRINTF2(m, len, "%s: %s", filename, p);
29660786Sps	return (m);
29760786Sps}
29860786Sps
299221715Sdelphij/* #define HAVE_FLOAT 0 */
300221715Sdelphij
301221715Sdelphij	static POSITION
302221715Sdelphijmuldiv(val, num, den)
303221715Sdelphij	POSITION val, num, den;
304221715Sdelphij{
305221715Sdelphij#if HAVE_FLOAT
306221715Sdelphij	double v = (((double) val) * num) / den;
307221715Sdelphij	return ((POSITION) (v + 0.5));
308221715Sdelphij#else
309221715Sdelphij	POSITION v = ((POSITION) val) * num;
310221715Sdelphij
311221715Sdelphij	if (v / num == val)
312221715Sdelphij		/* No overflow */
313221715Sdelphij		return (POSITION) (v / den);
314221715Sdelphij	else
315221715Sdelphij		/* Above calculation overflows;
316221715Sdelphij		 * use a method that is less precise but won't overflow. */
317221715Sdelphij		return (POSITION) (val / (den / num));
318221715Sdelphij#endif
319221715Sdelphij}
320221715Sdelphij
32160786Sps/*
32260786Sps * Return the ratio of two POSITIONS, as a percentage.
32360786Sps * {{ Assumes a POSITION is a long int. }}
32460786Sps */
32560786Sps	public int
32660786Spspercentage(num, den)
327330570Sdelphij	POSITION num;
328330570Sdelphij	POSITION den;
32960786Sps{
330221715Sdelphij	return (int) muldiv(num,  (POSITION) 100, den);
33160786Sps}
33260786Sps
33360786Sps/*
33460786Sps * Return the specified percentage of a POSITION.
33560786Sps */
33660786Sps	public POSITION
337170256Sdelphijpercent_pos(pos, percent, fraction)
33860786Sps	POSITION pos;
33960786Sps	int percent;
340170256Sdelphij	long fraction;
34160786Sps{
342170256Sdelphij	/* Change percent (parts per 100) to perden (parts per NUM_FRAC_DENOM). */
343221715Sdelphij	POSITION perden = (percent * (NUM_FRAC_DENOM / 100)) + (fraction / 100);
34489019Sps
345170256Sdelphij	if (perden == 0)
34689019Sps		return (0);
347221715Sdelphij	return (POSITION) muldiv(pos, perden, (POSITION) NUM_FRAC_DENOM);
34860786Sps}
34960786Sps
35089019Sps#if !HAVE_STRCHR
35189019Sps/*
35289019Sps * strchr is used by regexp.c.
35389019Sps */
35489019Sps	char *
35589019Spsstrchr(s, c)
35689019Sps	char *s;
35789019Sps	int c;
35889019Sps{
35989019Sps	for ( ;  *s != '\0';  s++)
36089019Sps		if (*s == c)
36189019Sps			return (s);
36289019Sps	if (c == '\0')
36389019Sps		return (s);
36489019Sps	return (NULL);
36589019Sps}
36689019Sps#endif
36789019Sps
36889019Sps#if !HAVE_MEMCPY
36989019Sps	VOID_POINTER
37089019Spsmemcpy(dst, src, len)
37189019Sps	VOID_POINTER dst;
37289019Sps	VOID_POINTER src;
37389019Sps	int len;
37489019Sps{
37589019Sps	char *dstp = (char *) dst;
37689019Sps	char *srcp = (char *) src;
37789019Sps	int i;
37889019Sps
37989019Sps	for (i = 0;  i < len;  i++)
38089019Sps		dstp[i] = srcp[i];
38189019Sps	return (dst);
38289019Sps}
38389019Sps#endif
38489019Sps
38560786Sps#ifdef _OSK_MWC32
38660786Sps
38760786Sps/*
38860786Sps * This implements an ANSI-style intercept setup for Microware C 3.2
38960786Sps */
39060786Sps	public int
39160786Spsos9_signal(type, handler)
39260786Sps	int type;
39360786Sps	RETSIGTYPE (*handler)();
39460786Sps{
39560786Sps	intercept(handler);
39660786Sps}
39760786Sps
39860786Sps#include <sgstat.h>
39960786Sps
40089019Sps	int
40160786Spsisatty(f)
40260786Sps	int f;
40360786Sps{
40460786Sps	struct sgbuf sgbuf;
40560786Sps
40660786Sps	if (_gs_opt(f, &sgbuf) < 0)
40760786Sps		return -1;
40860786Sps	return (sgbuf.sg_class == 0);
40960786Sps}
41060786Sps
41160786Sps#endif
412369759Sgit2svn
413369759Sgit2svn	public void
414369759Sgit2svnsleep_ms(ms)
415369759Sgit2svn	int ms;
416369759Sgit2svn{
417369759Sgit2svn#if MSDOS_COMPILER==WIN32C
418369759Sgit2svn	Sleep(ms);
419369759Sgit2svn#else
420369759Sgit2svn#if HAVE_NANOSLEEP
421369759Sgit2svn	int sec = ms / 1000;
422369759Sgit2svn	struct timespec t = { sec, (ms - sec*1000) * 1000000 };
423369759Sgit2svn	nanosleep(&t, NULL);
424369759Sgit2svn#else
425369759Sgit2svn#if HAVE_USLEEP
426369759Sgit2svn	usleep(ms);
427369759Sgit2svn#else
428369759Sgit2svn	sleep((ms+999) / 1000);
429369759Sgit2svn#endif
430369759Sgit2svn#endif
431369759Sgit2svn#endif
432369759Sgit2svn}
433