Version 3.0.23.01.25
[clinfo] / src / strbuf.h
1 /* multi-purpose string _strbuf, will be initialized to be
2  * at least 1024 bytes long.
3  */
4
5 #ifndef STRBUF_H
6 #define STRBUF_H
7
8 #include <ctype.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stdarg.h>
13 #include "memory.h"
14 #include "fmtmacros.h"
15
16 struct _strbuf
17 {
18         char *buf;
19         size_t sz; /* allocated size */
20         size_t end; /* offset to terminating null byte */
21 };
22
23 static inline void realloc_strbuf(struct _strbuf *str, size_t nusz, const char* what)
24 {
25         if (nusz > str->sz) {
26                 REALLOC(str->buf, nusz, what);
27                 str->sz = nusz;
28         }
29 }
30
31 static inline void reset_strbuf(struct _strbuf *str)
32 {
33         str->end = 0;
34         if (str->buf) str->buf[0] = '\0';
35 }
36
37 static inline void init_strbuf(struct _strbuf *str, const char *what)
38 {
39         str->sz = 0;
40         str->buf = NULL;
41         realloc_strbuf(str, 1024, what);
42         reset_strbuf(str);
43 }
44
45 static inline void free_strbuf(struct _strbuf *str)
46 {
47         free(str->buf);
48         str->buf = NULL;
49         reset_strbuf(str);
50 }
51
52 static inline void strbuf_append(const char *what, struct _strbuf *str, const char *fmt, ...)
53 {
54         va_list ap;
55         size_t room = str->sz - str->end - 1;
56         size_t written = 0;
57
58         /* write if we have room */
59         va_start(ap, fmt);
60         written = vsnprintf(str->buf + str->end, room, fmt, ap);
61         va_end(ap);
62
63         /* if we would have written more, we need to expand the storage */
64         if (written >= room) {
65                 realloc_strbuf(str, str->end + written + 1, what);
66                 room = str->sz - str->end;
67
68                 /* and re-write */
69                 va_start(ap, fmt);
70                 written = vsnprintf(str->buf + str->end, room, fmt, ap);
71                 va_end(ap);
72         }
73         str->end += written;
74 }
75
76 static inline void strbuf_append_str_len(const char *what, struct _strbuf *str,
77         const char *to_append, /* string to append */
78         size_t len) /* length of string to append */
79 {
80         size_t room = str->sz - str->end - 1;
81
82         if (len >= room) {
83                 realloc_strbuf(str, str->end + len + 1, what);
84         }
85         /* copy up to the terminating NULL */
86         memcpy(str->buf + str->end, to_append, len);
87         str->end += len;
88         /* ensure we have a NULL in last position, since len may have been used
89          * to override the original string length */
90         str->buf[str->end] = '\0';
91 }
92
93 static inline void strbuf_append_str(const char *what, struct _strbuf *str, const char *to_append)
94 {
95         strbuf_append_str_len(what, str, to_append, strlen(to_append));
96 }
97
98 #define GET_STRING(str, err, cmd, param, param_str, ...) do { \
99         size_t nusz; \
100         err = cmd(__VA_ARGS__, param, 0, NULL, &nusz); \
101         if (REPORT_ERROR(str, err, "get " param_str " size")) break; \
102         realloc_strbuf(str, nusz, #param); \
103         err = cmd(__VA_ARGS__, param, (str)->sz, (str)->buf, NULL); \
104         if (REPORT_ERROR(str, err, "get " param_str)) break; \
105         (str)->end = nusz; \
106 } while (0)
107
108 #define GET_STRING_LOC(ret, loc, cmd, ...) do { \
109         size_t nusz; \
110         ret->err = REPORT_ERROR_LOC(ret, \
111                 cmd(__VA_ARGS__, 0, NULL, &nusz), \
112                 loc, "get %s size"); \
113         if (!ret->err) { \
114                 realloc_strbuf(&ret->str, nusz, loc->sname); \
115                 ret->err = REPORT_ERROR_LOC(ret, \
116                         cmd(__VA_ARGS__, ret->str.sz, ret->str.buf, NULL), \
117                         loc, "get %s"); \
118         } \
119         if (!ret->err) { \
120                 ret->str.end = nusz; \
121         } \
122 } while (0)
123
124 /* Skip leading whitespace in a string */
125 static inline const char* skip_leading_ws(const char *str)
126 {
127         const char *ret = str;
128         while (isspace((unsigned char) *ret)) ++ret;
129         return ret;
130 }
131
132 /* Separators: we want to be able to prepend separators as needed to _strbuf,
133  * which we do only if halfway through the buffer. The callers should first
134  * call a 'set_separator' and then use add_separator(&offset) to add it, where szval
135  * is an offset inside the buffer, which will be incremented as needed
136  */
137
138 const char *sep;
139 size_t sepsz;
140
141 void set_separator(const char* _sep)
142 {
143         sep = _sep;
144         sepsz = strlen(sep);
145 }
146
147 #endif