Blame src/lib/libast/sfio/sfpkrd.c

Packit 992a25
/***********************************************************************
Packit 992a25
*                                                                      *
Packit 992a25
*               This software is part of the ast package               *
Packit 992a25
*          Copyright (c) 1985-2012 AT&T Intellectual Property          *
Packit 992a25
*                      and is licensed under the                       *
Packit 992a25
*                 Eclipse Public License, Version 1.0                  *
Packit 992a25
*                    by AT&T Intellectual Property                     *
Packit 992a25
*                                                                      *
Packit 992a25
*                A copy of the License is available at                 *
Packit 992a25
*          http://www.eclipse.org/org/documents/epl-v10.html           *
Packit 992a25
*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
Packit 992a25
*                                                                      *
Packit 992a25
*              Information and Software Systems Research               *
Packit 992a25
*                            AT&T Research                             *
Packit 992a25
*                           Florham Park NJ                            *
Packit 992a25
*                                                                      *
Packit 992a25
*                 Glenn Fowler <gsf@research.att.com>                  *
Packit 992a25
*                  David Korn <dgk@research.att.com>                   *
Packit 992a25
*                   Phong Vo <kpv@research.att.com>                    *
Packit 992a25
*                                                                      *
Packit 992a25
***********************************************************************/
Packit 992a25
#include	"sfhdr.h"
Packit 992a25
#if !_PACKAGE_ast
Packit 992a25
#ifndef FIONREAD
Packit 992a25
#if _sys_ioctl
Packit 992a25
#include	<sys/ioctl.h>
Packit 992a25
#endif
Packit 992a25
#endif
Packit 992a25
#endif
Packit 992a25
Packit 992a25
/*	Read/Peek a record from an unseekable device
Packit 992a25
**
Packit 992a25
**	Written by Kiem-Phong Vo.
Packit 992a25
*/
Packit 992a25
Packit 992a25
#define STREAM_PEEK	001
Packit 992a25
#define SOCKET_PEEK	002
Packit 992a25
Packit 992a25
#if __STD_C
Packit 992a25
ssize_t sfpkrd(int fd, Void_t* argbuf, size_t n, int rc, long tm, int action)
Packit 992a25
#else
Packit 992a25
ssize_t sfpkrd(fd, argbuf, n, rc, tm, action)
Packit 992a25
int	fd;	/* file descriptor */
Packit 992a25
Void_t*	argbuf;	/* buffer to read data */
Packit 992a25
size_t	n;	/* buffer size */
Packit 992a25
int	rc;	/* record character */
Packit 992a25
long	tm;	/* time-out */
Packit 992a25
int	action;	/* >0: peeking, if rc>=0, get action records,
Packit 992a25
		   <0: no peeking, if rc>=0, get -action records,
Packit 992a25
		   =0: no peeking, if rc>=0, must get a single record
Packit 992a25
		*/
