From: David Levine Date: Sun, 24 Aug 2014 13:23:55 +0000 (-0500) Subject: Added charstring "class". X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/c61294eca5edf4f245c2d8818bf53d3bdc3d80c1?ds=sidebyside;hp=-c Added charstring "class". --- c61294eca5edf4f245c2d8818bf53d3bdc3d80c1 diff --git a/Makefile.am b/Makefile.am index 24632c4d..6b6d66d2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -547,8 +547,8 @@ uninstall-hook: ## Our rules to build our internal libraries (libmh.a, libmts.a) ## sbr_libmh_a_SOURCES = sbr/addrsbr.c sbr/ambigsw.c sbr/atooi.c sbr/arglist.c \ - sbr/base64.c \ - sbr/brkstring.c sbr/check_charset.c sbr/client.c \ + sbr/base64.c sbr/brkstring.c \ + sbr/charstring.c sbr/check_charset.c sbr/client.c \ sbr/closefds.c sbr/concat.c sbr/context_del.c \ sbr/context_find.c sbr/context_foil.c sbr/context_read.c \ sbr/context_replace.c sbr/context_save.c \ diff --git a/h/mh.h b/h/mh.h index 5e75c0ba..503797c6 100644 --- a/h/mh.h +++ b/h/mh.h @@ -38,6 +38,34 @@ typedef unsigned char boolean; /* not int so we can pack in a structure */ #define NMH_UNUSED(i) i #endif +/* + * char array that keeps track of size in both bytes and characters + * Usage note: + * Don't store return value of charstring_buffer() and use later + * after intervening push_back's; use charstring_buffer_copy() + * instead. + */ +typedef struct charstring *charstring_t; + +charstring_t charstring_create (size_t); +charstring_t charstring_copy (const charstring_t); +void charstring_free (charstring_t); +/* Append a single-byte character: */ +void charstring_push_back (charstring_t, const char); +/* Append possibly multi-byte character(s): */ +void charstring_push_back_chars (charstring_t, const char [], size_t, size_t); +void charstring_append (charstring_t, const charstring_t); +void charstring_clear (charstring_t); +/* 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); +/* User is responsible for free'ing result of buffer copy. */ +char *charstring_buffer_copy (const charstring_t); +size_t charstring_bytes (const charstring_t); +size_t charstring_chars (const charstring_t); +/* Length of the last character in the charstring. */ +int charstring_last_char_len (const charstring_t); + /* * user context/profile structure */ diff --git a/sbr/charstring.c b/sbr/charstring.c new file mode 100644 index 00000000..76d9fa2a --- /dev/null +++ b/sbr/charstring.c @@ -0,0 +1,179 @@ +/* + * 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 = mh_xmalloc (sizeof *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 = mh_xmalloc (sizeof *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, num + (dest->cur - dest->buffer)); + memcpy (dest->cur, src->buffer, num); + dest->cur += num; + dest->chars += src->chars; + } +} + +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 = strdup (charstring_buffer (s)); + + if (copy) { + return copy; + } else { + advise ("strdup", "unable to copy charstring buffer"); + return NULL; + } +} + +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); + + (void) mbtowc (NULL, NULL, 0); /* reset shift state */ + + while (*sp && remaining > 0) { + wchar_t wide_char; + + len = mbtowc (&wide_char, sp, + MB_CUR_MAX < remaining ? 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; +}