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
sfpkrd(int fd,Void_t * argbuf,size_t n,int rc,long tm,int action)40 ssize_t sfpkrd(int fd, Void_t* argbuf, size_t n, int rc, long tm, int action)
41 #else
42 ssize_t sfpkrd(fd, argbuf, n, rc, tm, action)
43 int	fd;	/* file descriptor */
44 Void_t*	argbuf;	/* buffer to read data */
45 size_t	n;	/* buffer size */
46 int	rc;	/* record character */
47 long	tm;	/* time-out */
48 int	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