1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 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
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 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