OSDN Git Service

Make run-test.sh POSIXly-portable
[yash/yash.git] / strbuf.c
1 /* Yash: yet another shell */
2 /* strbuf.c: modifiable string buffer */
3 /* (C) 2007-2020 magicant */
4
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.
9  * 
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.
14  * 
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/>.  */
17
18
19 #include "common.h"
20 #include "strbuf.h"
21 #include <assert.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdarg.h>
25 #include <stdbool.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <wchar.h>
32 #include "util.h"
33
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);
37 #endif
38
39
40 /* If the type of the return value of the functions below is string buffer,
41  * the return value is the argument buffer. */
42
43
44 /********** Multibyte String Buffer **********/
45
46 /* Initializes the specified string buffer as an empty string. */
47 xstrbuf_T *sb_initwithmax(xstrbuf_T *buf, size_t max)
48 {
49     // buf->contents = xmalloce(max, 1, sizeof (char));
50     buf->contents = xmalloc(add(max, 1));
51     buf->contents[0] = '\0';
52     buf->length = 0;
53     buf->maxlength = max;
54     return buf;
55 }
56
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)
62 {
63     buf->contents = s;
64     buf->length = buf->maxlength = strlen(s);
65     return buf;
66 }
67
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)
72 {
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)
78         buf->length = newmax;
79     return buf;
80 }
81
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)
85 {
86     if (max <= buf->maxlength)
87         return buf;
88
89     size_t len15 = buf->maxlength + (buf->maxlength >> 1);
90     if (max < len15)
91         max = len15;
92     if (max < buf->maxlength + 10)
93         max = buf->maxlength + 10;
94     return sb_setmax(buf, max);
95 }
96
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)
105 {
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;
112     return buf;
113 }
114
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)
125 {
126     sn = xstrnlen(s, sn);
127     if (i > buf->length)
128         i = buf->length;
129     if (bn > buf->length - i)
130         bn = buf->length - i;
131     return sb_replace_force(buf, i, bn, s, sn);
132 }
133
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)
137 {
138     sb_ensuremax(buf, add(buf->length, 1));
139     buf->contents[buf->length++] = c;
140     buf->contents[buf->length] = '\0';
141     return buf;
142 }
143
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)
147 {
148     sb_ensuremax(buf, add(buf->length, n));
149     memset(&buf->contents[buf->length], c, n);
150     buf->length += n;
151     buf->contents[buf->length] = '\0';
152     return buf;
153 }
154
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)
162 {
163     size_t count;
164
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';
169         return false;
170     }
171     assert(0 < count && count <= buf->maxlength - buf->length);
172     buf->length += count;
173     if (c == L'\0')
174         buf->length--;
175     else
176         buf->contents[buf->length] = '\0';
177     assert(buf->contents[buf->length] == '\0');
178     return true;
179 }
180
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)
190 {
191 #if HAVE_WCSNRTOMBS
192     for (;;) {
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';
199             break;
200         }
201         buf->length += count;
202         if (s == NULL)
203             break;
204         assert((size_t) (s - saves) <= n);
205         n -= s - saves;
206         if (n == 0) {
207             buf->contents[buf->length] = '\0';
208             s = NULL;
209             break;
210         }
211         sb_ensuremax(buf, add(buf->maxlength, MB_CUR_MAX));
212     }
213     assert(buf->contents[buf->length] == '\0');
214     return (wchar_t *) s;
215 #else
216     while (n > 0) {
217         if (!sb_wccat(buf, *s, ps))
218             return (wchar_t *) s;
219         if (*s == L'\0')
220             return NULL;
221         s++, n--;
222     }
223     return NULL;
224 #endif
225 }
226
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. */
233 #if !HAVE_WCSNRTOMBS
234 wchar_t *sb_wcscat(xstrbuf_T *restrict buf,
235         const wchar_t *restrict s, mbstate_t *restrict ps)
236 {
237     for (;;) {
238         size_t count = wcsrtombs(&buf->contents[buf->length],
239                 (const wchar_t **) &s,
240                 buf->maxlength - buf->length,
241                 ps);
242         if (count == (size_t) -1) {
243             buf->contents[buf->length] = '\0';
244             break;
245         }
246         buf->length += count;
247         if (s == NULL)
248             break;
249         sb_ensuremax(buf, add(buf->maxlength, MB_CUR_MAX));
250     }
251     assert(buf->contents[buf->length] == '\0');
252     return (wchar_t *) s;
253 }
254 #endif
255
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)
261 {
262     va_list copyap;
263     va_copy(copyap, ap);
264
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
267     // successfully.
268     size_t rest = buf->maxlength - buf->length + 1;
269     int result = vsnprintf(&buf->contents[buf->length], rest, format, ap);
270
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)
277             alloc_failed();
278 #endif
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);
283     }
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);
288 #endif
289
290     if (result >= 0)
291         buf->length += result;
292     else
293         buf->contents[buf->length] = '\0';
294     assert(buf->contents[buf->length] == '\0');
295     va_end(copyap);
296     return result;
297 }
298
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, ...)
304 {
305     va_list ap;
306     int result;
307
308     va_start(ap, format);
309     result = sb_vprintf(buf, format, ap);
310     va_end(ap);
311     return result;
312 }
313
314
315 /********** Wide String Buffer **********/
316
317 /* Initializes the specified wide string buffer as an empty string. */
318 xwcsbuf_T *wb_initwithmax(xwcsbuf_T *buf, size_t max)
319 {
320     buf->contents = xmalloce(max, 1, sizeof (wchar_t));
321     buf->contents[0] = L'\0';
322     buf->length = 0;
323     buf->maxlength = max;
324     return buf;
325 }
326
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)
332 {
333     buf->contents = s;
334     buf->length = buf->maxlength = wcslen(s);
335     return buf;
336 }
337
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)
342 {
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;
348     return buf;
349 }
350
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)
354 {
355     if (max <= buf->maxlength)
356         return buf;
357
358     size_t len15 = buf->maxlength + (buf->maxlength >> 1);
359     if (max < len15)
360         max = len15;
361     if (max < buf->maxlength + 8)
362         max = buf->maxlength + 8;
363     return wb_setmax(buf, max);
364 }
365
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)
374 {
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;
381     return buf;
382 }
383
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)
394 {
395     sn = xwcsnlen(s, sn);
396     if (i > buf->length)
397         i = buf->length;
398     if (bn > buf->length - i)
399         bn = buf->length - i;
400     return wb_replace_force(buf, i, bn, s, sn);
401 }
402
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)
406 {
407     wb_ensuremax(buf, add(buf->length, 1));
408     buf->contents[buf->length++] = c;
409     buf->contents[buf->length] = L'\0';
410     return buf;
411 }
412
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)
419 {
420     mbstate_t state;
421     size_t count;
422
423     memset(&state, 0, sizeof state);  // initialize as the initial shift state
424
425     for (;;) {
426         count = mbsrtowcs(&buf->contents[buf->length], (const char **) &s,
427                 buf->maxlength - buf->length + 1, &state);
428         if (count == (size_t) -1)
429             break;
430         buf->length += count;
431         if (s == NULL)
432             break;
433         wb_ensuremax(buf, add(buf->maxlength, 1));
434     }
435
436     buf->contents[buf->length] = L'\0';
437     return (char *) s;
438 }
439
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. */
444 int wb_vwprintf(
445         xwcsbuf_T *restrict buf, const wchar_t *restrict format, va_list ap)
446 {
447     va_list copyap;
448     size_t rest;
449     int result;
450
451     for (int i = 0; i < 20; i++) {
452         va_copy(copyap, ap);
453         rest = buf->maxlength - buf->length + 1;
454         result = vswprintf(&buf->contents[buf->length], rest, format, copyap);
455         va_end(copyap);
456
457         if (0 <= result &&
458 #if INT_MAX < SIZE_MAX
459                 (size_t) result < rest)
460 #else // INT_MAX >= SIZE_MAX
461                 result < (int) rest)
462 #endif
463             break;
464 #if INT_MAX > SIZE_MAX
465         if (result > (int) SIZE_MAX)
466             alloc_failed();
467 #endif
468
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. */
472         wb_ensuremax(buf,
473                 add(buf->length, result < 0 ? mul(2, rest) : (size_t) result));
474     }
475     if (result >= 0)
476         buf->length += result;
477     else
478         buf->contents[buf->length] = L'\0';
479     assert(buf->contents[buf->length] == L'\0');
480     return result;
481 }
482
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, ...)
488 {
489     va_list ap;
490     int result;
491
492     va_start(ap, format);
493     result = wb_vwprintf(buf, format, ap);
494     va_end(ap);
495     return result;
496 }
497
498
499
500 /********** Multibyte-Wide Conversion Utilities **********/
501
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)
507 {
508     xstrbuf_T buf;
509     mbstate_t state;
510
511     sb_init(&buf);
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);
515     } else {
516         sb_destroy(&buf);
517         return NULL;
518     }
519 }
520
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.*/
524 #if !HAVE_WCSNRTOMBS
525 char *malloc_wcstombs(const wchar_t *s)
526 {
527     xstrbuf_T buf;
528     mbstate_t state;
529
530     sb_init(&buf);
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);
534     } else {
535         sb_destroy(&buf);
536         return NULL;
537     }
538 }
539 #endif
540
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)
544 {
545     xwcsbuf_T buf;
546
547     wb_init(&buf);
548     if (wb_mbscat(&buf, s) == NULL) {
549         return wb_towcs(&buf);
550     } else {
551         wb_destroy(&buf);
552         return NULL;
553     }
554 }
555
556
557 /********** Formatting Utilities **********/
558
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)
562 {
563     xstrbuf_T buf;
564     sb_init(&buf);
565     if (sb_vprintf(&buf, format, ap) < 0)
566         xerror(errno, Ngt("unexpected error"));
567     return sb_tostr(&buf);
568 }
569
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, ...)
573 {
574     va_list ap;
575     char *result;
576     va_start(ap, format);
577     result = malloc_vprintf(format, ap);
578     va_end(ap);
579     return result;
580 }
581
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)
585 {
586     xwcsbuf_T buf;
587     wb_init(&buf);
588     if (wb_vwprintf(&buf, format, ap) < 0)
589         xerror(errno, Ngt("unexpected error"));
590     return wb_towcs(&buf);
591 }
592
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, ...)
596 {
597     va_list ap;
598     wchar_t *result;
599     va_start(ap, format);
600     result = malloc_vwprintf(format, ap);
601     va_end(ap);
602     return result;
603 }
604
605
606 /********** String Creating Utilities **********/
607
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)
613 {
614     size_t elemcount, ccount = 0;
615
616     /* count the full length of the resulting string */
617     for (elemcount = 0; array[elemcount] != NULL; elemcount++)
618         ccount = add(ccount, wcslen(array[elemcount]));
619     if (elemcount > 0)
620         ccount = add(ccount, mul(wcslen(padding), elemcount - 1));
621
622     /* do copying */
623     wchar_t *const result = xmalloce(ccount, 1, sizeof *result);
624     wchar_t *s = result;
625     for (size_t i = 0; i < elemcount; i++) {
626         wchar_t *elem = array[i];
627         while (*elem != L'\0')
628             *s++ = *elem++;
629         if (i + 1 < elemcount) {
630             const wchar_t *pad = padding;
631             while (*pad != L'\0')
632                 *s++ = *pad++;
633         }
634     }
635     *s = L'\0';
636
637     return result;
638 }
639
640
641 /* vim: set ts=8 sts=4 sw=4 noet tw=80: */