/* * charstring -- dynamically-sized char array that can report size * in both characters and bytes * * This code is Copyright (c) 2014, by the authors of nmh. See the * COPYRIGHT file in the root directory of the nmh distribution for * complete copyright information. */ #include #include #ifdef MULTIBYTE_SUPPORT # define NMH_MAX_CHARWIDTH MB_CUR_MAX #else # define NMH_MAX_CHARWIDTH 1 #endif #define CHARSTRING_DEFAULT_SIZE 64 struct charstring { char *buffer; /* the char array, not always null-terminated */ size_t max; /* current size of the char array, in bytes */ char *cur; /* size in bytes = cur - buffer, without trailing null */ size_t chars; /* size in characters */ }; static void charstring_reserve (charstring_t s, size_t need) { const size_t cur = s->cur - s->buffer; while (need >= s->max - cur) { /* Insufficient capacity, so double it. */ s->buffer = mh_xrealloc (s->buffer, s->max *= 2); s->cur = s->buffer + cur; } } /* * max is in characters */ charstring_t charstring_create (size_t max) { charstring_t s; NEW(s); s->max = NMH_MAX_CHARWIDTH * (max ? max : CHARSTRING_DEFAULT_SIZE); s->cur = s->buffer = mh_xmalloc (s->max); s->chars = 0; return s; } charstring_t charstring_copy (const charstring_t src) { const size_t num = src->cur - src->buffer; charstring_t s; NEW(s); s->max = src->max; s->buffer = mh_xmalloc (s->max); memcpy (s->buffer, src->buffer, num); s->cur = s->buffer + num; s->chars = src->chars; return s; } /* * OK to call charstring_free with a NULL argument. */ void charstring_free (charstring_t s) { if (s) { free (s->buffer); free (s); } } void charstring_push_back (charstring_t s, const char c) { charstring_reserve (s, s->cur - s->buffer + 1); *s->cur++ = c; ++s->chars; } /* * num is the number of bytes in c, width is the display width * occupied by the character(s). */ void charstring_push_back_chars (charstring_t s, const char c[], size_t num, size_t width) { size_t i; charstring_reserve (s, s->cur - s->buffer + num); for (i = 0; i < num; ++i) { *s->cur++ = *c++; } s->chars += width; } void charstring_append (charstring_t dest, const charstring_t src) { const size_t num = src->cur - src->buffer; if (num > 0) { charstring_reserve (dest, dest->cur - dest->buffer + num); memcpy (dest->cur, src->buffer, num); dest->cur += num; dest->chars += src->chars; } } void charstring_append_cstring (charstring_t dest, const char src[]) { const size_t num = strlen (src); if (num > 0) { charstring_reserve (dest, dest->cur - dest->buffer + num); memcpy (dest->cur, src, num); /* Exclude src's trailing newline. */ dest->cur += num; dest->chars += num; } } void charstring_clear (charstring_t s) { s->cur = s->buffer; s->chars = 0; } /* * Don't store return value of charstring_buffer() and use later after * intervening push_back's; use charstring_buffer_copy() instead. */ const char * charstring_buffer (const charstring_t s) { charstring_reserve (s, s->cur - s->buffer + 1); /* This is the only place that we null-terminate the buffer. */ *s->cur = '\0'; /* Don't increment cur so that more can be appended later, and so that charstring_bytes() behaves as strlen() by not counting the null. */ return s->buffer; } char * charstring_buffer_copy (const charstring_t s) { char *copy = mh_xmalloc (s->cur - s->buffer + 1); /* Use charstring_buffer() to null terminate the buffer. */ memcpy (copy, charstring_buffer (s), s->cur - s->buffer + 1); return copy; } size_t charstring_bytes (const charstring_t s) { return s->cur - s->buffer; } size_t charstring_chars (const charstring_t s) { return s->chars; } int charstring_last_char_len (const charstring_t s) { int len = 0; #ifdef MULTIBYTE_SUPPORT const char *sp = charstring_buffer (s); size_t remaining = charstring_bytes (s); if (mbtowc (NULL, NULL, 0)) {} /* reset shift state */ while (*sp && remaining > 0) { wchar_t wide_char; len = mbtowc (&wide_char, sp, (size_t) MB_CUR_MAX < remaining ? (size_t) MB_CUR_MAX : remaining); sp += len > 0 ? len : 1; remaining -= len > 0 ? len : 1; } #else /* ! MULTIBYTE_SUPPORT */ if (charstring_bytes (s) > 0) { len = 1; } #endif /* ! MULTIBYTE_SUPPORT */ return len; }