/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1985-2010 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * Glenn Fowler * * David Korn * * Phong Vo * * * ***********************************************************************/ #include "sfhdr.h" #if !_PACKAGE_ast #ifndef FIONREAD #if _sys_ioctl #include #endif #endif #endif /* Read/Peek a record from an unseekable device ** ** Written by Kiem-Phong Vo. */ #define STREAM_PEEK 001 #define SOCKET_PEEK 002 #if __STD_C ssize_t sfpkrd(int fd, Void_t* argbuf, size_t n, int rc, long tm, int action) #else ssize_t sfpkrd(fd, argbuf, n, rc, tm, action) int fd; /* file descriptor */ Void_t* argbuf; /* buffer to read data */ size_t n; /* buffer size */ int rc; /* record character */ long tm; /* time-out */ int action; /* >0: peeking, if rc>=0, get action records, <0: no peeking, if rc>=0, get -action records, =0: no peeking, if rc>=0, must get a single record */ #endif { reg ssize_t r; reg int ntry, t; reg char *buf = (char*)argbuf, *endbuf; if(rc < 0 && tm < 0 && action <= 0) return sysreadf(fd,buf,n); t = (action > 0 || rc >= 0) ? (STREAM_PEEK|SOCKET_PEEK) : 0; #if !_stream_peek t &= ~STREAM_PEEK; #endif #if !_socket_peek t &= ~SOCKET_PEEK; #endif for(ntry = 0; ntry < 2; ++ntry) { r = -1; #if _stream_peek if((t&STREAM_PEEK) && (ntry == 1 || tm < 0) ) { struct strpeek pbuf; pbuf.flags = 0; pbuf.ctlbuf.maxlen = -1; pbuf.ctlbuf.len = 0; pbuf.ctlbuf.buf = NIL(char*); pbuf.databuf.maxlen = n; pbuf.databuf.buf = buf; pbuf.databuf.len = 0; if((r = ioctl(fd,I_PEEK,&pbuf)) < 0) { if(errno == EINTR) return -1; t &= ~STREAM_PEEK; } else { t &= ~SOCKET_PEEK; if(r > 0 && (r = pbuf.databuf.len) <= 0) { if(action <= 0) /* read past eof */ r = sysreadf(fd,buf,1); return r; } if(r == 0) r = -1; else if(r > 0) break; } } #endif /* stream_peek */ if(ntry == 1) break; /* poll or select to see if data is present. */ while(tm >= 0 || action > 0 || /* block until there is data before peeking again */ ((t&STREAM_PEEK) && rc >= 0) || /* let select be interrupted instead of recv which autoresumes */ (t&SOCKET_PEEK) ) { r = -2; #if _lib_poll if(r == -2) { struct pollfd po; po.fd = fd; po.events = POLLIN; po.revents = 0; if((r = SFPOLL(&po,1,tm)) < 0) { if(errno == EINTR) return -1; else if(errno == EAGAIN) { errno = 0; continue; } else r = -2; } else r = (po.revents&POLLIN) ? 1 : -1; } #endif /*_lib_poll*/ #if _lib_select if(r == -2) { #if _hpux_threads && vt_threaded #define fd_set int #endif fd_set rd; struct timeval tmb, *tmp; FD_ZERO(&rd); FD_SET(fd,&rd); if(tm < 0) tmp = NIL(struct timeval*); else { tmp = &tmb; tmb.tv_sec = tm/SECOND; tmb.tv_usec = (tm%SECOND)*SECOND; } r = select(fd+1,&rd,NIL(fd_set*),NIL(fd_set*),tmp); if(r < 0) { if(errno == EINTR) return -1; else if(errno == EAGAIN) { errno = 0; continue; } else r = -2; } else r = FD_ISSET(fd,&rd) ? 1 : -1; } #endif /*_lib_select*/ if(r == -2) { #if !_lib_poll && !_lib_select /* both poll and select can't be used */ #ifdef FIONREAD /* quick and dirty check for availability */ long nsec = tm < 0 ? 0 : (tm+999)/1000; while(nsec > 0 && r < 0) { long avail = -1; if((r = ioctl(fd,FIONREAD,&avail)) < 0) { if(errno == EINTR) return -1; else if(errno == EAGAIN) { errno = 0; continue; } else /* ioctl failed completely */ { r = -2; break; } } else r = avail <= 0 ? -1 : (ssize_t)avail; if(r < 0 && nsec-- > 0) sleep(1); } #endif #endif } if(r > 0) /* there is data now */ { if(action <= 0 && rc < 0) return sysreadf(fd,buf,n); else r = -1; } else if(tm >= 0) /* timeout exceeded */ return -1; else r = -1; break; } #if _socket_peek if(t&SOCKET_PEEK) { #if __MACH__ && __APPLE__ /* * work around macos 10.4 recv(MSG_PEEK) bug that consumes pipe() data */ struct stat st; static int recv_peek_ok; if (!recv_peek_ok) { int fds[2]; char tst[2]; tst[0] = 'a'; tst[1] = 'z'; 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; close(fds[0]); close(fds[1]); } if (recv_peek_ok < 0 && !fstat(fd, &st) && !S_ISSOCK(st.st_mode)) { r = -1; t &= ~SOCKET_PEEK; } else #endif while((r = recv(fd,(char*)buf,n,MSG_PEEK)) < 0) { if(errno == EINTR) return -1; else if(errno == EAGAIN) { errno = 0; continue; } t &= ~SOCKET_PEEK; break; } if(r >= 0) { t &= ~STREAM_PEEK; if(r > 0) break; else /* read past eof */ { if(action <= 0) r = sysreadf(fd,buf,1); return r; } } } #endif } if(r < 0) { if(tm >= 0 || action > 0) return -1; else /* get here means: tm < 0 && action <= 0 && rc >= 0 */ { /* number of records read at a time */ if((action = action ? -action : 1) > (int)n) action = n; r = 0; while((t = sysreadf(fd,buf,action)) > 0) { r += t; for(endbuf = buf+t; buf < endbuf;) if(*buf++ == rc) action -= 1; if(action == 0 || (int)(n-r) < action) break; } return r == 0 ? t : r; } } /* successful peek, find the record end */ if(rc >= 0) { reg char* sp; t = action == 0 ? 1 : action < 0 ? -action : action; for(endbuf = (sp = buf)+r; sp < endbuf; ) if(*sp++ == rc) if((t -= 1) == 0) break; r = sp - buf; } /* advance */ if(action <= 0) r = sysreadf(fd,buf,r); return r; }