#include "printf.h" #include #include #include #include #include #include #define CHECK_FLAG(str, f) ((((str)->bstr_flags) & (f)) == (f)) #define IS_DYNAMIC(p) (CHECK_FLAG(p, B_BSTR_F_ALLOC)) /* number of bytes that bstr_buf is extended by when required */ #define CAPACITY_STEP 32 void b_bstr_begin(struct b_bstr *str, char *buf, size_t max) { memset(str, 0x0, sizeof *str); str->bstr_magic = B_BSTR_MAGIC; str->bstr_buf = buf; str->bstr_capacity = max; str->bstr_len = 0; str->bstr_flags = B_BSTR_F_NONE; } void b_bstr_begin_dynamic(struct b_bstr *str) { memset(str, 0x0, sizeof *str); str->bstr_magic = B_BSTR_MAGIC; str->bstr_buf = NULL; str->bstr_capacity = 0; str->bstr_len = 0; str->bstr_flags = B_BSTR_F_ALLOC; } static char *truncate_buffer(char *s, size_t len) { if (!s || !len) { return NULL; } char *final = realloc(s, len + 1); if (!final) { return s; } final[len] = '\0'; return final; } char *b_bstr_end(struct b_bstr *str) { char *out = str->bstr_buf; size_t len = str->bstr_len; if (IS_DYNAMIC(str) && str->bstr_capacity - 1 > str->bstr_len) { /* bstr_buf is larger than required to contain the string. * re-allocate it so it's only as large as necessary */ out = truncate_buffer(out, len); } if (str->bstr_istack) { free(str->bstr_istack); } memset(str, 0x0, sizeof *str); return out; } enum b_status b_bstr_reserve(struct b_bstr *strv, size_t len) { if (!IS_DYNAMIC(strv)) { return B_SUCCESS; } if (strv->bstr_capacity > 0 && strv->bstr_capacity - 1 >= len) { return B_SUCCESS; } char *new_buf = realloc(strv->bstr_buf, len + 1); if (!new_buf) { return B_ERR_NO_MEMORY; } strv->bstr_buf = new_buf; strv->bstr_capacity = len + 1; strv->bstr_buf[strv->bstr_len] = '\0'; return B_SUCCESS; } static int current_indent(struct b_bstr *str) { if (!str->bstr_istack || !str->bstr_istack_size) { return 0; } return str->bstr_istack[str->bstr_istack_ptr]; } static void __formatter_putchar(struct b_bstr *str, char c) { if (str->bstr_capacity > 0 && str->bstr_len < str->bstr_capacity - 1) { str->bstr_buf[str->bstr_len++] = c; str->bstr_buf[str->bstr_len] = '\0'; return; } if (!CHECK_FLAG(str, B_BSTR_F_ALLOC)) { return; } size_t old_capacity = str->bstr_capacity; size_t new_capacity = old_capacity + CAPACITY_STEP; char *new_buf = realloc(str->bstr_buf, new_capacity); if (!new_buf) { return; } str->bstr_buf = new_buf; str->bstr_capacity = new_capacity; str->bstr_buf[str->bstr_len++] = c; str->bstr_buf[str->bstr_len] = '\0'; } static void formatter_putchar(struct b_bstr *f, char c) { if (f->bstr_add_indent && c != '\n') { int indent = current_indent(f); for (int i = 0; i < indent; i++) { __formatter_putchar(f, ' '); __formatter_putchar(f, ' '); } f->bstr_add_indent = 0; } __formatter_putchar(f, c); if (c == '\n') { f->bstr_add_indent = 1; } } static void bstr_fctprintf(char c, void *arg) { struct b_bstr *str = arg; formatter_putchar(str, c); } static enum b_status bstr_putcs( struct b_bstr *str, const char *s, size_t len, size_t *nr_written) { for (size_t i = 0; i < len; i++) { formatter_putchar(str, s[i]); } if (nr_written) { *nr_written = len; } return B_SUCCESS; } static enum b_status bstr_puts(struct b_bstr *str, const char *s, size_t *nr_written) { size_t i; for (i = 0; s[i]; i++) { formatter_putchar(str, s[i]); } if (nr_written) { *nr_written = i; } return B_SUCCESS; } enum b_status b_bstr_push_indent(struct b_bstr *str, int indent) { if (!str->bstr_istack) { str->bstr_istack = calloc(4, sizeof(int)); str->bstr_istack_size = 4; str->bstr_istack_ptr = 0; } if (str->bstr_istack_ptr + 1 > str->bstr_istack_size) { int *buf = realloc( str->bstr_istack, (str->bstr_istack_size + 4) * sizeof(int)); if (!buf) { return B_ERR_NO_MEMORY; } str->bstr_istack = buf; str->bstr_istack_size += 4; } int cur_indent = str->bstr_istack[str->bstr_istack_ptr]; str->bstr_istack[++str->bstr_istack_ptr] = cur_indent + indent; return B_SUCCESS; } enum b_status b_bstr_pop_indent(struct b_bstr *strv) { if (strv->bstr_istack_ptr > 0) { strv->bstr_istack_ptr--; } return B_SUCCESS; } enum b_status b_bstr_write_char(struct b_bstr *str, char c) { formatter_putchar(str, c); return B_SUCCESS; } enum b_status b_bstr_write_chars( struct b_bstr *str, const char *s, size_t len, size_t *nr_written) { return bstr_putcs(str, s, len, nr_written); } enum b_status b_bstr_write_cstr(struct b_bstr *str, const char *s, size_t *nr_written) { return bstr_puts(str, s, nr_written); } enum b_status b_bstr_write_cstr_list( struct b_bstr *str, const char **strs, size_t *nr_written) { size_t w = 0; enum b_status status = B_SUCCESS; for (size_t i = 0; strs[i]; i++) { size_t tmp = 0; status = bstr_puts(str, strs[i], &tmp); w += tmp; if (B_ERR(status)) { break; } } if (nr_written) { *nr_written = w; } return status; } enum b_status b_bstr_write_cstr_array( struct b_bstr *str, const char **strs, size_t count, size_t *nr_written) { enum b_status status = B_SUCCESS; size_t w = 0; for (size_t i = 0; i < count; i++) { if (!strs[i]) { continue; } size_t tmp = 0; status = bstr_puts(str, strs[i], &tmp); w += tmp; if (B_ERR(status)) { break; } } if (nr_written) { *nr_written = w; } return status; } enum b_status b_bstr_add_many(struct b_bstr *str, size_t *nr_written, ...) { va_list arg; va_start(arg, nr_written); const char *s = NULL; size_t w = 0; enum b_status status = B_SUCCESS; while ((s = va_arg(arg, const char *))) { size_t tmp = 0; status = bstr_puts(str, s, &tmp); w += tmp; if (B_ERR(status)) { break; } } if (nr_written) { *nr_written = w; } return status; } enum b_status b_bstr_write_rope( b_bstr *strv, const struct b_rope *rope, size_t *nr_written) { size_t start = strv->bstr_len; enum b_status status = b_rope_to_bstr(rope, strv); size_t end = strv->bstr_len; if (nr_written) { *nr_written = end - start; } return status; } enum b_status b_bstr_write_fmt( struct b_bstr *str, size_t *nr_written, const char *format, ...) { va_list arg; va_start(arg, format); enum b_status result = b_bstr_write_vfmt(str, nr_written, format, arg); va_end(arg); return result; } enum b_status b_bstr_write_vfmt( struct b_bstr *str, size_t *nr_written, const char *format, va_list arg) { size_t start = str->bstr_len; z__b_fctprintf(bstr_fctprintf, str, format, arg); size_t end = str->bstr_len; if (nr_written) { *nr_written = end - start; } /* TODO update z__b_fctprintf to support propagating error codes */ return B_SUCCESS; } char *b_bstr_rope(const struct b_rope *rope, size_t *nr_written) { struct b_bstr str; b_bstr_begin_dynamic(&str); b_bstr_write_rope(&str, rope, nr_written); return b_bstr_end(&str); } char *b_bstr_fmt(size_t *nr_written, const char *format, ...) { va_list arg; va_start(arg, format); char *s = b_bstr_vfmt(nr_written, format, arg); va_end(arg); return s; } char *b_bstr_vfmt(size_t *nr_written, const char *format, va_list arg) { struct b_bstr str; b_bstr_begin_dynamic(&str); b_bstr_write_vfmt(&str, nr_written, format, arg); return b_bstr_end(&str); }