]> diplodocus.org Git - nmh/blob - sbr/charstring.c
A value of 0 for the width switch of scan(1), inc(1), ap(1), dp(1),
[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, num + (dest->cur - dest->buffer));
106 memcpy (dest->cur, src->buffer, num);
107 dest->cur += num;
108 dest->chars += src->chars;
109 }
110 }
111
112 void
113 charstring_clear (charstring_t s) {
114 s->cur = s->buffer;
115 s->chars = 0;
116 }
117
118 /*
119 * Don't store return value of charstring_buffer() and use later after
120 * intervening push_back's; use charstring_buffer_copy() instead.
121 */
122 const char *
123 charstring_buffer (const charstring_t s) {
124 charstring_reserve (s, s->cur - s->buffer + 1);
125
126 /* This is the only place that we null-terminate the buffer. */
127 *s->cur = '\0';
128 /* Don't increment cur so that more can be appended later, and so
129 that charstring_bytes() behaves as strlen() by not counting the
130 null. */
131
132 return s->buffer;
133 }
134
135 char *
136 charstring_buffer_copy (const charstring_t s) {
137 char *copy = strdup (charstring_buffer (s));
138
139 if (copy) {
140 return copy;
141 } else {
142 advise ("strdup", "unable to copy charstring buffer");
143 return NULL;
144 }
145 }
146
147 size_t
148 charstring_bytes (const charstring_t s) {
149 return s->cur - s->buffer;
150 }
151
152 size_t
153 charstring_chars (const charstring_t s) {
154 return s->chars;
155 }
156
157 int
158 charstring_last_char_len (const charstring_t s) {
159 int len = 0;
160 #ifdef MULTIBYTE_SUPPORT
161 const char *sp = charstring_buffer (s);
162 size_t remaining = charstring_bytes (s);
163
164 (void) mbtowc (NULL, NULL, 0); /* reset shift state */
165
166 while (*sp && remaining > 0) {
167 wchar_t wide_char;
168
169 len = mbtowc (&wide_char, sp, (size_t) MB_CUR_MAX < remaining
170 ? (size_t) MB_CUR_MAX
171 : remaining);
172 sp += len > 0 ? len : 1;
173 remaining -= len > 0 ? len : 1;
174 }
175 #else /* ! MULTIBYTE_SUPPORT */
176 if (charstring_bytes (s) > 0) { len = 1; }
177 #endif /* ! MULTIBYTE_SUPPORT */
178
179 return len;
180 }