2 * copyright (c) 2001-2011 Emil Mikulic.
4 * str.c: string buffer with pool-based reallocation
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.
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.
26 #include <stdint.h> /* for uint32_t on Linux and OS X */
29 #define INITIAL_LEN 1024
39 struct str
*s
= xmalloc(sizeof(*s
));
41 s
->pool
= INITIAL_LEN
;
42 s
->buf
= xmalloc(s
->pool
);
47 str_free(struct str
*s
)
54 * Extract struct str into buffer and length, freeing the struct in the
58 str_extract(struct str
*s
, size_t *len
, char **str
)
66 str_appendn(struct str
*buf
, const char *s
, const size_t len
)
68 if (buf
->pool
< buf
->len
+ len
) {
69 /* pool has dried up */
70 while (buf
->pool
< buf
->len
+ len
)
72 buf
->buf
= xrealloc(buf
->buf
, buf
->pool
);
74 memcpy(buf
->buf
+ buf
->len
, s
, len
);
79 str_appendstr(struct str
*buf
, const struct str
*s
)
81 str_appendn(buf
, s
->buf
, s
->len
);
86 str_append(struct str
*buf
, const char *s
)
88 str_appendn(buf
, s
, strlen(s
));
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,
99 /* 2^32 = 4,294,967,296 (10 digits, 13 chars) */
100 #define I32_MAXLEN 13
102 /* 2^64 = 18,446,744,073,709,551,616 (20 digits, 26 chars) */
103 #define I64_MAXLEN 26
106 str_append_u32(struct str
*s
, const uint32_t i
, const int mod_sep
)
108 char out
[I32_MAXLEN
];
125 rem
= rem
- next
* 10;
127 out
[pos
] = '0' + rem
;
131 if (mod_sep
&& (rem
> 0) && (len
> 0) && (len
% 3 == 0)) {
136 str_appendn(s
, out
+pos
+1, sizeof(out
)-1-pos
);
140 str_append_i32(struct str
*s
, int32_t i
, const int mod_sep
)
146 str_append_u32(s
, (uint32_t)i
, mod_sep
);
150 str_append_u64(struct str
*s
, const uint64_t i
, const int mod_sep
)
152 char out
[I64_MAXLEN
];
156 uint32_t rem32
, next32
;
167 while (rem
>= 4294967295U) {
170 rem
= rem
- next
* 10;
172 out
[pos
] = '0' + rem
;
176 if (mod_sep
&& (rem
> 0) && (len
> 0) && (len
% 3 == 0)) {
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?
186 rem32
= (uint32_t)rem
;
190 rem32
= rem32
- next32
* 10;
192 out
[pos
] = '0' + rem32
;
196 if (mod_sep
&& (rem32
> 0) && (len
> 0) && (len
% 3 == 0)) {
201 str_appendn(s
, out
+pos
+1, sizeof(out
)-1-pos
);
205 str_append_i64(struct str
*s
, int64_t i
, const int mod_sep
)
211 str_append_u64(s
, (uint64_t)i
, mod_sep
);
215 str_append_hex8(struct str
*s
, const uint8_t b
)
218 static const char hexset
[] = "0123456789abcdef";
220 out
[0] = hexset
[ ((b
>> 4) & 15) ];
221 out
[1] = hexset
[ (b
& 15) ];
222 str_appendn(s
, out
, 2);
225 /* accepted formats: %s %d %u %x
226 * accepted modifiers: q and '
228 * %x is equivalent to %02x and expects a uint8_t
230 void str_vappendf(struct str
*s
, const char *format
, va_list va
) {
232 len
= strlen(format
);
234 for (pos
=0; pos
<len
; pos
++) {
235 size_t span_start
= pos
, span_len
= 0;
237 while ((format
[pos
] != '\0') && (format
[pos
] != '%')) {
242 str_appendn(s
, format
+span_start
, span_len
);
244 if (format
[pos
] == '%') {
245 int mod_quad
= 0, mod_sep
= 0;
249 switch (format
[pos
]) {
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
268 str_append_i64(s
, va_arg(va
, int64_t), mod_sep
);
270 str_append_i32(s
, (int32_t)va_arg(va
, int), mod_sep
);
274 str_append_u64(s
, va_arg(va
, uint64_t), mod_sep
);
276 str_append_u32(s
, (uint32_t)va_arg(va
, unsigned int), mod_sep
);
279 str_append_hex8(s
, (uint8_t)va_arg(va
, int));
282 errx(1, "format string is \"%s\", unknown format '%c' at %u",
283 format
, format
[pos
], (unsigned int)pos
);
290 str_appendf(struct str
*s
, const char *format
, ...)
293 va_start(va
, format
);
294 str_vappendf(s
, format
, va
);
299 xvasprintf(char **result
, const char *format
, va_list va
)
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
);
310 xasprintf(char **result
, const char *format
, ...)
314 va_start(va
, format
);
315 ret
= xvasprintf(result
, format
, va
);
321 * Format a length of time in seconds to "n days, n hrs, n mins, n secs".
322 * Returns a newly allocated str.
325 length_of_time(const time_t t
)
327 struct str
*buf
= str_make();
329 int mins
= (t
/ 60) % 60;
330 int hours
= (t
/ 3600) % 24;
331 int days
= t
/ 86400;
336 str_appendf(buf
, "%d %s", days
, (days
==1)?"day":"days");
340 if (show_zeroes
|| (hours
> 0)) {
341 if (show_zeroes
) str_append(buf
, ", ");
342 str_appendf(buf
, "%d %s", hours
, (hours
==1)?"hr":"hrs");
346 if (show_zeroes
|| (mins
> 0)) {
347 if (show_zeroes
) str_append(buf
, ", ");
348 str_appendf(buf
, "%d %s", mins
, (mins
==1)?"min":"mins");
352 if (show_zeroes
) str_append(buf
, ", ");
353 str_appendf(buf
, "%d %s", secs
, (secs
==1)?"sec":"secs");
358 ssize_t
str_write(const struct str
* const buf
, const int fd
) {
359 return write(fd
, buf
->buf
, buf
->len
);
362 size_t str_len(const struct str
* const buf
) {
366 /* vim:set ts=3 sw=3 tw=78 expandtab: */