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 63 public int reading; 64 65 static jmp_buf read_label; 66 67 extern int sigs; 68 extern int ignore_eoi; 69 #if !MSDOS_COMPILER 70 extern 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 78 poll_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 96 iread(fd, buf, len) 97 int fd; 98 unsigned char *buf; 99 unsigned int len; 100 { 101 int n; 102 103 start: 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 234 intread(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 244 get_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 * 259 strerror(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 * 279 errno_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 302 muldiv(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 326 percentage(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 337 percent_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 * 355 strchr(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 370 memcpy(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 391 os9_signal(type, handler) 392 int type; 393 RETSIGTYPE (*handler)(); 394 { 395 intercept(handler); 396 } 397 398 #include <sgstat.h> 399 400 int 401 isatty(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 414 sleep_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