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