1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
/* $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 <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#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;
}
}
|