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