162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include "cache.h"
362306a36Sopenharmony_ci#include "debug.h"
462306a36Sopenharmony_ci#include "strbuf.h"
562306a36Sopenharmony_ci#include <linux/kernel.h>
662306a36Sopenharmony_ci#include <linux/string.h>
762306a36Sopenharmony_ci#include <linux/zalloc.h>
862306a36Sopenharmony_ci#include <errno.h>
962306a36Sopenharmony_ci#include <stdio.h>
1062306a36Sopenharmony_ci#include <stdlib.h>
1162306a36Sopenharmony_ci#include <unistd.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/*
1462306a36Sopenharmony_ci * Used as the default ->buf value, so that people can always assume
1562306a36Sopenharmony_ci * buf is non NULL and ->buf is NUL terminated even for a freshly
1662306a36Sopenharmony_ci * initialized strbuf.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_cichar strbuf_slopbuf[1];
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciint strbuf_init(struct strbuf *sb, ssize_t hint)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	sb->alloc = sb->len = 0;
2362306a36Sopenharmony_ci	sb->buf = strbuf_slopbuf;
2462306a36Sopenharmony_ci	if (hint)
2562306a36Sopenharmony_ci		return strbuf_grow(sb, hint);
2662306a36Sopenharmony_ci	return 0;
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_civoid strbuf_release(struct strbuf *sb)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	if (sb->alloc) {
3262306a36Sopenharmony_ci		zfree(&sb->buf);
3362306a36Sopenharmony_ci		strbuf_init(sb, 0);
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cichar *strbuf_detach(struct strbuf *sb, size_t *sz)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	char *res = sb->alloc ? sb->buf : NULL;
4062306a36Sopenharmony_ci	if (sz)
4162306a36Sopenharmony_ci		*sz = sb->len;
4262306a36Sopenharmony_ci	strbuf_init(sb, 0);
4362306a36Sopenharmony_ci	return res;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ciint strbuf_grow(struct strbuf *sb, size_t extra)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	char *buf;
4962306a36Sopenharmony_ci	size_t nr = sb->len + extra + 1;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (nr < sb->alloc)
5262306a36Sopenharmony_ci		return 0;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (nr <= sb->len)
5562306a36Sopenharmony_ci		return -E2BIG;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (alloc_nr(sb->alloc) > nr)
5862306a36Sopenharmony_ci		nr = alloc_nr(sb->alloc);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/*
6162306a36Sopenharmony_ci	 * Note that sb->buf == strbuf_slopbuf if sb->alloc == 0, and it is
6262306a36Sopenharmony_ci	 * a static variable. Thus we have to avoid passing it to realloc.
6362306a36Sopenharmony_ci	 */
6462306a36Sopenharmony_ci	buf = realloc(sb->alloc ? sb->buf : NULL, nr * sizeof(*buf));
6562306a36Sopenharmony_ci	if (!buf)
6662306a36Sopenharmony_ci		return -ENOMEM;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	sb->buf = buf;
6962306a36Sopenharmony_ci	sb->alloc = nr;
7062306a36Sopenharmony_ci	return 0;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciint strbuf_addch(struct strbuf *sb, int c)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	int ret = strbuf_grow(sb, 1);
7662306a36Sopenharmony_ci	if (ret)
7762306a36Sopenharmony_ci		return ret;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	sb->buf[sb->len++] = c;
8062306a36Sopenharmony_ci	sb->buf[sb->len] = '\0';
8162306a36Sopenharmony_ci	return 0;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ciint strbuf_add(struct strbuf *sb, const void *data, size_t len)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	int ret = strbuf_grow(sb, len);
8762306a36Sopenharmony_ci	if (ret)
8862306a36Sopenharmony_ci		return ret;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	memcpy(sb->buf + sb->len, data, len);
9162306a36Sopenharmony_ci	return strbuf_setlen(sb, sb->len + len);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int len, ret;
9762306a36Sopenharmony_ci	va_list ap_saved;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (!strbuf_avail(sb)) {
10062306a36Sopenharmony_ci		ret = strbuf_grow(sb, 64);
10162306a36Sopenharmony_ci		if (ret)
10262306a36Sopenharmony_ci			return ret;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	va_copy(ap_saved, ap);
10662306a36Sopenharmony_ci	len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
10762306a36Sopenharmony_ci	if (len < 0) {
10862306a36Sopenharmony_ci		va_end(ap_saved);
10962306a36Sopenharmony_ci		return len;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci	if (len > strbuf_avail(sb)) {
11262306a36Sopenharmony_ci		ret = strbuf_grow(sb, len);
11362306a36Sopenharmony_ci		if (ret) {
11462306a36Sopenharmony_ci			va_end(ap_saved);
11562306a36Sopenharmony_ci			return ret;
11662306a36Sopenharmony_ci		}
11762306a36Sopenharmony_ci		len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap_saved);
11862306a36Sopenharmony_ci		if (len > strbuf_avail(sb)) {
11962306a36Sopenharmony_ci			pr_debug("this should not happen, your vsnprintf is broken");
12062306a36Sopenharmony_ci			va_end(ap_saved);
12162306a36Sopenharmony_ci			return -EINVAL;
12262306a36Sopenharmony_ci		}
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	va_end(ap_saved);
12562306a36Sopenharmony_ci	return strbuf_setlen(sb, sb->len + len);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ciint strbuf_addf(struct strbuf *sb, const char *fmt, ...)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	va_list ap;
13162306a36Sopenharmony_ci	int ret;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	va_start(ap, fmt);
13462306a36Sopenharmony_ci	ret = strbuf_addv(sb, fmt, ap);
13562306a36Sopenharmony_ci	va_end(ap);
13662306a36Sopenharmony_ci	return ret;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cissize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	size_t oldlen = sb->len;
14262306a36Sopenharmony_ci	size_t oldalloc = sb->alloc;
14362306a36Sopenharmony_ci	int ret;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	ret = strbuf_grow(sb, hint ? hint : 8192);
14662306a36Sopenharmony_ci	if (ret)
14762306a36Sopenharmony_ci		return ret;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	for (;;) {
15062306a36Sopenharmony_ci		ssize_t cnt;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		cnt = read(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
15362306a36Sopenharmony_ci		if (cnt < 0) {
15462306a36Sopenharmony_ci			if (oldalloc == 0)
15562306a36Sopenharmony_ci				strbuf_release(sb);
15662306a36Sopenharmony_ci			else
15762306a36Sopenharmony_ci				strbuf_setlen(sb, oldlen);
15862306a36Sopenharmony_ci			return cnt;
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci		if (!cnt)
16162306a36Sopenharmony_ci			break;
16262306a36Sopenharmony_ci		sb->len += cnt;
16362306a36Sopenharmony_ci		ret = strbuf_grow(sb, 8192);
16462306a36Sopenharmony_ci		if (ret)
16562306a36Sopenharmony_ci			return ret;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	sb->buf[sb->len] = '\0';
16962306a36Sopenharmony_ci	return sb->len - oldlen;
17062306a36Sopenharmony_ci}
171