chiark / gitweb /
Merge branch 'running' of login.chiark.greenend.org.uk:public-git/inn-innduct
[innduct.git] / lib / qio.c
1 /*  $Id: qio.c 6943 2004-06-10 22:20:24Z hkehoe $
2 **
3 **  Quick I/O package.
4 **
5 **  A set of routines optimized for reading through files line by line.
6 **  This package uses internal buffering like stdio, but is even more
7 **  aggressive about its buffering.  The basic read call reads a single line
8 **  and returns the whole line, provided that it can fit in the buffer.
9 */
10
11 #include "config.h"
12 #include "clibrary.h"
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <sys/stat.h>
16
17 #include "inn/qio.h"
18 #include "libinn.h"
19
20 /* A reasonable default buffer size to use. */
21 #define QIO_BUFFERSIZE  8192
22
23 /*
24 **  Given a file descriptor, return a reasonable buffer size to use for that
25 **  file.  Uses st_blksize if available and reasonable, QIO_BUFFERSIZE
26 **  otherwise.
27 */
28 static size_t
29 buffer_size(int fd)
30 {
31     size_t size = QIO_BUFFERSIZE;
32
33 #if HAVE_ST_BLKSIZE
34     struct stat st;
35
36     /* The Solaris 2.6 man page says that st_blksize is not defined for
37        block or character special devices (and could contain garbage), so
38        only use this value for regular files. */
39     if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
40         size = st.st_blksize;
41         if (size > (4 * QIO_BUFFERSIZE) || size == 0)
42             size = QIO_BUFFERSIZE;
43         else
44             while(size < QIO_BUFFERSIZE)
45                 size += st.st_blksize;
46     }
47 #endif /* HAVE_ST_BLKSIZE */
48
49     return size;
50 }
51
52
53 /*
54 **  Open a quick file from a descriptor.
55 */
56 QIOSTATE *
57 QIOfdopen(const int fd)
58 {
59     QIOSTATE *qp;
60
61     qp = xmalloc(sizeof(*qp));
62     qp->_fd = fd;
63     qp->_length = 0;
64     qp->_size = buffer_size(fd);
65     qp->_buffer = xmalloc(qp->_size);
66     qp->_start = qp->_buffer;
67     qp->_end = qp->_buffer;
68     qp->_count = 0;
69     qp->_flag = QIO_ok;
70
71     return qp;
72 }
73
74
75 /*
76 **  Open a quick file from a file name.
77 */
78 QIOSTATE *
79 QIOopen(const char *name)
80 {
81     int fd;
82
83     fd = open(name, O_RDONLY);
84     if (fd < 0)
85         return NULL;
86     return QIOfdopen(fd);
87 }
88
89
90 /*
91 **  Close an open quick file.
92 */
93 void
94 QIOclose(QIOSTATE *qp)
95 {
96     close(qp->_fd);
97     free(qp->_buffer);
98     free(qp);
99 }
100
101
102 /*
103 **  Rewind a quick file.  Reads the first buffer full of data automatically,
104 **  anticipating the first read from the file.  Returns -1 on error, 0 on
105 **  success.
106 */
107 int
108 QIOrewind(QIOSTATE *qp)
109 {
110     ssize_t nread;
111
112     if (lseek(qp->_fd, 0, SEEK_SET) < 0)
113         return -1;
114     nread = read(qp->_fd, qp->_buffer, qp->_size);
115     if (nread < 0)
116         return nread;
117     qp->_count = nread;
118     qp->_start = qp->_buffer;
119     qp->_end = qp->_buffer + nread;
120     return 0;
121 }
122
123
124 /*
125 **  Get the next newline-terminated line from a quick file, replacing the
126 **  newline with a nul.  Returns a pointer to that line on success and NULL
127 **  on failure or end of file, with _flag set appropriately.
128 */
129 char *
130 QIOread(QIOSTATE *qp)
131 {
132     char *p, *line;
133     ssize_t nread;
134     size_t nleft;
135
136     /* Loop until we get a result or fill the buffer. */
137     qp->_flag = QIO_ok;
138     while (1) {
139         nleft = qp->_end - qp->_start;
140
141         /* If nleft <= 0, the buffer currently contains no data that hasn't
142            previously been returned by QIOread, so we can overwrite the
143            buffer with new data.  Otherwise, first check the existing data
144            to see if we have a full line. */
145         if (nleft <= 0) {
146             qp->_start = qp->_buffer;
147             qp->_end = qp->_buffer;
148         } else {
149             p = memchr(qp->_start, '\n', nleft);
150             if (p != NULL) {
151                 *p = '\0';
152                 qp->_length = p - qp->_start;
153                 line = qp->_start;
154                 qp->_start = p + 1;
155                 return (qp->_flag == QIO_long) ? NULL : line;
156             }
157
158             /* Not there.  See if our buffer is full.  If so, tag as having
159                seen too long of a line.  This will cause us to keep reading
160                as normal until we finally see the end of a line and then
161                return NULL. */
162             if (nleft >= qp->_size) {
163                 qp->_flag = QIO_long;
164                 qp->_start = qp->_end;
165                 nleft = 0;
166             }
167
168             /* We need to read more data.  If there's read data in buffer,
169                then move the unread data down to the beginning of the buffer
170                first. */
171             if (qp->_start > qp->_buffer) {
172                 if (nleft > 0)
173                     memmove(qp->_buffer, qp->_start, nleft);
174                 qp->_start = qp->_buffer;
175                 qp->_end = qp->_buffer + nleft;
176             }
177         }
178
179         /* Read in some more data, and then let the loop try to find the
180            newline again or discover that the line is too long. */
181         do {
182             nread = read(qp->_fd, qp->_end, qp->_size - nleft);
183         } while (nread == -1 && errno == EINTR);
184         if (nread <= 0) {
185             if (nread < 0)
186                 qp->_flag = QIO_error;
187             return NULL;
188         }
189         qp->_count += nread;
190         qp->_end += nread;
191     }
192 }