xref: /freebsd/contrib/less/os.c (revision ce4946daa5ce852d28008dac492029500ab2ee95)
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_SIGPROCMASK
103 		{
104 		  sigset_t mask;
105 		  sigemptyset(&mask);
106 		  sigprocmask(SIG_SETMASK, &mask, NULL);
107 		}
108 #else
109 #if HAVE_SIGSETMASK
110 		sigsetmask(0);
111 #else
112 #ifdef _OSK
113 		sigmask(~0);
114 #endif
115 #endif
116 #endif
117 		return (READ_INTR);
118 	}
119 
120 	flush();
121 	reading = 1;
122 #if MSDOS_COMPILER==DJGPPC
123 	if (isatty(fd))
124 	{
125 		/*
126 		 * Don't try reading from a TTY until a character is
127 		 * available, because that makes some background programs
128 		 * believe DOS is busy in a way that prevents those
129 		 * programs from working while "less" waits.
130 		 */
131 		fd_set readfds;
132 
133 		FD_ZERO(&readfds);
134 		FD_SET(fd, &readfds);
135 		if (select(fd+1, &readfds, 0, 0, 0) == -1)
136 			return (-1);
137 	}
138 #endif
139 	n = read(fd, buf, len);
140 #if 1
141 	/*
142 	 * This is a kludge to workaround a problem on some systems
143 	 * where terminating a remote tty connection causes read() to
144 	 * start returning 0 forever, instead of -1.
145 	 */
146 	{
147 		extern int ignore_eoi;
148 		if (!ignore_eoi)
149 		{
150 			static int consecutive_nulls = 0;
151 			if (n == 0)
152 				consecutive_nulls++;
153 			else
154 				consecutive_nulls = 0;
155 			if (consecutive_nulls > 20)
156 				quit(QUIT_ERROR);
157 		}
158 	}
159 #endif
160 	reading = 0;
161 	if (n < 0)
162 		return (-1);
163 	return (n);
164 }
165 
166 /*
167  * Interrupt a pending iread().
168  */
169 	public void
170 intread()
171 {
172 	LONG_JUMP(read_label, 1);
173 }
174 
175 /*
176  * Return the current time.
177  */
178 #if HAVE_TIME
179 	public long
180 get_time()
181 {
182 	time_type t;
183 
184 	time(&t);
185 	return (t);
186 }
187 #endif
188 
189 
190 #if !HAVE_STRERROR
191 /*
192  * Local version of strerror, if not available from the system.
193  */
194 	static char *
195 strerror(err)
196 	int err;
197 {
198 #if HAVE_SYS_ERRLIST
199 	static char buf[16];
200 	extern char *sys_errlist[];
201 	extern int sys_nerr;
202 
203 	if (err < sys_nerr)
204 		return sys_errlist[err];
205 	sprintf(buf, "Error %d", err);
206 	return buf;
207 #else
208 	return ("cannot open");
209 #endif
210 }
211 #endif
212 
213 /*
214  * errno_message: Return an error message based on the value of "errno".
215  */
216 	public char *
217 errno_message(filename)
218 	char *filename;
219 {
220 	register char *p;
221 	register char *m;
222 #if HAVE_ERRNO
223 #if MUST_DEFINE_ERRNO
224 	extern int errno;
225 #endif
226 	p = strerror(errno);
227 #else
228 	p = "cannot open";
229 #endif
230 	m = (char *) ecalloc(strlen(filename) + strlen(p) + 3, sizeof(char));
231 	sprintf(m, "%s: %s", filename, p);
232 	return (m);
233 }
234 
235 /*
236  * Return the largest possible number that can fit in a long.
237  */
238 	static long
239 get_maxlong()
240 {
241 #ifdef LONG_MAX
242 	return (LONG_MAX);
243 #else
244 #ifdef MAXLONG
245 	return (MAXLONG);
246 #else
247 	long n, n2;
248 
249 	/*
250 	 * Keep doubling n until we overflow.
251 	 * {{ This actually only returns the largest power of two that
252 	 *    can fit in a long, but percentage() doesn't really need
253 	 *    it any more accurate than that. }}
254 	 */
255 	n2 = 128;  /* Hopefully no maxlong is less than 128! */
256 	do {
257 		n = n2;
258 		n2 *= 2;
259 	} while (n2 / 2 == n);
260 	return (n);
261 #endif
262 #endif
263 }
264 
265 /*
266  * Return the ratio of two POSITIONS, as a percentage.
267  * {{ Assumes a POSITION is a long int. }}
268  */
269 	public int
270 percentage(num, den)
271 	POSITION num, den;
272 {
273 	if (num <= get_maxlong() / 100)
274 		return ((100 * num) / den);
275 	else
276 		return (num / (den / 100));
277 }
278 
279 /*
280  * Return the specified percentage of a POSITION.
281  * {{ Assumes a POSITION is a long int. }}
282  */
283 	public POSITION
284 percent_pos(pos, percent)
285 	POSITION pos;
286 	int percent;
287 {
288 	if (pos <= get_maxlong() / 100)
289 		return ((percent * pos) / 100);
290 	else
291 		return (percent * (pos / 100));
292 }
293 
294 #ifdef _OSK_MWC32
295 
296 /*
297  * This implements an ANSI-style intercept setup for Microware C 3.2
298  */
299 	public int
300 os9_signal(type, handler)
301 	int type;
302 	RETSIGTYPE (*handler)();
303 {
304 	intercept(handler);
305 }
306 
307 #include <sgstat.h>
308 
309 	public int
310 isatty(f)
311 	int f;
312 {
313 	struct sgbuf sgbuf;
314 
315 	if (_gs_opt(f, &sgbuf) < 0)
316 		return -1;
317 	return (sgbuf.sg_class == 0);
318 }
319 
320 #endif
321