1/*
2 * Copyright (C) 1984-2017  Mark Nudelman
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Less License, as specified in the README file.
6 *
7 * For more information, see the README file.
8 */
9
10
11/*
12 * Operating system dependent routines.
13 *
14 * Most of the stuff in here is based on Unix, but an attempt
15 * has been made to make things work on other operating systems.
16 * This will sometimes result in a loss of functionality, unless
17 * someone rewrites code specifically for the new operating system.
18 *
19 * The makefile provides defines to decide whether various
20 * Unix features are present.
21 */
22
23#include "less.h"
24#include <signal.h>
25#include <setjmp.h>
26#if HAVE_TIME_H
27#include <time.h>
28#endif
29#if HAVE_ERRNO_H
30#include <errno.h>
31#endif
32#if HAVE_VALUES_H
33#include <values.h>
34#endif
35
36/*
37 * BSD setjmp() saves (and longjmp() restores) the signal mask.
38 * This costs a system call or two per setjmp(), so if possible we clear the
39 * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead.
40 * On other systems, setjmp() doesn't affect the signal mask and so
41 * _setjmp() does not exist; we just use setjmp().
42 */
43#if HAVE__SETJMP && HAVE_SIGSETMASK
44#define SET_JUMP	_setjmp
45#define LONG_JUMP	_longjmp
46#else
47#define SET_JUMP	setjmp
48#define LONG_JUMP	longjmp
49#endif
50
51public int reading;
52
53static jmp_buf read_label;
54
55extern int sigs;
56
57/*
58 * Like read() system call, but is deliberately interruptible.
59 * A call to intread() from a signal handler will interrupt
60 * any pending iread().
61 */
62	public int
63iread(fd, buf, len)
64	int fd;
65	unsigned char *buf;
66	unsigned int len;
67{
68	int n;
69
70start:
71#if MSDOS_COMPILER==WIN32C
72	if (ABORT_SIGS())
73		return (READ_INTR);
74#else
75#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
76	if (kbhit())
77	{
78		int c;
79
80		c = getch();
81		if (c == '\003')
82			return (READ_INTR);
83		ungetch(c);
84	}
85#endif
86#endif
87	if (SET_JUMP(read_label))
88	{
89		/*
90		 * We jumped here from intread.
91		 */
92		reading = 0;
93#if HAVE_SIGPROCMASK
94		{
95		  sigset_t mask;
96		  sigemptyset(&mask);
97		  sigprocmask(SIG_SETMASK, &mask, NULL);
98		}
99#else
100#if HAVE_SIGSETMASK
101		sigsetmask(0);
102#else
103#ifdef _OSK
104		sigmask(~0);
105#endif
106#endif
107#endif
108		return (READ_INTR);
109	}
110
111	flush();
112	reading = 1;
113#if MSDOS_COMPILER==DJGPPC
114	if (isatty(fd))
115	{
116		/*
117		 * Don't try reading from a TTY until a character is
118		 * available, because that makes some background programs
119		 * believe DOS is busy in a way that prevents those
120		 * programs from working while "less" waits.
121		 */
122		fd_set readfds;
123
124		FD_ZERO(&readfds);
125		FD_SET(fd, &readfds);
126		if (select(fd+1, &readfds, 0, 0, 0) == -1)
127			return (-1);
128	}
129#endif
130	n = read(fd, buf, len);
131#if 1
132	/*
133	 * This is a kludge to workaround a problem on some systems
134	 * where terminating a remote tty connection causes read() to
135	 * start returning 0 forever, instead of -1.
136	 */
137	{
138		extern int ignore_eoi;
139		if (!ignore_eoi)
140		{
141			static int consecutive_nulls = 0;
142			if (n == 0)
143				consecutive_nulls++;
144			else
145				consecutive_nulls = 0;
146			if (consecutive_nulls > 20)
147				quit(QUIT_ERROR);
148		}
149	}
150#endif
151	reading = 0;
152	if (n < 0)
153	{
154#if HAVE_ERRNO
155		/*
156		 * Certain values of errno indicate we should just retry the read.
157		 */
158#if MUST_DEFINE_ERRNO
159		extern int errno;
160#endif
161#ifdef EINTR
162		if (errno == EINTR)
163			goto start;
164#endif
165#ifdef EAGAIN
166		if (errno == EAGAIN)
167			goto start;
168#endif
169#endif
170		return (-1);
171	}
172	return (n);
173}
174
175/*
176 * Interrupt a pending iread().
177 */
178	public void
179intread()
180{
181	LONG_JUMP(read_label, 1);
182}
183
184/*
185 * Return the current time.
186 */
187#if HAVE_TIME
188	public time_type
189get_time()
190{
191	time_type t;
192
193	time(&t);
194	return (t);
195}
196#endif
197
198
199#if !HAVE_STRERROR
200/*
201 * Local version of strerror, if not available from the system.
202 */
203	static char *
204strerror(err)
205	int err;
206{
207#if HAVE_SYS_ERRLIST
208	static char buf[16];
209	extern char *sys_errlist[];
210	extern int sys_nerr;
211
212	if (err < sys_nerr)
213		return sys_errlist[err];
214	sprintf(buf, "Error %d", err);
215	return buf;
216#else
217	return ("cannot open");
218#endif
219}
220#endif
221
222/*
223 * errno_message: Return an error message based on the value of "errno".
224 */
225	public char *
226errno_message(filename)
227	char *filename;
228{
229	char *p;
230	char *m;
231	int len;
232#if HAVE_ERRNO
233#if MUST_DEFINE_ERRNO
234	extern int errno;
235#endif
236	p = strerror(errno);
237#else
238	p = "cannot open";
239#endif
240	len = (int) (strlen(filename) + strlen(p) + 3);
241	m = (char *) ecalloc(len, sizeof(char));
242	SNPRINTF2(m, len, "%s: %s", filename, p);
243	return (m);
244}
245
246/* #define HAVE_FLOAT 0 */
247
248	static POSITION
249muldiv(val, num, den)
250	POSITION val, num, den;
251{
252#if HAVE_FLOAT
253	double v = (((double) val) * num) / den;
254	return ((POSITION) (v + 0.5));
255#else
256	POSITION v = ((POSITION) val) * num;
257
258	if (v / num == val)
259		/* No overflow */
260		return (POSITION) (v / den);
261	else
262		/* Above calculation overflows;
263		 * use a method that is less precise but won't overflow. */
264		return (POSITION) (val / (den / num));
265#endif
266}
267
268/*
269 * Return the ratio of two POSITIONS, as a percentage.
270 * {{ Assumes a POSITION is a long int. }}
271 */
272	public int
273percentage(num, den)
274	POSITION num;
275	POSITION den;
276{
277	return (int) muldiv(num,  (POSITION) 100, den);
278}
279
280/*
281 * Return the specified percentage of a POSITION.
282 */
283	public POSITION
284percent_pos(pos, percent, fraction)
285	POSITION pos;
286	int percent;
287	long fraction;
288{
289	/* Change percent (parts per 100) to perden (parts per NUM_FRAC_DENOM). */
290	POSITION perden = (percent * (NUM_FRAC_DENOM / 100)) + (fraction / 100);
291
292	if (perden == 0)
293		return (0);
294	return (POSITION) muldiv(pos, perden, (POSITION) NUM_FRAC_DENOM);
295}
296
297#if !HAVE_STRCHR
298/*
299 * strchr is used by regexp.c.
300 */
301	char *
302strchr(s, c)
303	char *s;
304	int c;
305{
306	for ( ;  *s != '\0';  s++)
307		if (*s == c)
308			return (s);
309	if (c == '\0')
310		return (s);
311	return (NULL);
312}
313#endif
314
315#if !HAVE_MEMCPY
316	VOID_POINTER
317memcpy(dst, src, len)
318	VOID_POINTER dst;
319	VOID_POINTER src;
320	int len;
321{
322	char *dstp = (char *) dst;
323	char *srcp = (char *) src;
324	int i;
325
326	for (i = 0;  i < len;  i++)
327		dstp[i] = srcp[i];
328	return (dst);
329}
330#endif
331
332#ifdef _OSK_MWC32
333
334/*
335 * This implements an ANSI-style intercept setup for Microware C 3.2
336 */
337	public int
338os9_signal(type, handler)
339	int type;
340	RETSIGTYPE (*handler)();
341{
342	intercept(handler);
343}
344
345#include <sgstat.h>
346
347	int
348isatty(f)
349	int f;
350{
351	struct sgbuf sgbuf;
352
353	if (_gs_opt(f, &sgbuf) < 0)
354		return -1;
355	return (sgbuf.sg_class == 0);
356}
357
358#endif
359