Packit 992a25
#endif
Packit 992a25
{
Packit 992a25
	reg ssize_t	r;
Packit 992a25
	reg int		ntry, t;
Packit 992a25
	reg char	*buf = (char*)argbuf, *endbuf;
Packit 992a25
Packit 992a25
	if(rc < 0 && tm < 0 && action <= 0)
Packit 992a25
		return sysreadf(fd,buf,n);
Packit 992a25
Packit 992a25
	t = (action > 0 || rc >= 0) ? (STREAM_PEEK|SOCKET_PEEK) : 0;
Packit 992a25
#if !_stream_peek
Packit 992a25
	t &= ~STREAM_PEEK;
Packit 992a25
#endif
Packit 992a25
#if !_socket_peek
Packit 992a25
	t &= ~SOCKET_PEEK;
Packit 992a25
#endif
Packit 992a25
Packit 992a25
	for(ntry = 0; ntry < 2; ++ntry)
Packit 992a25
	{
Packit 992a25
		r = -1;
Packit 992a25
#if _stream_peek
Packit 992a25
		if((t&STREAM_PEEK) && (ntry == 1 || tm < 0) )
Packit 992a25
		{
Packit 992a25
#ifdef __sun
Packit 992a25
			/*
Packit 992a25
			 * I_PEEK on stdin can hang rsh+ksh on solaris
Packit 992a25
			 * this kludge will have to do until sun^H^H^Horacle fixes I_PEEK/rsh
Packit 992a25
			 */
Packit 992a25
			static int	stream_peek;
Packit 992a25
			if (stream_peek == 0) /* this will be done just once */
Packit 992a25
			{	char	*e;
Packit 992a25
				stream_peek = (
Packit 992a25
					getenv("LOGNAME") == 0 &&
Packit 992a25
					getenv("MAIL") == 0 &&
Packit 992a25
					((e = getenv("LANG")) == 0 || strcmp(e, "C") == 0) &&
Packit 992a25
					((e = getenv("PATH")) == 0 || strncmp(e, "/usr/bin:", 9) == 0)
Packit 992a25
					) ? -1 : 1;
Packit 992a25
			}
Packit 992a25
			if(stream_peek < 0)
Packit 992a25
				t &= ~STREAM_PEEK;
Packit 992a25
			else
Packit 992a25
#endif
Packit 992a25
			{	struct strpeek	pbuf;
Packit 992a25
				pbuf.flags = 0;
Packit 992a25
				pbuf.ctlbuf.maxlen = -1;
Packit 992a25
				pbuf.ctlbuf.len = 0;
Packit 992a25
				pbuf.ctlbuf.buf = NIL(char*);
Packit 992a25
				pbuf.databuf.maxlen = n;
Packit 992a25
				pbuf.databuf.buf = buf;
Packit 992a25
				pbuf.databuf.len = 0;
Packit 992a25
Packit 992a25
				if((r = ioctl(fd,I_PEEK,&pbuf)) < 0)
Packit 992a25
				{	if(errno == EINTR)
Packit 992a25
						return -1;
Packit 992a25
					t &= ~STREAM_PEEK;
Packit 992a25
				}
Packit 992a25
				else
Packit 992a25
				{	t &= ~SOCKET_PEEK;
Packit 992a25
					if(r > 0 && (r = pbuf.databuf.len) <= 0)
Packit 992a25
					{	if(action <= 0)	/* read past eof */
Packit 992a25
							r = sysreadf(fd,buf,1);
Packit 992a25
						return r;
Packit 992a25
					}
Packit 992a25
					if(r == 0)
Packit 992a25
						r = -1;
Packit 992a25
					else if(r > 0)
Packit 992a25
						break;
Packit 992a25
				}
Packit 992a25
			}
Packit 992a25
		}
Packit 992a25
#endif /* stream_peek */
Packit 992a25
Packit 992a25
		if(ntry == 1)
Packit 992a25
			break;
Packit 992a25
Packit 992a25
		/* poll or select to see if data is present.  */
Packit 992a25
		while(tm >= 0 || action > 0 ||
Packit 992a25
			/* block until there is data before peeking again */
Packit 992a25
			((t&STREAM_PEEK) && rc >= 0) ||
Packit 992a25
			/* let select be interrupted instead of recv which autoresumes */
Packit 992a25
			(t&SOCKET_PEEK) )
Packit 992a25
		{	r = -2;
Packit 992a25
#if _lib_poll
Packit 992a25
			if(r == -2)
Packit 992a25
			{
Packit 992a25
				struct pollfd	po;
Packit 992a25
				po.fd = fd;
Packit 992a25
				po.events = POLLIN;
Packit 992a25
				po.revents = 0;
Packit 992a25
Packit 992a25
				if((r = SFPOLL(&po,1,tm)) < 0)
Packit 992a25
				{	if(errno == EINTR)
Packit 992a25
						return -1;
Packit 992a25
					else if(errno == EAGAIN)
Packit 992a25
					{	errno = 0;
Packit 992a25
						continue;
Packit 992a25
					}
Packit 992a25
					else	r = -2;
Packit 992a25
				}
Packit 992a25
				else	r = (po.revents&POLLIN) ? 1 : -1;
Packit 992a25
			}
Packit 992a25
#endif /*_lib_poll*/
Packit 992a25
#if _lib_select
Packit 992a25
			if(r == -2)
Packit 992a25
			{
Packit 992a25
#if _hpux_threads && vt_threaded
Packit 992a25
#define fd_set	int
Packit 992a25
#endif
Packit 992a25
				fd_set		rd;
Packit 992a25
				struct timeval	tmb, *tmp;
Packit 992a25
				FD_ZERO(&rd);
Packit 992a25
				FD_SET(fd,&rd);
Packit 992a25
				if(tm < 0)
Packit 992a25
					tmp = NIL(struct timeval*);
Packit 992a25
				else
Packit 992a25
				{	tmp = &tm;;
Packit 992a25
					tmb.tv_sec = tm/SECOND;
Packit 992a25
					tmb.tv_usec = (tm%SECOND)*SECOND;
Packit 992a25
				}
Packit 992a25
				r = select(fd+1,&rd,NIL(fd_set*),NIL(fd_set*),tmp);
Packit 992a25
				if(r < 0)
Packit 992a25
				{	if(errno == EINTR)
Packit 992a25
						return -1;
Packit 992a25
					else if(errno == EAGAIN)
Packit 992a25
					{	errno = 0;
Packit 992a25
						continue;
Packit 992a25
					}
Packit 992a25
					else	r = -2;
Packit 992a25
				}
Packit 992a25
				else	r = FD_ISSET(fd,&rd) ? 1 : -1;
Packit 992a25
			}
Packit 992a25
#endif /*_lib_select*/
Packit 992a25
			if(r == -2)
Packit 992a25
			{
Packit 992a25
#if !_lib_poll && !_lib_select	/* both poll and select can't be used */
Packit 992a25
#ifdef FIONREAD			/* quick and dirty check for availability */
Packit 992a25
				long	nsec = tm < 0 ? 0 : (tm+999)/1000;
Packit 992a25
				while(nsec > 0 && r < 0)
Packit 992a25
				{	long	avail = -1;
Packit 992a25
					if((r = ioctl(fd,FIONREAD,&avail)) < 0)
Packit 992a25
					{	if(errno == EINTR)
Packit 992a25
							return -1;
Packit 992a25
						else if(errno == EAGAIN)
Packit 992a25
						{	errno = 0;
Packit 992a25
							continue;
Packit 992a25
						}
Packit 992a25
						else	/* ioctl failed completely */
Packit 992a25
						{	r = -2;
Packit 992a25
							break;
Packit 992a25
						}
Packit 992a25
					}
Packit 992a25
					else	r = avail <= 0 ? -1 : (ssize_t)avail;
Packit 992a25
Packit 992a25
					if(r < 0 && nsec-- > 0)
Packit 992a25
						sleep(1);
Packit 992a25
				}
Packit 992a25
#endif
Packit 992a25
#endif
Packit 992a25
			}
Packit 992a25
Packit 992a25
			if(r > 0)		/* there is data now */
Packit 992a25
			{	if(action <= 0 && rc < 0)
Packit 992a25
					return sysreadf(fd,buf,n);
Packit 992a25
				else	r = -1;
Packit 992a25
			}
Packit 992a25
			else if(tm >= 0)	/* timeout exceeded */
Packit 992a25
				return -1;
Packit 992a25
			else	r = -1;
Packit 992a25
			break;
Packit 992a25
		}
Packit 992a25
Packit 992a25
#if _socket_peek
Packit 992a25
		if(t&SOCKET_PEEK)
Packit 992a25
		{
Packit 992a25
#if __MACH__ && __APPLE__ /* check 10.4 recv(MSG_PEEK) bug that consumes pipe data */
Packit 992a25
			static int	recv_peek_pipe;
Packit 992a25
			if (recv_peek_pipe == 0) /* this will be done just once */
Packit 992a25
			{	int	fds[2], r;
Packit 992a25
				char	tst[2];
Packit 992a25
Packit 992a25
				tst[0] = 'a'; tst[1] = 'z';
Packit 992a25
Packit 992a25
				/* open a pipe and write to it */
Packit 992a25
				recv_peek_pipe = 1;
Packit 992a25
				if(recv_peek_pipe == 1 && pipe(fds) < 0)
Packit 992a25
					recv_peek_pipe = -1;
Packit 992a25
				if(recv_peek_pipe == 1 && write(fds[1], tst, 2) != 2)
Packit 992a25
					recv_peek_pipe = -1;
Packit 992a25
Packit 992a25
				/* try recv() to see if it gets anything */
Packit 992a25
				tst[0] = tst[1] = 0;
Packit 992a25
				if(recv_peek_pipe == 1 && (r = recv(fds[0], tst, 1, MSG_PEEK)) != 1)
Packit 992a25
					recv_peek_pipe = -1;
Packit 992a25
				if(recv_peek_pipe == 1 && tst[0] != 'a')
Packit 992a25
					recv_peek_pipe = -1;
Packit 992a25
Packit 992a25
				/* make sure that recv() did not consume data */
Packit 992a25
				tst[0] = tst[1] = 0;
Packit 992a25
				if(recv_peek_pipe == 1 && (r = recv(fds[0], tst, 2, MSG_PEEK)) != 2)
Packit 992a25
					recv_peek_pipe = -1;
Packit 992a25
				if(recv_peek_pipe == 1 && (tst[0] != 'a' || tst[1] != 'z') )
Packit 992a25
					recv_peek_pipe = -1;
Packit 992a25
Packit 992a25
				close(fds[0]);
Packit 992a25
				close(fds[1]);
Packit 992a25
			}
Packit 992a25
Packit 992a25
			if(recv_peek_pipe < 0)
Packit 992a25
			{	struct stat st; /* recv should work on sockets */
Packit 992a25
				if(fstat(fd, &st) < 0 || !S_ISSOCK(st.st_mode) )
Packit 992a25
				{	r = -1;
Packit 992a25
					t &= ~SOCKET_PEEK;
Packit 992a25
				}
Packit 992a25
			}
Packit 992a25
#endif
Packit 992a25
			while((t&SOCKET_PEEK) && (r = recv(fd,(char*)buf,n,MSG_PEEK)) < 0)
Packit 992a25
			{	if(errno == EINTR)
Packit 992a25
					return -1;
Packit 992a25
				else if(errno == EAGAIN)
Packit 992a25
					errno = 0;
Packit 992a25
				else	t &= ~SOCKET_PEEK;
Packit 992a25
			}
Packit 992a25
			if(r >= 0)
Packit 992a25
			{	t &= ~STREAM_PEEK;
Packit 992a25
				if(r > 0)
Packit 992a25
					break;
Packit 992a25
				else	/* read past eof */
Packit 992a25
				{	if(action <= 0)
Packit 992a25
						r = sysreadf(fd,buf,1);
Packit 992a25
					return r;
Packit 992a25
				}
Packit 992a25
			}
Packit 992a25
		}
Packit 992a25
#endif
Packit 992a25
	}
Packit 992a25
Packit 992a25
	if(r < 0)
Packit 992a25
	{	if(tm >= 0 || action > 0)
Packit 992a25
			return -1;
Packit 992a25
		else /* get here means: tm < 0 && action <= 0 && rc >= 0 */
Packit 992a25
		{	/* number of records read at a time */
Packit 992a25
			if((action = action ? -action : 1) > (int)n)
Packit 992a25
				action = n;
Packit 992a25
			r = 0;
Packit 992a25
			while((t = sysreadf(fd,buf,action)) > 0)
Packit 992a25
			{	r += t;
Packit 992a25
				for(endbuf = buf+t; buf < endbuf;)
Packit 992a25
					if(*buf++ == rc)
Packit 992a25
						action -= 1;
Packit 992a25
				if(action == 0 || (int)(n-r) < action)
Packit 992a25
					break;
Packit 992a25
			}
Packit 992a25
			return r == 0 ? t : r;
Packit 992a25
		}
Packit 992a25
	}
Packit 992a25
Packit 992a25
	/* successful peek, find the record end */
Packit 992a25
	if(rc >= 0)
Packit 992a25
	{	reg char*	sp;	
Packit 992a25
Packit 992a25
		t = action == 0 ? 1 : action < 0 ? -action : action;
Packit 992a25
		for(endbuf = (sp = buf)+r; sp < endbuf; )
Packit 992a25
			if(*sp++ == rc)
Packit 992a25
				if((t -= 1) == 0)
Packit 992a25
					break;
Packit 992a25
		r = sp - buf;
Packit 992a25
	}
Packit 992a25
Packit 992a25
	/* advance */
Packit 992a25
	if(action <= 0)
Packit 992a25
		r = sysreadf(fd,buf,r);
Packit 992a25
Packit 992a25
	return r;
Packit 992a25
}