171 lines · c
1// SPDX-License-Identifier: GPL-2.02#include "cache.h"3#include "debug.h"4#include "strbuf.h"5#include <linux/kernel.h>6#include <linux/string.h>7#include <linux/zalloc.h>8#include <errno.h>9#include <stdio.h>10#include <stdlib.h>11#include <unistd.h>12 13/*14 * Used as the default ->buf value, so that people can always assume15 * buf is non NULL and ->buf is NUL terminated even for a freshly16 * initialized strbuf.17 */18char strbuf_slopbuf[1];19 20int strbuf_init(struct strbuf *sb, ssize_t hint)21{22 sb->alloc = sb->len = 0;23 sb->buf = strbuf_slopbuf;24 if (hint)25 return strbuf_grow(sb, hint);26 return 0;27}28 29void strbuf_release(struct strbuf *sb)30{31 if (sb->alloc) {32 zfree(&sb->buf);33 strbuf_init(sb, 0);34 }35}36 37char *strbuf_detach(struct strbuf *sb, size_t *sz)38{39 char *res = sb->alloc ? sb->buf : NULL;40 if (sz)41 *sz = sb->len;42 strbuf_init(sb, 0);43 return res;44}45 46int strbuf_grow(struct strbuf *sb, size_t extra)47{48 char *buf;49 size_t nr = sb->len + extra + 1;50 51 if (nr < sb->alloc)52 return 0;53 54 if (nr <= sb->len)55 return -E2BIG;56 57 if (alloc_nr(sb->alloc) > nr)58 nr = alloc_nr(sb->alloc);59 60 /*61 * Note that sb->buf == strbuf_slopbuf if sb->alloc == 0, and it is62 * a static variable. Thus we have to avoid passing it to realloc.63 */64 buf = realloc(sb->alloc ? sb->buf : NULL, nr * sizeof(*buf));65 if (!buf)66 return -ENOMEM;67 68 sb->buf = buf;69 sb->alloc = nr;70 return 0;71}72 73int strbuf_addch(struct strbuf *sb, int c)74{75 int ret = strbuf_grow(sb, 1);76 if (ret)77 return ret;78 79 sb->buf[sb->len++] = c;80 sb->buf[sb->len] = '\0';81 return 0;82}83 84int strbuf_add(struct strbuf *sb, const void *data, size_t len)85{86 int ret = strbuf_grow(sb, len);87 if (ret)88 return ret;89 90 memcpy(sb->buf + sb->len, data, len);91 return strbuf_setlen(sb, sb->len + len);92}93 94static int strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap)95{96 int len, ret;97 va_list ap_saved;98 99 if (!strbuf_avail(sb)) {100 ret = strbuf_grow(sb, 64);101 if (ret)102 return ret;103 }104 105 va_copy(ap_saved, ap);106 len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);107 if (len < 0) {108 va_end(ap_saved);109 return len;110 }111 if (len > strbuf_avail(sb)) {112 ret = strbuf_grow(sb, len);113 if (ret) {114 va_end(ap_saved);115 return ret;116 }117 len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap_saved);118 if (len > strbuf_avail(sb)) {119 pr_debug("this should not happen, your vsnprintf is broken");120 va_end(ap_saved);121 return -EINVAL;122 }123 }124 va_end(ap_saved);125 return strbuf_setlen(sb, sb->len + len);126}127 128int strbuf_addf(struct strbuf *sb, const char *fmt, ...)129{130 va_list ap;131 int ret;132 133 va_start(ap, fmt);134 ret = strbuf_addv(sb, fmt, ap);135 va_end(ap);136 return ret;137}138 139ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)140{141 size_t oldlen = sb->len;142 size_t oldalloc = sb->alloc;143 int ret;144 145 ret = strbuf_grow(sb, hint ? hint : 8192);146 if (ret)147 return ret;148 149 for (;;) {150 ssize_t cnt;151 152 cnt = read(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);153 if (cnt < 0) {154 if (oldalloc == 0)155 strbuf_release(sb);156 else157 strbuf_setlen(sb, oldlen);158 return cnt;159 }160 if (!cnt)161 break;162 sb->len += cnt;163 ret = strbuf_grow(sb, 8192);164 if (ret)165 return ret;166 }167 168 sb->buf[sb->len] = '\0';169 return sb->len - oldlen;170}171