diff options
Diffstat (limited to 'strbuf.c')
-rw-r--r-- | strbuf.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/strbuf.c b/strbuf.c new file mode 100644 index 0000000..f823884 --- /dev/null +++ b/strbuf.c | |||
@@ -0,0 +1,130 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <stdarg.h> | ||
4 | #include <string.h> | ||
5 | |||
6 | #include "strbuf.h" | ||
7 | |||
8 | static void die(const char *format, ...) | ||
9 | { | ||
10 | va_list arg; | ||
11 | |||
12 | va_start(arg, format); | ||
13 | vfprintf(stderr, format, arg); | ||
14 | va_end(arg); | ||
15 | |||
16 | exit(-1); | ||
17 | } | ||
18 | |||
19 | void strbuf_init(strbuf_t *s) | ||
20 | { | ||
21 | s->data = NULL; | ||
22 | s->size = 0; | ||
23 | s->length = 0; | ||
24 | s->increment = STRBUF_DEFAULT_INCREMENT; | ||
25 | } | ||
26 | |||
27 | strbuf_t *strbuf_new() | ||
28 | { | ||
29 | strbuf_t *s; | ||
30 | |||
31 | s = malloc(sizeof(strbuf_t)); | ||
32 | if (!s) | ||
33 | die("Out of memory"); | ||
34 | |||
35 | strbuf_init(s); | ||
36 | |||
37 | return s; | ||
38 | } | ||
39 | |||
40 | void strbuf_set_increment(strbuf_t *s, int increment) | ||
41 | { | ||
42 | if (increment <= 0) | ||
43 | die("BUG: Invalid string increment"); | ||
44 | |||
45 | s->increment = increment; | ||
46 | } | ||
47 | |||
48 | void strbuf_free(strbuf_t *s) | ||
49 | { | ||
50 | if (s->data) | ||
51 | free(s->data); | ||
52 | free(s); | ||
53 | } | ||
54 | |||
55 | char *strbuf_to_char(strbuf_t *s, int *len) | ||
56 | { | ||
57 | char *data; | ||
58 | |||
59 | data = s->data; | ||
60 | if (len) | ||
61 | *len = s->length; | ||
62 | |||
63 | free(s); | ||
64 | |||
65 | return data; | ||
66 | } | ||
67 | |||
68 | /* Ensure strbuf can handle a string length bytes long (ignoring NULL | ||
69 | * optional termination). */ | ||
70 | void strbuf_resize(strbuf_t *s, int len) | ||
71 | { | ||
72 | int newsize; | ||
73 | |||
74 | /* Esnure there is room for optional NULL termination */ | ||
75 | newsize = len + 1; | ||
76 | /* Round up to the next increment */ | ||
77 | newsize = ((newsize + s->increment - 1) / s->increment) * s->increment; | ||
78 | s->size = newsize; | ||
79 | s->data = realloc(s->data, s->size); | ||
80 | if (!s->data) | ||
81 | die("Out of memory"); | ||
82 | } | ||
83 | |||
84 | void strbuf_append_mem(strbuf_t *s, const char *c, int len) | ||
85 | { | ||
86 | if (len > strbuf_emptylen(s)) | ||
87 | strbuf_resize(s, s->length + len); | ||
88 | |||
89 | memcpy(s->data + s->length, c, len); | ||
90 | s->length += len; | ||
91 | } | ||
92 | |||
93 | void strbuf_ensure_null(strbuf_t *s) | ||
94 | { | ||
95 | s->data[s->length] = 0; | ||
96 | } | ||
97 | |||
98 | void strbuf_append_fmt(strbuf_t *s, const char *fmt, ...) | ||
99 | { | ||
100 | va_list arg; | ||
101 | int fmt_len, try; | ||
102 | int empty_len; | ||
103 | |||
104 | /* If the first attempt to append fails, resize the buffer appropriately | ||
105 | * and try again */ | ||
106 | for (try = 0; ; try++) { | ||
107 | va_start(arg, fmt); | ||
108 | /* Append the new formatted string */ | ||
109 | /* fmt_len is the length of the string required, excluding the | ||
110 | * trailing NULL */ | ||
111 | empty_len = strbuf_emptylen(s); | ||
112 | /* Add 1 since there is also space for the terminating NULL. | ||
113 | * If the string hasn't been allocated then empty_len == -1, | ||
114 | * and vsprintf() won't store anything on the first pass */ | ||
115 | fmt_len = vsnprintf(s->data + s->length, empty_len + 1, fmt, arg); | ||
116 | va_end(arg); | ||
117 | |||
118 | if (fmt_len <= empty_len) | ||
119 | break; /* SUCCESS */ | ||
120 | if (try > 0) | ||
121 | die("BUG: length of formatted string changed"); | ||
122 | |||
123 | strbuf_resize(s, s->length + fmt_len); | ||
124 | } | ||
125 | |||
126 | s->length += fmt_len; | ||
127 | } | ||
128 | |||
129 | /* vi:ai et sw=4 ts=4: | ||
130 | */ | ||