0e210a9e38dac592b09df10b505978a7f16c2b25
[ccan] / ccan / rbuf / rbuf.c
1 /* Licensed under BSD-MIT - see LICENSE file for details */
2 #include <ccan/rbuf/rbuf.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include <string.h>
8 #include <fcntl.h>
9
10 bool rbuf_open(struct rbuf *rbuf, const char *name, char *buf, size_t buf_max)
11 {
12         int fd = open(name, O_RDONLY);
13         if (fd >= 0) {
14                 rbuf_init(rbuf, fd, buf, buf_max);
15                 return true;
16         }
17         return false;
18 }
19
20 static size_t rem(const struct rbuf *buf)
21 {
22         return buf->buf_end - (buf->start + buf->len);
23 }
24
25 size_t rbuf_good_size(int fd)
26 {
27         struct stat st;
28
29         if (fstat(fd, &st) == 0 && st.st_blksize >= 4096)
30                 return st.st_blksize;
31         return 4096;
32 }
33
34 static bool enlarge_buf(struct rbuf *buf, size_t len,
35                         void *(*resize)(void *buf, size_t len))
36 {
37         char *new;
38         if (!resize) {
39                 errno = ENOMEM;
40                 return false;
41         }
42         if (!len)
43                 len = rbuf_good_size(buf->fd);
44         new = resize(buf->buf, len);
45         if (!new)
46                 return false;
47         buf->start += (new - buf->buf);
48         buf->buf = new;
49         buf->buf_end = new + len;
50         return true;
51 }
52
53 static ssize_t get_more(struct rbuf *rbuf,
54                         void *(*resize)(void *buf, size_t len))
55 {
56         size_t r;
57
58         if (rbuf->start + rbuf->len == rbuf->buf_end) {
59                 if (!enlarge_buf(rbuf, (rbuf->buf_end - rbuf->buf) * 2, resize))
60                         return -1;
61         }
62
63         r = read(rbuf->fd, rbuf->start + rbuf->len, rem(rbuf));
64         if (r <= 0)
65                 return r;
66
67         rbuf->len += r;
68         return r;
69 }
70
71 void *rbuf_fill_all(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len))
72 {
73         ssize_t r;
74
75         /* Move back to start of buffer if we're empty. */
76         if (!rbuf->len)
77                 rbuf->start = rbuf->buf;
78
79         while ((r = get_more(rbuf, resize)) != 0)
80                 if (r < 0)
81                         return NULL;
82         return rbuf->start;
83 }
84
85 void *rbuf_fill(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len))
86 {
87         if (!rbuf->len) {
88                 rbuf->start = rbuf->buf;
89                 if (get_more(rbuf, resize) < 0)
90                         return NULL;
91         }
92         return rbuf->start;
93 }
94
95 char *rbuf_read_str(struct rbuf *rbuf, char term,
96                     void *(*resize)(void *buf, size_t len))
97 {
98         char *p, *ret;
99         ssize_t r = 0;
100         size_t prev = 0;
101
102         /* Move back to start of buffer if we're empty. */
103         if (!rbuf->len)
104                 rbuf->start = rbuf->buf;
105
106         while (!(p = memchr(rbuf->start + prev, term, rbuf->len - prev))) {
107                 prev += r;
108                 r = get_more(rbuf, resize);
109                 if (r < 0)
110                         return NULL;
111                 /* EOF with no term. */
112                 if (r == 0) {
113                         /* Nothing read at all? */
114                         if (!rbuf->len && term) {
115                                 errno = 0;
116                                 return NULL;
117                         }
118                         /* Put term after input (get_more made room). */
119                         assert(rbuf->start + rbuf->len < rbuf->buf_end);
120                         rbuf->start[rbuf->len] = '\0';
121                         ret = rbuf->start;
122                         rbuf_consume(rbuf, rbuf->len);
123                         return ret;
124                 }
125         }
126         *p = '\0';
127         ret = rbuf->start;
128         rbuf_consume(rbuf, p + 1 - ret);
129         return ret;
130 }