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