blob: f6652e6f10b97e423cd398f019e71ee2afb582ae [file] [log] [blame]
Damien68f59a92013-10-20 14:39:58 +01001#include <stdio.h>
2#include <stdarg.h>
3#include <string.h>
4#include "misc.h"
5
6// returned value is always at least 1 greater than argument
7#define ROUND_ALLOC(a) (((a) & ((~0) - 7)) + 8)
8
9void vstr_init(vstr_t *vstr) {
10 vstr->alloc = 32;
11 vstr->len = 0;
12 vstr->buf = m_new(char, vstr->alloc);
13 if (vstr->buf == NULL) {
14 m_free(vstr);
15 vstr->had_error = true;
16 return;
17 }
18 vstr->buf[0] = 0;
19 vstr->had_error = false;
20}
21
22void vstr_clear(vstr_t *vstr) {
23 m_free(vstr->buf);
24 vstr->buf = NULL;
25}
26
Damien8b3a7c22013-10-23 20:20:17 +010027vstr_t *vstr_new(void) {
Damien68f59a92013-10-20 14:39:58 +010028 vstr_t *vstr = m_new(vstr_t, 1);
29 if (vstr == NULL) {
30 return NULL;
31 }
32 vstr_init(vstr);
33 return vstr;
34}
35
36void vstr_free(vstr_t *vstr) {
37 if (vstr != NULL) {
38 m_free(vstr->buf);
39 m_free(vstr);
40 }
41}
42
43void vstr_reset(vstr_t *vstr) {
44 vstr->len = 0;
45 vstr->buf[0] = 0;
46 vstr->had_error = false;
47}
48
49bool vstr_had_error(vstr_t *vstr) {
50 return vstr->had_error;
51}
52
53char *vstr_str(vstr_t *vstr) {
54 if (vstr->had_error) {
55 return NULL;
56 }
57 return vstr->buf;
58}
59
60int vstr_len(vstr_t *vstr) {
61 if (vstr->had_error) {
62 return 0;
63 }
64 return vstr->len;
65}
66
67bool vstr_ensure_extra(vstr_t *vstr, int size) {
68 if (vstr->len + size + 1 > vstr->alloc) {
69 int new_alloc = ROUND_ALLOC((vstr->len + size + 1) * 2);
70 char *new_buf = m_renew(char, vstr->buf, new_alloc);
71 if (new_buf == NULL) {
72 vstr->had_error = true;
73 return false;
74 }
75 vstr->alloc = new_alloc;
76 vstr->buf = new_buf;
77 }
78 return true;
79}
80
81void vstr_hint_size(vstr_t *vstr, int size) {
82 // it's not an error if we fail to allocate for the size hint
83 bool er = vstr->had_error;
84 vstr_ensure_extra(vstr, size);
85 vstr->had_error = er;
86}
87
88char *vstr_add_len(vstr_t *vstr, int len) {
89 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
90 return NULL;
91 }
92 char *buf = vstr->buf + vstr->len;
93 vstr->len += len;
94 vstr->buf[vstr->len] = 0;
95 return buf;
96}
97
98void vstr_add_byte(vstr_t *vstr, byte v) {
99 byte *buf = (byte*)vstr_add_len(vstr, 1);
100 if (buf == NULL) {
101 return;
102 }
103 buf[0] = v;
104}
105
106void vstr_add_char(vstr_t *vstr, unichar c) {
107 // TODO UNICODE
108 byte *buf = (byte*)vstr_add_len(vstr, 1);
109 if (buf == NULL) {
110 return;
111 }
112 buf[0] = c;
113}
114
115void vstr_add_str(vstr_t *vstr, const char *str) {
116 vstr_add_strn(vstr, str, strlen(str));
117}
118
119void vstr_add_strn(vstr_t *vstr, const char *str, int len) {
120 if (vstr->had_error || !vstr_ensure_extra(vstr, len)) {
121 return;
122 }
123 memmove(vstr->buf + vstr->len, str, len);
124 vstr->len += len;
125 vstr->buf[vstr->len] = 0;
126}
127
128/*
129void vstr_add_le16(vstr_t *vstr, unsigned short v) {
130 byte *buf = (byte*)vstr_add_len(vstr, 2);
131 if (buf == NULL) {
132 return;
133 }
134 encode_le16(buf, v);
135}
136
137void vstr_add_le32(vstr_t *vstr, unsigned int v) {
138 byte *buf = (byte*)vstr_add_len(vstr, 4);
139 if (buf == NULL) {
140 return;
141 }
142 encode_le32(buf, v);
143}
144*/
145
146void vstr_cut_tail(vstr_t *vstr, int len) {
147 if (vstr->had_error) {
148 return;
149 }
150 if (len > vstr->len) {
151 vstr->len = 0;
152 } else {
153 vstr->len -= len;
154 }
155}
156
Damien68f59a92013-10-20 14:39:58 +0100157void vstr_printf(vstr_t *vstr, const char *fmt, ...) {
Damien2f06c572013-11-03 18:20:56 +0000158 va_list ap;
159 va_start(ap, fmt);
160 vstr_vprintf(vstr, fmt, ap);
161 va_end(ap);
162}
163
164void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap) {
Damien68f59a92013-10-20 14:39:58 +0100165 if (vstr->had_error || !vstr_ensure_extra(vstr, strlen(fmt))) {
166 return;
167 }
168
169 while (1) {
170 // try to print in the allocated space
171 int size = vstr->alloc - vstr->len;
Damien68f59a92013-10-20 14:39:58 +0100172 int n = vsnprintf(vstr->buf + vstr->len, size, fmt, ap);
Damien68f59a92013-10-20 14:39:58 +0100173
174 // if that worked, return
175 if (n > -1 && n < size) {
176 vstr->len += n;
177 return;
178 }
179
180 // else try again with more space
181 if (n > -1) { // glibc 2.1
182 // n + 1 is precisely what is needed
183 if (!vstr_ensure_extra(vstr, n + 1)) {
184 return;
185 }
186 } else { // glibc 2.0
187 // increase to twice the old size
188 if (!vstr_ensure_extra(vstr, size * 2)) {
189 return;
190 }
191 }
192 }
193}
Damien68f59a92013-10-20 14:39:58 +0100194
195/** testing *****************************************************/
196
197/*
Damien8b3a7c22013-10-23 20:20:17 +0100198int main(void) {
Damien68f59a92013-10-20 14:39:58 +0100199 vstr_t *vstr = vstr_new();
200 int i;
201 for (i = 0; i < 10; i++) {
202 vstr_printf(vstr, "%d) this is a test %d %s\n", i, 1234, "'a string'");
203 vstr_add_str(vstr, "-----");
204 vstr_printf(vstr, "this is another test %d %s\n", 1234, "'a second string'");
205 printf("%s", vstr->buf);
206 }
207}
208*/