1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2011 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                   Phong Vo <kpv@research.att.com>                    *
20*                                                                      *
21***********************************************************************/
22#include	"sfhdr.h"
23#if !_PACKAGE_ast
24#ifndef FIONREAD
25#if _sys_ioctl
26#include	<sys/ioctl.h>
27#endif
28#endif
29#endif
30
31/*	Read/Peek a record from an unseekable device
32**
33**	Written by Kiem-Phong Vo.
34*/
35
36#define STREAM_PEEK	001
37#define SOCKET_PEEK	002
38
39#if __STD_C
40ssize_t sfpkrd(int fd, Void_t* argbuf, size_t n, int rc, long tm, int action)
41#else
42ssize_t sfpkrd(fd, argbuf, n, rc, tm, action)
43int	fd;	/* file descriptor */
44Void_t*	argbuf;	/* buffer to read data */
45size_t	n;	/* buffer size */
46int	rc;	/* record character */
47long	tm;	/* time-out */
48int	action;	/* >0: peeking, if rc>=0, get action records,
49		   <0: no peeking, if rc>=0, get -action records,
50		   =0: no peeking, if rc>=0, must get a single record
51		*/
52#endif
53{
54	reg ssize_t	r;
55	reg int		ntry, t;
56	reg char	*buf = (char*)argbuf, *endbuf;
57
58	if(rc < 0 && tm < 0 && action <= 0)
59		return sysreadf(fd,buf,n);
60
61	t = (action > 0 || rc >= 0) ? (STREAM_PEEK|SOCKET_PEEK) : 0;
62#if !_stream_peek
63	t &= ~STREAM_PEEK;
64#endif
65#if !_socket_peek
66	t &= ~SOCKET_PEEK;
67#endif
68
69	for(ntry = 0; ntry < 2; ++ntry)
70	{
71		r = -1;
72#if _stream_peek
73		if((t&STREAM_PEEK) && (ntry == 1 || tm < 0) )
74		{
75			struct strpeek	pbuf;
76			pbuf.flags = 0;
77			pbuf.ctlbuf.maxlen = -1;
78			pbuf.ctlbuf.len = 0;
79			pbuf.ctlbuf.buf = NIL(char*);
80			pbuf.databuf.maxlen = n;
81			pbuf.databuf.buf = buf;
82			pbuf.databuf.len = 0;
83
84			if((r = ioctl(fd,I_PEEK,&pbuf)) < 0)
85			{	if(errno == EINTR)
86					return -1;
87				t &= ~STREAM_PEEK;
88			}
89			else
90			{	t &= ~SOCKET_PEEK;
91				if(r > 0 && (r = pbuf.databuf.len) <= 0)
92				{	if(action <= 0)	/* read past eof */
93						r = sysreadf(fd,buf,1);
94					return r;
95				}
96				if(r == 0)
97					r = -1;
98				else if(r > 0)
99					break;
100			}
101		}
102#endif /* stream_peek */
103
104		if(ntry == 1)
105			break;
106
107		/* poll or select to see if data is present.  */
108		while(tm >= 0 || action > 0 ||
109			/* block until there is data before peeking again */
110			((t&STREAM_PEEK) && rc >= 0) ||
111			/* let select be interrupted instead of recv which autoresumes */
112			(t&SOCKET_PEEK) )
113		{	r = -2;
114#if _lib_poll
115			if(r == -2)
116			{
117				struct pollfd	po;
118				po.fd = fd;
119				po.events = POLLIN;
120				po.revents = 0;
121
122				if((r = SFPOLL(&po,1,tm)) < 0)
123				{	if(errno == EINTR)
124						return -1;
125					else if(errno == EAGAIN)
126					{	errno = 0;
127						continue;
128					}
129					else	r = -2;
130				}
131				else	r = (po.revents&POLLIN) ? 1 : -1;
132			}
133#endif /*_lib_poll*/
134#if _lib_select
135			if(r == -2)
136			{
137#if _hpux_threads && vt_threaded
138#define fd_set	int
139#endif
140				fd_set		rd;
141				struct timeval	tmb, *tmp;
142				FD_ZERO(&rd);
143				FD_SET(fd,&rd);
144				if(tm < 0)
145					tmp = NIL(struct timeval*);
146				else
147				{	tmp = &tmb;
148					tmb.tv_sec = tm/SECOND;
149					tmb.tv_usec = (tm%SECOND)*SECOND;
150				}
151				r = select(fd+1,&rd,NIL(fd_set*),NIL(fd_set*),tmp);
152				if(r < 0)
153				{	if(errno == EINTR)
154						return -1;
155					else if(errno == EAGAIN)
156					{	errno = 0;
157						continue;
158					}
159					else	r = -2;
160				}
161				else	r = FD_ISSET(fd,&rd) ? 1 : -1;
162			}
163#endif /*_lib_select*/
164			if(r == -2)
165			{
166#if !_lib_poll && !_lib_select	/* both poll and select can't be used */
167#ifdef FIONREAD			/* quick and dirty check for availability */
168				long	nsec = tm < 0 ? 0 : (tm+999)/1000;
169				while(nsec > 0 && r < 0)
170				{	long	avail = -1;
171					if((r = ioctl(fd,FIONREAD,&avail)) < 0)
172					{	if(errno == EINTR)
173							return -1;
174						else if(errno == EAGAIN)
175						{	errno = 0;
176							continue;
177						}
178						else	/* ioctl failed completely */
179						{	r = -2;
180							break;
181						}
182					}
183					else	r = avail <= 0 ? -1 : (ssize_t)avail;
184
185					if(r < 0 && nsec-- > 0)
186						sleep(1);
187				}
188#endif
189#endif
190			}
191
192			if(r > 0)		/* there is data now */
193			{	if(action <= 0 && rc < 0)
194					return sysreadf(fd,buf,n);
195				else	r = -1;
196			}
197			else if(tm >= 0)	/* timeout exceeded */
198				return -1;
199			else	r = -1;
200			break;
201		}
202
203#if _socket_peek
204		if(t&SOCKET_PEEK)
205		{
206#if __MACH__ && __APPLE__
207			/*
208			 * work around macos 10.4 recv(MSG_PEEK) bug that consumes pipe() data
209			 */
210
211			struct stat	st;
212			static int	recv_peek_ok;
213			if (!recv_peek_ok)
214			{
215				int	fds[2];
216				char	tst[2];
217				tst[0] = 'a';
218				tst[1] = 'z';
219				recv_peek_ok = (!pipe(fds) && write(fds[1], tst, 2) && recv(fds[0], tst, 1, MSG_PEEK) == 1 && tst[0] == 'a' && recv(fds[0], tst, 1, MSG_PEEK) == 1 && tst[0] == 'a') ? 1 : -1;
220				close(fds[0]);
221				close(fds[1]);
222			}
223			if (recv_peek_ok < 0 && !fstat(fd, &st) && !S_ISSOCK(st.st_mode))
224			{
225				r = -1;
226				t &= ~SOCKET_PEEK;
227			}
228			else
229#endif
230			while((r = recv(fd,(char*)buf,n,MSG_PEEK)) < 0)
231			{	if(errno == EINTR)
232					return -1;
233				else if(errno == EAGAIN)
234				{	errno = 0;
235					continue;
236				}
237				t &= ~SOCKET_PEEK;
238				break;
239			}
240			if(r >= 0)
241			{	t &= ~STREAM_PEEK;
242				if(r > 0)
243					break;
244				else	/* read past eof */
245				{	if(action <= 0)
246						r = sysreadf(fd,buf,1);
247					return r;
248				}
249			}
250		}
251#endif
252	}
253
254	if(r < 0)
255	{	if(tm >= 0 || action > 0)
256			return -1;
257		else /* get here means: tm < 0 && action <= 0 && rc >= 0 */
258		{	/* number of records read at a time */
259			if((action = action ? -action : 1) > (int)n)
260				action = n;
261			r = 0;
262			while((t = sysreadf(fd,buf,action)) > 0)
263			{	r += t;
264				for(endbuf = buf+t; buf < endbuf;)
265					if(*buf++ == rc)
266						action -= 1;
267				if(action == 0 || (int)(n-r) < action)
268					break;
269			}
270			return r == 0 ? t : r;
271		}
272	}
273
274	/* successful peek, find the record end */
275	if(rc >= 0)
276	{	reg char*	sp;
277
278		t = action == 0 ? 1 : action < 0 ? -action : action;
279		for(endbuf = (sp = buf)+r; sp < endbuf; )
280			if(*sp++ == rc)
281				if((t -= 1) == 0)
282					break;
283		r = sp - buf;
284	}
285
286	/* advance */
287	if(action <= 0)
288		r = sysreadf(fd,buf,r);
289
290	return r;
291}
292