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