11133e27eSPeter Avalos /*
2*e433da38SAaron LI * Copyright (C) 1984-2024 Mark Nudelman
31133e27eSPeter Avalos *
41133e27eSPeter Avalos * You may distribute under the terms of either the GNU General Public
51133e27eSPeter Avalos * License or the Less License, as specified in the README file.
61133e27eSPeter Avalos *
7e639dc31SJohn Marino * For more information, see the README file.
81133e27eSPeter Avalos */
91133e27eSPeter Avalos
101133e27eSPeter Avalos
111133e27eSPeter Avalos /*
121133e27eSPeter Avalos * Operating system dependent routines.
131133e27eSPeter Avalos *
141133e27eSPeter Avalos * Most of the stuff in here is based on Unix, but an attempt
151133e27eSPeter Avalos * has been made to make things work on other operating systems.
161133e27eSPeter Avalos * This will sometimes result in a loss of functionality, unless
171133e27eSPeter Avalos * someone rewrites code specifically for the new operating system.
181133e27eSPeter Avalos *
191133e27eSPeter Avalos * The makefile provides defines to decide whether various
201133e27eSPeter Avalos * Unix features are present.
211133e27eSPeter Avalos */
221133e27eSPeter Avalos
231133e27eSPeter Avalos #include "less.h"
241133e27eSPeter Avalos #include <signal.h>
251133e27eSPeter Avalos #include <setjmp.h>
260c7ad07eSAntonio Huete Jimenez #if MSDOS_COMPILER==WIN32C
270c7ad07eSAntonio Huete Jimenez #include <windows.h>
280c7ad07eSAntonio Huete Jimenez #endif
291133e27eSPeter Avalos #if HAVE_TIME_H
301133e27eSPeter Avalos #include <time.h>
311133e27eSPeter Avalos #endif
321133e27eSPeter Avalos #if HAVE_ERRNO_H
331133e27eSPeter Avalos #include <errno.h>
341133e27eSPeter Avalos #endif
351133e27eSPeter Avalos #if HAVE_VALUES_H
361133e27eSPeter Avalos #include <values.h>
371133e27eSPeter Avalos #endif
381133e27eSPeter Avalos
39320d7c8aSAaron LI #if defined(__APPLE__)
40320d7c8aSAaron LI #include <sys/utsname.h>
41320d7c8aSAaron LI #endif
42320d7c8aSAaron LI
43320d7c8aSAaron LI #if HAVE_POLL && !MSDOS_COMPILER
440c7ad07eSAntonio Huete Jimenez #define USE_POLL 1
45*e433da38SAaron LI static lbool use_poll = TRUE;
460c7ad07eSAntonio Huete Jimenez #else
470c7ad07eSAntonio Huete Jimenez #define USE_POLL 0
480c7ad07eSAntonio Huete Jimenez #endif
490c7ad07eSAntonio Huete Jimenez #if USE_POLL
500c7ad07eSAntonio Huete Jimenez #include <poll.h>
51*e433da38SAaron LI static lbool any_data = FALSE;
520c7ad07eSAntonio Huete Jimenez #endif
530c7ad07eSAntonio Huete Jimenez
541133e27eSPeter Avalos /*
551133e27eSPeter Avalos * BSD setjmp() saves (and longjmp() restores) the signal mask.
561133e27eSPeter Avalos * This costs a system call or two per setjmp(), so if possible we clear the
571133e27eSPeter Avalos * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead.
581133e27eSPeter Avalos * On other systems, setjmp() doesn't affect the signal mask and so
591133e27eSPeter Avalos * _setjmp() does not exist; we just use setjmp().
601133e27eSPeter Avalos */
611133e27eSPeter Avalos #if HAVE__SETJMP && HAVE_SIGSETMASK
621133e27eSPeter Avalos #define SET_JUMP _setjmp
631133e27eSPeter Avalos #define LONG_JUMP _longjmp
641133e27eSPeter Avalos #else
651133e27eSPeter Avalos #define SET_JUMP setjmp
661133e27eSPeter Avalos #define LONG_JUMP longjmp
671133e27eSPeter Avalos #endif
681133e27eSPeter Avalos
691133e27eSPeter Avalos public int reading;
70*e433da38SAaron LI public lbool waiting_for_data;
710c7ad07eSAntonio Huete Jimenez public int consecutive_nulls = 0;
721133e27eSPeter Avalos
73320d7c8aSAaron LI /* Milliseconds to wait for data before displaying "waiting for data" message. */
74320d7c8aSAaron LI static int waiting_for_data_delay = 4000;
751133e27eSPeter Avalos static jmp_buf read_label;
761133e27eSPeter Avalos
771133e27eSPeter Avalos extern int sigs;
780c7ad07eSAntonio Huete Jimenez extern int ignore_eoi;
790c7ad07eSAntonio Huete Jimenez extern int exit_F_on_close;
80320d7c8aSAaron LI extern int follow_mode;
81320d7c8aSAaron LI extern int scanning_eof;
82320d7c8aSAaron LI extern char intr_char;
830c7ad07eSAntonio Huete Jimenez #if !MSDOS_COMPILER
840c7ad07eSAntonio Huete Jimenez extern int tty;
850c7ad07eSAntonio Huete Jimenez #endif
86320d7c8aSAaron LI
init_poll(void)87320d7c8aSAaron LI public void init_poll(void)
88320d7c8aSAaron LI {
89*e433da38SAaron LI constant char *delay = lgetenv("LESS_DATA_DELAY");
90320d7c8aSAaron LI int idelay = (delay == NULL) ? 0 : atoi(delay);
91320d7c8aSAaron LI if (idelay > 0)
92320d7c8aSAaron LI waiting_for_data_delay = idelay;
93320d7c8aSAaron LI #if USE_POLL
94320d7c8aSAaron LI #if defined(__APPLE__)
95320d7c8aSAaron LI /* In old versions of MacOS, poll() does not work with /dev/tty. */
96320d7c8aSAaron LI struct utsname uts;
97320d7c8aSAaron LI if (uname(&uts) < 0 || lstrtoi(uts.release, NULL, 10) < 20)
98320d7c8aSAaron LI use_poll = FALSE;
99320d7c8aSAaron LI #endif
100320d7c8aSAaron LI #endif
101320d7c8aSAaron LI }
1020c7ad07eSAntonio Huete Jimenez
1030c7ad07eSAntonio Huete Jimenez #if USE_POLL
1040c7ad07eSAntonio Huete Jimenez /*
105320d7c8aSAaron LI * Check whether data is available, either from a file/pipe or from the tty.
106320d7c8aSAaron LI * Return READ_AGAIN if no data currently available, but caller should retry later.
107320d7c8aSAaron LI * Return READ_INTR to abort F command (forw_loop).
108320d7c8aSAaron LI * Return 0 if safe to read from fd.
1090c7ad07eSAntonio Huete Jimenez */
check_poll(int fd,int tty)110320d7c8aSAaron LI static int check_poll(int fd, int tty)
1110c7ad07eSAntonio Huete Jimenez {
112320d7c8aSAaron LI struct pollfd poller[2] = { { fd, POLLIN, 0 }, { tty, POLLIN, 0 } };
113320d7c8aSAaron LI int timeout = (waiting_for_data && !(scanning_eof && follow_mode == FOLLOW_NAME)) ? -1 : waiting_for_data_delay;
114320d7c8aSAaron LI if (!any_data)
115320d7c8aSAaron LI {
116320d7c8aSAaron LI /*
117320d7c8aSAaron LI * Don't do polling if no data has yet been received,
118320d7c8aSAaron LI * to allow a program piping data into less to have temporary
119320d7c8aSAaron LI * access to the tty (like sudo asking for a password).
120320d7c8aSAaron LI */
121320d7c8aSAaron LI return (0);
1220c7ad07eSAntonio Huete Jimenez }
123320d7c8aSAaron LI poll(poller, 2, timeout);
124320d7c8aSAaron LI #if LESSTEST
125*e433da38SAaron LI if (!is_lesstest()) /* Check for ^X only on a real tty. */
126320d7c8aSAaron LI #endif /*LESSTEST*/
127320d7c8aSAaron LI {
128320d7c8aSAaron LI if (poller[1].revents & POLLIN)
129320d7c8aSAaron LI {
130*e433da38SAaron LI int ch = getchr();
131*e433da38SAaron LI if (ch < 0 || ch == intr_char)
132320d7c8aSAaron LI /* Break out of "waiting for data". */
133320d7c8aSAaron LI return (READ_INTR);
134*e433da38SAaron LI ungetcc_back((char) ch);
135320d7c8aSAaron LI }
136320d7c8aSAaron LI }
137320d7c8aSAaron LI if (ignore_eoi && exit_F_on_close && (poller[0].revents & (POLLHUP|POLLIN)) == POLLHUP)
138320d7c8aSAaron LI /* Break out of F loop on HUP due to --exit-follow-on-close. */
139320d7c8aSAaron LI return (READ_INTR);
140320d7c8aSAaron LI if ((poller[0].revents & (POLLIN|POLLHUP|POLLERR)) == 0)
141320d7c8aSAaron LI /* No data available; let caller take action, then try again. */
142320d7c8aSAaron LI return (READ_AGAIN);
143320d7c8aSAaron LI /* There is data (or HUP/ERR) available. Safe to call read() without blocking. */
144320d7c8aSAaron LI return (0);
145320d7c8aSAaron LI }
146320d7c8aSAaron LI #endif /* USE_POLL */
147320d7c8aSAaron LI
supports_ctrl_x(void)148320d7c8aSAaron LI public int supports_ctrl_x(void)
149320d7c8aSAaron LI {
150*e433da38SAaron LI #if MSDOS_COMPILER==WIN32C
151*e433da38SAaron LI return (TRUE);
152*e433da38SAaron LI #else
153320d7c8aSAaron LI #if USE_POLL
154320d7c8aSAaron LI return (use_poll);
155320d7c8aSAaron LI #else
156320d7c8aSAaron LI return (FALSE);
157320d7c8aSAaron LI #endif /* USE_POLL */
158*e433da38SAaron LI #endif /* MSDOS_COMPILER==WIN32C */
159320d7c8aSAaron LI }
1601133e27eSPeter Avalos
1611133e27eSPeter Avalos /*
1621133e27eSPeter Avalos * Like read() system call, but is deliberately interruptible.
1631133e27eSPeter Avalos * A call to intread() from a signal handler will interrupt
1641133e27eSPeter Avalos * any pending iread().
1651133e27eSPeter Avalos */
iread(int fd,unsigned char * buf,size_t len)166*e433da38SAaron LI public ssize_t iread(int fd, unsigned char *buf, size_t len)
1671133e27eSPeter Avalos {
168*e433da38SAaron LI ssize_t n;
1691133e27eSPeter Avalos
1701133e27eSPeter Avalos start:
1711133e27eSPeter Avalos #if MSDOS_COMPILER==WIN32C
1721133e27eSPeter Avalos if (ABORT_SIGS())
1731133e27eSPeter Avalos return (READ_INTR);
1741133e27eSPeter Avalos #else
1751133e27eSPeter Avalos #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC
1761133e27eSPeter Avalos if (kbhit())
1771133e27eSPeter Avalos {
1781133e27eSPeter Avalos int c;
1791133e27eSPeter Avalos
1801133e27eSPeter Avalos c = getch();
1811133e27eSPeter Avalos if (c == '\003')
1821133e27eSPeter Avalos return (READ_INTR);
1831133e27eSPeter Avalos ungetch(c);
1841133e27eSPeter Avalos }
1851133e27eSPeter Avalos #endif
1861133e27eSPeter Avalos #endif
187320d7c8aSAaron LI if (!reading && SET_JUMP(read_label))
1881133e27eSPeter Avalos {
1891133e27eSPeter Avalos /*
1901133e27eSPeter Avalos * We jumped here from intread.
1911133e27eSPeter Avalos */
192*e433da38SAaron LI reading = FALSE;
1931133e27eSPeter Avalos #if HAVE_SIGPROCMASK
1941133e27eSPeter Avalos {
1951133e27eSPeter Avalos sigset_t mask;
1961133e27eSPeter Avalos sigemptyset(&mask);
1971133e27eSPeter Avalos sigprocmask(SIG_SETMASK, &mask, NULL);
1981133e27eSPeter Avalos }
1991133e27eSPeter Avalos #else
2001133e27eSPeter Avalos #if HAVE_SIGSETMASK
2011133e27eSPeter Avalos sigsetmask(0);
2021133e27eSPeter Avalos #else
2031133e27eSPeter Avalos #ifdef _OSK
2041133e27eSPeter Avalos sigmask(~0);
2051133e27eSPeter Avalos #endif
2061133e27eSPeter Avalos #endif
2071133e27eSPeter Avalos #endif
208320d7c8aSAaron LI #if !MSDOS_COMPILER
209320d7c8aSAaron LI if (fd != tty && !ABORT_SIGS())
210320d7c8aSAaron LI /* Non-interrupt signal like SIGWINCH. */
211320d7c8aSAaron LI return (READ_AGAIN);
212320d7c8aSAaron LI #endif
2131133e27eSPeter Avalos return (READ_INTR);
2141133e27eSPeter Avalos }
2151133e27eSPeter Avalos
2161133e27eSPeter Avalos flush();
217*e433da38SAaron LI reading = TRUE;
2181133e27eSPeter Avalos #if MSDOS_COMPILER==DJGPPC
2191133e27eSPeter Avalos if (isatty(fd))
2201133e27eSPeter Avalos {
2211133e27eSPeter Avalos /*
2221133e27eSPeter Avalos * Don't try reading from a TTY until a character is
2231133e27eSPeter Avalos * available, because that makes some background programs
2241133e27eSPeter Avalos * believe DOS is busy in a way that prevents those
2251133e27eSPeter Avalos * programs from working while "less" waits.
226320d7c8aSAaron LI * {{ This code was added 12 Jan 2007; still needed? }}
2271133e27eSPeter Avalos */
2281133e27eSPeter Avalos fd_set readfds;
2291133e27eSPeter Avalos
2301133e27eSPeter Avalos FD_ZERO(&readfds);
2311133e27eSPeter Avalos FD_SET(fd, &readfds);
2321133e27eSPeter Avalos if (select(fd+1, &readfds, 0, 0, 0) == -1)
2330c7ad07eSAntonio Huete Jimenez {
234*e433da38SAaron LI reading = FALSE;
235320d7c8aSAaron LI return (READ_ERR);
2361133e27eSPeter Avalos }
2370c7ad07eSAntonio Huete Jimenez }
2380c7ad07eSAntonio Huete Jimenez #endif
2390c7ad07eSAntonio Huete Jimenez #if USE_POLL
240320d7c8aSAaron LI if (fd != tty && use_poll)
2410c7ad07eSAntonio Huete Jimenez {
242320d7c8aSAaron LI int ret = check_poll(fd, tty);
243320d7c8aSAaron LI if (ret != 0)
2440c7ad07eSAntonio Huete Jimenez {
245320d7c8aSAaron LI if (ret == READ_INTR)
2460c7ad07eSAntonio Huete Jimenez sigs |= S_INTERRUPT;
247*e433da38SAaron LI reading = FALSE;
248320d7c8aSAaron LI return (ret);
2490c7ad07eSAntonio Huete Jimenez }
2500c7ad07eSAntonio Huete Jimenez }
2510c7ad07eSAntonio Huete Jimenez #else
2520c7ad07eSAntonio Huete Jimenez #if MSDOS_COMPILER==WIN32C
253320d7c8aSAaron LI if (win32_kbhit())
254320d7c8aSAaron LI {
255320d7c8aSAaron LI int c;
256320d7c8aSAaron LI
257320d7c8aSAaron LI c = WIN32getch();
258320d7c8aSAaron LI if (c == intr_char)
2590c7ad07eSAntonio Huete Jimenez {
2600c7ad07eSAntonio Huete Jimenez sigs |= S_INTERRUPT;
261*e433da38SAaron LI reading = FALSE;
2620c7ad07eSAntonio Huete Jimenez return (READ_INTR);
2630c7ad07eSAntonio Huete Jimenez }
264320d7c8aSAaron LI WIN32ungetch(c);
265320d7c8aSAaron LI }
2660c7ad07eSAntonio Huete Jimenez #endif
2671133e27eSPeter Avalos #endif
2681133e27eSPeter Avalos n = read(fd, buf, len);
269*e433da38SAaron LI reading = FALSE;
2701133e27eSPeter Avalos #if 1
2711133e27eSPeter Avalos /*
2721133e27eSPeter Avalos * This is a kludge to workaround a problem on some systems
2731133e27eSPeter Avalos * where terminating a remote tty connection causes read() to
2741133e27eSPeter Avalos * start returning 0 forever, instead of -1.
2751133e27eSPeter Avalos */
2761133e27eSPeter Avalos {
2771133e27eSPeter Avalos if (!ignore_eoi)
2781133e27eSPeter Avalos {
2791133e27eSPeter Avalos if (n == 0)
2801133e27eSPeter Avalos consecutive_nulls++;
2811133e27eSPeter Avalos else
2821133e27eSPeter Avalos consecutive_nulls = 0;
2831133e27eSPeter Avalos if (consecutive_nulls > 20)
2841133e27eSPeter Avalos quit(QUIT_ERROR);
2851133e27eSPeter Avalos }
2861133e27eSPeter Avalos }
2871133e27eSPeter Avalos #endif
2881133e27eSPeter Avalos if (n < 0)
2891133e27eSPeter Avalos {
2901133e27eSPeter Avalos #if HAVE_ERRNO
2911133e27eSPeter Avalos /*
2921133e27eSPeter Avalos * Certain values of errno indicate we should just retry the read.
2931133e27eSPeter Avalos */
2941133e27eSPeter Avalos #if MUST_DEFINE_ERRNO
2951133e27eSPeter Avalos extern int errno;
2961133e27eSPeter Avalos #endif
2971133e27eSPeter Avalos #ifdef EINTR
2981133e27eSPeter Avalos if (errno == EINTR)
2991133e27eSPeter Avalos goto start;
3001133e27eSPeter Avalos #endif
3011133e27eSPeter Avalos #ifdef EAGAIN
3021133e27eSPeter Avalos if (errno == EAGAIN)
3031133e27eSPeter Avalos goto start;
3041133e27eSPeter Avalos #endif
3051133e27eSPeter Avalos #endif
306320d7c8aSAaron LI return (READ_ERR);
3071133e27eSPeter Avalos }
308320d7c8aSAaron LI #if USE_POLL
309320d7c8aSAaron LI if (fd != tty && n > 0)
310320d7c8aSAaron LI any_data = TRUE;
311320d7c8aSAaron LI #endif
3121133e27eSPeter Avalos return (n);
3131133e27eSPeter Avalos }
3141133e27eSPeter Avalos
3151133e27eSPeter Avalos /*
3161133e27eSPeter Avalos * Interrupt a pending iread().
3171133e27eSPeter Avalos */
intread(void)318320d7c8aSAaron LI public void intread(void)
3191133e27eSPeter Avalos {
3201133e27eSPeter Avalos LONG_JUMP(read_label, 1);
3211133e27eSPeter Avalos }
3221133e27eSPeter Avalos
3231133e27eSPeter Avalos /*
3241133e27eSPeter Avalos * Return the current time.
3251133e27eSPeter Avalos */
3261133e27eSPeter Avalos #if HAVE_TIME
get_time(void)327320d7c8aSAaron LI public time_type get_time(void)
3281133e27eSPeter Avalos {
3291133e27eSPeter Avalos time_type t;
3301133e27eSPeter Avalos
3311133e27eSPeter Avalos time(&t);
3321133e27eSPeter Avalos return (t);
3331133e27eSPeter Avalos }
3341133e27eSPeter Avalos #endif
3351133e27eSPeter Avalos
3361133e27eSPeter Avalos
3371133e27eSPeter Avalos #if !HAVE_STRERROR
3381133e27eSPeter Avalos /*
3391133e27eSPeter Avalos * Local version of strerror, if not available from the system.
3401133e27eSPeter Avalos */
strerror(int err)341320d7c8aSAaron LI static char * strerror(int err)
3421133e27eSPeter Avalos {
3430c7ad07eSAntonio Huete Jimenez static char buf[INT_STRLEN_BOUND(int)+12];
3441133e27eSPeter Avalos #if HAVE_SYS_ERRLIST
3451133e27eSPeter Avalos extern char *sys_errlist[];
3461133e27eSPeter Avalos extern int sys_nerr;
3471133e27eSPeter Avalos
3481133e27eSPeter Avalos if (err < sys_nerr)
3491133e27eSPeter Avalos return sys_errlist[err];
3500c7ad07eSAntonio Huete Jimenez #endif
3511133e27eSPeter Avalos sprintf(buf, "Error %d", err);
3521133e27eSPeter Avalos return buf;
3531133e27eSPeter Avalos }
3541133e27eSPeter Avalos #endif
3551133e27eSPeter Avalos
3561133e27eSPeter Avalos /*
3571133e27eSPeter Avalos * errno_message: Return an error message based on the value of "errno".
3581133e27eSPeter Avalos */
errno_message(constant char * filename)359*e433da38SAaron LI public char * errno_message(constant char *filename)
3601133e27eSPeter Avalos {
36102d62a0fSDaniel Fojt char *p;
36202d62a0fSDaniel Fojt char *m;
363*e433da38SAaron LI size_t len;
3641133e27eSPeter Avalos #if HAVE_ERRNO
3651133e27eSPeter Avalos #if MUST_DEFINE_ERRNO
3661133e27eSPeter Avalos extern int errno;
3671133e27eSPeter Avalos #endif
3681133e27eSPeter Avalos p = strerror(errno);
3691133e27eSPeter Avalos #else
3701133e27eSPeter Avalos p = "cannot open";
3711133e27eSPeter Avalos #endif
372*e433da38SAaron LI len = strlen(filename) + strlen(p) + 3;
3731133e27eSPeter Avalos m = (char *) ecalloc(len, sizeof(char));
3741133e27eSPeter Avalos SNPRINTF2(m, len, "%s: %s", filename, p);
3751133e27eSPeter Avalos return (m);
3761133e27eSPeter Avalos }
3771133e27eSPeter Avalos
378320d7c8aSAaron LI /*
379320d7c8aSAaron LI * Return a description of a signal.
380320d7c8aSAaron LI * The return value is good until the next call to this function.
381320d7c8aSAaron LI */
signal_message(int sig)382*e433da38SAaron LI public constant char * signal_message(int sig)
38325ce721eSPeter Avalos {
384320d7c8aSAaron LI static char sigbuf[sizeof("Signal ") + INT_STRLEN_BOUND(sig) + 1];
385320d7c8aSAaron LI #if HAVE_STRSIGNAL
386*e433da38SAaron LI constant char *description = strsignal(sig);
387320d7c8aSAaron LI if (description)
388320d7c8aSAaron LI return description;
38925ce721eSPeter Avalos #endif
390320d7c8aSAaron LI sprintf(sigbuf, "Signal %d", sig);
391320d7c8aSAaron LI return sigbuf;
392320d7c8aSAaron LI }
393320d7c8aSAaron LI
394320d7c8aSAaron LI /*
395320d7c8aSAaron LI * Return (VAL * NUM) / DEN, where DEN is positive
396320d7c8aSAaron LI * and min(VAL, NUM) <= DEN so the result cannot overflow.
397320d7c8aSAaron LI * Round to the nearest integer, breaking ties by rounding to even.
398320d7c8aSAaron LI */
umuldiv(uintmax val,uintmax num,uintmax den)399*e433da38SAaron LI public uintmax umuldiv(uintmax val, uintmax num, uintmax den)
400320d7c8aSAaron LI {
401320d7c8aSAaron LI /*
402320d7c8aSAaron LI * Like round(val * (double) num / den), but without rounding error.
403320d7c8aSAaron LI * Overflow cannot occur, so there is no need for floating point.
404320d7c8aSAaron LI */
405320d7c8aSAaron LI uintmax q = val / den;
406320d7c8aSAaron LI uintmax r = val % den;
407320d7c8aSAaron LI uintmax qnum = q * num;
408320d7c8aSAaron LI uintmax rnum = r * num;
409320d7c8aSAaron LI uintmax quot = qnum + rnum / den;
410320d7c8aSAaron LI uintmax rem = rnum % den;
411320d7c8aSAaron LI return quot + (den / 2 < rem + (quot & ~den & 1));
41225ce721eSPeter Avalos }
41325ce721eSPeter Avalos
4141133e27eSPeter Avalos /*
4151133e27eSPeter Avalos * Return the ratio of two POSITIONS, as a percentage.
4161133e27eSPeter Avalos * {{ Assumes a POSITION is a long int. }}
4171133e27eSPeter Avalos */
percentage(POSITION num,POSITION den)418320d7c8aSAaron LI public int percentage(POSITION num, POSITION den)
4191133e27eSPeter Avalos {
420*e433da38SAaron LI return (int) muldiv(num, 100, den);
4211133e27eSPeter Avalos }
4221133e27eSPeter Avalos
4231133e27eSPeter Avalos /*
4241133e27eSPeter Avalos * Return the specified percentage of a POSITION.
425320d7c8aSAaron LI * Assume (0 <= POS && 0 <= PERCENT <= 100
426320d7c8aSAaron LI * && 0 <= FRACTION < (PERCENT == 100 ? 1 : NUM_FRAC_DENOM)),
427320d7c8aSAaron LI * so the result cannot overflow. Round to even.
4281133e27eSPeter Avalos */
percent_pos(POSITION pos,int percent,long fraction)429320d7c8aSAaron LI public POSITION percent_pos(POSITION pos, int percent, long fraction)
4301133e27eSPeter Avalos {
431320d7c8aSAaron LI /*
432320d7c8aSAaron LI * Change from percent (parts per 100)
433320d7c8aSAaron LI * to pctden (parts per 100 * NUM_FRAC_DENOM).
434320d7c8aSAaron LI */
435320d7c8aSAaron LI POSITION pctden = (percent * NUM_FRAC_DENOM) + fraction;
4361133e27eSPeter Avalos
437*e433da38SAaron LI return (POSITION) muldiv(pos, pctden, 100 * NUM_FRAC_DENOM);
4381133e27eSPeter Avalos }
4391133e27eSPeter Avalos
4401133e27eSPeter Avalos #if !HAVE_STRCHR
4411133e27eSPeter Avalos /*
4421133e27eSPeter Avalos * strchr is used by regexp.c.
4431133e27eSPeter Avalos */
strchr(char * s,char c)444320d7c8aSAaron LI char * strchr(char *s, char c)
4451133e27eSPeter Avalos {
4461133e27eSPeter Avalos for ( ; *s != '\0'; s++)
4471133e27eSPeter Avalos if (*s == c)
4481133e27eSPeter Avalos return (s);
4491133e27eSPeter Avalos if (c == '\0')
4501133e27eSPeter Avalos return (s);
4511133e27eSPeter Avalos return (NULL);
4521133e27eSPeter Avalos }
4531133e27eSPeter Avalos #endif
4541133e27eSPeter Avalos
4551133e27eSPeter Avalos #if !HAVE_MEMCPY
memcpy(void * dst,void * src,size_t len)456*e433da38SAaron LI void * memcpy(void *dst, void *src, size_t len)
4571133e27eSPeter Avalos {
4581133e27eSPeter Avalos char *dstp = (char *) dst;
4591133e27eSPeter Avalos char *srcp = (char *) src;
4601133e27eSPeter Avalos int i;
4611133e27eSPeter Avalos
4621133e27eSPeter Avalos for (i = 0; i < len; i++)
4631133e27eSPeter Avalos dstp[i] = srcp[i];
4641133e27eSPeter Avalos return (dst);
4651133e27eSPeter Avalos }
4661133e27eSPeter Avalos #endif
4671133e27eSPeter Avalos
4681133e27eSPeter Avalos #ifdef _OSK_MWC32
4691133e27eSPeter Avalos
4701133e27eSPeter Avalos /*
4711133e27eSPeter Avalos * This implements an ANSI-style intercept setup for Microware C 3.2
4721133e27eSPeter Avalos */
os9_signal(int type,RETSIGTYPE (* handler)())473320d7c8aSAaron LI public int os9_signal(int type, RETSIGTYPE (*handler)())
4741133e27eSPeter Avalos {
4751133e27eSPeter Avalos intercept(handler);
4761133e27eSPeter Avalos }
4771133e27eSPeter Avalos
4781133e27eSPeter Avalos #include <sgstat.h>
4791133e27eSPeter Avalos
isatty(int f)480320d7c8aSAaron LI int isatty(int f)
4811133e27eSPeter Avalos {
4821133e27eSPeter Avalos struct sgbuf sgbuf;
4831133e27eSPeter Avalos
4841133e27eSPeter Avalos if (_gs_opt(f, &sgbuf) < 0)
4851133e27eSPeter Avalos return -1;
4861133e27eSPeter Avalos return (sgbuf.sg_class == 0);
4871133e27eSPeter Avalos }
4881133e27eSPeter Avalos
4891133e27eSPeter Avalos #endif
4900c7ad07eSAntonio Huete Jimenez
sleep_ms(int ms)491320d7c8aSAaron LI public void sleep_ms(int ms)
4920c7ad07eSAntonio Huete Jimenez {
4930c7ad07eSAntonio Huete Jimenez #if MSDOS_COMPILER==WIN32C
4940c7ad07eSAntonio Huete Jimenez Sleep(ms);
4950c7ad07eSAntonio Huete Jimenez #else
4960c7ad07eSAntonio Huete Jimenez #if HAVE_NANOSLEEP
4970c7ad07eSAntonio Huete Jimenez int sec = ms / 1000;
4980c7ad07eSAntonio Huete Jimenez struct timespec t = { sec, (ms - sec*1000) * 1000000 };
4990c7ad07eSAntonio Huete Jimenez nanosleep(&t, NULL);
5000c7ad07eSAntonio Huete Jimenez #else
5010c7ad07eSAntonio Huete Jimenez #if HAVE_USLEEP
502*e433da38SAaron LI usleep(ms * 1000);
5030c7ad07eSAntonio Huete Jimenez #else
504320d7c8aSAaron LI sleep(ms / 1000 + (ms % 1000 != 0));
5050c7ad07eSAntonio Huete Jimenez #endif
5060c7ad07eSAntonio Huete Jimenez #endif
5070c7ad07eSAntonio Huete Jimenez #endif
5080c7ad07eSAntonio Huete Jimenez }
509