1 /* 2 * Copyright (C) 1984-2026 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 MUST_DEFINE_ERRNO 36 extern int errno; 37 #endif 38 #if HAVE_VALUES_H 39 #include <values.h> 40 #endif 41 42 #if defined(__APPLE__) 43 #include <sys/utsname.h> 44 #endif 45 46 #if HAVE_POLL && !MSDOS_COMPILER && !defined(__MVS__) 47 #define USE_POLL 1 48 static lbool use_poll = TRUE; 49 #else 50 #define USE_POLL 0 51 #endif 52 #if USE_POLL 53 #include <poll.h> 54 static lbool any_data = FALSE; 55 #endif 56 57 /* 58 * BSD setjmp() saves (and longjmp() restores) the signal mask. 59 * This costs a system call or two per setjmp(), so if possible we clear the 60 * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead. 61 * On other systems, setjmp() doesn't affect the signal mask and so 62 * _setjmp() does not exist; we just use setjmp(). 63 */ 64 #if HAVE_SIGSETJMP 65 #define SET_JUMP(label) sigsetjmp(label, 1) 66 #define LONG_JUMP(label, val) siglongjmp(label, val) 67 #define JUMP_BUF sigjmp_buf 68 #else 69 #if HAVE__SETJMP && HAVE_SIGSETMASK 70 #define SET_JUMP(label) _setjmp(label) 71 #define LONG_JUMP(label, val) _longjmp(label, val) 72 #define JUMP_BUF jmp_buf 73 #else 74 #define SET_JUMP(label) setjmp(label) 75 #define LONG_JUMP(label, val) longjmp(label, val) 76 #define JUMP_BUF jmp_buf 77 #endif 78 #endif 79 80 static lbool reading; 81 static lbool opening; 82 public lbool waiting_for_data; 83 public int consecutive_nulls = 0; 84 public lbool getting_one_screen = FALSE; 85 86 /* Milliseconds to wait for data before displaying "waiting for data" message. */ 87 static int waiting_for_data_delay = 4000; 88 /* Max milliseconds expected to "normally" read and display a screen of text. */ 89 public int screenfill_ms = 3000; 90 91 static JUMP_BUF read_label; 92 static JUMP_BUF open_label; 93 94 extern int sigs; 95 extern lbool ignore_eoi; 96 extern int exit_F_on_close; 97 extern int follow_mode; 98 extern int scanning_eof; 99 extern char intr_char; 100 extern int is_tty; 101 extern int quit_if_one_screen; 102 extern int one_screen; 103 #if HAVE_TIME 104 extern time_type less_start_time; 105 #endif 106 #if LESS_IREAD_TTY 107 extern int tty; 108 #endif 109 110 public void init_poll(void) 111 { 112 constant char *delay = lgetenv("LESS_DATA_DELAY"); 113 int idelay = (delay == NULL) ? 0 : atoi(delay); 114 if (idelay > 0) 115 waiting_for_data_delay = idelay; 116 delay = lgetenv("LESS_SCREENFILL_TIME"); 117 idelay = (delay == NULL) ? 0 : atoi(delay); 118 if (idelay > 0) 119 screenfill_ms = idelay; 120 #if USE_POLL 121 #if defined(__APPLE__) 122 /* In old versions of MacOS, poll() does not work with /dev/tty. */ 123 struct utsname uts; 124 if (uname(&uts) < 0 || lstrtoi(uts.release, NULL, 10) < 20) 125 use_poll = FALSE; 126 #endif 127 #endif 128 } 129 130 #if USE_POLL 131 /* 132 * Check whether data is available, either from a file/pipe or from the tty. 133 * Return READ_AGAIN if no data currently available, but caller should retry later. 134 * Return READ_INTR to abort F command (forw_loop). 135 * Return 0 if safe to read from fd. 136 */ 137 static int check_poll(int fd, int tty) 138 { 139 struct pollfd poller[2] = { { fd, POLLIN, 0 }, { tty, POLLIN, 0 } }; 140 int timeout = (waiting_for_data && !(scanning_eof && follow_mode == FOLLOW_NAME)) ? -1 : (ignore_eoi && !waiting_for_data) ? 0 : waiting_for_data_delay; 141 #if HAVE_TIME 142 if (getting_one_screen && get_time() < less_start_time + screenfill_ms/1000) 143 return (0); 144 #endif 145 if (!any_data) 146 { 147 /* 148 * Don't do polling if no data has yet been received, 149 * to allow a program piping data into less to have temporary 150 * access to the tty (like sudo asking for a password). 151 */ 152 return (0); 153 } 154 poll(poller, 2, timeout); 155 #if LESSTEST 156 if (!is_lesstest()) /* Check for ^X only on a real tty. */ 157 #endif /*LESSTEST*/ 158 { 159 if (poller[1].revents & POLLIN) 160 { 161 int ch = getchr(); 162 if (ch < 0 || ch == intr_char) 163 /* Break out of "waiting for data". */ 164 return (READ_INTR); 165 ungetcc_back((char) ch); 166 } 167 } 168 if (ignore_eoi && exit_F_on_close && (poller[0].revents & (POLLHUP|POLLIN)) == POLLHUP) 169 /* Break out of F loop on HUP due to --exit-follow-on-close. */ 170 return (READ_INTR); 171 if ((poller[0].revents & (POLLIN|POLLHUP|POLLERR)) == 0) 172 /* No data available; let caller take action, then try again. */ 173 return (READ_AGAIN); 174 /* There is data (or HUP/ERR) available. Safe to call read() without blocking. */ 175 return (0); 176 } 177 #endif /* USE_POLL */ 178 179 /* 180 * Is a character available to be read from the tty? 181 */ 182 public lbool ttyin_ready(void) 183 { 184 #if MSDOS_COMPILER==WIN32C 185 return win32_kbhit(); 186 #else 187 #if MSDOS_COMPILER 188 return kbhit(); 189 #else 190 #if USE_POLL 191 #if LESSTEST 192 if (is_lesstest()) 193 return FALSE; 194 #endif /*LESSTEST*/ 195 if (!use_poll) 196 return FALSE; 197 { 198 /* {{ assert LESS_IREAD_TTY }} */ 199 struct pollfd poller[1] = { { tty, POLLIN, 0 } }; 200 poll(poller, 1, 0); 201 return ((poller[0].revents & POLLIN) != 0); 202 } 203 #else 204 return FALSE; 205 #endif 206 #endif 207 #endif 208 } 209 210 public lbool supports_ctrl_x(void) 211 { 212 #if MSDOS_COMPILER==WIN32C 213 return (TRUE); 214 #else 215 #if USE_POLL 216 return (use_poll); 217 #else 218 return (FALSE); 219 #endif /* USE_POLL */ 220 #endif /* MSDOS_COMPILER==WIN32C */ 221 } 222 223 /* 224 * Like read() system call, but is deliberately interruptible. 225 * A call to intio() from a signal handler will interrupt 226 * any pending iread(). 227 */ 228 public ssize_t iread(int fd, unsigned char *buf, size_t len) 229 { 230 ssize_t n; 231 232 start: 233 #if MSDOS_COMPILER==WIN32C 234 if (ABORT_SIGS()) 235 return (READ_INTR); 236 #else 237 #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC 238 if (kbhit()) 239 { 240 int c; 241 242 c = getch(); 243 if (c == '\003') 244 return (READ_INTR); 245 ungetch(c); 246 } 247 #endif 248 #endif 249 if (!reading && SET_JUMP(read_label)) 250 { 251 /* 252 * We jumped here from intio. 253 */ 254 reading = FALSE; 255 #if HAVE_SIGPROCMASK 256 { 257 sigset_t mask; 258 sigemptyset(&mask); 259 sigprocmask(SIG_SETMASK, &mask, NULL); 260 } 261 #else 262 #if HAVE_SIGSETMASK 263 sigsetmask(0); 264 #else 265 #ifdef _OSK 266 sigmask(~0); 267 #endif 268 #endif 269 #endif 270 #if !MSDOS_COMPILER /* {{ LESS_IREAD_TTY? }} */ 271 if (fd != tty && !ABORT_SIGS()) 272 /* Non-interrupt signal like SIGWINCH. */ 273 return (READ_AGAIN); 274 #endif 275 return (READ_INTR); 276 } 277 278 flush(); 279 reading = TRUE; 280 #if MSDOS_COMPILER==DJGPPC 281 if (isatty(fd)) 282 { 283 /* 284 * Don't try reading from a TTY until a character is 285 * available, because that makes some background programs 286 * believe DOS is busy in a way that prevents those 287 * programs from working while "less" waits. 288 * {{ This code was added 12 Jan 2007; still needed? }} 289 */ 290 fd_set readfds; 291 292 FD_ZERO(&readfds); 293 FD_SET(fd, &readfds); 294 if (select(fd+1, &readfds, 0, 0, 0) == -1) 295 { 296 reading = FALSE; 297 return (READ_ERR); 298 } 299 } 300 #endif 301 #if USE_POLL 302 if (is_tty && fd != tty && use_poll && !(quit_if_one_screen && one_screen)) 303 { 304 int ret = check_poll(fd, tty); 305 if (ret != 0) 306 { 307 if (ret == READ_INTR) 308 sigs |= S_SWINTERRUPT; 309 reading = FALSE; 310 return (ret); 311 } 312 } 313 #else 314 #if MSDOS_COMPILER==WIN32C 315 if (!(quit_if_one_screen && one_screen) && win32_kbhit2(TRUE)) 316 { 317 int c = WIN32getch(); 318 if (c == CONTROL('C') || c == intr_char) 319 { 320 sigs |= S_SWINTERRUPT; 321 reading = FALSE; 322 return (READ_INTR); 323 } 324 WIN32ungetch(c); 325 } 326 #endif 327 #endif 328 n = read(fd, buf, len); 329 reading = FALSE; 330 #if 0 331 /* 332 * This is a kludge to workaround a problem on some systems 333 * where terminating a remote tty connection causes read() to 334 * start returning 0 forever, instead of -1. 335 */ 336 { 337 if (!ignore_eoi) 338 { 339 if (n == 0) 340 consecutive_nulls++; 341 else 342 consecutive_nulls = 0; 343 if (consecutive_nulls > 20) 344 quit(QUIT_ERROR); 345 } 346 } 347 #endif 348 if (n < 0) 349 { 350 #if HAVE_ERRNO 351 /* 352 * Certain values of errno indicate we should just retry the read. 353 */ 354 #ifdef EINTR 355 if (errno == EINTR) 356 goto start; 357 #endif 358 #ifdef EAGAIN 359 if (errno == EAGAIN) 360 goto start; 361 #endif 362 #endif 363 return (READ_ERR); 364 } 365 #if LESS_IREAD_TTY 366 if (fd != tty) 367 #endif 368 { 369 if (n > 0) 370 polling_ok(); 371 } 372 return (n); 373 } 374 375 /* 376 * Like open() system call, but is interruptible. 377 */ 378 public int iopen(constant char *filename, int flags) 379 { 380 int r; 381 while (!opening && SET_JUMP(open_label)) 382 { 383 opening = FALSE; 384 if (sigs & (S_INTERRUPT|S_SWINTERRUPT)) 385 { 386 sigs = 0; 387 #if HAVE_SETTABLE_ERRNO 388 #ifdef EINTR 389 errno = EINTR; 390 #endif 391 #endif 392 return -1; 393 } 394 psignals(); /* Handle S_STOP or S_WINCH */ 395 } 396 opening = TRUE; 397 r = open(filename, flags); 398 opening = FALSE; 399 return r; 400 } 401 402 /* 403 * Interrupt a pending iopen() or iread(). 404 */ 405 public void intio(void) 406 { 407 if (opening) 408 { 409 LONG_JUMP(open_label, 1); 410 } 411 if (reading) 412 { 413 LONG_JUMP(read_label, 1); 414 } 415 } 416 417 /* 418 * We can start polling the input file. 419 */ 420 public void polling_ok(void) 421 { 422 #if USE_POLL 423 any_data = TRUE; 424 #endif 425 } 426 427 /* 428 * Return the current time. 429 */ 430 #if HAVE_TIME 431 public time_type get_time(void) 432 { 433 time_type t; 434 435 time(&t); 436 return (t); 437 } 438 #endif 439 440 441 #if !HAVE_STRERROR 442 /* 443 * Local version of strerror, if not available from the system. 444 */ 445 static char * strerror(int err) 446 { 447 static char buf[INT_STRLEN_BOUND(int)+12]; 448 #if HAVE_SYS_ERRLIST 449 extern char *sys_errlist[]; 450 extern int sys_nerr; 451 452 if (err < sys_nerr) 453 return sys_errlist[err]; 454 #endif 455 sprintf(buf, "Error %d", err); 456 return buf; 457 } 458 #endif 459 460 /* 461 * errno_message: Return an error message based on the value of "errno". 462 */ 463 public char * errno_message(constant char *filename) 464 { 465 char *p; 466 char *m; 467 size_t len; 468 #if HAVE_ERRNO 469 p = strerror(errno); 470 #else 471 p = "cannot open"; 472 #endif 473 len = strlen(filename) + strlen(p) + 3; 474 m = (char *) ecalloc(len, sizeof(char)); 475 SNPRINTF2(m, len, "%s: %s", filename, p); 476 return (m); 477 } 478 479 /* 480 * Return a description of a signal. 481 * The return value is good until the next call to this function. 482 */ 483 public constant char * signal_message(int sig) 484 { 485 static char sigbuf[sizeof("Signal ") + INT_STRLEN_BOUND(sig) + 1]; 486 #if HAVE_STRSIGNAL 487 constant char *description = strsignal(sig); 488 if (description) 489 return description; 490 #endif 491 sprintf(sigbuf, "Signal %d", sig); 492 return sigbuf; 493 } 494 495 /* 496 * Return (VAL * NUM) / DEN, where DEN is positive 497 * and min(VAL, NUM) <= DEN so the result cannot overflow. 498 * Round to the nearest integer, breaking ties by rounding to even. 499 */ 500 public uintmax umuldiv(uintmax val, uintmax num, uintmax den) 501 { 502 /* 503 * Like round(val * (double) num / den), but without rounding error. 504 * Overflow cannot occur, so there is no need for floating point. 505 */ 506 uintmax q = val / den; 507 uintmax r = val % den; 508 uintmax qnum = q * num; 509 uintmax rnum = r * num; 510 uintmax quot = qnum + rnum / den; 511 uintmax rem = rnum % den; 512 return quot + (den / 2 < rem + (quot & ~den & 1)); 513 } 514 515 /* 516 * Return the ratio of two POSITIONS, as a percentage. 517 * {{ Assumes a POSITION is a long int. }} 518 */ 519 public int percentage(POSITION num, POSITION den) 520 { 521 return (int) muldiv(num, 100, den); 522 } 523 524 /* 525 * Return the specified percentage of a POSITION. 526 * Assume (0 <= POS && 0 <= PERCENT <= 100 527 * && 0 <= FRACTION < (PERCENT == 100 ? 1 : NUM_FRAC_DENOM)), 528 * so the result cannot overflow. Round to even. 529 */ 530 public POSITION percent_pos(POSITION pos, int percent, long fraction) 531 { 532 /* 533 * Change from percent (parts per 100) 534 * to pctden (parts per 100 * NUM_FRAC_DENOM). 535 */ 536 POSITION pctden = (percent * NUM_FRAC_DENOM) + fraction; 537 538 return (POSITION) muldiv(pos, pctden, 100 * NUM_FRAC_DENOM); 539 } 540 541 #if !HAVE_STRCHR 542 /* 543 * strchr is used by regexp.c. 544 */ 545 char * strchr(char *s, char c) 546 { 547 for ( ; *s != '\0'; s++) 548 if (*s == c) 549 return (s); 550 if (c == '\0') 551 return (s); 552 return (NULL); 553 } 554 #endif 555 556 #if !HAVE_MEMCPY 557 void * memcpy(void *dst, constant void *src, size_t len) 558 { 559 char *dstp = (char *) dst; 560 char *srcp = (char *) src; 561 int i; 562 563 for (i = 0; i < len; i++) 564 dstp[i] = srcp[i]; 565 return (dst); 566 } 567 #endif 568 569 #if !HAVE_STRSTR 570 char * strstr(constant char *haystack, constant char *needle) 571 { 572 if (*needle == '\0') 573 return (char *) haystack; 574 for (; *haystack; haystack++) { 575 constant char *h = haystack; 576 constant char *n = needle; 577 while (*h != '\0' && *n != '\0' && *h == *n) { 578 h++; 579 n++; 580 } 581 if (*n == '\0') 582 return (char *) haystack; 583 } 584 return NULL; 585 } 586 #endif 587 588 #ifdef _OSK_MWC32 589 590 /* 591 * This implements an ANSI-style intercept setup for Microware C 3.2 592 */ 593 public int os9_signal(int type, RETSIGTYPE (*handler)()) 594 { 595 intercept(handler); 596 } 597 598 #include <sgstat.h> 599 600 int isatty(int f) 601 { 602 struct sgbuf sgbuf; 603 604 if (_gs_opt(f, &sgbuf) < 0) 605 return -1; 606 return (sgbuf.sg_class == 0); 607 } 608 609 #endif 610 611 public void sleep_ms(int ms) 612 { 613 #if MSDOS_COMPILER==WIN32C 614 Sleep(ms); 615 #else 616 #if HAVE_NANOSLEEP 617 int sec = ms / 1000; 618 struct timespec t = { sec, (ms - sec*1000) * 1000000 }; 619 nanosleep(&t, NULL); 620 #else 621 #if HAVE_USLEEP 622 usleep(ms * 1000); 623 #else 624 sleep(ms / 1000 + (ms % 1000 != 0)); 625 #endif 626 #endif 627 #endif 628 } 629