Realloc bufsz only if no error
[clinfo] / src / strbuf.h
1 /* multi-purpose string strbuf, will be initialized to be
2  * at least 1024 bytes long.
3  */
4
5 #include <ctype.h>
6 #include <string.h>
7 #include <stdio.h>
8 #include "fmtmacros.h"
9
10 char *strbuf;
11 size_t bufsz, nusz;
12
13 #define GET_STRING(cmd, param, param_str, ...) do { \
14         error = cmd(__VA_ARGS__, param, 0, NULL, &nusz); \
15         if (REPORT_ERROR("get " param_str " size")) break; \
16         if (nusz > bufsz) { \
17                 REALLOC(strbuf, nusz, #param); \
18                 bufsz = nusz; \
19         } \
20         error = cmd(__VA_ARGS__, param, bufsz, strbuf, NULL); \
21         REPORT_ERROR("get " param_str); \
22 } while (0)
23
24 /* Skip leading whitespace in a string */
25 static inline const char* skip_leading_ws(const char *str)
26 {
27         const char *ret = str;
28         while (isspace(*ret)) ++ret;
29         return ret;
30 }
31
32 /* replace last 3 chars in strbuf with ... */
33 static const char ellip[] = "...";
34
35 static void trunc_strbuf(void)
36 {
37         memcpy(strbuf + bufsz - 4, ellip, 4);
38 }
39
40 /* copy a string to strbuf, at the given offset,
41  * returning the amount of bytes written (excluding the
42  * closing NULL byte)
43  */
44 static inline size_t bufcpy_len(size_t offset, const char *str, size_t len)
45 {
46         size_t maxlen = bufsz - offset - 1;
47         char *dst = strbuf + offset;
48         int trunc = 0;
49         if (bufsz < offset) {
50                 fprintf(stderr, "bufcpy overflow copying %s at offset %" PRIuS "/%" PRIuS " (%s)\n",
51                         str, offset, bufsz, strbuf);
52                 maxlen = 0;
53                 trunc = 1;
54         }
55         if (len > maxlen) {
56                 len = maxlen;
57                 trunc = 1;
58                 /* TODO enlarge strbuf instead, if maxlen > 0 */
59         }
60         memcpy(dst, str, len);
61         offset += len;
62         if (trunc)
63                 trunc_strbuf();
64         else
65                 strbuf[offset] = '\0';
66         return len;
67 }
68
69 /* As above, auto-compute string length */
70 static inline size_t bufcpy(size_t offset, const char *str)
71 {
72         return bufcpy_len(offset, str, strlen(str));
73 }
74
75
76 /* Separators: we want to be able to prepend separators as needed to strbuf,
77  * which we do only if halfway through the buffer. The callers should first
78  * call a 'set_separator' and then use add_separator(&offset) to add it, where szval
79  * is an offset inside the buffer, which will be incremented as needed
80  */
81
82 const char *sep;
83 size_t sepsz;
84
85 void set_separator(const char* _sep)
86 {
87         sep = _sep;
88         sepsz = strlen(sep);
89 }
90
91 /* Note that no overflow check is done: it is assumed that strbuf will have enough room */
92 void add_separator(size_t *offset)
93 {
94         if (*offset)
95                 *offset += bufcpy_len(*offset, sep, sepsz);
96 }