/* $Id: qio.c 6943 2004-06-10 22:20:24Z hkehoe $ ** ** Quick I/O package. ** ** A set of routines optimized for reading through files line by line. ** This package uses internal buffering like stdio, but is even more ** aggressive about its buffering. The basic read call reads a single line ** and returns the whole line, provided that it can fit in the buffer. */ #include "config.h" #include "clibrary.h" #include #include #include #include "inn/qio.h" #include "libinn.h" /* A reasonable default buffer size to use. */ #define QIO_BUFFERSIZE 8192 /* ** Given a file descriptor, return a reasonable buffer size to use for that ** file. Uses st_blksize if available and reasonable, QIO_BUFFERSIZE ** otherwise. */ static size_t buffer_size(int fd) { size_t size = QIO_BUFFERSIZE; #if HAVE_ST_BLKSIZE struct stat st; /* The Solaris 2.6 man page says that st_blksize is not defined for block or character special devices (and could contain garbage), so only use this value for regular files. */ if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) { size = st.st_blksize; if (size > (4 * QIO_BUFFERSIZE) || size == 0) size = QIO_BUFFERSIZE; else while(size < QIO_BUFFERSIZE) size += st.st_blksize; } #endif /* HAVE_ST_BLKSIZE */ return size; } /* ** Open a quick file from a descriptor. */ QIOSTATE * QIOfdopen(const int fd) { QIOSTATE *qp; qp = xmalloc(sizeof(*qp)); qp->_fd = fd; qp->_length = 0; qp->_size = buffer_size(fd); qp->_buffer = xmalloc(qp->_size); qp->_start = qp->_buffer; qp->_end = qp->_buffer; qp->_count = 0; qp->_flag = QIO_ok; return qp; } /* ** Open a quick file from a file name. */ QIOSTATE * QIOopen(const char *name) { int fd; fd = open(name, O_RDONLY); if (fd < 0) return NULL; return QIOfdopen(fd); } /* ** Close an open quick file. */ void QIOclose(QIOSTATE *qp) { close(qp->_fd); free(qp->_buffer); free(qp); } /* ** Rewind a quick file. Reads the first buffer full of data automatically, ** anticipating the first read from the file. Returns -1 on error, 0 on ** success. */ int QIOrewind(QIOSTATE *qp) { ssize_t nread; if (lseek(qp->_fd, 0, SEEK_SET) < 0) return -1; nread = read(qp->_fd, qp->_buffer, qp->_size); if (nread < 0) return nread; qp->_count = nread; qp->_start = qp->_buffer; qp->_end = qp->_buffer + nread; return 0; } /* ** Get the next newline-terminated line from a quick file, replacing the ** newline with a nul. Returns a pointer to that line on success and NULL ** on failure or end of file, with _flag set appropriately. */ char * QIOread(QIOSTATE *qp) { char *p, *line; ssize_t nread; size_t nleft; /* Loop until we get a result or fill the buffer. */ qp->_flag = QIO_ok; while (1) { nleft = qp->_end - qp->_start; /* If nleft <= 0, the buffer currently contains no data that hasn't previously been returned by QIOread, so we can overwrite the buffer with new data. Otherwise, first check the existing data to see if we have a full line. */ if (nleft <= 0) { qp->_start = qp->_buffer; qp->_end = qp->_buffer; } else { p = memchr(qp->_start, '\n', nleft); if (p != NULL) { *p = '\0'; qp->_length = p - qp->_start; line = qp->_start; qp->_start = p + 1; return (qp->_flag == QIO_long) ? NULL : line; } /* Not there. See if our buffer is full. If so, tag as having seen too long of a line. This will cause us to keep reading as normal until we finally see the end of a line and then return NULL. */ if (nleft >= qp->_size) { qp->_flag = QIO_long; qp->_start = qp->_end; nleft = 0; } /* We need to read more data. If there's read data in buffer, then move the unread data down to the beginning of the buffer first. */ if (qp->_start > qp->_buffer) { if (nleft > 0) memmove(qp->_buffer, qp->_start, nleft); qp->_start = qp->_buffer; qp->_end = qp->_buffer + nleft; } } /* Read in some more data, and then let the loop try to find the newline again or discover that the line is too long. */ do { nread = read(qp->_fd, qp->_end, qp->_size - nleft); } while (nread == -1 && errno == EINTR); if (nread <= 0) { if (nread < 0) qp->_flag = QIO_error; return NULL; } qp->_count += nread; qp->_end += nread; } }