1 /* 2 * Copyright (C) 1984-2008 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 /* 254 * Return the ratio of two POSITIONS, as a percentage. 255 * {{ Assumes a POSITION is a long int. }} 256 */ 257 public int 258 percentage(num, den) 259 POSITION num, den; 260 { 261 POSITION num100 = num * 100; 262 263 if (num100 / 100 == num) 264 return (num100 / den); 265 else 266 return (num / (den / 100)); 267 } 268 269 /* 270 * Return the specified percentage of a POSITION. 271 */ 272 public POSITION 273 percent_pos(pos, percent, fraction) 274 POSITION pos; 275 int percent; 276 long fraction; 277 { 278 /* Change percent (parts per 100) to perden (parts per NUM_FRAC_DENOM). */ 279 long perden = (percent * (NUM_FRAC_DENOM / 100)) + (fraction / 100); 280 POSITION temp; 281 282 if (perden == 0) 283 return (0); 284 temp = pos * perden; /* This might overflow. */ 285 if (temp / perden == pos) 286 /* No overflow */ 287 return (temp / NUM_FRAC_DENOM); 288 else 289 /* Above calculation overflows; 290 * use a method that is less precise but won't overflow. */ 291 return (perden * (pos / NUM_FRAC_DENOM)); 292 } 293 294 #if !HAVE_STRCHR 295 /* 296 * strchr is used by regexp.c. 297 */ 298 char * 299 strchr(s, c) 300 char *s; 301 int c; 302 { 303 for ( ; *s != '\0'; s++) 304 if (*s == c) 305 return (s); 306 if (c == '\0') 307 return (s); 308 return (NULL); 309 } 310 #endif 311 312 #if !HAVE_MEMCPY 313 VOID_POINTER 314 memcpy(dst, src, len) 315 VOID_POINTER dst; 316 VOID_POINTER src; 317 int len; 318 { 319 char *dstp = (char *) dst; 320 char *srcp = (char *) src; 321 int i; 322 323 for (i = 0; i < len; i++) 324 dstp[i] = srcp[i]; 325 return (dst); 326 } 327 #endif 328 329 #ifdef _OSK_MWC32 330 331 /* 332 * This implements an ANSI-style intercept setup for Microware C 3.2 333 */ 334 public int 335 os9_signal(type, handler) 336 int type; 337 RETSIGTYPE (*handler)(); 338 { 339 intercept(handler); 340 } 341 342 #include <sgstat.h> 343 344 int 345 isatty(f) 346 int f; 347 { 348 struct sgbuf sgbuf; 349 350 if (_gs_opt(f, &sgbuf) < 0) 351 return -1; 352 return (sgbuf.sg_class == 0); 353 } 354 355 #endif 356