1 /* darkstat 3
2  * copyright (c) 2001-2012 Emil Mikulic.
3  *
4  * str.c: string buffer with pool-based reallocation
5  *
6  * Permission to use, copy, modify, and distribute this file for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include "conv.h"
20 #include "err.h"
21 #include "str.h"
22 
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdint.h> /* for uint32_t on Linux and OS X */
27 #include <unistd.h>
28 
29 #define INITIAL_LEN 1024
30 
31 struct str {
32    char *buf;
33    size_t len, pool;
34 };
35 
36 struct str *
str_make(void)37 str_make(void)
38 {
39    struct str *s = xmalloc(sizeof(*s));
40    s->len = 0;
41    s->pool = INITIAL_LEN;
42    s->buf = xmalloc(s->pool);
43    return (s);
44 }
45 
46 void
str_free(struct str * s)47 str_free(struct str *s)
48 {
49    free(s->buf);
50    free(s);
51 }
52 
53 /*
54  * Extract struct str into buffer and length, freeing the struct in the
55  * process.
56  */
57 void
str_extract(struct str * s,size_t * len,char ** str)58 str_extract(struct str *s, size_t *len, char **str)
59 {
60    *len = s->len;
61    *str = s->buf;
62    free(s);
63 }
64 
65 void
str_appendn(struct str * buf,const char * s,const size_t len)66 str_appendn(struct str *buf, const char *s, const size_t len)
67 {
68    if (buf->pool < buf->len + len) {
69       /* pool has dried up */
70       while (buf->pool < buf->len + len)
71          buf->pool *= 2;
72       buf->buf = xrealloc(buf->buf, buf->pool);
73    }
74    memcpy(buf->buf + buf->len, s, len);
75    buf->len += len;
76 }
77 
78 void
str_appendstr(struct str * buf,const struct str * s)79 str_appendstr(struct str *buf, const struct str *s)
80 {
81    str_appendn(buf, s->buf, s->len);
82 }
83 
84 #ifndef str_append
85 void
str_append(struct str * buf,const char * s)86 str_append(struct str *buf, const char *s)
87 {
88    str_appendn(buf, s, strlen(s));
89 }
90 #endif
91 
92 /*
93  * Apparently, some wacky locales use periods, or another character that isn't
94  * a comma, to separate thousands.  If you are afflicted by such a locale,
95  * change this macro:
96  */
97 #define COMMA ','
98 
99 /* 2^32 = 4,294,967,296 (10 digits, 13 chars) */
100 #define I32_MAXLEN 13
101 
102 /* 2^64 = 18,446,744,073,709,551,616 (20 digits, 26 chars) */
103 #define I64_MAXLEN 26
104 
105 static void
str_append_u32(struct str * s,const uint32_t i,const int mod_sep)106 str_append_u32(struct str *s, const uint32_t i, const int mod_sep)
107 {
108    char out[I32_MAXLEN];
109    int pos;
110    unsigned int len;
111    uint32_t rem, next;
112 
113    if (i == 0) {
114       str_append(s, "0");
115       return;
116    }
117 
118    pos = sizeof(out)-1;
119    len = 0;
120    rem = i;
121 
122    while (rem > 0) {
123       assert(pos >= 0);
124       next = rem / 10;
125       rem = rem - next * 10;
126       assert(rem < 10);
127       out[pos] = '0' + rem;
128       pos--;
129       len++;
130       rem = next;
131       if (mod_sep && (rem > 0) && (len > 0) && (len % 3 == 0)) {
132          out[pos] = COMMA;
133          pos--;
134       }
135    }
136    str_appendn(s, out+pos+1, sizeof(out)-1-pos);
137 }
138 
139 static void
str_append_i32(struct str * s,int32_t i,const int mod_sep)140 str_append_i32(struct str *s, int32_t i, const int mod_sep)
141 {
142    if (i < 0) {
143       str_append(s, "-");
144       i = -i;
145    }
146    str_append_u32(s, (uint32_t)i, mod_sep);
147 }
148 
149 static void
str_append_u64(struct str * s,const uint64_t i,const int mod_sep)150 str_append_u64(struct str *s, const uint64_t i, const int mod_sep)
151 {
152    char out[I64_MAXLEN];
153    int pos;
154    unsigned int len;
155    uint64_t rem, next;
156    uint32_t rem32, next32;
157 
158    if (i == 0) {
159       str_append(s, "0");
160       return;
161    }
162 
163    pos = sizeof(out)-1;
164    len = 0;
165    rem = i;
166 
167    while (rem >= 4294967295U) {
168       assert(pos >= 0);
169       next = rem / 10;
170       rem = rem - next * 10;
171       assert(rem < 10);
172       out[pos] = '0' + rem;
173       pos--;
174       len++;
175       rem = next;
176       if (mod_sep && (rem > 0) && (len > 0) && (len % 3 == 0)) {
177          out[pos] = COMMA;
178          pos--;
179       }
180    }
181 
182    /*
183     * Stick to 32-bit math when we can as it's faster on 32-bit platforms.
184     * FIXME: a tunable way to switch this off?
185     */
186    rem32 = (uint32_t)rem;
187    while (rem32 > 0) {
188       assert(pos >= 0);
189       next32 = rem32 / 10;
190       rem32 = rem32 - next32 * 10;
191       assert(rem32 < 10);
192       out[pos] = '0' + rem32;
193       pos--;
194       len++;
195       rem32 = next32;
196       if (mod_sep && (rem32 > 0) && (len > 0) && (len % 3 == 0)) {
197          out[pos] = COMMA;
198          pos--;
199       }
200    }
201    str_appendn(s, out+pos+1, sizeof(out)-1-pos);
202 }
203 
204 static void
str_append_i64(struct str * s,int64_t i,const int mod_sep)205 str_append_i64(struct str *s, int64_t i, const int mod_sep)
206 {
207    if (i < 0) {
208       str_append(s, "-");
209       i = -i;
210    }
211    str_append_u64(s, (uint64_t)i, mod_sep);
212 }
213 
214 static void
str_append_hex8(struct str * s,const uint8_t b)215 str_append_hex8(struct str *s, const uint8_t b)
216 {
217    char out[2];
218    static const char hexset[] = "0123456789abcdef";
219 
220    out[0] = hexset[ ((b >> 4) & 15) ];
221    out[1] = hexset[ (b & 15) ];
222    str_appendn(s, out, 2);
223 }
224 
225 /* accepted formats: %s %d %u %x
226  * accepted modifiers: q and '
227  *
228  * %x is equivalent to %02x and expects a uint8_t
229  */
str_vappendf(struct str * s,const char * format,va_list va)230 void str_vappendf(struct str *s, const char *format, va_list va) {
231    size_t pos, len;
232    len = strlen(format);
233 
234    for (pos=0; pos<len; pos++) {
235       size_t span_start = pos, span_len = 0;
236 
237       while ((format[pos] != '\0') && (format[pos] != '%')) {
238          span_len++;
239          pos++;
240       }
241       if (span_len > 0)
242          str_appendn(s, format+span_start, span_len);
243 
244       if (format[pos] == '%') {
245          int mod_quad = 0, mod_sep = 0;
246          char *arg_str;
247 FORMAT:
248          pos++;
249          switch (format[pos]) {
250          case '%':
251             str_append(s, "%");
252             break;
253          case 'q':
254             mod_quad = 1;
255             goto FORMAT;
256          case '\'':
257             mod_sep = 1;
258             goto FORMAT;
259          case 's':
260             arg_str = va_arg(va, char*);
261             str_append(s, arg_str);
262             /* str_append can be a macro!  passing it va_arg can result in
263              * va_arg being called twice
264              */
265             break;
266          case 'd':
267             if (mod_quad)
268                str_append_i64(s, va_arg(va, int64_t), mod_sep);
269             else
270                str_append_i32(s, (int32_t)va_arg(va, int), mod_sep);
271             break;
272          case 'u':
273             if (mod_quad)
274                str_append_u64(s, va_arg(va, uint64_t), mod_sep);
275             else
276                str_append_u32(s, (uint32_t)va_arg(va, unsigned int), mod_sep);
277             break;
278          case 'x':
279             str_append_hex8(s, (uint8_t)va_arg(va, int));
280             break;
281          default:
282             errx(1, "format string is \"%s\", unknown format '%c' at %u",
283                format, format[pos], (unsigned int)pos);
284          }
285       }
286    }
287 }
288 
289 void
str_appendf(struct str * s,const char * format,...)290 str_appendf(struct str *s, const char *format, ...)
291 {
292    va_list va;
293    va_start(va, format);
294    str_vappendf(s, format, va);
295    va_end(va);
296 }
297 
298 size_t
xvasprintf(char ** result,const char * format,va_list va)299 xvasprintf(char **result, const char *format, va_list va)
300 {
301    size_t len;
302    struct str *s = str_make();
303    str_vappendf(s, format, va);
304    str_appendn(s, "", 1); /* "" still contains \0 */
305    str_extract(s, &len, result);
306    return (len-1);
307 }
308 
309 size_t
xasprintf(char ** result,const char * format,...)310 xasprintf(char **result, const char *format, ...)
311 {
312    va_list va;
313    size_t ret;
314    va_start(va, format);
315    ret = xvasprintf(result, format, va);
316    va_end(va);
317    return (ret);
318 }
319 
320 /*
321  * Format a length of time in seconds to "n days, n hrs, n mins, n secs".
322  * Returns a newly allocated str.
323  */
324 struct str *
length_of_time(const time_t t)325 length_of_time(const time_t t)
326 {
327    struct str *buf = str_make();
328    int secs  =  t % 60;
329    int mins  = (t / 60) % 60;
330    int hours = (t / 3600) % 24;
331    int days  =  t / 86400;
332 
333    int show_zeroes = 0;
334 
335    if (days > 0) {
336       str_appendf(buf, "%d %s", days, (days==1)?"day":"days");
337       show_zeroes = 1;
338    }
339 
340    if (show_zeroes || (hours > 0)) {
341       if (show_zeroes) str_append(buf, ", ");
342       str_appendf(buf, "%d %s", hours, (hours==1)?"hr":"hrs");
343       show_zeroes = 1;
344    }
345 
346    if (show_zeroes || (mins > 0)) {
347       if (show_zeroes) str_append(buf, ", ");
348       str_appendf(buf, "%d %s", mins, (mins==1)?"min":"mins");
349       show_zeroes = 1;
350    }
351 
352    if (show_zeroes) str_append(buf, ", ");
353    str_appendf(buf, "%d %s", secs, (secs==1)?"sec":"secs");
354 
355    return buf;
356 }
357 
str_write(const struct str * const buf,const int fd)358 ssize_t str_write(const struct str * const buf, const int fd) {
359    return write(fd, buf->buf, buf->len);
360 }
361 
str_len(const struct str * const buf)362 size_t str_len(const struct str * const buf) {
363    return buf->len;
364 }
365 
366 /* vim:set ts=3 sw=3 tw=78 expandtab: */
367