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.
22 #include <stdint.h> /* for uint32_t on Linux and OS X */
28 #define INITIAL_LEN 1024
38 struct str
*s
= xmalloc(sizeof(*s
));
40 s
->pool
= INITIAL_LEN
;
41 s
->buf
= xmalloc(s
->pool
);
46 str_free(struct str
*s
)
53 * Extract struct str into buffer and length, freeing the struct in the
57 str_extract(struct str
*s
, size_t *len
, char **str
)
65 str_appendn(struct str
*buf
, const char *s
, const size_t len
)
67 if (buf
->pool
< buf
->len
+ len
) {
68 /* pool has dried up */
69 while (buf
->pool
< buf
->len
+ len
)
71 buf
->buf
= xrealloc(buf
->buf
, buf
->pool
);
73 memcpy(buf
->buf
+ buf
->len
, s
, len
);
78 str_appendstr(struct str
*buf
, const struct str
*s
)
80 str_appendn(buf
, s
->buf
, s
->len
);
85 str_append(struct str
*buf
, const char *s
)
87 str_appendn(buf
, s
, strlen(s
));
92 * Apparently, some wacky locales use periods, or another character that isn't
93 * a comma, to separate thousands. If you are afflicted by such a locale,
98 /* 2^32 = 4,294,967,296 (10 digits, 13 chars) */
101 /* 2^64 = 18,446,744,073,709,551,616 (20 digits, 26 chars) */
102 #define I64_MAXLEN 26
105 str_append_u32(struct str
*s
, const uint32_t i
, const int mod_sep
)
107 char out
[I32_MAXLEN
];
123 rem
= rem
- next
* 10;
125 out
[pos
] = '0' + rem
;
129 if (mod_sep
&& (rem
> 0) && (len
> 0) && (len
% 3 == 0)) {
134 str_appendn(s
, out
+pos
+1, sizeof(out
)-1-pos
);
138 str_append_i32(struct str
*s
, int32_t i
, const int mod_sep
)
144 str_append_u32(s
, (uint32_t)i
, mod_sep
);
148 str_append_u64(struct str
*s
, const uint64_t i
, const int mod_sep
)
150 char out
[I64_MAXLEN
];
153 uint32_t rem32
, next32
;
164 while (rem
>= 4294967295U) {
167 rem
= rem
- next
* 10;
169 out
[pos
] = '0' + rem
;
173 if (mod_sep
&& (rem
> 0) && (len
> 0) && (len
% 3 == 0)) {
180 * Stick to 32-bit math when we can as it's faster on 32-bit platforms.
181 * FIXME: a tunable way to switch this off?
183 rem32
= (uint32_t)rem
;
187 rem32
= rem32
- next32
* 10;
189 out
[pos
] = '0' + rem32
;
193 if (mod_sep
&& (rem32
> 0) && (len
> 0) && (len
% 3 == 0)) {
198 str_appendn(s
, out
+pos
+1, sizeof(out
)-1-pos
);
202 str_append_i64(struct str
*s
, int64_t i
, const int mod_sep
)
208 str_append_u64(s
, (uint64_t)i
, mod_sep
);
212 str_append_hex8(struct str
*s
, const uint8_t b
)
215 static const char hexset
[] = "0123456789abcdef";
217 out
[0] = hexset
[ ((b
>> 4) & 15) ];
218 out
[1] = hexset
[ (b
& 15) ];
219 str_appendn(s
, out
, 2);
222 /* accepted formats: %s %d %u %x
223 * accepted modifiers: q and '
225 * %x is equivalent to %02x and expects a uint8_t
228 str_vappendf(struct str
*s
, const char *format
, va_list va
)
231 len
= strlen(format
);
233 for (pos
=0; pos
<len
; pos
++) {
234 size_t span_start
= pos
, span_len
= 0;
236 while ((format
[pos
] != '\0') && (format
[pos
] != '%')) {
241 str_appendn(s
, format
+span_start
, span_len
);
243 if (format
[pos
] == '%') {
244 int mod_quad
= 0, mod_sep
= 0;
248 switch (format
[pos
]) {
259 arg_str
= va_arg(va
, char*);
260 str_append(s
, arg_str
);
261 /* str_append can be a macro! passing it va_arg can result in
262 * va_arg being called twice
267 str_append_i64(s
, va_arg(va
, int64_t), mod_sep
);
269 str_append_i32(s
, (int32_t)va_arg(va
, int), mod_sep
);
273 str_append_u64(s
, va_arg(va
, uint64_t), mod_sep
);
275 str_append_u32(s
, (uint32_t)va_arg(va
, unsigned int), mod_sep
);
278 str_append_hex8(s
, (uint8_t)va_arg(va
, int));
281 errx(1, "format string is \"%s\", unknown format '%c' at %u",
282 format
, format
[pos
], (unsigned int)pos
);
289 str_appendf(struct str
*s
, const char *format
, ...)
292 va_start(va
, format
);
293 str_vappendf(s
, format
, va
);
298 xvasprintf(char **result
, const char *format
, va_list va
)
301 struct str
*s
= str_make();
302 str_vappendf(s
, format
, va
);
303 str_appendn(s
, "", 1); /* "" still contains \0 */
304 str_extract(s
, &len
, result
);
309 xasprintf(char **result
, const char *format
, ...)
313 va_start(va
, format
);
314 ret
= xvasprintf(result
, format
, va
);
320 * Format a length of time in seconds to "n days, n hrs, n mins, n secs".
321 * Returns a newly allocated str.
324 length_of_time(const time_t t
)
326 struct str
*buf
= str_make();
328 int mins
= (t
/ 60) % 60;
329 int hours
= (t
/ 3600) % 24;
330 int days
= t
/ 86400;
335 str_appendf(buf
, "%d %s", days
, (days
==1)?"day":"days");
339 if (show_zeroes
|| (hours
> 0)) {
340 if (show_zeroes
) str_append(buf
, ", ");
341 str_appendf(buf
, "%d %s", hours
, (hours
==1)?"hr":"hrs");
345 if (show_zeroes
|| (mins
> 0)) {
346 if (show_zeroes
) str_append(buf
, ", ");
347 str_appendf(buf
, "%d %s", mins
, (mins
==1)?"min":"mins");
351 if (show_zeroes
) str_append(buf
, ", ");
352 str_appendf(buf
, "%d %s", secs
, (secs
==1)?"sec":"secs");
357 /* vim:set ts=3 sw=3 tw=78 expandtab: */