]> diplodocus.org Git - nmh/blob - sbr/charstring.c
pending-release-notes: add mhshow's "-prefer", and mh-format's %(kibi/kilo)
[nmh] / sbr / charstring.c
1 /*
2 * charstring -- dynamically-sized char array that can report size
3 * in both characters and bytes
4 *
5 * This code is Copyright (c) 2014, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
8 */
9
10 #include <h/mh.h>
11 #include <h/utils.h>
12
13 #ifdef MULTIBYTE_SUPPORT
14 # define NMH_MAX_CHARWIDTH MB_CUR_MAX
15 #else
16 # define NMH_MAX_CHARWIDTH 1
17 #endif
18
19 #define CHARSTRING_DEFAULT_SIZE 64
20
21 struct charstring {
22 char *buffer; /* the char array, not always null-terminated */
23 size_t max; /* current size of the char array, in bytes */
24 char *cur; /* size in bytes = cur - buffer, without trailing null */
25 size_t chars; /* size in characters */
26 };
27
28
29 static void
30 charstring_reserve (charstring_t s, size_t need) {
31 const size_t cur = s->cur - s->buffer;
32
33 while (need >= s->max - cur) {
34 /* Insufficient capacity, so double it. */
35 s->buffer = mh_xrealloc (s->buffer, s->max *= 2);
36 s->cur = s->buffer + cur;
37 }
38 }
39
40 /*
41 * max is in characters
42 */
43 charstring_t
44 charstring_create (size_t max) {
45 charstring_t s = mh_xmalloc (sizeof *s);
46
47 s->max = NMH_MAX_CHARWIDTH * (max ? max : CHARSTRING_DEFAULT_SIZE);
48 s->cur = s->buffer = mh_xmalloc (s->max);
49 s->chars = 0;
50
51 return s;
52 }
53
54 charstring_t
55 charstring_copy (const charstring_t src) {
56 const size_t num = src->cur - src->buffer;
57 charstring_t s = mh_xmalloc (sizeof *s);
58
59 s->max = src->max;
60 s->buffer = mh_xmalloc (s->max);
61 memcpy (s->buffer, src->buffer, num);
62 s->cur = s->buffer + num;
63 s->chars = src->chars;
64
65 return s;
66 }
67
68 /*
69 * OK to call charstring_free with a NULL argument.
70 */
71 void
72 charstring_free (charstring_t s) {
73 if (s) {
74 free (s->buffer);
75 free (s);
76 }
77 }
78
79 void
80 charstring_push_back (charstring_t s, const char c) {
81 charstring_reserve (s, s->cur - s->buffer + 1);
82 *s->cur++ = c;
83 ++s->chars;
84 }
85
86 /*
87 * num is the number of bytes in c, width is the display width
88 * occupied by the character(s).
89 */
90 void
91 charstring_push_back_chars (charstring_t s, const char c[], size_t num,
92 size_t width) {
93 size_t i;
94
95 charstring_reserve (s, s->cur - s->buffer + num);
96 for (i = 0; i < num; ++i) { *s->cur++ = *c++; }
97 s->chars += width;
98 }
99
100 void
101 charstring_append (charstring_t dest, const charstring_t src) {
102 const size_t num = src->cur - src->buffer;
103
104 if (num > 0) {
105 charstring_reserve (dest, dest->cur - dest->buffer + num);
106 memcpy (dest->cur, src->buffer, num);
107 dest->cur += num;
108 dest->chars += src->chars;
109 }
110 }
111
112 void
113 charstring_append_cstring (charstring_t dest, const char src[]) {
114 const size_t num = strlen (src);
115
116 if (num > 0) {
117 charstring_reserve (dest, dest->cur - dest->buffer + num);
118 memcpy (dest->cur, src, num); /* Exclude src's trailing newline. */
119 dest->cur += num;
120 dest->chars += num;
121 }
122 }
123
124 void
125 charstring_clear (charstring_t s) {
126 s->cur = s->buffer;
127 s->chars = 0;
128 }
129
130 /*
131 * Don't store return value of charstring_buffer() and use later after
132 * intervening push_back's; use charstring_buffer_copy() instead.
133 */
134 const char *
135 charstring_buffer (const charstring_t s) {
136 charstring_reserve (s, s->cur - s->buffer + 1);
137
138 /* This is the only place that we null-terminate the buffer. */
139 *s->cur = '\0';
140 /* Don't increment cur so that more can be appended later, and so
141 that charstring_bytes() behaves as strlen() by not counting the
142 null. */
143
144 return s->buffer;
145 }
146
147 char *
148 charstring_buffer_copy (const charstring_t s) {
149 char *copy = mh_xmalloc (s->cur - s->buffer + 1);
150
151 /* Use charstring_buffer() to null terminate the buffer. */
152 memcpy (copy, charstring_buffer (s), s->cur - s->buffer + 1);
153
154 return copy;
155 }
156
157 size_t
158 charstring_bytes (const charstring_t s) {
159 return s->cur - s->buffer;
160 }
161
162 size_t
163 charstring_chars (const charstring_t s) {
164 return s->chars;
165 }
166
167 int
168 charstring_last_char_len (const charstring_t s) {
169 int len = 0;
170 #ifdef MULTIBYTE_SUPPORT
171 const char *sp = charstring_buffer (s);
172 size_t remaining = charstring_bytes (s);
173
174 if (mbtowc (NULL, NULL, 0)) {} /* reset shift state */
175
176 while (*sp && remaining > 0) {
177 wchar_t wide_char;
178
179 len = mbtowc (&wide_char, sp, (size_t) MB_CUR_MAX < remaining
180 ? (size_t) MB_CUR_MAX
181 : remaining);
182 sp += len > 0 ? len : 1;
183 remaining -= len > 0 ? len : 1;
184 }
185 #else /* ! MULTIBYTE_SUPPORT */
186 if (charstring_bytes (s) > 0) { len = 1; }
187 #endif /* ! MULTIBYTE_SUPPORT */
188
189 return len;
190 }