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