1 /* Yash: yet another shell */
2 /* strbuf.c: modifiable string buffer */
3 /* (C) 2007-2020 magicant */
5 /* This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. */
34 #if HAVE_WCSNRTOMBS && !defined(wcsnrtombs)
35 size_t wcsnrtombs(char *restrict dst, const wchar_t **restrict src, size_t nwc,
36 size_t len, mbstate_t *restrict ps);
40 /* If the type of the return value of the functions below is string buffer,
41 * the return value is the argument buffer. */
44 /********** Multibyte String Buffer **********/
46 /* Initializes the specified string buffer as an empty string. */
47 xstrbuf_T *sb_initwithmax(xstrbuf_T *buf, size_t max)
49 // buf->contents = xmalloce(max, 1, sizeof (char));
50 buf->contents = xmalloc(add(max, 1));
51 buf->contents[0] = '\0';
57 /* Initializes the specified multibyte string buffer with the specified string.
58 * String `s' must be `free'able.
59 * After calling this function, the string is used as the buffer, so you must
60 * not touch or `free' it any more. */
61 xstrbuf_T *sb_initwith(xstrbuf_T *restrict buf, char *restrict s)
64 buf->length = buf->maxlength = strlen(s);
68 /* Changes the maximum length of the specified buffer.
69 * If `newmax' is less than the current length of the buffer, the end of
70 * the buffer contents is truncated. */
71 xstrbuf_T *sb_setmax(xstrbuf_T *buf, size_t newmax)
73 // buf->contents = xrealloce(buf->contents, newmax, 1, sizeof (char));
74 buf->contents = xrealloc(buf->contents, add(newmax, 1));
75 buf->maxlength = newmax;
76 buf->contents[newmax] = '\0';
77 if (newmax < buf->length)
82 /* If `buf->maxlength' is less than `max', reallocates the buffer so that
83 * `buf->maxlength' is no less than `max'. */
84 xstrbuf_T *sb_ensuremax(xstrbuf_T *buf, size_t max)
86 if (max <= buf->maxlength)
89 size_t len15 = buf->maxlength + (buf->maxlength >> 1);
92 if (max < buf->maxlength + 10)
93 max = buf->maxlength + 10;
94 return sb_setmax(buf, max);
97 /* Replaces the specified part of the buffer with another string.
98 * `bn' characters starting at offset `i' in buffer `buf' is removed and
99 * the first `sn' characters of `s' take place of them.
100 * No boundary checks are done and null characters are not considered special.
101 * `s' must not be part of `buf->contents'. */
102 xstrbuf_T *sb_replace_force(
103 xstrbuf_T *restrict buf, size_t i, size_t bn,
104 const char *restrict s, size_t sn)
106 size_t newlength = add(buf->length - bn, sn);
107 sb_ensuremax(buf, newlength);
108 memmove(buf->contents + i + sn, buf->contents + i + bn,
109 buf->length - (i + bn) + 1);
110 memcpy(buf->contents + i, s, sn);
111 buf->length = newlength;
115 /* Replaces the specified part of the buffer with another string.
116 * `bn' characters starting at offset `i' in buffer `buf' is removed and
117 * the first `sn' characters of `s' take place of them.
118 * If (strlen(s) < sn), the whole of `s' is replaced with.
119 * If (buf->length < i + sn), all the characters after offset `i' in the buffer
120 * is replaced. Especially, if (buf->length <= i), `s' is appended.
121 * `s' must not be part of `buf->contents'. */
122 xstrbuf_T *sb_replace(
123 xstrbuf_T *restrict buf, size_t i, size_t bn,
124 const char *restrict s, size_t sn)
126 sn = xstrnlen(s, sn);
129 if (bn > buf->length - i)
130 bn = buf->length - i;
131 return sb_replace_force(buf, i, bn, s, sn);
134 /* Appends byte `c' to the end of string buffer `buf'.
135 * The byte is appended even if it is a null byte. */
136 xstrbuf_T *sb_ccat(xstrbuf_T *buf, char c)
138 sb_ensuremax(buf, add(buf->length, 1));
139 buf->contents[buf->length++] = c;
140 buf->contents[buf->length] = '\0';
144 /* Appends `n' bytes of `c' to the end of buffer `buf'.
145 * The bytes are appended even if `c' is a null byte. */
146 xstrbuf_T *sb_ccat_repeat(xstrbuf_T *buf, char c, size_t n)
148 sb_ensuremax(buf, add(buf->length, n));
149 memset(&buf->contents[buf->length], c, n);
151 buf->contents[buf->length] = '\0';
155 /* Converts wide character `c' into a multibyte string and appends it to buffer
156 * `buf'. Shift state `ps' is used for the conversion.
157 * If `c' is a null character, the shift state is reset to the initial state but
158 * the null character is not appended to the buffer.
159 * Returns true iff successful. On error, `errno' is set to EILSEQ and the state
160 * is left undefined. */
161 bool sb_wccat(xstrbuf_T *restrict buf, wchar_t c, mbstate_t *restrict ps)
165 sb_ensuremax(buf, add(buf->length, MB_CUR_MAX));
166 count = wcrtomb(&buf->contents[buf->length], c, ps);
167 if (count == (size_t) -1) {
168 buf->contents[buf->length] = '\0';
171 assert(0 < count && count <= buf->maxlength - buf->length);
172 buf->length += count;
176 buf->contents[buf->length] = '\0';
177 assert(buf->contents[buf->length] == '\0');
181 /* Appends first `n' characters of wide string `s' to multibyte buffer `buf'.
182 * The wide string is converted to multibyte string using shift state `ps'.
183 * If `n' is larger than the length of `s', the whole string is appended and
184 * the shift state is reset to the initial shift state.
185 * Returns NULL if the string is converted and appended successfully,
186 * otherwise a pointer to the character in `s' that caused the error.
187 * A partial result may be left in the buffer on error. */
188 wchar_t *sb_wcsncat(xstrbuf_T *restrict buf,
189 const wchar_t *restrict s, size_t n, mbstate_t *restrict ps)
193 const wchar_t *saves = s;
194 size_t count = wcsnrtombs(&buf->contents[buf->length],
195 (const wchar_t **) &s, n,
196 buf->maxlength - buf->length, ps);
197 if (count == (size_t) -1) {
198 buf->contents[buf->length] = '\0';
201 buf->length += count;
204 assert((size_t) (s - saves) <= n);
207 buf->contents[buf->length] = '\0';
211 sb_ensuremax(buf, add(buf->maxlength, MB_CUR_MAX));
213 assert(buf->contents[buf->length] == '\0');
214 return (wchar_t *) s;
217 if (!sb_wccat(buf, *s, ps))
218 return (wchar_t *) s;
227 /* Appends wide string `s' to multibyte buffer `buf'. The wide string is
228 * converted to multibyte string using shift state `ps'. After successful
229 * conversion, the shift state is reset to the initial shift state.
230 * Returns NULL if the whole string is converted and appended successfully,
231 * otherwise a pointer to the character in `s' that caused the error.
232 * A partial result may be left in the buffer on error. */
234 wchar_t *sb_wcscat(xstrbuf_T *restrict buf,
235 const wchar_t *restrict s, mbstate_t *restrict ps)
238 size_t count = wcsrtombs(&buf->contents[buf->length],
239 (const wchar_t **) &s,
240 buf->maxlength - buf->length,
242 if (count == (size_t) -1) {
243 buf->contents[buf->length] = '\0';
246 buf->length += count;
249 sb_ensuremax(buf, add(buf->maxlength, MB_CUR_MAX));
251 assert(buf->contents[buf->length] == '\0');
252 return (wchar_t *) s;
256 /* Appends the result of `vsprintf' to the specified buffer.
257 * `format' and the following arguments must not be part of `buf->contents'.
258 * Returns the number of appended bytes if successful.
259 * On error, the buffer is not changed and -1 is returned. */
260 int sb_vprintf(xstrbuf_T *restrict buf, const char *restrict format, va_list ap)
265 // No need to check for overflow in `buf->maxlength - buf->length + 1' here.
266 // Should overflow occur, the buffer would not have been allocated
268 size_t rest = buf->maxlength - buf->length + 1;
269 int result = vsnprintf(&buf->contents[buf->length], rest, format, ap);
271 // If the buffer was too small...
272 #if INT_MAX < SIZE_MAX
273 if (result >= 0 && (size_t) result >= rest) {
274 #else // INT_MAX >= SIZE_MAX
275 if (result >= (int) rest) {
276 if (result > (int) SIZE_MAX)
279 // retry with a larger buffer
280 sb_ensuremax(buf, add(buf->length, (size_t) result));
281 rest = buf->maxlength - buf->length + 1;
282 result = vsnprintf(&buf->contents[buf->length], rest, format, copyap);
284 #if INT_MAX < SIZE_MAX
285 assert(result < 0 || (size_t) result < rest);
286 #else // INT_MAX >= SIZE_MAX
287 assert(result < (int) rest);
291 buf->length += result;
293 buf->contents[buf->length] = '\0';
294 assert(buf->contents[buf->length] == '\0');
299 /* Appends the result of `sprintf' to the specified buffer.
300 * `format' and the following arguments must not be part of `buf->contents'.
301 * Returns the number of appended bytes if successful.
302 * On error, the buffer is not changed and -1 is returned. */
303 int sb_printf(xstrbuf_T *restrict buf, const char *restrict format, ...)
308 va_start(ap, format);
309 result = sb_vprintf(buf, format, ap);
315 /********** Wide String Buffer **********/
317 /* Initializes the specified wide string buffer as an empty string. */
318 xwcsbuf_T *wb_initwithmax(xwcsbuf_T *buf, size_t max)
320 buf->contents = xmalloce(max, 1, sizeof (wchar_t));
321 buf->contents[0] = L'\0';
323 buf->maxlength = max;
327 /* Initializes the specified wide string buffer with the specified string.
328 * String `s' must be `free'able.
329 * After calling this function, the string is used as the buffer, so you must
330 * not touch or `free' it any more. */
331 xwcsbuf_T *wb_initwith(xwcsbuf_T *restrict buf, wchar_t *restrict s)
334 buf->length = buf->maxlength = wcslen(s);
338 /* Changes the maximum length of the specified buffer.
339 * If `newmax' is less than the current length of the buffer, the end of
340 * the buffer contents is truncated. */
341 xwcsbuf_T *wb_setmax(xwcsbuf_T *buf, size_t newmax)
343 buf->contents = xrealloce(buf->contents, newmax, 1, sizeof (wchar_t));
344 buf->maxlength = newmax;
345 buf->contents[newmax] = L'\0';
346 if (newmax < buf->length)
347 buf->length = newmax;
351 /* If `buf->maxlength' is less than `max', reallocates the buffer so that
352 * `buf->maxlength' is no less than `max'. */
353 xwcsbuf_T *wb_ensuremax(xwcsbuf_T *buf, size_t max)
355 if (max <= buf->maxlength)
358 size_t len15 = buf->maxlength + (buf->maxlength >> 1);
361 if (max < buf->maxlength + 8)
362 max = buf->maxlength + 8;
363 return wb_setmax(buf, max);
366 /* Replaces the specified part of the buffer with another string.
367 * `bn' characters starting at offset `i' in buffer `buf' is removed and
368 * the first `sn' characters of `s' take place of them.
369 * No boundary checks are done and null characters are not considered special.
370 * `s' must not be part of `buf->contents'. */
371 xwcsbuf_T *wb_replace_force(
372 xwcsbuf_T *restrict buf, size_t i, size_t bn,
373 const wchar_t *restrict s, size_t sn)
375 size_t newlength = add(buf->length - bn, sn);
376 wb_ensuremax(buf, newlength);
377 wmemmove(buf->contents + i + sn, buf->contents + i + bn,
378 buf->length - (i + bn) + 1);
379 wmemcpy(buf->contents + i, s, sn);
380 buf->length = newlength;
384 /* Replaces the specified part of the buffer with another string.
385 * `bn' characters starting at offset `i' in buffer `buf' is removed and
386 * the first `sn' characters of `s' take place of them.
387 * If (wcslen(s) < sn), the whole of `s' is replaced with.
388 * If (buf->length < i + sn), all the characters after offset `i' in the buffer
389 * is replaced. Especially, if (buf->length <= i), `s' is appended.
390 * `s' must not be part of `buf->contents'. */
391 xwcsbuf_T *wb_replace(
392 xwcsbuf_T *restrict buf, size_t i, size_t bn,
393 const wchar_t *restrict s, size_t sn)
395 sn = xwcsnlen(s, sn);
398 if (bn > buf->length - i)
399 bn = buf->length - i;
400 return wb_replace_force(buf, i, bn, s, sn);
403 /* Appends wide character `c' to the end of buffer `buf'.
404 * The character is appended even if it is a null wide character. */
405 xwcsbuf_T *wb_wccat(xwcsbuf_T *buf, wchar_t c)
407 wb_ensuremax(buf, add(buf->length, 1));
408 buf->contents[buf->length++] = c;
409 buf->contents[buf->length] = L'\0';
413 /* Converts multibyte string `s' into a wide string and appends it to buffer
414 * `buf'. The multibyte string is assumed to start in the initial shift state.
415 * Returns NULL if the whole string is converted and appended successfully,
416 * otherwise a pointer to the character in `s' that caused the error.
417 * A partial result may be left in the buffer on error. */
418 char *wb_mbscat(xwcsbuf_T *restrict buf, const char *restrict s)
423 memset(&state, 0, sizeof state); // initialize as the initial shift state
426 count = mbsrtowcs(&buf->contents[buf->length], (const char **) &s,
427 buf->maxlength - buf->length + 1, &state);
428 if (count == (size_t) -1)
430 buf->length += count;
433 wb_ensuremax(buf, add(buf->maxlength, 1));
436 buf->contents[buf->length] = L'\0';
440 /* Appends the result of `vswprintf' to the specified buffer.
441 * `format' and the following arguments must not be part of `buf->contents'.
442 * Returns the number of appended characters if successful.
443 * On error, the buffer is not changed and -1 is returned. */
445 xwcsbuf_T *restrict buf, const wchar_t *restrict format, va_list ap)
451 for (int i = 0; i < 20; i++) {
453 rest = buf->maxlength - buf->length + 1;
454 result = vswprintf(&buf->contents[buf->length], rest, format, copyap);
458 #if INT_MAX < SIZE_MAX
459 (size_t) result < rest)
460 #else // INT_MAX >= SIZE_MAX
464 #if INT_MAX > SIZE_MAX
465 if (result > (int) SIZE_MAX)
469 /* According to POSIX, if the buffer is too short, `vswprintf' returns
470 * a negative integer. On some systems, however, it returns a desired
471 * buffer length as `vsprintf' does, which is rather preferable. */
473 add(buf->length, result < 0 ? mul(2, rest) : (size_t) result));
476 buf->length += result;
478 buf->contents[buf->length] = L'\0';
479 assert(buf->contents[buf->length] == L'\0');
483 /* Appends the result of `swprintf' to the specified buffer.
484 * `format' and the following arguments must not be part of `buf->contents'.
485 * Returns the number of appended characters if successful.
486 * On error, the buffer is not changed and -1 is returned. */
487 int wb_wprintf(xwcsbuf_T *restrict buf, const wchar_t *restrict format, ...)
492 va_start(ap, format);
493 result = wb_vwprintf(buf, format, ap);
500 /********** Multibyte-Wide Conversion Utilities **********/
502 /* Converts the specified wide string into a newly malloced multibyte string.
503 * Only the first `n' characters of `s' is converted at most.
504 * Returns NULL on error.
505 * The resulting string starts and ends in the initial shift state.*/
506 char *malloc_wcsntombs(const wchar_t *s, size_t n)
512 memset(&state, 0, sizeof state); // initialize as the initial shift state
513 if (sb_wcsncat(&buf, s, n, &state) == NULL) {
514 return sb_tostr(&buf);
521 /* Converts the specified wide string into a newly malloced multibyte string.
522 * Returns NULL on error.
523 * The resulting string starts and ends in the initial shift state.*/
525 char *malloc_wcstombs(const wchar_t *s)
531 memset(&state, 0, sizeof state); // initialize as the initial shift state
532 if (sb_wcscat(&buf, s, &state) == NULL) {
533 return sb_tostr(&buf);
541 /* Converts the specified multibyte string into a newly malloced wide string.
542 * Returns NULL on error. */
543 wchar_t *malloc_mbstowcs(const char *s)
548 if (wb_mbscat(&buf, s) == NULL) {
549 return wb_towcs(&buf);
557 /********** Formatting Utilities **********/
559 /* Returns the result of `vsprintf' as a newly malloced string.
560 * An error message is printed on error. The return value is non-NULL anyway. */
561 char *malloc_vprintf(const char *format, va_list ap)
565 if (sb_vprintf(&buf, format, ap) < 0)
566 xerror(errno, Ngt("unexpected error"));
567 return sb_tostr(&buf);
570 /* Returns the result of `sprintf' as a newly malloced string.
571 * An error message is printed on error. The return value is non-NULL anyway. */
572 char *malloc_printf(const char *format, ...)
576 va_start(ap, format);
577 result = malloc_vprintf(format, ap);
582 /* Returns the result of `vswprintf' as a newly malloced string.
583 * An error message is printed on error. The return value is non-NULL anyway. */
584 wchar_t *malloc_vwprintf(const wchar_t *format, va_list ap)
588 if (wb_vwprintf(&buf, format, ap) < 0)
589 xerror(errno, Ngt("unexpected error"));
590 return wb_towcs(&buf);
593 /* Returns the result of `swprintf' as a newly malloced string.
594 * An error message is printed on error. The return value is non-NULL anyway. */
595 wchar_t *malloc_wprintf(const wchar_t *format, ...)
599 va_start(ap, format);
600 result = malloc_vwprintf(format, ap);
606 /********** String Creating Utilities **********/
608 /* Joins the wide-character strings in the specified NULL-terminated array. The
609 * array elements are considered pointers to wide strings.
610 * `padding' is padded between each joined element.
611 * Returns a newly malloced string. */
612 wchar_t *joinwcsarray(void *const *array, const wchar_t *padding)
614 size_t elemcount, ccount = 0;
616 /* count the full length of the resulting string */
617 for (elemcount = 0; array[elemcount] != NULL; elemcount++)
618 ccount = add(ccount, wcslen(array[elemcount]));
620 ccount = add(ccount, mul(wcslen(padding), elemcount - 1));
623 wchar_t *const result = xmalloce(ccount, 1, sizeof *result);
625 for (size_t i = 0; i < elemcount; i++) {
626 wchar_t *elem = array[i];
627 while (*elem != L'\0')
629 if (i + 1 < elemcount) {
630 const wchar_t *pad = padding;
631 while (*pad != L'\0')
641 /* vim: set ts=8 sts=4 sw=4 noet tw=80: */