/* ** cstring.h - written by vesely in milano on 18 feb 2002 ** simple string that grows Copyright (C) 2002, 2015, 2022 Alessandro Vesely This file is part of zdkimfilter zdkimfilter is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. zdkimfilter is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License version 3 along with zdkimfilter. If not, see . Additional permission under GNU GPLv3 section 7: If you modify zdkimfilter, or any covered part of it, by linking or combining it with OpenSSL, OpenDKIM, Sendmail, or any software developed by The Trusted Domain Project or Sendmail Inc., containing parts covered by the applicable licence, the licensor of zdkimfilter grants you additional permission to convey the resulting work. */ #include #if !ZDKIMFILTER_DEBUG #define NDEBUG #endif #include #include #include // #if defined(TEST_MAIN) #include // for vsnprintf // #endif #include "cstring.h" #include #if !defined(NDEBUG) static int cstr_assert(cstring const *const s) { #if defined(TEST_MAIN) printf("%p: %zu/%zu = \"%s\"\n", s, s->length, s->alloc, s->data); #endif return s != NULL && s->alloc >= s->length && s->data[s->length] == 0; } #endif /* NDEBUG */ cstring* cstr_init(size_t const total) /* alloc one byte more than needed */ { cstring *const rtc = (cstring*)malloc(total + sizeof(cstring)); if (rtc) { rtc->alloc = total; rtc->length = 0; rtc->data[0] = 0; } assert(rtc == NULL || cstr_assert(rtc)); return rtc; } cstring* cstr_from_string(char const *data) { size_t length = strlen(data); cstring *rtc = cstr_init(length); if (rtc) rtc = cstr_addblob(rtc, data, length); assert(rtc == NULL || cstr_assert(rtc)); return rtc; } cstring* cstr_reserve(cstring* const s, size_t const total) { cstring *rtc; assert(cstr_assert(s)); if (s->alloc >= total) return s; rtc = (cstring*)realloc(s, total + sizeof(cstring)); if (rtc != NULL) rtc->alloc = total; else free(s); assert(rtc == NULL || cstr_assert(rtc)); return rtc; #if 0 if ((rtc = (cstring*)malloc(total + sizeof(cstring))) != NULL) { size_t const l = rtc->length = s->length; memcpy(rtc->data, s->data, l + 1); rtc->alloc = total; } free(s); return rtc; #endif } cstring* cstr_grow(cstring* const s, size_t const incr) { cstring *rtc; size_t total; size_t const req = s->length + incr; assert(cstr_assert(s)); if (s->alloc >= req) return s; total = 2 * s->alloc; if (total <= req) total = req; rtc = cstr_reserve(s, total); assert(rtc == NULL || cstr_assert(rtc)); return rtc; } cstring* cstr_addch(cstring* s, int const ch) { assert(cstr_assert(s)); if ((s = cstr_grow(s, 1)) != NULL) { s->data[s->length] = ch; s->data[++s->length] = 0; } assert(s == NULL || cstr_assert(s)); return s; } cstring* cstr_addutf8(cstring* s, uint32_t ch) { assert(cstr_assert(s)); if (ch < 0x7fL) return cstr_addch(s, ch); int bytes, first; if (ch <= 0x7ff) { bytes = 2; first = 0xc0 | (ch >> 6); } else if (ch >= 0xd800 && ch <= 0xdfff) return s; // reserved "surrogate" for utf16, silently discard else if (ch <= 0xffff) { bytes = 3; first = 0xe0 | (ch >> 12); } else if (ch <= 0x10ffff) { bytes = 4; first = 0xf0 | (ch >> 18); } else // invalid, silently discard return s; if ((s = cstr_grow(s, bytes)) != NULL) { s->data[s->length] = first; if (bytes >= 4) s->data[++s->length] = 0x80 | ((ch >> 12) & 0x3f); if (bytes >= 3) s->data[++s->length] = 0x80 | ((ch >> 6) & 0x3f); s->data[++s->length] = 0x80 | (ch & 0x3f); s->data[++s->length] = 0; } return s; } cstring* cstr_insch(cstring* s, size_t pos, int character) /* * Insert character at 0-based position pos. Must be pos <= length. */ { assert(cstr_assert(s)); if (pos <= s->length && (s = cstr_grow(s, 1)) != NULL) { s->length += 1; if (pos < s->length) memmove(&s->data[pos+1], &s->data[pos], s->length - pos); s->data[pos] = character; } assert(s == NULL || cstr_assert(s)); return s; } cstring* cstr_insstr(cstring* s, size_t pos, char const *str) /* * Insert string at 0-based position pos. Must be pos <= length. */ { assert(cstr_assert(s)); assert(str); size_t len = strlen(str); if (pos <= s->length && (s = cstr_grow(s, len)) != NULL) { s->length += len; if (pos < s->length) memmove(&s->data[pos+len], &s->data[pos], s->length - pos); memcpy(&s->data[pos], str, len); } assert(s == NULL || cstr_assert(s)); return s; } cstring* cstr_addblob(cstring* s, char const* str, size_t const l) { assert(cstr_assert(s)); if (l > 0 && (s = cstr_grow(s, l)) != NULL) { memcpy(&s->data[s->length], str, l); s->data[s->length += l] = 0; } assert(s == NULL || cstr_assert(s)); return s; } cstring* cstr_addstr(cstring* s, char const* str) { return cstr_addblob(s, str, strlen(str)); } void cstr_trunc(cstring* s, size_t l) { assert(cstr_assert(s)); if (l > s->alloc) l = s->alloc; s->data[s->length = l] = 0; assert(cstr_assert(s)); } cstring* cstr_final(cstring* const s) { size_t const l = s->length; cstring* rtc; assert(cstr_assert(s)); rtc = realloc(s, l + sizeof(cstring)); if (rtc == NULL) /* ??? */ rtc = s; else rtc->alloc = l; assert(rtc == NULL || cstr_assert(rtc)); return rtc; } cstring* cstr_dup(cstring const * const s) { size_t const l = s->length; cstring* rtc; assert(cstr_assert(s)); rtc = (cstring*)malloc(l + sizeof(cstring)); if (rtc) { rtc->length = rtc->alloc = l; memcpy(rtc->data, s->data, l + 1); } assert(rtc == NULL || cstr_assert(rtc)); return rtc; } cstring* cstr_setblob(cstring* s, char const* str, size_t const l) { assert(cstr_assert(s)); if (l > s->alloc && (s = cstr_grow(s, l - s->alloc)) == NULL) return NULL; memcpy(s->data, str, l); s->data[s->length = l] = 0; assert(s == NULL || cstr_assert(s)); return s; } cstring* cstr_setstr(cstring* s, char const* str) { return cstr_setblob(s, str, strlen(str)); } cstring* cstr_set(cstring* s, cstring const* str) { assert(cstr_assert(s)); assert(cstr_assert(str)); if (s == str) return s; return cstr_setblob(s, str->data, str->length); } cstring* cstr_add(cstring *s, cstring const *str) { cstring *rtc; assert(cstr_assert(s)); assert(cstr_assert(str)); if (s == str) { cstring *s1 = cstr_dup(str); if (s1 == NULL) return NULL; rtc = cstr_add(s, s1); free(s1); } else rtc = cstr_addblob(s, str->data, str->length); return rtc; } cstring *cstr_printf(cstring *s, char const *fmt, ...) { int nu; va_list ap; va_start(ap, fmt); cstring *rtc = cstr_printfrcv(s, &nu, fmt, ap); va_end(ap); return rtc; } cstring *cstr_printfrc(cstring *s, int *rc, char const *fmt, ...) { va_list ap; va_start(ap, fmt); cstring *rtc = cstr_printfrcv(s, rc, fmt, ap); va_end(ap); return rtc; } cstring *cstr_printfrcv(cstring *s, int *rc, char const *fmt, va_list ap) { assert(cstr_assert(s)); assert(fmt); assert(rc); size_t size = strlen(fmt) + 80; s = cstr_reserve(s, size); if (s == NULL) return NULL; size_t l = s->length, avail = s->alloc - l; for (;;) { va_list try_ap; va_copy(try_ap, ap); int grow = vsnprintf(s->data + l, avail, fmt, try_ap); va_end(try_ap); // Until glibc 2.0.6, vsnprintf would return -1 when the output was truncated if (grow > -1) { if ((size_t)grow < avail) { s->length += grow; *rc = grow; break; } size += grow; } else size *= 2; s->data[l] = 0; s = cstr_grow(s, size); if (s == NULL) break; avail = s->alloc -l; } assert(s == NULL || cstr_assert(s)); return s; } #if defined(TEST_MAIN) void do_final(cstring *s) { fputs("final:\n", stdout); s = cstr_final(s); free(s); } int main(int argc, char *argv[]) { int bychar = 0; cstring* s = NULL; for (int i = 1; i < argc; ++i) { int size = atoi(argv[i]); if (size > 0 || strcmp(argv[i], "0") == 0) { if (s) do_final(s); s = cstr_init(size); continue; } if (s == NULL) s = cstr_init(0); if (s == NULL) break; static char const lorem_ipsum[] = "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum\n" "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum\n" "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum\n" "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum\n" "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum\n" "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum\n" "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum\n"; if (strcmp(argv[i], "printf") == 0) { for (int j = 0; j < 4 && s; ++j) { s = cstr_printf(s, "\nj=%d, s->alloc=%zu, s->length=%zu\n%s", j, s->alloc, s->length, lorem_ipsum); } for (int j = 0; j < 4 && s; ++j) { int nu; s = cstr_printfrc(s, &nu, "\nj=%d, s->alloc=%zu, s->length=%zu\n%s", j, s->alloc, s->length, lorem_ipsum); } } else if (strchr(argv[i], '%')) { cstring *perc = cstr_from_string(argv[i]); for (size_t pos = 0; pos <= cstr_length(perc); ++pos) { cstring *d = cstr_dup(perc); if (d) d = cstr_insch(d, pos, '%'); if (d) do_final(d); } if (perc) do_final(perc); } else if (strstr(argv[i], "ins")) { cstring *perc = cstr_from_string(argv[i]); for (size_t pos = 0; pos <= cstr_length(perc); ++pos) { cstring *d = cstr_dup(perc); if (d) d = cstr_insstr(d, pos, "|-|"); if (d) do_final(d); } if (perc) do_final(perc); } else if (strcmp(argv[i], "dup") == 0) { int j; cstring *d[5] = {NULL}; d[0] = cstr_from_string(lorem_ipsum); for (j = 1; j < 5; ++j) if (d[j-1]) d[j] = cstr_dup(d[j-1]); for (j = 0; j < 5; ++j) if (d[j]) do_final(d[j]); } else if (argv[i][0] == 'u') { s = cstr_addutf8(s, atoi(&argv[i][1])); } else { int j = 0; if (++bychar > 4) bychar = 0; else for (; j < 4 && argv[i][j] != 0; ++j) if ((s = cstr_addch(s, argv[i][j])) == NULL) break; if (s && argv[i][j]) s = cstr_addstr(s, &argv[i][j]); } } if (s) do_final(s); else fputs("s == NULL!!!\n", stderr); return 0; } #endif