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.
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.
26 #if MSDOS_COMPILER==WIN32C
39 #if defined(__APPLE__)
40 #include <sys/utsname.h>
43 #if HAVE_POLL && !MSDOS_COMPILER
45 static lbool use_poll
= TRUE
;
51 static lbool any_data
= FALSE
;
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
65 #define SET_JUMP setjmp
66 #define LONG_JUMP longjmp
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
;
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
;
87 public void init_poll(void)
89 constant
char *delay
= lgetenv("LESS_DATA_DELAY");
90 int idelay
= (delay
== NULL
) ? 0 : atoi(delay
);
92 waiting_for_data_delay
= idelay
;
94 #if defined(__APPLE__)
95 /* In old versions of MacOS, poll() does not work with /dev/tty. */
97 if (uname(&uts
) < 0 || lstrtoi(uts
.release
, NULL
, 10) < 20)
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
;
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).
123 poll(poller
, 2, timeout
);
125 if (!is_lesstest()) /* Check for ^X only on a real tty. */
128 if (poller
[1].revents
& POLLIN
)
131 if (ch
< 0 || ch
== intr_char
)
132 /* Break out of "waiting for data". */
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. */
140 if ((poller
[0].revents
& (POLLIN
|POLLHUP
|POLLERR
)) == 0)
141 /* No data available; let caller take action, then try again. */
143 /* There is data (or HUP/ERR) available. Safe to call read() without blocking. */
146 #endif /* USE_POLL */
148 public int supports_ctrl_x(void)
150 #if MSDOS_COMPILER==WIN32C
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
)
171 #if MSDOS_COMPILER==WIN32C
175 #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
187 if (!reading
&& SET_JUMP(read_label
))
190 * We jumped here from intread.
197 sigprocmask(SIG_SETMASK
, &mask
, NULL
);
209 if (fd
!= tty
&& !ABORT_SIGS())
210 /* Non-interrupt signal like SIGWINCH. */
218 #if MSDOS_COMPILER==DJGPPC
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? }}
231 FD_SET(fd
, &readfds
);
232 if (select(fd
+1, &readfds
, 0, 0, 0) == -1)
240 if (fd
!= tty
&& use_poll
)
242 int ret
= check_poll(fd
, tty
);
245 if (ret
== READ_INTR
)
252 #if MSDOS_COMPILER==WIN32C
268 n
= read(fd
, buf
, len
);
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.
282 consecutive_nulls
= 0;
283 if (consecutive_nulls
> 20)
292 * Certain values of errno indicate we should just retry the read.
294 #if MUST_DEFINE_ERRNO
309 if (fd
!= tty
&& n
> 0)
316 * Interrupt a pending iread().
318 public void intread(void)
320 LONG_JUMP(read_label
, 1);
324 * Return the current time.
327 public time_type
get_time(void)
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];
345 extern char *sys_errlist
[];
349 return sys_errlist
[err
];
351 sprintf(buf
, "Error %d", err
);
357 * errno_message: Return an error message based on the value of "errno".
359 public char * errno_message(constant
char *filename
)
365 #if MUST_DEFINE_ERRNO
372 len
= strlen(filename
) + strlen(p
) + 3;
373 m
= (char *) ecalloc(len
, sizeof(char));
374 SNPRINTF2(m
, len
, "%s: %s", filename
, p
);
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];
386 constant
char *description
= strsignal(sig
);
390 sprintf(sigbuf
, "Signal %d", sig
);
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
);
442 * strchr is used by regexp.c.
444 char * strchr(char *s
, char c
)
446 for ( ; *s
!= '\0'; s
++)
456 void * memcpy(void *dst
, void *src
, size_t len
)
458 char *dstp
= (char *) dst
;
459 char *srcp
= (char *) src
;
462 for (i
= 0; i
< len
; i
++)
471 * This implements an ANSI-style intercept setup for Microware C 3.2
473 public int os9_signal(int type
, RETSIGTYPE (*handler
)())
484 if (_gs_opt(f
, &sgbuf
) < 0)
486 return (sgbuf
.sg_class
== 0);
491 public void sleep_ms(int ms
)
493 #if MSDOS_COMPILER==WIN32C
498 struct timespec t
= { sec
, (ms
- sec
*1000) * 1000000 };
504 sleep(ms
/ 1000 + (ms
% 1000 != 0));