poll - Fix events == 0 handling for TAP and TUN, fix console spam
[dragonfly.git] / contrib / less / os.c
blob61c336c8c510dde3c1f985dda52141c91ebbbd5d
1 /*
2 * Copyright (C) 1984-2024 Mark Nudelman
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.
7 * For more information, see the README file.
8 */
12 * Operating system dependent routines.
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.
19 * The makefile provides defines to decide whether various
20 * Unix features are present.
23 #include "less.h"
24 #include <signal.h>
25 #include <setjmp.h>
26 #if MSDOS_COMPILER==WIN32C
27 #include <windows.h>
28 #endif
29 #if HAVE_TIME_H
30 #include <time.h>
31 #endif
32 #if HAVE_ERRNO_H
33 #include <errno.h>
34 #endif
35 #if HAVE_VALUES_H
36 #include <values.h>
37 #endif
39 #if defined(__APPLE__)
40 #include <sys/utsname.h>
41 #endif
43 #if HAVE_POLL && !MSDOS_COMPILER
44 #define USE_POLL 1
45 static lbool use_poll = TRUE;
46 #else
47 #define USE_POLL 0
48 #endif
49 #if USE_POLL
50 #include <poll.h>
51 static lbool any_data = FALSE;
52 #endif
55 * BSD setjmp() saves (and longjmp() restores) the signal mask.
56 * This costs a system call or two per setjmp(), so if possible we clear the
57 * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead.
58 * On other systems, setjmp() doesn't affect the signal mask and so
59 * _setjmp() does not exist; we just use setjmp().
61 #if HAVE__SETJMP && HAVE_SIGSETMASK
62 #define SET_JUMP _setjmp
63 #define LONG_JUMP _longjmp
64 #else
65 #define SET_JUMP setjmp
66 #define LONG_JUMP longjmp
67 #endif
69 public int reading;
70 public lbool waiting_for_data;
71 public int consecutive_nulls = 0;
73 /* Milliseconds to wait for data before displaying "waiting for data" message. */
74 static int waiting_for_data_delay = 4000;
75 static jmp_buf read_label;
77 extern int sigs;
78 extern int ignore_eoi;
79 extern int exit_F_on_close;
80 extern int follow_mode;
81 extern int scanning_eof;
82 extern char intr_char;
83 #if !MSDOS_COMPILER
84 extern int tty;
85 #endif
87 public void init_poll(void)
89 constant char *delay = lgetenv("LESS_DATA_DELAY");
90 int idelay = (delay == NULL) ? 0 : atoi(delay);
91 if (idelay > 0)
92 waiting_for_data_delay = idelay;
93 #if USE_POLL
94 #if defined(__APPLE__)
95 /* In old versions of MacOS, poll() does not work with /dev/tty. */
96 struct utsname uts;
97 if (uname(&uts) < 0 || lstrtoi(uts.release, NULL, 10) < 20)
98 use_poll = FALSE;
99 #endif
100 #endif
103 #if USE_POLL
105 * Check whether data is available, either from a file/pipe or from the tty.
106 * Return READ_AGAIN if no data currently available, but caller should retry later.
107 * Return READ_INTR to abort F command (forw_loop).
108 * Return 0 if safe to read from fd.
110 static int check_poll(int fd, int tty)
112 struct pollfd poller[2] = { { fd, POLLIN, 0 }, { tty, POLLIN, 0 } };
113 int timeout = (waiting_for_data && !(scanning_eof && follow_mode == FOLLOW_NAME)) ? -1 : waiting_for_data_delay;
114 if (!any_data)
117 * Don't do polling if no data has yet been received,
118 * to allow a program piping data into less to have temporary
119 * access to the tty (like sudo asking for a password).
121 return (0);
123 poll(poller, 2, timeout);
124 #if LESSTEST
125 if (!is_lesstest()) /* Check for ^X only on a real tty. */
126 #endif /*LESSTEST*/
128 if (poller[1].revents & POLLIN)
130 int ch = getchr();
131 if (ch < 0 || ch == intr_char)
132 /* Break out of "waiting for data". */
133 return (READ_INTR);
134 ungetcc_back((char) ch);
137 if (ignore_eoi && exit_F_on_close && (poller[0].revents & (POLLHUP|POLLIN)) == POLLHUP)
138 /* Break out of F loop on HUP due to --exit-follow-on-close. */
139 return (READ_INTR);
140 if ((poller[0].revents & (POLLIN|POLLHUP|POLLERR)) == 0)
141 /* No data available; let caller take action, then try again. */
142 return (READ_AGAIN);
143 /* There is data (or HUP/ERR) available. Safe to call read() without blocking. */
144 return (0);
146 #endif /* USE_POLL */
148 public int supports_ctrl_x(void)
150 #if MSDOS_COMPILER==WIN32C
151 return (TRUE);
152 #else
153 #if USE_POLL
154 return (use_poll);
155 #else
156 return (FALSE);
157 #endif /* USE_POLL */
158 #endif /* MSDOS_COMPILER==WIN32C */
162 * Like read() system call, but is deliberately interruptible.
163 * A call to intread() from a signal handler will interrupt
164 * any pending iread().
166 public ssize_t iread(int fd, unsigned char *buf, size_t len)
168 ssize_t n;
170 start:
171 #if MSDOS_COMPILER==WIN32C
172 if (ABORT_SIGS())
173 return (READ_INTR);
174 #else
175 #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
176 if (kbhit())
178 int c;
180 c = getch();
181 if (c == '\003')
182 return (READ_INTR);
183 ungetch(c);
185 #endif
186 #endif
187 if (!reading && SET_JUMP(read_label))
190 * We jumped here from intread.
192 reading = FALSE;
193 #if HAVE_SIGPROCMASK
195 sigset_t mask;
196 sigemptyset(&mask);
197 sigprocmask(SIG_SETMASK, &mask, NULL);
199 #else
200 #if HAVE_SIGSETMASK
201 sigsetmask(0);
202 #else
203 #ifdef _OSK
204 sigmask(~0);
205 #endif
206 #endif
207 #endif
208 #if !MSDOS_COMPILER
209 if (fd != tty && !ABORT_SIGS())
210 /* Non-interrupt signal like SIGWINCH. */
211 return (READ_AGAIN);
212 #endif
213 return (READ_INTR);
216 flush();
217 reading = TRUE;
218 #if MSDOS_COMPILER==DJGPPC
219 if (isatty(fd))
222 * Don't try reading from a TTY until a character is
223 * available, because that makes some background programs
224 * believe DOS is busy in a way that prevents those
225 * programs from working while "less" waits.
226 * {{ This code was added 12 Jan 2007; still needed? }}
228 fd_set readfds;
230 FD_ZERO(&readfds);
231 FD_SET(fd, &readfds);
232 if (select(fd+1, &readfds, 0, 0, 0) == -1)
234 reading = FALSE;
235 return (READ_ERR);
238 #endif
239 #if USE_POLL
240 if (fd != tty && use_poll)
242 int ret = check_poll(fd, tty);
243 if (ret != 0)
245 if (ret == READ_INTR)
246 sigs |= S_INTERRUPT;
247 reading = FALSE;
248 return (ret);
251 #else
252 #if MSDOS_COMPILER==WIN32C
253 if (win32_kbhit())
255 int c;
257 c = WIN32getch();
258 if (c == intr_char)
260 sigs |= S_INTERRUPT;
261 reading = FALSE;
262 return (READ_INTR);
264 WIN32ungetch(c);
266 #endif
267 #endif
268 n = read(fd, buf, len);
269 reading = FALSE;
270 #if 1
272 * This is a kludge to workaround a problem on some systems
273 * where terminating a remote tty connection causes read() to
274 * start returning 0 forever, instead of -1.
277 if (!ignore_eoi)
279 if (n == 0)
280 consecutive_nulls++;
281 else
282 consecutive_nulls = 0;
283 if (consecutive_nulls > 20)
284 quit(QUIT_ERROR);
287 #endif
288 if (n < 0)
290 #if HAVE_ERRNO
292 * Certain values of errno indicate we should just retry the read.
294 #if MUST_DEFINE_ERRNO
295 extern int errno;
296 #endif
297 #ifdef EINTR
298 if (errno == EINTR)
299 goto start;
300 #endif
301 #ifdef EAGAIN
302 if (errno == EAGAIN)
303 goto start;
304 #endif
305 #endif
306 return (READ_ERR);
308 #if USE_POLL
309 if (fd != tty && n > 0)
310 any_data = TRUE;
311 #endif
312 return (n);
316 * Interrupt a pending iread().
318 public void intread(void)
320 LONG_JUMP(read_label, 1);
324 * Return the current time.
326 #if HAVE_TIME
327 public time_type get_time(void)
329 time_type t;
331 time(&t);
332 return (t);
334 #endif
337 #if !HAVE_STRERROR
339 * Local version of strerror, if not available from the system.
341 static char * strerror(int err)
343 static char buf[INT_STRLEN_BOUND(int)+12];
344 #if HAVE_SYS_ERRLIST
345 extern char *sys_errlist[];
346 extern int sys_nerr;
348 if (err < sys_nerr)
349 return sys_errlist[err];
350 #endif
351 sprintf(buf, "Error %d", err);
352 return buf;
354 #endif
357 * errno_message: Return an error message based on the value of "errno".
359 public char * errno_message(constant char *filename)
361 char *p;
362 char *m;
363 size_t len;
364 #if HAVE_ERRNO
365 #if MUST_DEFINE_ERRNO
366 extern int errno;
367 #endif
368 p = strerror(errno);
369 #else
370 p = "cannot open";
371 #endif
372 len = strlen(filename) + strlen(p) + 3;
373 m = (char *) ecalloc(len, sizeof(char));
374 SNPRINTF2(m, len, "%s: %s", filename, p);
375 return (m);
379 * Return a description of a signal.
380 * The return value is good until the next call to this function.
382 public constant char * signal_message(int sig)
384 static char sigbuf[sizeof("Signal ") + INT_STRLEN_BOUND(sig) + 1];
385 #if HAVE_STRSIGNAL
386 constant char *description = strsignal(sig);
387 if (description)
388 return description;
389 #endif
390 sprintf(sigbuf, "Signal %d", sig);
391 return sigbuf;
395 * Return (VAL * NUM) / DEN, where DEN is positive
396 * and min(VAL, NUM) <= DEN so the result cannot overflow.
397 * Round to the nearest integer, breaking ties by rounding to even.
399 public uintmax umuldiv(uintmax val, uintmax num, uintmax den)
402 * Like round(val * (double) num / den), but without rounding error.
403 * Overflow cannot occur, so there is no need for floating point.
405 uintmax q = val / den;
406 uintmax r = val % den;
407 uintmax qnum = q * num;
408 uintmax rnum = r * num;
409 uintmax quot = qnum + rnum / den;
410 uintmax rem = rnum % den;
411 return quot + (den / 2 < rem + (quot & ~den & 1));
415 * Return the ratio of two POSITIONS, as a percentage.
416 * {{ Assumes a POSITION is a long int. }}
418 public int percentage(POSITION num, POSITION den)
420 return (int) muldiv(num, 100, den);
424 * Return the specified percentage of a POSITION.
425 * Assume (0 <= POS && 0 <= PERCENT <= 100
426 * && 0 <= FRACTION < (PERCENT == 100 ? 1 : NUM_FRAC_DENOM)),
427 * so the result cannot overflow. Round to even.
429 public POSITION percent_pos(POSITION pos, int percent, long fraction)
432 * Change from percent (parts per 100)
433 * to pctden (parts per 100 * NUM_FRAC_DENOM).
435 POSITION pctden = (percent * NUM_FRAC_DENOM) + fraction;
437 return (POSITION) muldiv(pos, pctden, 100 * NUM_FRAC_DENOM);
440 #if !HAVE_STRCHR
442 * strchr is used by regexp.c.
444 char * strchr(char *s, char c)
446 for ( ; *s != '\0'; s++)
447 if (*s == c)
448 return (s);
449 if (c == '\0')
450 return (s);
451 return (NULL);
453 #endif
455 #if !HAVE_MEMCPY
456 void * memcpy(void *dst, void *src, size_t len)
458 char *dstp = (char *) dst;
459 char *srcp = (char *) src;
460 int i;
462 for (i = 0; i < len; i++)
463 dstp[i] = srcp[i];
464 return (dst);
466 #endif
468 #ifdef _OSK_MWC32
471 * This implements an ANSI-style intercept setup for Microware C 3.2
473 public int os9_signal(int type, RETSIGTYPE (*handler)())
475 intercept(handler);
478 #include <sgstat.h>
480 int isatty(int f)
482 struct sgbuf sgbuf;
484 if (_gs_opt(f, &sgbuf) < 0)
485 return -1;
486 return (sgbuf.sg_class == 0);
489 #endif
491 public void sleep_ms(int ms)
493 #if MSDOS_COMPILER==WIN32C
494 Sleep(ms);
495 #else
496 #if HAVE_NANOSLEEP
497 int sec = ms / 1000;
498 struct timespec t = { sec, (ms - sec*1000) * 1000000 };
499 nanosleep(&t, NULL);
500 #else
501 #if HAVE_USLEEP
502 usleep(ms * 1000);
503 #else
504 sleep(ms / 1000 + (ms % 1000 != 0));
505 #endif
506 #endif
507 #endif