xref: /freebsd/contrib/less/os.c (revision 64db83a8ab2d1f72a9b2174b39d2ef42b5b0580c)
1 /*
2  * Copyright (C) 1984-2000  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 #if HAVE_LIMITS_H
37 #include <limits.h>
38 #endif
39 
40 #if HAVE_TIME_T
41 #define time_type	time_t
42 #else
43 #define	time_type	long
44 #endif
45 
46 /*
47  * BSD setjmp() saves (and longjmp() restores) the signal mask.
48  * This costs a system call or two per setjmp(), so if possible we clear the
49  * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead.
50  * On other systems, setjmp() doesn't affect the signal mask and so
51  * _setjmp() does not exist; we just use setjmp().
52  */
53 #if HAVE__SETJMP && HAVE_SIGSETMASK
54 #define SET_JUMP	_setjmp
55 #define LONG_JUMP	_longjmp
56 #else
57 #define SET_JUMP	setjmp
58 #define LONG_JUMP	longjmp
59 #endif
60 
61 public int reading;
62 
63 static jmp_buf read_label;
64 
65 extern int sigs;
66 
67 /*
68  * Like read() system call, but is deliberately interruptible.
69  * A call to intread() from a signal handler will interrupt
70  * any pending iread().
71  */
72 	public int
73 iread(fd, buf, len)
74 	int fd;
75 	char *buf;
76 	unsigned int len;
77 {
78 	register int n;
79 
80 #if MSDOS_COMPILER==WIN32C
81 	if (ABORT_SIGS())
82 		return (READ_INTR);
83 #else
84 #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
85 	if (kbhit())
86 	{
87 		int c;
88 
89 		c = getch();
90 		if (c == '\003')
91 			return (READ_INTR);
92 		ungetch(c);
93 	}
94 #endif
95 #endif
96 	if (SET_JUMP(read_label))
97 	{
98 		/*
99 		 * We jumped here from intread.
100 		 */
101 		reading = 0;
102 #if HAVE_SIGSETMASK
103 		sigsetmask(0);
104 #else
105 #ifdef _OSK
106 		sigmask(~0);
107 #endif
108 #endif
109 		return (READ_INTR);
110 	}
111 
112 	flush();
113 	reading = 1;
114 #if MSDOS_COMPILER==DJGPPC
115 	if (isatty(fd))
116 	{
117 		/*
118 		 * Don't try reading from a TTY until a character is
119 		 * available, because that makes some background programs
120 		 * believe DOS is busy in a way that prevents those
121 		 * programs from working while "less" waits.
122 		 */
123 		fd_set readfds;
124 
125 		FD_ZERO(&readfds);
126 		FD_SET(fd, &readfds);
127 		if (select(fd+1, &readfds, 0, 0, 0) == -1)
128 			return (-1);
129 	}
130 #endif
131 	n = read(fd, buf, len);
132 #if 1
133 	/*
134 	 * This is a kludge to workaround a problem on some systems
135 	 * where terminating a remote tty connection causes read() to
136 	 * start returning 0 forever, instead of -1.
137 	 */
138 	{
139 		extern int ignore_eoi;
140 		if (!ignore_eoi)
141 		{
142 			static int consecutive_nulls = 0;
143 			if (n == 0)
144 				consecutive_nulls++;
145 			else
146 				consecutive_nulls = 0;
147 			if (consecutive_nulls > 20)
148 				quit(QUIT_ERROR);
149 		}
150 	}
151 #endif
152 	reading = 0;
153 	if (n < 0)
154 		return (-1);
155 	return (n);
156 }
157 
158 /*
159  * Interrupt a pending iread().
160  */
161 	public void
162 intread()
163 {
164 	LONG_JUMP(read_label, 1);
165 }
166 
167 /*
168  * Return the current time.
169  */
170 #if HAVE_TIME
171 	public long
172 get_time()
173 {
174 	time_type t;
175 
176 	time(&t);
177 	return (t);
178 }
179 #endif
180 
181 
182 #if !HAVE_STRERROR
183 /*
184  * Local version of strerror, if not available from the system.
185  */
186 	static char *
187 strerror(err)
188 	int err;
189 {
190 #if HAVE_SYS_ERRLIST
191 	static char buf[16];
192 	extern char *sys_errlist[];
193 	extern int sys_nerr;
194 
195 	if (err < sys_nerr)
196 		return sys_errlist[err];
197 	sprintf(buf, "Error %d", err);
198 	return buf;
199 #else
200 	return ("cannot open");
201 #endif
202 }
203 #endif
204 
205 /*
206  * errno_message: Return an error message based on the value of "errno".
207  */
208 	public char *
209 errno_message(filename)
210 	char *filename;
211 {
212 	register char *p;
213 	register char *m;
214 #if HAVE_ERRNO
215 #if MUST_DEFINE_ERRNO
216 	extern int errno;
217 #endif
218 	p = strerror(errno);
219 #else
220 	p = "cannot open";
221 #endif
222 	m = (char *) ecalloc(strlen(filename) + strlen(p) + 3, sizeof(char));
223 	sprintf(m, "%s: %s", filename, p);
224 	return (m);
225 }
226 
227 /*
228  * Return the largest possible number that can fit in a long.
229  */
230 	static long
231 get_maxlong()
232 {
233 #ifdef LONG_MAX
234 	return (LONG_MAX);
235 #else
236 #ifdef MAXLONG
237 	return (MAXLONG);
238 #else
239 	long n, n2;
240 
241 	/*
242 	 * Keep doubling n until we overflow.
243 	 * {{ This actually only returns the largest power of two that
244 	 *    can fit in a long, but percentage() doesn't really need
245 	 *    it any more accurate than that. }}
246 	 */
247 	n2 = 128;  /* Hopefully no maxlong is less than 128! */
248 	do {
249 		n = n2;
250 		n2 *= 2;
251 	} while (n2 / 2 == n);
252 	return (n);
253 #endif
254 #endif
255 }
256 
257 /*
258  * Return the ratio of two POSITIONS, as a percentage.
259  * {{ Assumes a POSITION is a long int. }}
260  */
261 	public int
262 percentage(num, den)
263 	POSITION num, den;
264 {
265 	if (num <= get_maxlong() / 100)
266 		return ((100 * num) / den);
267 	else
268 		return (num / (den / 100));
269 }
270 
271 /*
272  * Return the specified percentage of a POSITION.
273  * {{ Assumes a POSITION is a long int. }}
274  */
275 	public POSITION
276 percent_pos(pos, percent)
277 	POSITION pos;
278 	int percent;
279 {
280 	if (pos <= get_maxlong() / 100)
281 		return ((percent * pos) / 100);
282 	else
283 		return (percent * (pos / 100));
284 }
285 
286 #ifdef _OSK_MWC32
287 
288 /*
289  * This implements an ANSI-style intercept setup for Microware C 3.2
290  */
291 	public int
292 os9_signal(type, handler)
293 	int type;
294 	RETSIGTYPE (*handler)();
295 {
296 	intercept(handler);
297 }
298 
299 #include <sgstat.h>
300 
301 	public int
302 isatty(f)
303 	int f;
304 {
305 	struct sgbuf sgbuf;
306 
307 	if (_gs_opt(f, &sgbuf) < 0)
308 		return -1;
309 	return (sgbuf.sg_class == 0);
310 }
311 
312 #endif
313