/* Licensed under BSD-MIT - see LICENSE file for details */ #include #include #include #include #include #include #include bool rbuf_open(struct rbuf *rbuf, const char *name, char *buf, size_t buf_max) { int fd = open(name, O_RDONLY); if (fd >= 0) { rbuf_init(rbuf, fd, buf, buf_max); return true; } return false; } static size_t rem(const struct rbuf *buf) { return buf->buf_end - (buf->start + buf->len); } size_t rbuf_good_size(int fd) { struct stat st; if (fstat(fd, &st) == 0 && st.st_blksize >= 4096) return st.st_blksize; return 4096; } static bool enlarge_buf(struct rbuf *buf, size_t len, void *(*resize)(void *buf, size_t len)) { char *new; if (!resize) { errno = ENOMEM; return false; } if (!len) len = rbuf_good_size(buf->fd); new = resize(buf->buf, len); if (!new) return false; buf->start += (new - buf->buf); buf->buf = new; buf->buf_end = new + len; return true; } static ssize_t get_more(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len)) { size_t r; if (rbuf->start + rbuf->len == rbuf->buf_end) { if (!enlarge_buf(rbuf, (rbuf->buf_end - rbuf->buf) * 2, resize)) return -1; } r = read(rbuf->fd, rbuf->start + rbuf->len, rem(rbuf)); if (r <= 0) return r; rbuf->len += r; return r; } void *rbuf_fill_all(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len)) { ssize_t r; /* Move back to start of buffer if we're empty. */ if (!rbuf->len) rbuf->start = rbuf->buf; while ((r = get_more(rbuf, resize)) != 0) if (r < 0) return NULL; return rbuf->start; } void *rbuf_fill(struct rbuf *rbuf, void *(*resize)(void *buf, size_t len)) { if (!rbuf->len) { rbuf->start = rbuf->buf; if (get_more(rbuf, resize) < 0) return NULL; } return rbuf->start; } char *rbuf_read_str(struct rbuf *rbuf, char term, void *(*resize)(void *buf, size_t len)) { char *p, *ret; ssize_t r = 0; size_t prev = 0; /* Move back to start of buffer if we're empty. */ if (!rbuf->len) rbuf->start = rbuf->buf; while (!(p = memchr(rbuf->start + prev, term, rbuf->len - prev))) { prev += r; r = get_more(rbuf, resize); if (r < 0) return NULL; /* EOF with no term. */ if (r == 0) { /* Nothing read at all? */ if (!rbuf->len && term) { errno = 0; return NULL; } /* Put term after input (get_more made room). */ assert(rbuf->start + rbuf->len < rbuf->buf_end); rbuf->start[rbuf->len] = '\0'; ret = rbuf->start; rbuf_consume(rbuf, rbuf->len); return ret; } } *p = '\0'; ret = rbuf->start; rbuf_consume(rbuf, p + 1 - ret); return ret; }