1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2012 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                 Eclipse Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*          http://www.eclipse.org/org/documents/epl-v10.html           *
11*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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#ifdef __sun
76			/*
77			 * I_PEEK on stdin can hang rsh+ksh on solaris
78			 * this kludge will have to do until sun^H^H^Horacle fixes I_PEEK/rsh
79			 */
80			static int	stream_peek;
81			if (stream_peek == 0) /* this will be done just once */
82			{	char	*e;
83				stream_peek = (
84					getenv("LOGNAME") == 0 &&
85					getenv("MAIL") == 0 &&
86					((e = getenv("LANG")) == 0 || strcmp(e, "C") == 0) &&
87					((e = getenv("PATH")) == 0 || strncmp(e, "/usr/bin:", 9) == 0)
88					) ? -1 : 1;
89			}
90			if(stream_peek < 0)
91				t &= ~STREAM_PEEK;
92			else
93#endif
94			{	struct strpeek	pbuf;
95				pbuf.flags = 0;
96				pbuf.ctlbuf.maxlen = -1;
97				pbuf.ctlbuf.len = 0;
98				pbuf.ctlbuf.buf = NIL(char*);
99				pbuf.databuf.maxlen = n;
100				pbuf.databuf.buf = buf;
101				pbuf.databuf.len = 0;
102
103				if((r = ioctl(fd,I_PEEK,&pbuf)) < 0)
104				{	if(errno == EINTR)
105						return -1;
106					t &= ~STREAM_PEEK;
107				}
108				else
109				{	t &= ~SOCKET_PEEK;
110					if(r > 0 && (r = pbuf.databuf.len) <= 0)
111					{	if(action <= 0)	/* read past eof */
112							r = sysreadf(fd,buf,1);
113						return r;
114					}
115					if(r == 0)
116						r = -1;
117					else if(r > 0)
118						break;
119				}
120			}
121		}
122#endif /* stream_peek */
123
124		if(ntry == 1)
125			break;
126
127		/* poll or select to see if data is present.  */
128		while(tm >= 0 || action > 0 ||
129			/* block until there is data before peeking again */
130			((t&STREAM_PEEK) && rc >= 0) ||
131			/* let select be interrupted instead of recv which autoresumes */
132			(t&SOCKET_PEEK) )
133		{	r = -2;
134#if _lib_poll
135			if(r == -2)
136			{
137				struct pollfd	po;
138				po.fd = fd;
139				po.events = POLLIN;
140				po.revents = 0;
141
142				if((r = SFPOLL(&po,1,tm)) < 0)
143				{	if(errno == EINTR)
144						return -1;
145					else if(errno == EAGAIN)
146					{	errno = 0;
147						continue;
148					}
149					else	r = -2;
150				}
151				else	r = (po.revents&POLLIN) ? 1 : -1;
152			}
153#endif /*_lib_poll*/
154#if _lib_select
155			if(r == -2)
156			{
157#if _hpux_threads && vt_threaded
158#define fd_set	int
159#endif
160				fd_set		rd;
161				struct timeval	tmb, *tmp;
162				FD_ZERO(&rd);
163				FD_SET(fd,&rd);
164				if(tm < 0)
165					tmp = NIL(struct timeval*);
166				else
167				{	tmp = &tmb;
168					tmb.tv_sec = tm/SECOND;
169					tmb.tv_usec = (tm%SECOND)*SECOND;
170				}
171				r = select(fd+1,&rd,NIL(fd_set*),NIL(fd_set*),tmp);
172				if(r < 0)
173				{	if(errno == EINTR)
174						return -1;
175					else if(errno == EAGAIN)
176					{	errno = 0;
177						continue;
178					}
179					else	r = -2;
180				}
181				else	r = FD_ISSET(fd,&rd) ? 1 : -1;
182			}
183#endif /*_lib_select*/
184			if(r == -2)
185			{
186#if !_lib_poll && !_lib_select	/* both poll and select can't be used */
187#ifdef FIONREAD			/* quick and dirty check for availability */
188				long	nsec = tm < 0 ? 0 : (tm+999)/1000;
189				while(nsec > 0 && r < 0)
190				{	long	avail = -1;
191					if((r = ioctl(fd,FIONREAD,&avail)) < 0)
192					{	if(errno == EINTR)
193							return -1;
194						else if(errno == EAGAIN)
195						{	errno = 0;
196							continue;
197						}
198						else	/* ioctl failed completely */
199						{	r = -2;
200							break;
201						}
202					}
203					else	r = avail <= 0 ? -1 : (ssize_t)avail;
204
205					if(r < 0 && nsec-- > 0)
206						sleep(1);
207				}
208#endif
209#endif
210			}
211
212			if(r > 0)		/* there is data now */
213			{	if(action <= 0 && rc < 0)
214					return sysreadf(fd,buf,n);
215				else	r = -1;
216			}
217			else if(tm >= 0)	/* timeout exceeded */
218				return -1;
219			else	r = -1;
220			break;
221		}
222
223#if _socket_peek
224		if(t&SOCKET_PEEK)
225		{
226#if __MACH__ && __APPLE__ /* check 10.4 recv(MSG_PEEK) bug that consumes pipe data */
227			static int	recv_peek_pipe;
228			if (recv_peek_pipe == 0) /* this will be done just once */
229			{	int	fds[2], r;
230				char	tst[2];
231
232				tst[0] = 'a'; tst[1] = 'z';
233
234				/* open a pipe and write to it */
235				recv_peek_pipe = 1;
236				if(recv_peek_pipe == 1 && pipe(fds) < 0)
237					recv_peek_pipe = -1;
238				if(recv_peek_pipe == 1 && write(fds[1], tst, 2) != 2)
239					recv_peek_pipe = -1;
240
241				/* try recv() to see if it gets anything */
242				tst[0] = tst[1] = 0;
243				if(recv_peek_pipe == 1 && (r = recv(fds[0], tst, 1, MSG_PEEK)) != 1)
244					recv_peek_pipe = -1;
245				if(recv_peek_pipe == 1 && tst[0] != 'a')
246					recv_peek_pipe = -1;
247
248				/* make sure that recv() did not consume data */
249				tst[0] = tst[1] = 0;
250				if(recv_peek_pipe == 1 && (r = recv(fds[0], tst, 2, MSG_PEEK)) != 2)
251					recv_peek_pipe = -1;
252				if(recv_peek_pipe == 1 && (tst[0] != 'a' || tst[1] != 'z') )
253					recv_peek_pipe = -1;
254
255				close(fds[0]);
256				close(fds[1]);
257			}
258
259			if(recv_peek_pipe < 0)
260			{	struct stat st; /* recv should work on sockets */
261				if(fstat(fd, &st) < 0 || !S_ISSOCK(st.st_mode) )
262				{	r = -1;
263					t &= ~SOCKET_PEEK;
264				}
265			}
266#endif
267			while((t&SOCKET_PEEK) && (r = recv(fd,(char*)buf,n,MSG_PEEK)) < 0)
268			{	if(errno == EINTR)
269					return -1;
270				else if(errno == EAGAIN)
271					errno = 0;
272				else	t &= ~SOCKET_PEEK;
273			}
274			if(r >= 0)
275			{	t &= ~STREAM_PEEK;
276				if(r > 0)
277					break;
278				else	/* read past eof */
279				{	if(action <= 0)
280						r = sysreadf(fd,buf,1);
281					return r;
282				}
283			}
284		}
285#endif
286	}
287
288	if(r < 0)
289	{	if(tm >= 0 || action > 0)
290			return -1;
291		else /* get here means: tm < 0 && action <= 0 && rc >= 0 */
292		{	/* number of records read at a time */
293			if((action = action ? -action : 1) > (int)n)
294				action = n;
295			r = 0;
296			while((t = sysreadf(fd,buf,action)) > 0)
297			{	r += t;
298				for(endbuf = buf+t; buf < endbuf;)
299					if(*buf++ == rc)
300						action -= 1;
301				if(action == 0 || (int)(n-r) < action)
302					break;
303			}
304			return r == 0 ? t : r;
305		}
306	}
307
308	/* successful peek, find the record end */
309	if(rc >= 0)
310	{	reg char*	sp;
311
312		t = action == 0 ? 1 : action < 0 ? -action : action;
313		for(endbuf = (sp = buf)+r; sp < endbuf; )
314			if(*sp++ == rc)
315				if((t -= 1) == 0)
316					break;
317		r = sp - buf;
318	}
319
320	/* advance */
321	if(action <= 0)
322		r = sysreadf(fd,buf,r);
323
324	return r;
325}
326