xref: /freebsd/contrib/less/os.c (revision 5944f899a2519c6321bac3c17cc076418643a088)
1 /*
2  * Copyright (C) 1984-2015  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 
51 public int reading;
52 
53 static jmp_buf read_label;
54 
55 extern 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
63 iread(int fd, char *buf, unsigned int len)
64 {
65 	int n;
66 
67 start:
68 #if MSDOS_COMPILER==WIN32C
69 	if (ABORT_SIGS())
70 		return (READ_INTR);
71 #else
72 #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
73 	if (kbhit())
74 	{
75 		int c;
76 
77 		c = getch();
78 		if (c == '\003')
79 			return (READ_INTR);
80 		ungetch(c);
81 	}
82 #endif
83 #endif
84 	if (SET_JUMP(read_label))
85 	{
86 		/*
87 		 * We jumped here from intread.
88 		 */
89 		reading = 0;
90 #if HAVE_SIGPROCMASK
91 		{
92 		  sigset_t mask;
93 		  sigemptyset(&mask);
94 		  sigprocmask(SIG_SETMASK, &mask, NULL);
95 		}
96 #else
97 #if HAVE_SIGSETMASK
98 		sigsetmask(0);
99 #else
100 #ifdef _OSK
101 		sigmask(~0);
102 #endif
103 #endif
104 #endif
105 		return (READ_INTR);
106 	}
107 
108 	flush();
109 	reading = 1;
110 #if MSDOS_COMPILER==DJGPPC
111 	if (isatty(fd))
112 	{
113 		/*
114 		 * Don't try reading from a TTY until a character is
115 		 * available, because that makes some background programs
116 		 * believe DOS is busy in a way that prevents those
117 		 * programs from working while "less" waits.
118 		 */
119 		fd_set readfds;
120 
121 		FD_ZERO(&readfds);
122 		FD_SET(fd, &readfds);
123 		if (select(fd+1, &readfds, 0, 0, 0) == -1)
124 			return (-1);
125 	}
126 #endif
127 	n = read(fd, buf, len);
128 #if 1
129 	/*
130 	 * This is a kludge to workaround a problem on some systems
131 	 * where terminating a remote tty connection causes read() to
132 	 * start returning 0 forever, instead of -1.
133 	 */
134 	{
135 		extern int ignore_eoi;
136 		if (!ignore_eoi)
137 		{
138 			static int consecutive_nulls = 0;
139 			if (n == 0)
140 				consecutive_nulls++;
141 			else
142 				consecutive_nulls = 0;
143 			if (consecutive_nulls > 20)
144 				quit(QUIT_ERROR);
145 		}
146 	}
147 #endif
148 	reading = 0;
149 	if (n < 0)
150 	{
151 #if HAVE_ERRNO
152 		/*
153 		 * Certain values of errno indicate we should just retry the read.
154 		 */
155 #if MUST_DEFINE_ERRNO
156 		extern int errno;
157 #endif
158 #ifdef EINTR
159 		if (errno == EINTR)
160 			goto start;
161 #endif
162 #ifdef EAGAIN
163 		if (errno == EAGAIN)
164 			goto start;
165 #endif
166 #endif
167 		return (-1);
168 	}
169 	return (n);
170 }
171 
172 /*
173  * Interrupt a pending iread().
174  */
175 	public void
176 intread(void)
177 {
178 	LONG_JUMP(read_label, 1);
179 }
180 
181 /*
182  * Return the current time.
183  */
184 #if HAVE_TIME
185 	public time_type
186 get_time(void)
187 {
188 	time_type t;
189 
190 	time(&t);
191 	return (t);
192 }
193 #endif
194 
195 
196 #if !HAVE_STRERROR
197 /*
198  * Local version of strerror, if not available from the system.
199  */
200 	static char *
201 strerror(int err)
202 {
203 #if HAVE_SYS_ERRLIST
204 	static char buf[16];
205 	extern char *sys_errlist[];
206 	extern int sys_nerr;
207 
208 	if (err < sys_nerr)
209 		return sys_errlist[err];
210 	sprintf(buf, "Error %d", err);
211 	return buf;
212 #else
213 	return ("cannot open");
214 #endif
215 }
216 #endif
217 
218 /*
219  * errno_message: Return an error message based on the value of "errno".
220  */
221 	public char *
222 errno_message(char *filename)
223 {
224 	char *p;
225 	char *m;
226 	int len;
227 #if HAVE_ERRNO
228 #if MUST_DEFINE_ERRNO
229 	extern int errno;
230 #endif
231 	p = strerror(errno);
232 #else
233 	p = "cannot open";
234 #endif
235 	len = (int) (strlen(filename) + strlen(p) + 3);
236 	m = (char *) ecalloc(len, sizeof(char));
237 	SNPRINTF2(m, len, "%s: %s", filename, p);
238 	return (m);
239 }
240 
241 /* #define HAVE_FLOAT 0 */
242 
243 	static POSITION
244 muldiv(POSITION val, POSITION num, POSITION den)
245 {
246 #if HAVE_FLOAT
247 	double v = (((double) val) * num) / den;
248 	return ((POSITION) (v + 0.5));
249 #else
250 	POSITION v = ((POSITION) val) * num;
251 
252 	if (v / num == val)
253 		/* No overflow */
254 		return (POSITION) (v / den);
255 	else
256 		/* Above calculation overflows;
257 		 * use a method that is less precise but won't overflow. */
258 		return (POSITION) (val / (den / num));
259 #endif
260 }
261 
262 /*
263  * Return the ratio of two POSITIONS, as a percentage.
264  * {{ Assumes a POSITION is a long int. }}
265  */
266 	public int
267 percentage(POSITION num, POSITION den)
268 {
269 	return (int) muldiv(num,  (POSITION) 100, den);
270 }
271 
272 /*
273  * Return the specified percentage of a POSITION.
274  */
275 	public POSITION
276 percent_pos(POSITION pos, int percent, long fraction)
277 {
278 	/* Change percent (parts per 100) to perden (parts per NUM_FRAC_DENOM). */
279 	POSITION perden = (percent * (NUM_FRAC_DENOM / 100)) + (fraction / 100);
280 
281 	if (perden == 0)
282 		return (0);
283 	return (POSITION) muldiv(pos, perden, (POSITION) NUM_FRAC_DENOM);
284 }
285 
286 #if !HAVE_STRCHR
287 /*
288  * strchr is used by regexp.c.
289  */
290 	char *
291 strchr(s, c)
292 	char *s;
293 	int c;
294 {
295 	for ( ;  *s != '\0';  s++)
296 		if (*s == c)
297 			return (s);
298 	if (c == '\0')
299 		return (s);
300 	return (NULL);
301 }
302 #endif
303 
304 #if !HAVE_MEMCPY
305 	VOID_POINTER
306 memcpy(dst, src, len)
307 	VOID_POINTER dst;
308 	VOID_POINTER src;
309 	int len;
310 {
311 	char *dstp = (char *) dst;
312 	char *srcp = (char *) src;
313 	int i;
314 
315 	for (i = 0;  i < len;  i++)
316 		dstp[i] = srcp[i];
317 	return (dst);
318 }
319 #endif
320 
321 #ifdef _OSK_MWC32
322 
323 /*
324  * This implements an ANSI-style intercept setup for Microware C 3.2
325  */
326 	public int
327 os9_signal(int type, RETSIGTYPE (*handler)())
328 {
329 	intercept(handler);
330 }
331 
332 #include <sgstat.h>
333 
334 	int
335 isatty(f)
336 	int f;
337 {
338 	struct sgbuf sgbuf;
339 
340 	if (_gs_opt(f, &sgbuf) < 0)
341 		return -1;
342 	return (sgbuf.sg_class == 0);
343 }
344 
345 #endif
346