os.c revision 240121
152419Sjulian/* 252419Sjulian * Copyright (C) 1984-2012 Mark Nudelman 3139823Simp * 4139823Simp * You may distribute under the terms of either the GNU General Public 5139823Simp * License or the Less License, as specified in the README file. 652419Sjulian * 752419Sjulian * For more information, see the README file. 852419Sjulian */ 952419Sjulian 1052419Sjulian 1152419Sjulian/* 1252419Sjulian * Operating system dependent routines. 1352419Sjulian * 1452419Sjulian * Most of the stuff in here is based on Unix, but an attempt 1552419Sjulian * has been made to make things work on other operating systems. 1652419Sjulian * This will sometimes result in a loss of functionality, unless 1752419Sjulian * someone rewrites code specifically for the new operating system. 1852419Sjulian * 1952419Sjulian * The makefile provides defines to decide whether various 2052419Sjulian * Unix features are present. 2152419Sjulian */ 2252419Sjulian 2352419Sjulian#include "less.h" 2452419Sjulian#include <signal.h> 2552419Sjulian#include <setjmp.h> 2652419Sjulian#if HAVE_TIME_H 2752419Sjulian#include <time.h> 2852419Sjulian#endif 2952419Sjulian#if HAVE_ERRNO_H 3052419Sjulian#include <errno.h> 3152419Sjulian#endif 3252419Sjulian#if HAVE_VALUES_H 3352419Sjulian#include <values.h> 3452419Sjulian#endif 3552419Sjulian 3652419Sjulian#if HAVE_TIME_T 3752419Sjulian#define time_type time_t 3867506Sjulian#else 3952419Sjulian#define time_type long 4052419Sjulian#endif 4152752Sjulian 4252419Sjulian/* 4352419Sjulian * BSD setjmp() saves (and longjmp() restores) the signal mask. 4452419Sjulian * This costs a system call or two per setjmp(), so if possible we clear the 4552419Sjulian * signal mask with sigsetmask(), and use _setjmp()/_longjmp() instead. 4652419Sjulian * On other systems, setjmp() doesn't affect the signal mask and so 4752419Sjulian * _setjmp() does not exist; we just use setjmp(). 4852419Sjulian */ 4952419Sjulian#if HAVE__SETJMP && HAVE_SIGSETMASK 5052419Sjulian#define SET_JUMP _setjmp 5152419Sjulian#define LONG_JUMP _longjmp 5252419Sjulian#else 5352419Sjulian#define SET_JUMP setjmp 5452419Sjulian#define LONG_JUMP longjmp 5552419Sjulian#endif 5652419Sjulian 5752419Sjulianpublic int reading; 5852419Sjulian 5953913Sarchiestatic jmp_buf read_label; 6052419Sjulian 6152419Sjulianextern int sigs; 6252419Sjulian 6370870Sjulian/* 64249132Smav * Like read() system call, but is deliberately interruptible. 6570870Sjulian * A call to intread() from a signal handler will interrupt 6670870Sjulian * any pending iread(). 6770870Sjulian */ 6870870Sjulian public int 6970870Sjulianiread(fd, buf, len) 7052419Sjulian int fd; 7152419Sjulian char *buf; 7252419Sjulian unsigned int len; 7352419Sjulian{ 7452419Sjulian register int n; 7552419Sjulian 7653394Sarchiestart: 7752419Sjulian#if MSDOS_COMPILER==WIN32C 7852419Sjulian if (ABORT_SIGS()) 7952419Sjulian return (READ_INTR); 8052419Sjulian#else 8152419Sjulian#if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC 8252419Sjulian if (kbhit()) 8352419Sjulian { 8452419Sjulian int c; 8552419Sjulian 8652419Sjulian c = getch(); 8752419Sjulian if (c == '\003') 8852419Sjulian return (READ_INTR); 8953394Sarchie ungetch(c); 9052419Sjulian } 9152419Sjulian#endif 9252419Sjulian#endif 9352419Sjulian if (SET_JUMP(read_label)) 9452419Sjulian { 9552419Sjulian /* 9652419Sjulian * We jumped here from intread. 9753913Sarchie */ 9852752Sjulian reading = 0; 9952752Sjulian#if HAVE_SIGPROCMASK 10052752Sjulian { 10152752Sjulian sigset_t mask; 10252752Sjulian sigemptyset(&mask); 10352419Sjulian sigprocmask(SIG_SETMASK, &mask, NULL); 10452419Sjulian } 10570700Sjulian#else 10670700Sjulian#if HAVE_SIGSETMASK 10752419Sjulian sigsetmask(0); 10853913Sarchie#else 10997685Sarchie#ifdef _OSK 11097685Sarchie sigmask(~0); 11153913Sarchie#endif 11253913Sarchie#endif 11397685Sarchie#endif 11453913Sarchie return (READ_INTR); 11553913Sarchie } 11653913Sarchie 11797685Sarchie flush(); 11897685Sarchie reading = 1; 11953913Sarchie#if MSDOS_COMPILER==DJGPPC 12053913Sarchie if (isatty(fd)) 12197685Sarchie { 12253913Sarchie /* 12353913Sarchie * Don't try reading from a TTY until a character is 12453913Sarchie * available, because that makes some background programs 12553913Sarchie * believe DOS is busy in a way that prevents those 12653913Sarchie * programs from working while "less" waits. 12753913Sarchie */ 12853913Sarchie fd_set readfds; 12953913Sarchie 13053913Sarchie FD_ZERO(&readfds); 13153913Sarchie FD_SET(fd, &readfds); 13253913Sarchie if (select(fd+1, &readfds, 0, 0, 0) == -1) 13353913Sarchie return (-1); 13453913Sarchie } 13553913Sarchie#endif 13653913Sarchie n = read(fd, buf, len); 13753913Sarchie#if 1 13853913Sarchie /* 13953913Sarchie * This is a kludge to workaround a problem on some systems 14053913Sarchie * where terminating a remote tty connection causes read() to 14153913Sarchie * start returning 0 forever, instead of -1. 14253913Sarchie */ 14353913Sarchie { 14453913Sarchie extern int ignore_eoi; 14553913Sarchie if (!ignore_eoi) 14653913Sarchie { 14753913Sarchie static int consecutive_nulls = 0; 14853913Sarchie if (n == 0) 14953913Sarchie consecutive_nulls++; 15053913Sarchie else 15153913Sarchie consecutive_nulls = 0; 15253913Sarchie if (consecutive_nulls > 20) 15353913Sarchie quit(QUIT_ERROR); 15453913Sarchie } 15553913Sarchie } 15653913Sarchie#endif 15752419Sjulian reading = 0; 15852419Sjulian if (n < 0) 159129823Sjulian { 160129823Sjulian#if HAVE_ERRNO 161129823Sjulian /* 162129823Sjulian * Certain values of errno indicate we should just retry the read. 163129823Sjulian */ 164129823Sjulian#if MUST_DEFINE_ERRNO 165129823Sjulian extern int errno; 166129823Sjulian#endif 167129823Sjulian#ifdef EINTR 16852419Sjulian if (errno == EINTR) 16952419Sjulian goto start; 17052419Sjulian#endif 17152419Sjulian#ifdef EAGAIN 17252419Sjulian if (errno == EAGAIN) 17352419Sjulian goto start; 17452419Sjulian#endif 17552419Sjulian#endif 17652419Sjulian return (-1); 17752419Sjulian } 17852419Sjulian return (n); 17952419Sjulian} 18052419Sjulian 18152419Sjulian/* 18270700Sjulian * Interrupt a pending iread(). 18352419Sjulian */ 18452419Sjulian public void 18552419Sjulianintread() 186220768Sglebius{ 18752419Sjulian LONG_JUMP(read_label, 1); 18852419Sjulian} 18952419Sjulian 19052419Sjulian/* 191184214Sdes * Return the current time. 192220768Sglebius */ 193184214Sdes#if HAVE_TIME 194220768Sglebius public long 19570784Sjulianget_time() 19670700Sjulian{ 19752419Sjulian time_type t; 19852419Sjulian 19952419Sjulian time(&t); 20052419Sjulian return (t); 20152419Sjulian} 20252419Sjulian#endif 20352419Sjulian 20452419Sjulian 20552419Sjulian#if !HAVE_STRERROR 20670784Sjulian/* 20752419Sjulian * Local version of strerror, if not available from the system. 20852419Sjulian */ 20970700Sjulian static char * 21070700Sjulianstrerror(err) 21170700Sjulian int err; 21270700Sjulian{ 21370700Sjulian#if HAVE_SYS_ERRLIST 21470700Sjulian static char buf[16]; 21570784Sjulian extern char *sys_errlist[]; 21652419Sjulian extern int sys_nerr; 21770700Sjulian 21870700Sjulian if (err < sys_nerr) 21970700Sjulian return sys_errlist[err]; 22070700Sjulian sprintf(buf, "Error %d", err); 22170700Sjulian return buf; 22270700Sjulian#else 22370700Sjulian return ("cannot open"); 22470700Sjulian#endif 22570700Sjulian} 22670784Sjulian#endif 22752419Sjulian 22870700Sjulian/* 22952419Sjulian * errno_message: Return an error message based on the value of "errno". 23070700Sjulian */ 23170700Sjulian public char * 23252419Sjulianerrno_message(filename) 23352419Sjulian char *filename; 23452419Sjulian{ 23552419Sjulian register char *p; 23652419Sjulian register char *m; 23752419Sjulian int len; 23852419Sjulian#if HAVE_ERRNO 23952419Sjulian#if MUST_DEFINE_ERRNO 24052419Sjulian extern int errno; 24170700Sjulian#endif 24252419Sjulian p = strerror(errno); 24370784Sjulian#else 24452419Sjulian p = "cannot open"; 24552419Sjulian#endif 24670700Sjulian len = strlen(filename) + strlen(p) + 3; 24752976Sarchie m = (char *) ecalloc(len, sizeof(char)); 24870700Sjulian SNPRINTF2(m, len, "%s: %s", filename, p); 249213794Srpaulo return (m); 25052419Sjulian} 25152419Sjulian 25252419Sjulian/* #define HAVE_FLOAT 0 */ 25352419Sjulian 25452419Sjulian static POSITION 25552419Sjulianmuldiv(val, num, den) 25670700Sjulian POSITION val, num, den; 25752419Sjulian{ 25870784Sjulian#if HAVE_FLOAT 25952419Sjulian double v = (((double) val) * num) / den; 26052419Sjulian return ((POSITION) (v + 0.5)); 26170700Sjulian#else 26270700Sjulian POSITION v = ((POSITION) val) * num; 26370700Sjulian 26452419Sjulian if (v / num == val) 26552419Sjulian /* No overflow */ 26652419Sjulian return (POSITION) (v / den); 26752419Sjulian else 26852419Sjulian /* Above calculation overflows; 26952419Sjulian * use a method that is less precise but won't overflow. */ 27052419Sjulian return (POSITION) (val / (den / num)); 27152419Sjulian#endif 27252419Sjulian} 27352419Sjulian 27452419Sjulian/* 27552419Sjulian * Return the ratio of two POSITIONS, as a percentage. 27652419Sjulian * {{ Assumes a POSITION is a long int. }} 27752419Sjulian */ 27852419Sjulian public int 27952419Sjulianpercentage(num, den) 28052419Sjulian POSITION num, den; 28152419Sjulian{ 28252419Sjulian return (int) muldiv(num, (POSITION) 100, den); 28352419Sjulian} 28452419Sjulian 28552419Sjulian/* 28652419Sjulian * Return the specified percentage of a POSITION. 28752419Sjulian */ 28852419Sjulian public POSITION 28952419Sjulianpercent_pos(pos, percent, fraction) 29052419Sjulian POSITION pos; 291184205Sdes int percent; 29270870Sjulian long fraction; 29352419Sjulian{ 29452419Sjulian /* Change percent (parts per 100) to perden (parts per NUM_FRAC_DENOM). */ 295184205Sdes POSITION perden = (percent * (NUM_FRAC_DENOM / 100)) + (fraction / 100); 29652419Sjulian 29752419Sjulian if (perden == 0) 29852419Sjulian return (0); 299184205Sdes return (POSITION) muldiv(pos, perden, (POSITION) NUM_FRAC_DENOM); 30070870Sjulian} 30152419Sjulian 30252419Sjulian#if !HAVE_STRCHR 303184205Sdes/* 30452419Sjulian * strchr is used by regexp.c. 30552419Sjulian */ 30652419Sjulian char * 30752419Sjulianstrchr(s, c) 30852419Sjulian char *s; 30952419Sjulian int c; 31052419Sjulian{ 31152419Sjulian for ( ; *s != '\0'; s++) 31252419Sjulian if (*s == c) 31352419Sjulian return (s); 31452419Sjulian if (c == '\0') 31552419Sjulian return (s); 31652419Sjulian return (NULL); 31752419Sjulian} 31852419Sjulian#endif 31952419Sjulian 32052419Sjulian#if !HAVE_MEMCPY 32152419Sjulian VOID_POINTER 32252419Sjulianmemcpy(dst, src, len) 32352419Sjulian VOID_POINTER dst; 32452419Sjulian VOID_POINTER src; 32552419Sjulian int len; 32652419Sjulian{ 32752419Sjulian char *dstp = (char *) dst; 32852419Sjulian char *srcp = (char *) src; 32970700Sjulian int i; 33070700Sjulian 33152419Sjulian for (i = 0; i < len; i++) 33252419Sjulian dstp[i] = srcp[i]; 33352419Sjulian return (dst); 33452419Sjulian} 33552419Sjulian#endif 33652419Sjulian 33752419Sjulian#ifdef _OSK_MWC32 33852419Sjulian 33952419Sjulian/* 34070784Sjulian * This implements an ANSI-style intercept setup for Microware C 3.2 34152419Sjulian */ 342184205Sdes public int 343184205Sdesos9_signal(type, handler) 34452419Sjulian int type; 345184205Sdes RETSIGTYPE (*handler)(); 34670784Sjulian{ 34770784Sjulian intercept(handler); 34852419Sjulian} 34952419Sjulian 35052419Sjulian#include <sgstat.h> 35152419Sjulian 35252419Sjulian int 35352419Sjulianisatty(f) 35452419Sjulian int f; 35552419Sjulian{ 35652419Sjulian struct sgbuf sgbuf; 35770784Sjulian 35852419Sjulian if (_gs_opt(f, &sgbuf) < 0) 35952419Sjulian return -1; 36052419Sjulian return (sgbuf.sg_class == 0); 36152419Sjulian} 36252419Sjulian 36352419Sjulian#endif 36452419Sjulian