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