1/* 2 * Copyright (C) 1984-2021 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, see the README file. 8 */ 9 10 11/* 12 * Operating system dependent routines. 13 * 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. 18 * 19 * The makefile provides defines to decide whether various 20 * Unix features are present. 21 */ 22 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 38 39#if HAVE_POLL && !MSDOS_COMPILER && !defined(__APPLE__) 40#define USE_POLL 1 41#else 42#define USE_POLL 0 43#endif 44#if USE_POLL 45#include <poll.h> 46#endif 47 48/* 49 * BSD setjmp() saves (and longjmp() restores) the signal mask. 50 * This costs a system call or two per setjmp(), so if possible we clear the 51 * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead. 52 * On other systems, setjmp() doesn't affect the signal mask and so 53 * _setjmp() does not exist; we just use setjmp(). 54 */ 55#if HAVE__SETJMP && HAVE_SIGSETMASK 56#define SET_JUMP _setjmp 57#define LONG_JUMP _longjmp 58#else 59#define SET_JUMP setjmp 60#define LONG_JUMP longjmp 61#endif 62 63public int reading; 64 65static jmp_buf read_label; 66 67extern int sigs; 68extern int ignore_eoi; 69#if !MSDOS_COMPILER 70extern int tty; 71#endif 72 73#if USE_POLL 74/* 75 * Return true if one of the events has occurred on the specified file. 76 */ 77 static int 78poll_events(fd, events) 79 int fd; 80 int events; 81{ 82 struct pollfd poller = { fd, events, 0 }; 83 int n = poll(&poller, 1, 0); 84 if (n <= 0) 85 return 0; 86 return (poller.revents & events); 87} 88#endif 89 90/* 91 * Like read() system call, but is deliberately interruptible. 92 * A call to intread() from a signal handler will interrupt 93 * any pending iread(). 94 */ 95 public int 96iread(fd, buf, len) 97 int fd; 98 unsigned char *buf; 99 unsigned int len; 100{ 101 int n; 102 103start: 104#if MSDOS_COMPILER==WIN32C 105 if (ABORT_SIGS()) 106 return (READ_INTR); 107#else 108#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC 109 if (kbhit()) 110 { 111 int c; 112 113 c = getch(); 114 if (c == '\003') 115 return (READ_INTR); 116 ungetch(c); 117 } 118#endif 119#endif 120 if (SET_JUMP(read_label)) 121 { 122 /* 123 * We jumped here from intread. 124 */ 125 reading = 0; 126#if HAVE_SIGPROCMASK 127 { 128 sigset_t mask; 129 sigemptyset(&mask); 130 sigprocmask(SIG_SETMASK, &mask, NULL); 131 } 132#else 133#if HAVE_SIGSETMASK 134 sigsetmask(0); 135#else 136#ifdef _OSK 137 sigmask(~0); 138#endif 139#endif 140#endif 141 return (READ_INTR); 142 } 143 144 flush(); 145 reading = 1; 146#if MSDOS_COMPILER==DJGPPC 147 if (isatty(fd)) 148 { 149 /* 150 * Don't try reading from a TTY until a character is 151 * available, because that makes some background programs 152 * believe DOS is busy in a way that prevents those 153 * programs from working while "less" waits. 154 */ 155 fd_set readfds; 156 157 FD_ZERO(&readfds); 158 FD_SET(fd, &readfds); 159 if (select(fd+1, &readfds, 0, 0, 0) == -1) 160 return (-1); 161 } 162#endif 163#if USE_POLL 164 if (ignore_eoi && fd != tty) 165 { 166 if (poll_events(tty, POLLIN) && getchr() == CONTROL('X')) 167 { 168 sigs |= S_INTERRUPT; 169 return (READ_INTR); 170 } 171 if (poll_events(fd, POLLERR|POLLHUP)) 172 { 173 sigs |= S_INTERRUPT; 174 return (READ_INTR); 175 } 176 } 177#else 178#if MSDOS_COMPILER==WIN32C 179 if (win32_kbhit() && WIN32getch() == CONTROL('X')) 180 { 181 sigs |= S_INTERRUPT; 182 return (READ_INTR); 183 } 184#endif 185#endif 186 n = read(fd, buf, len); 187#if 1 188 /* 189 * This is a kludge to workaround a problem on some systems 190 * where terminating a remote tty connection causes read() to 191 * start returning 0 forever, instead of -1. 192 */ 193 { 194 if (!ignore_eoi) 195 { 196 static int consecutive_nulls = 0; 197 if (n == 0) 198 consecutive_nulls++; 199 else 200 consecutive_nulls = 0; 201 if (consecutive_nulls > 20) 202 quit(QUIT_ERROR); 203 } 204 } 205#endif 206 reading = 0; 207 if (n < 0) 208 { 209#if HAVE_ERRNO 210 /* 211 * Certain values of errno indicate we should just retry the read. 212 */ 213#if MUST_DEFINE_ERRNO 214 extern int errno; 215#endif 216#ifdef EINTR 217 if (errno == EINTR) 218 goto start; 219#endif 220#ifdef EAGAIN 221 if (errno == EAGAIN) 222 goto start; 223#endif 224#endif 225 return (-1); 226 } 227 return (n); 228} 229 230/* 231 * Interrupt a pending iread(). 232 */ 233 public void 234intread(VOID_PARAM) 235{ 236 LONG_JUMP(read_label, 1); 237} 238 239/* 240 * Return the current time. 241 */ 242#if HAVE_TIME 243 public time_type 244get_time(VOID_PARAM) 245{ 246 time_type t; 247 248 time(&t); 249 return (t); 250} 251#endif 252 253 254#if !HAVE_STRERROR 255/* 256 * Local version of strerror, if not available from the system. 257 */ 258 static char * 259strerror(err) 260 int err; 261{ 262 static char buf[16]; 263#if HAVE_SYS_ERRLIST 264 extern char *sys_errlist[]; 265 extern int sys_nerr; 266 267 if (err < sys_nerr) 268 return sys_errlist[err]; 269#endif 270 sprintf(buf, "Error %d", err); 271 return buf; 272} 273#endif 274 275/* 276 * errno_message: Return an error message based on the value of "errno". 277 */ 278 public char * 279errno_message(filename) 280 char *filename; 281{ 282 char *p; 283 char *m; 284 int len; 285#if HAVE_ERRNO 286#if MUST_DEFINE_ERRNO 287 extern int errno; 288#endif 289 p = strerror(errno); 290#else 291 p = "cannot open"; 292#endif 293 len = (int) (strlen(filename) + strlen(p) + 3); 294 m = (char *) ecalloc(len, sizeof(char)); 295 SNPRINTF2(m, len, "%s: %s", filename, p); 296 return (m); 297} 298 299/* #define HAVE_FLOAT 0 */ 300 301 static POSITION 302muldiv(val, num, den) 303 POSITION val, num, den; 304{ 305#if HAVE_FLOAT 306 double v = (((double) val) * num) / den; 307 return ((POSITION) (v + 0.5)); 308#else 309 POSITION v = ((POSITION) val) * num; 310 311 if (v / num == val) 312 /* No overflow */ 313 return (POSITION) (v / den); 314 else 315 /* Above calculation overflows; 316 * use a method that is less precise but won't overflow. */ 317 return (POSITION) (val / (den / num)); 318#endif 319} 320 321/* 322 * Return the ratio of two POSITIONS, as a percentage. 323 * {{ Assumes a POSITION is a long int. }} 324 */ 325 public int 326percentage(num, den) 327 POSITION num; 328 POSITION den; 329{ 330 return (int) muldiv(num, (POSITION) 100, den); 331} 332 333/* 334 * Return the specified percentage of a POSITION. 335 */ 336 public POSITION 337percent_pos(pos, percent, fraction) 338 POSITION pos; 339 int percent; 340 long fraction; 341{ 342 /* Change percent (parts per 100) to perden (parts per NUM_FRAC_DENOM). */ 343 POSITION perden = (percent * (NUM_FRAC_DENOM / 100)) + (fraction / 100); 344 345 if (perden == 0) 346 return (0); 347 return (POSITION) muldiv(pos, perden, (POSITION) NUM_FRAC_DENOM); 348} 349 350#if !HAVE_STRCHR 351/* 352 * strchr is used by regexp.c. 353 */ 354 char * 355strchr(s, c) 356 char *s; 357 int c; 358{ 359 for ( ; *s != '\0'; s++) 360 if (*s == c) 361 return (s); 362 if (c == '\0') 363 return (s); 364 return (NULL); 365} 366#endif 367 368#if !HAVE_MEMCPY 369 VOID_POINTER 370memcpy(dst, src, len) 371 VOID_POINTER dst; 372 VOID_POINTER src; 373 int len; 374{ 375 char *dstp = (char *) dst; 376 char *srcp = (char *) src; 377 int i; 378 379 for (i = 0; i < len; i++) 380 dstp[i] = srcp[i]; 381 return (dst); 382} 383#endif 384 385#ifdef _OSK_MWC32 386 387/* 388 * This implements an ANSI-style intercept setup for Microware C 3.2 389 */ 390 public int 391os9_signal(type, handler) 392 int type; 393 RETSIGTYPE (*handler)(); 394{ 395 intercept(handler); 396} 397 398#include <sgstat.h> 399 400 int 401isatty(f) 402 int f; 403{ 404 struct sgbuf sgbuf; 405 406 if (_gs_opt(f, &sgbuf) < 0) 407 return -1; 408 return (sgbuf.sg_class == 0); 409} 410 411#endif 412 413 public void 414sleep_ms(ms) 415 int ms; 416{ 417#if MSDOS_COMPILER==WIN32C 418 Sleep(ms); 419#else 420#if HAVE_NANOSLEEP 421 int sec = ms / 1000; 422 struct timespec t = { sec, (ms - sec*1000) * 1000000 }; 423 nanosleep(&t, NULL); 424#else 425#if HAVE_USLEEP 426 usleep(ms); 427#else 428 sleep((ms+999) / 1000); 429#endif 430#endif 431#endif 432} 433