1238730Sdelphij/* 2369759Sgit2svn * Copyright (C) 1984-2021 Mark Nudelman 3238730Sdelphij * 4238730Sdelphij * You may distribute under the terms of either the GNU General Public 5238730Sdelphij * License or the Less License, as specified in the README file. 6238730Sdelphij * 7238730Sdelphij * For more information, see the README file. 8238730Sdelphij */ 960786Sps 1060786Sps 1160786Sps/* 1260786Sps * Operating system dependent routines. 1360786Sps * 1460786Sps * Most of the stuff in here is based on Unix, but an attempt 1560786Sps * has been made to make things work on other operating systems. 1660786Sps * This will sometimes result in a loss of functionality, unless 1760786Sps * someone rewrites code specifically for the new operating system. 1860786Sps * 1960786Sps * The makefile provides defines to decide whether various 2060786Sps * Unix features are present. 2160786Sps */ 2260786Sps 2360786Sps#include "less.h" 2460786Sps#include <signal.h> 2560786Sps#include <setjmp.h> 26369759Sgit2svn#if MSDOS_COMPILER==WIN32C 27369759Sgit2svn#include <windows.h> 28369759Sgit2svn#endif 2960786Sps#if HAVE_TIME_H 3060786Sps#include <time.h> 3160786Sps#endif 3260786Sps#if HAVE_ERRNO_H 3360786Sps#include <errno.h> 3460786Sps#endif 3560786Sps#if HAVE_VALUES_H 3660786Sps#include <values.h> 3760786Sps#endif 3860786Sps 39369759Sgit2svn#if HAVE_POLL && !MSDOS_COMPILER && !defined(__APPLE__) 40369759Sgit2svn#define USE_POLL 1 41369759Sgit2svn#else 42369759Sgit2svn#define USE_POLL 0 43369759Sgit2svn#endif 44369759Sgit2svn#if USE_POLL 45369759Sgit2svn#include <poll.h> 46369759Sgit2svn#endif 47369759Sgit2svn 4860786Sps/* 4960786Sps * BSD setjmp() saves (and longjmp() restores) the signal mask. 5060786Sps * This costs a system call or two per setjmp(), so if possible we clear the 5160786Sps * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead. 5260786Sps * On other systems, setjmp() doesn't affect the signal mask and so 5360786Sps * _setjmp() does not exist; we just use setjmp(). 5460786Sps */ 5560786Sps#if HAVE__SETJMP && HAVE_SIGSETMASK 56369759Sgit2svn#define SET_JUMP _setjmp 57369759Sgit2svn#define LONG_JUMP _longjmp 5860786Sps#else 59369759Sgit2svn#define SET_JUMP setjmp 60369759Sgit2svn#define LONG_JUMP longjmp 6160786Sps#endif 6260786Sps 6360786Spspublic int reading; 6460786Sps 6560786Spsstatic jmp_buf read_label; 6660786Sps 6760786Spsextern int sigs; 68369759Sgit2svnextern int ignore_eoi; 69369759Sgit2svn#if !MSDOS_COMPILER 70369759Sgit2svnextern int tty; 71369759Sgit2svn#endif 7260786Sps 73369759Sgit2svn#if USE_POLL 7460786Sps/* 75369759Sgit2svn * Return true if one of the events has occurred on the specified file. 76369759Sgit2svn */ 77369759Sgit2svn static int 78369759Sgit2svnpoll_events(fd, events) 79369759Sgit2svn int fd; 80369759Sgit2svn int events; 81369759Sgit2svn{ 82369759Sgit2svn struct pollfd poller = { fd, events, 0 }; 83369759Sgit2svn int n = poll(&poller, 1, 0); 84369759Sgit2svn if (n <= 0) 85369759Sgit2svn return 0; 86369759Sgit2svn return (poller.revents & events); 87369759Sgit2svn} 88369759Sgit2svn#endif 89369759Sgit2svn 90369759Sgit2svn/* 9160786Sps * Like read() system call, but is deliberately interruptible. 9260786Sps * A call to intread() from a signal handler will interrupt 9360786Sps * any pending iread(). 9460786Sps */ 9560786Sps public int 9660786Spsiread(fd, buf, len) 9760786Sps int fd; 98330570Sdelphij unsigned char *buf; 9960786Sps unsigned int len; 10060786Sps{ 101330570Sdelphij int n; 10260786Sps 103170256Sdelphijstart: 10460786Sps#if MSDOS_COMPILER==WIN32C 10560786Sps if (ABORT_SIGS()) 10660786Sps return (READ_INTR); 10760786Sps#else 10860786Sps#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC 10960786Sps if (kbhit()) 11060786Sps { 11160786Sps int c; 11260786Sps 11360786Sps c = getch(); 11460786Sps if (c == '\003') 11560786Sps return (READ_INTR); 11660786Sps ungetch(c); 11760786Sps } 11860786Sps#endif 11960786Sps#endif 12060786Sps if (SET_JUMP(read_label)) 12160786Sps { 12260786Sps /* 12360786Sps * We jumped here from intread. 12460786Sps */ 12560786Sps reading = 0; 12663128Sps#if HAVE_SIGPROCMASK 12763128Sps { 12863128Sps sigset_t mask; 12963128Sps sigemptyset(&mask); 13063128Sps sigprocmask(SIG_SETMASK, &mask, NULL); 13163128Sps } 13263128Sps#else 13360786Sps#if HAVE_SIGSETMASK 13460786Sps sigsetmask(0); 13560786Sps#else 13660786Sps#ifdef _OSK 13760786Sps sigmask(~0); 13860786Sps#endif 13960786Sps#endif 14063128Sps#endif 14160786Sps return (READ_INTR); 14260786Sps } 14360786Sps 14460786Sps flush(); 14560786Sps reading = 1; 14660786Sps#if MSDOS_COMPILER==DJGPPC 14760786Sps if (isatty(fd)) 14860786Sps { 14960786Sps /* 15060786Sps * Don't try reading from a TTY until a character is 15160786Sps * available, because that makes some background programs 15260786Sps * believe DOS is busy in a way that prevents those 15360786Sps * programs from working while "less" waits. 15460786Sps */ 15560786Sps fd_set readfds; 15660786Sps 15760786Sps FD_ZERO(&readfds); 15860786Sps FD_SET(fd, &readfds); 15960786Sps if (select(fd+1, &readfds, 0, 0, 0) == -1) 16060786Sps return (-1); 16160786Sps } 16260786Sps#endif 163369759Sgit2svn#if USE_POLL 164369759Sgit2svn if (ignore_eoi && fd != tty) 165369759Sgit2svn { 166369759Sgit2svn if (poll_events(tty, POLLIN) && getchr() == CONTROL('X')) 167369759Sgit2svn { 168369759Sgit2svn sigs |= S_INTERRUPT; 169369759Sgit2svn return (READ_INTR); 170369759Sgit2svn } 171369759Sgit2svn if (poll_events(fd, POLLERR|POLLHUP)) 172369759Sgit2svn { 173369759Sgit2svn sigs |= S_INTERRUPT; 174369759Sgit2svn return (READ_INTR); 175369759Sgit2svn } 176369759Sgit2svn } 177369759Sgit2svn#else 178369759Sgit2svn#if MSDOS_COMPILER==WIN32C 179369759Sgit2svn if (win32_kbhit() && WIN32getch() == CONTROL('X')) 180369759Sgit2svn { 181369759Sgit2svn sigs |= S_INTERRUPT; 182369759Sgit2svn return (READ_INTR); 183369759Sgit2svn } 184369759Sgit2svn#endif 185369759Sgit2svn#endif 18660786Sps n = read(fd, buf, len); 18760786Sps#if 1 18860786Sps /* 18960786Sps * This is a kludge to workaround a problem on some systems 19060786Sps * where terminating a remote tty connection causes read() to 19160786Sps * start returning 0 forever, instead of -1. 19260786Sps */ 19360786Sps { 19460786Sps if (!ignore_eoi) 19560786Sps { 19660786Sps static int consecutive_nulls = 0; 19760786Sps if (n == 0) 19860786Sps consecutive_nulls++; 19960786Sps else 20060786Sps consecutive_nulls = 0; 20160786Sps if (consecutive_nulls > 20) 20260786Sps quit(QUIT_ERROR); 20360786Sps } 20460786Sps } 20560786Sps#endif 20660786Sps reading = 0; 20760786Sps if (n < 0) 208170256Sdelphij { 209170256Sdelphij#if HAVE_ERRNO 210170256Sdelphij /* 211170256Sdelphij * Certain values of errno indicate we should just retry the read. 212170256Sdelphij */ 213170256Sdelphij#if MUST_DEFINE_ERRNO 214170256Sdelphij extern int errno; 215170256Sdelphij#endif 216170256Sdelphij#ifdef EINTR 217170256Sdelphij if (errno == EINTR) 218170256Sdelphij goto start; 219170256Sdelphij#endif 220170256Sdelphij#ifdef EAGAIN 221170256Sdelphij if (errno == EAGAIN) 222170256Sdelphij goto start; 223170256Sdelphij#endif 224170256Sdelphij#endif 22560786Sps return (-1); 226170256Sdelphij } 22760786Sps return (n); 22860786Sps} 22960786Sps 23060786Sps/* 23160786Sps * Interrupt a pending iread(). 23260786Sps */ 23360786Sps public void 234355504Sdelphijintread(VOID_PARAM) 23560786Sps{ 23660786Sps LONG_JUMP(read_label, 1); 23760786Sps} 23860786Sps 23960786Sps/* 24060786Sps * Return the current time. 24160786Sps */ 24260786Sps#if HAVE_TIME 243293190Sdelphij public time_type 244355504Sdelphijget_time(VOID_PARAM) 24560786Sps{ 24660786Sps time_type t; 24760786Sps 24860786Sps time(&t); 24960786Sps return (t); 25060786Sps} 25160786Sps#endif 25260786Sps 25360786Sps 25460786Sps#if !HAVE_STRERROR 25560786Sps/* 25660786Sps * Local version of strerror, if not available from the system. 25760786Sps */ 25860786Sps static char * 25960786Spsstrerror(err) 26060786Sps int err; 26160786Sps{ 262369759Sgit2svn static char buf[16]; 26360786Sps#if HAVE_SYS_ERRLIST 26460786Sps extern char *sys_errlist[]; 26560786Sps extern int sys_nerr; 26660786Sps 26760786Sps if (err < sys_nerr) 26860786Sps return sys_errlist[err]; 269369759Sgit2svn#endif 27060786Sps sprintf(buf, "Error %d", err); 27160786Sps return buf; 27260786Sps} 27360786Sps#endif 27460786Sps 27560786Sps/* 27660786Sps * errno_message: Return an error message based on the value of "errno". 27760786Sps */ 27860786Sps public char * 27960786Spserrno_message(filename) 28060786Sps char *filename; 28160786Sps{ 282330570Sdelphij char *p; 283330570Sdelphij char *m; 284161475Sdelphij int len; 28560786Sps#if HAVE_ERRNO 28660786Sps#if MUST_DEFINE_ERRNO 28760786Sps extern int errno; 28860786Sps#endif 28960786Sps p = strerror(errno); 29060786Sps#else 29160786Sps p = "cannot open"; 29260786Sps#endif 293293190Sdelphij len = (int) (strlen(filename) + strlen(p) + 3); 294161475Sdelphij m = (char *) ecalloc(len, sizeof(char)); 295161475Sdelphij SNPRINTF2(m, len, "%s: %s", filename, p); 29660786Sps return (m); 29760786Sps} 29860786Sps 299221715Sdelphij/* #define HAVE_FLOAT 0 */ 300221715Sdelphij 301221715Sdelphij static POSITION 302221715Sdelphijmuldiv(val, num, den) 303221715Sdelphij POSITION val, num, den; 304221715Sdelphij{ 305221715Sdelphij#if HAVE_FLOAT 306221715Sdelphij double v = (((double) val) * num) / den; 307221715Sdelphij return ((POSITION) (v + 0.5)); 308221715Sdelphij#else 309221715Sdelphij POSITION v = ((POSITION) val) * num; 310221715Sdelphij 311221715Sdelphij if (v / num == val) 312221715Sdelphij /* No overflow */ 313221715Sdelphij return (POSITION) (v / den); 314221715Sdelphij else 315221715Sdelphij /* Above calculation overflows; 316221715Sdelphij * use a method that is less precise but won't overflow. */ 317221715Sdelphij return (POSITION) (val / (den / num)); 318221715Sdelphij#endif 319221715Sdelphij} 320221715Sdelphij 32160786Sps/* 32260786Sps * Return the ratio of two POSITIONS, as a percentage. 32360786Sps * {{ Assumes a POSITION is a long int. }} 32460786Sps */ 32560786Sps public int 32660786Spspercentage(num, den) 327330570Sdelphij POSITION num; 328330570Sdelphij POSITION den; 32960786Sps{ 330221715Sdelphij return (int) muldiv(num, (POSITION) 100, den); 33160786Sps} 33260786Sps 33360786Sps/* 33460786Sps * Return the specified percentage of a POSITION. 33560786Sps */ 33660786Sps public POSITION 337170256Sdelphijpercent_pos(pos, percent, fraction) 33860786Sps POSITION pos; 33960786Sps int percent; 340170256Sdelphij long fraction; 34160786Sps{ 342170256Sdelphij /* Change percent (parts per 100) to perden (parts per NUM_FRAC_DENOM). */ 343221715Sdelphij POSITION perden = (percent * (NUM_FRAC_DENOM / 100)) + (fraction / 100); 34489019Sps 345170256Sdelphij if (perden == 0) 34689019Sps return (0); 347221715Sdelphij return (POSITION) muldiv(pos, perden, (POSITION) NUM_FRAC_DENOM); 34860786Sps} 34960786Sps 35089019Sps#if !HAVE_STRCHR 35189019Sps/* 35289019Sps * strchr is used by regexp.c. 35389019Sps */ 35489019Sps char * 35589019Spsstrchr(s, c) 35689019Sps char *s; 35789019Sps int c; 35889019Sps{ 35989019Sps for ( ; *s != '\0'; s++) 36089019Sps if (*s == c) 36189019Sps return (s); 36289019Sps if (c == '\0') 36389019Sps return (s); 36489019Sps return (NULL); 36589019Sps} 36689019Sps#endif 36789019Sps 36889019Sps#if !HAVE_MEMCPY 36989019Sps VOID_POINTER 37089019Spsmemcpy(dst, src, len) 37189019Sps VOID_POINTER dst; 37289019Sps VOID_POINTER src; 37389019Sps int len; 37489019Sps{ 37589019Sps char *dstp = (char *) dst; 37689019Sps char *srcp = (char *) src; 37789019Sps int i; 37889019Sps 37989019Sps for (i = 0; i < len; i++) 38089019Sps dstp[i] = srcp[i]; 38189019Sps return (dst); 38289019Sps} 38389019Sps#endif 38489019Sps 38560786Sps#ifdef _OSK_MWC32 38660786Sps 38760786Sps/* 38860786Sps * This implements an ANSI-style intercept setup for Microware C 3.2 38960786Sps */ 39060786Sps public int 39160786Spsos9_signal(type, handler) 39260786Sps int type; 39360786Sps RETSIGTYPE (*handler)(); 39460786Sps{ 39560786Sps intercept(handler); 39660786Sps} 39760786Sps 39860786Sps#include <sgstat.h> 39960786Sps 40089019Sps int 40160786Spsisatty(f) 40260786Sps int f; 40360786Sps{ 40460786Sps struct sgbuf sgbuf; 40560786Sps 40660786Sps if (_gs_opt(f, &sgbuf) < 0) 40760786Sps return -1; 40860786Sps return (sgbuf.sg_class == 0); 40960786Sps} 41060786Sps 41160786Sps#endif 412369759Sgit2svn 413369759Sgit2svn public void 414369759Sgit2svnsleep_ms(ms) 415369759Sgit2svn int ms; 416369759Sgit2svn{ 417369759Sgit2svn#if MSDOS_COMPILER==WIN32C 418369759Sgit2svn Sleep(ms); 419369759Sgit2svn#else 420369759Sgit2svn#if HAVE_NANOSLEEP 421369759Sgit2svn int sec = ms / 1000; 422369759Sgit2svn struct timespec t = { sec, (ms - sec*1000) * 1000000 }; 423369759Sgit2svn nanosleep(&t, NULL); 424369759Sgit2svn#else 425369759Sgit2svn#if HAVE_USLEEP 426369759Sgit2svn usleep(ms); 427369759Sgit2svn#else 428369759Sgit2svn sleep((ms+999) / 1000); 429369759Sgit2svn#endif 430369759Sgit2svn#endif 431369759Sgit2svn#endif 432369759Sgit2svn} 433