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