OSDN Git Service

Make run-test.sh POSIXly-portable
[yash/yash.git] / strbuf.h
1 /* Yash: yet another shell */
2 /* strbuf.h: 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 #ifndef YASH_STRBUF_H
20 #define YASH_STRBUF_H
21
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <wchar.h>
25
26 #define Size_max ((size_t) -1)  // = SIZE_MAX
27
28 #ifndef XSTRBUF_INITSIZE
29 #define XSTRBUF_INITSIZE 15
30 #endif
31 #ifndef XWCSBUF_INITSIZE
32 #define XWCSBUF_INITSIZE 15
33 #endif
34
35
36 typedef struct xstrbuf_T {
37     char *contents;
38     size_t length, maxlength;
39 } xstrbuf_T;
40 typedef struct xwcsbuf_T {
41     wchar_t *contents;
42     size_t length, maxlength;
43 } xwcsbuf_T;
44
45 static inline xstrbuf_T *sb_init(xstrbuf_T *buf)
46     __attribute__((nonnull));
47 extern xstrbuf_T *sb_initwith(xstrbuf_T *restrict buf, char *restrict s)
48     __attribute__((nonnull));
49 extern xstrbuf_T *sb_initwithmax(xstrbuf_T *buf, size_t max)
50     __attribute__((nonnull));
51 static inline void sb_destroy(xstrbuf_T *buf)
52     __attribute__((nonnull));
53 static inline char *sb_tostr(xstrbuf_T *buf)
54     __attribute__((nonnull));
55 extern xstrbuf_T *sb_setmax(xstrbuf_T *buf, size_t newmax)
56     __attribute__((nonnull));
57 extern xstrbuf_T *sb_ensuremax(xstrbuf_T *buf, size_t max)
58     __attribute__((nonnull));
59 static inline xstrbuf_T *sb_truncate(xstrbuf_T *buf, size_t newlength)
60     __attribute__((nonnull));
61 static inline xstrbuf_T *sb_clear(xstrbuf_T *buf)
62     __attribute__((nonnull));
63 extern xstrbuf_T *sb_replace_force(
64         xstrbuf_T *restrict buf, size_t i, size_t bn,
65         const char *restrict s, size_t sn)
66     __attribute__((nonnull));
67 extern xstrbuf_T *sb_replace(
68         xstrbuf_T *restrict buf, size_t i, size_t bn,
69         const char *restrict s, size_t sn)
70     __attribute__((nonnull));
71 static inline xstrbuf_T *sb_ninsert_force(
72         xstrbuf_T *restrict buf, size_t i, const char *restrict s, size_t n)
73     __attribute__((nonnull));
74 static inline xstrbuf_T *sb_ninsert(
75         xstrbuf_T *restrict buf, size_t i, const char *restrict s, size_t n)
76     __attribute__((nonnull));
77 static inline xstrbuf_T *sb_insert(
78         xstrbuf_T *restrict buf, size_t i, const char *restrict s)
79     __attribute__((nonnull));
80 static inline xstrbuf_T *sb_ncat_force(
81         xstrbuf_T *restrict buf, const char *restrict s, size_t n)
82     __attribute__((nonnull));
83 static inline xstrbuf_T *sb_ncat(
84         xstrbuf_T *restrict buf, const char *restrict s, size_t n)
85     __attribute__((nonnull));
86 static inline xstrbuf_T *sb_cat(
87         xstrbuf_T *restrict buf, const char *restrict s)
88     __attribute__((nonnull));
89 static inline xstrbuf_T *sb_catfree(
90         xstrbuf_T *restrict buf, char *restrict s)
91     __attribute__((nonnull));
92 static inline xstrbuf_T *sb_remove(xstrbuf_T *buf, size_t i, size_t n)
93     __attribute__((nonnull));
94 extern xstrbuf_T *sb_ccat(xstrbuf_T *buf, char c)
95     __attribute__((nonnull));
96 extern xstrbuf_T *sb_ccat_repeat(xstrbuf_T *buf, char c, size_t n)
97     __attribute__((nonnull));
98 extern _Bool sb_wccat(
99         xstrbuf_T *restrict buf, wchar_t c, mbstate_t *restrict ps)
100     __attribute__((nonnull));
101 extern wchar_t *sb_wcsncat(xstrbuf_T *restrict buf,
102         const wchar_t *restrict s, size_t n, mbstate_t *restrict ps)
103     __attribute__((nonnull));
104 #if HAVE_WCSNRTOMBS
105 static inline
106 #else
107 extern
108 #endif
109 wchar_t *sb_wcscat(xstrbuf_T *restrict buf,
110         const wchar_t *restrict s, mbstate_t *restrict ps)
111     __attribute__((nonnull));
112 extern int sb_vprintf(
113         xstrbuf_T *restrict buf, const char *restrict format, va_list ap)
114     __attribute__((nonnull(1,2),format(printf,2,0)));
115 extern int sb_printf(
116         xstrbuf_T *restrict buf, const char *restrict format, ...)
117     __attribute__((nonnull(1,2),format(printf,2,3)));
118
119 static inline xwcsbuf_T *wb_init(xwcsbuf_T *buf)
120     __attribute__((nonnull));
121 extern xwcsbuf_T *wb_initwith(xwcsbuf_T *restrict buf, wchar_t *restrict s)
122     __attribute__((nonnull));
123 extern xwcsbuf_T *wb_initwithmax(xwcsbuf_T *buf, size_t max)
124     __attribute__((nonnull));
125 static inline void wb_destroy(xwcsbuf_T *buf)
126     __attribute__((nonnull));
127 static inline wchar_t *wb_towcs(xwcsbuf_T *buf)
128     __attribute__((nonnull));
129 extern xwcsbuf_T *wb_setmax(xwcsbuf_T *buf, size_t newmax)
130     __attribute__((nonnull));
131 extern xwcsbuf_T *wb_ensuremax(xwcsbuf_T *buf, size_t max)
132     __attribute__((nonnull));
133 static inline xwcsbuf_T *wb_truncate(xwcsbuf_T *buf, size_t newlength)
134     __attribute__((nonnull));
135 static inline xwcsbuf_T *wb_clear(xwcsbuf_T *buf)
136     __attribute__((nonnull));
137 extern xwcsbuf_T *wb_replace_force(
138         xwcsbuf_T *restrict buf, size_t i, size_t bn,
139         const wchar_t *restrict s, size_t sn)
140     __attribute__((nonnull));
141 extern xwcsbuf_T *wb_replace(
142         xwcsbuf_T *restrict buf, size_t i, size_t bn,
143         const wchar_t *restrict s, size_t sn)
144     __attribute__((nonnull));
145 static inline xwcsbuf_T *wb_ninsert_force(
146         xwcsbuf_T *restrict buf, size_t i, const wchar_t *restrict s, size_t n)
147     __attribute__((nonnull));
148 static inline xwcsbuf_T *wb_ninsert(
149         xwcsbuf_T *restrict buf, size_t i, const wchar_t *restrict s, size_t n)
150     __attribute__((nonnull));
151 static inline xwcsbuf_T *wb_insert(
152         xwcsbuf_T *restrict buf, size_t i, const wchar_t *restrict s)
153     __attribute__((nonnull));
154 static inline xwcsbuf_T *wb_ncat_force(
155         xwcsbuf_T *restrict buf, const wchar_t *restrict s, size_t n)
156     __attribute__((nonnull));
157 static inline xwcsbuf_T *wb_ncat(
158         xwcsbuf_T *restrict buf, const wchar_t *restrict s, size_t n)
159     __attribute__((nonnull));
160 static inline xwcsbuf_T *wb_cat(
161         xwcsbuf_T *restrict buf, const wchar_t *restrict s)
162     __attribute__((nonnull));
163 static inline xwcsbuf_T *wb_catfree(
164         xwcsbuf_T *restrict buf, wchar_t *restrict s)
165     __attribute__((nonnull));
166 static inline xwcsbuf_T *wb_remove(xwcsbuf_T *buf, size_t i, size_t n)
167     __attribute__((nonnull));
168 extern xwcsbuf_T *wb_wccat(xwcsbuf_T *buf, wchar_t c)
169     __attribute__((nonnull));
170 extern char *wb_mbscat(xwcsbuf_T *restrict buf, const char *restrict s)
171     __attribute__((nonnull));
172 extern int wb_vwprintf(
173         xwcsbuf_T *restrict buf, const wchar_t *restrict format, va_list ap)
174     __attribute__((nonnull(1,2)));
175 extern int wb_wprintf(
176         xwcsbuf_T *restrict buf, const wchar_t *restrict format, ...)
177     __attribute__((nonnull(1,2)));
178
179 extern char *malloc_wcsntombs(const wchar_t *s, size_t n)
180     __attribute__((nonnull,malloc,warn_unused_result));
181 #if HAVE_WCSNRTOMBS
182 static inline
183 #else
184 extern
185 #endif
186 char *malloc_wcstombs(const wchar_t *s)
187     __attribute__((nonnull,malloc,warn_unused_result));
188 static inline char *realloc_wcstombs(wchar_t *s)
189     __attribute__((nonnull,malloc,warn_unused_result));
190 extern wchar_t *malloc_mbstowcs(const char *s)
191     __attribute__((nonnull,malloc,warn_unused_result));
192 static inline wchar_t *realloc_mbstowcs(char *s)
193     __attribute__((nonnull,malloc,warn_unused_result));
194
195 extern char *malloc_vprintf(const char *format, va_list ap)
196     __attribute__((nonnull(1),malloc,warn_unused_result,format(printf,1,0)));
197 extern char *malloc_printf(const char *format, ...)
198     __attribute__((nonnull(1),malloc,warn_unused_result,format(printf,1,2)));
199 extern wchar_t *malloc_vwprintf(const wchar_t *format, va_list ap)
200     __attribute__((nonnull(1),malloc,warn_unused_result));
201 extern wchar_t *malloc_wprintf(const wchar_t *format, ...)
202     __attribute__((nonnull(1),malloc,warn_unused_result));
203
204 extern wchar_t *joinwcsarray(void *const *array, const wchar_t *padding)
205     __attribute__((malloc,warn_unused_result,nonnull));
206
207
208 /* Initializes the specified string buffer as an empty string. */
209 xstrbuf_T *sb_init(xstrbuf_T *buf)
210 {
211     return sb_initwithmax(buf, XSTRBUF_INITSIZE);
212 }
213
214 /* Frees the specified multibyte string buffer. The contents are lost. */
215 void sb_destroy(xstrbuf_T *buf)
216 {
217     free(sb_tostr(buf));
218 }
219
220 /* Frees the specified multibyte string buffer and returns the contents.
221  * The caller must `free' the return value. */
222 char *sb_tostr(xstrbuf_T *buf)
223 {
224     char *s = buf->contents;
225 #ifndef NDEBUG
226     buf->contents = &s[buf->maxlength];
227     buf->length = buf->maxlength = Size_max;
228 #endif
229     return s;
230 }
231
232 /* Shrinks the length of the buffer to `newlength'.
233  * `newlength' must not be larger than the current length.
234  * Characters beyond the new length are lost.
235  * `maxlength' of the buffer is not changed. */
236 xstrbuf_T *sb_truncate(xstrbuf_T *buf, size_t newlength)
237 {
238 #ifdef assert
239     assert(newlength <= buf->length);
240 #endif
241     buf->contents[buf->length = newlength] = '\0';
242     return buf;
243 }
244
245 /* Clears the contents of the specified string buffer.
246  * `maxlength' of the buffer is not changed. */
247 xstrbuf_T *sb_clear(xstrbuf_T *buf)
248 {
249     return sb_truncate(buf, 0);
250 }
251
252 /* Inserts the first `n' bytes of multibyte string `s' at offset `i' in buffer
253  * `buf'.
254  * No boundary checks are done and null characters are not considered special.
255  * `s' must not be part of `buf->contents'. */
256 xstrbuf_T *sb_ninsert_force(
257         xstrbuf_T *restrict buf, size_t i, const char *restrict s, size_t n)
258 {
259     return sb_replace_force(buf, i, 0, s, n);
260 }
261
262 /* Inserts the first `n' bytes of multibyte string `s' at offset `i' in buffer
263  * `buf'.
264  * If (strlen(s) <= n), the whole of `s' is inserted.
265  * If (buf->length <= i), `s' is appended to the end of the buffer.
266  * `s' must not be part of `buf->contents'. */
267 xstrbuf_T *sb_ninsert(
268         xstrbuf_T *restrict buf, size_t i, const char *restrict s, size_t n)
269 {
270     return sb_replace(buf, i, 0, s, n);
271 }
272
273 /* Inserts multibyte string `s' at offset `i' in buffer `buf'.
274  * If (buf->length <= i), `s' is appended to the end of the buffer.
275  * `s' must not be part of `buf->contents'. */
276 xstrbuf_T *sb_insert(xstrbuf_T *restrict buf, size_t i, const char *restrict s)
277 {
278     return sb_replace(buf, i, 0, s, Size_max);
279 }
280
281 /* Appends the first `n' bytes of multibyte string `s' to buffer `buf'.
282  * No boundary checks are done and null characters are not considered special.
283  * `s' must not be part of `buf->contents'. */
284 xstrbuf_T *sb_ncat_force(
285         xstrbuf_T *restrict buf, const char *restrict s, size_t n)
286 {
287     return sb_replace_force(buf, buf->length, 0, s, n);
288 }
289
290 /* Appends the first `n' bytes of multibyte string `s' to buffer `buf'.
291  * If (strlen(s) <= n), the whole of `s' is appended.
292  * `s' must not be part of `buf->contents'. */
293 xstrbuf_T *sb_ncat(xstrbuf_T *restrict buf, const char *restrict s, size_t n)
294 {
295     return sb_replace(buf, Size_max, 0, s, n);
296 }
297
298 /* Appends multibyte string `s' to buffer `buf'.
299  * `s' must not be part of `buf->contents'. */
300 xstrbuf_T *sb_cat(xstrbuf_T *restrict buf, const char *restrict s)
301 {
302     return sb_replace(buf, Size_max, 0, s, Size_max);
303 }
304
305 /* Appends multibyte string `s' to buffer `buf' and free the string.
306  * `s' must not be part of `buf->contents'. */
307 xstrbuf_T *sb_catfree(xstrbuf_T *restrict buf, char *restrict s)
308 {
309     sb_cat(buf, s);
310     free(s);
311     return buf;
312 }
313
314 /* Removes `n' bytes at offset `i' in buffer `buf'.
315  * If (buf->length <= i), `buf' is unchanged.
316  * If (buf->length <= i + n), `buf' is truncated to `i' bytes. */
317 xstrbuf_T *sb_remove(xstrbuf_T *buf, size_t i, size_t n)
318 {
319     return sb_replace(buf, i, n, "", 0);
320 }
321
322 #if HAVE_WCSNRTOMBS
323 wchar_t *sb_wcscat(xstrbuf_T *restrict buf,
324         const wchar_t *restrict s, mbstate_t *restrict ps)
325 {
326     return sb_wcsncat(buf, s, Size_max, ps);
327 }
328 #endif
329
330 /* Initializes the specified wide string buffer as an empty string. */
331 xwcsbuf_T *wb_init(xwcsbuf_T *buf)
332 {
333     return wb_initwithmax(buf, XWCSBUF_INITSIZE);
334 }
335
336 /* Frees the specified wide string buffer. The contents are lost. */
337 void wb_destroy(xwcsbuf_T *buf)
338 {
339     free(wb_towcs(buf));
340 }
341
342 /* Frees the specified wide string buffer and returns the contents.
343  * The caller must `free' the return value. */
344 wchar_t *wb_towcs(xwcsbuf_T *buf)
345 {
346     wchar_t *s = buf->contents;
347 #ifndef NDEBUG
348     buf->contents = &s[buf->maxlength];
349     buf->length = buf->maxlength = Size_max;
350 #endif
351     return s;
352 }
353
354 /* Shrinks the length of the specified buffer to `newlength'.
355  * `newlength' must not be larger than the current length.
356  * Characters beyond the new length are lost.
357  * `maxlength' of the buffer is not changed. */
358 xwcsbuf_T *wb_truncate(xwcsbuf_T *buf, size_t newlength)
359 {
360 #ifdef assert
361     assert(newlength <= buf->length);
362 #endif
363     buf->contents[buf->length = newlength] = L'\0';
364     return buf;
365 }
366
367 /* Clears the contents of the specified string buffer.
368  * `maxlength' of the buffer is not changed. */
369 xwcsbuf_T *wb_clear(xwcsbuf_T *buf)
370 {
371     return wb_truncate(buf, 0);
372 }
373
374 /* Inserts the first `n' characters of wide string `s' at offset `i' in buffer
375  * `buf'.
376  * No boundary checks are done and null characters are not considered special.
377  * `s' must not be part of `buf->contents'. */
378 xwcsbuf_T *wb_ninsert_force(
379         xwcsbuf_T *restrict buf, size_t i, const wchar_t *restrict s, size_t n)
380 {
381     return wb_replace_force(buf, i, 0, s, n);
382 }
383
384 /* Inserts the first `n' characters of wide string `s` at offset `i' in buffer
385  * `buf'.
386  * If (wcslen(s) <= n), the whole of `s' is inserted.
387  * If (buf->length <= i), `s' is appended to the end of the buffer.
388  * `s' must not be part of `buf->contents'. */
389 xwcsbuf_T *wb_ninsert(
390         xwcsbuf_T *restrict buf, size_t i, const wchar_t *restrict s, size_t n)
391 {
392     return wb_replace(buf, i, 0, s, n);
393 }
394
395 /* Inserts wide string `s' at offset `i' in buffer `buf'.
396  * If (buf->length <= i), `s' is appended to the end of the buffer.
397  * `s' must not be part of `buf->contents'. */
398 xwcsbuf_T *wb_insert(
399         xwcsbuf_T *restrict buf, size_t i, const wchar_t *restrict s)
400 {
401     return wb_replace(buf, i, 0, s, Size_max);
402 }
403
404 /* Appends the first `n' characters of wide string `s' to buffer `buf'.
405  * No boundary checks are done and null characters are not considered special.
406  * `s' must not be part of `buf->contents'. */
407 xwcsbuf_T *wb_ncat_force(
408         xwcsbuf_T *restrict buf, const wchar_t *restrict s, size_t n)
409 {
410     return wb_replace_force(buf, buf->length, 0, s, n);
411 }
412
413 /* Appends the first `n' characters of wide string `s' to buffer `buf'.
414  * If (wcslen(s) <= n), the whole of `s' is appended.
415  * `s' must not be part of `buf->contents'. */
416 xwcsbuf_T *wb_ncat(xwcsbuf_T *restrict buf, const wchar_t *restrict s, size_t n)
417 {
418     return wb_replace(buf, Size_max, 0, s, n);
419 }
420
421 /* Appends wide string `s' to buffer `buf'.
422  * `s' must not be part of `buf->contents'. */
423 xwcsbuf_T *wb_cat(xwcsbuf_T *restrict buf, const wchar_t *restrict s)
424 {
425     return wb_replace(buf, Size_max, 0, s, Size_max);
426 }
427
428 /* Appends wide string `s' to buffer and frees the string.
429  * `s' must not be part of `buf->contents'. */
430 xwcsbuf_T *wb_catfree(xwcsbuf_T *restrict buf, wchar_t *restrict s)
431 {
432     wb_cat(buf, s);
433     free(s);
434     return buf;
435 }
436
437 /* Removes `n' characters at offset `i' in buffer `buf'.
438  * If (buf->length <= i), `buf' is unchanged.
439  * If (buf->length <= i + n), `buf' is truncated to `i' characters. */
440 xwcsbuf_T *wb_remove(xwcsbuf_T *buf, size_t i, size_t n)
441 {
442     return wb_replace(buf, i, n, L"", 0);
443 }
444
445 #if HAVE_WCSNRTOMBS
446 char *malloc_wcstombs(const wchar_t *s)
447 {
448     return malloc_wcsntombs(s, Size_max);
449 }
450 #endif
451
452 /* Converts the specified wide string into a newly malloced multibyte string and
453  * frees the original wide string.
454  * Returns NULL on error. The wide string is freed anyway.
455  * The resulting string starts and ends in the initial shift state.*/
456 char *realloc_wcstombs(wchar_t *s)
457 {
458     char *result = malloc_wcstombs(s);
459     free(s);
460     return result;
461 }
462
463 /* Converts the specified multibyte string into a newly malloced wide string and
464  * frees the multibyte string.
465  * Returns NULL on error. The multibyte string is freed anyway. */
466 wchar_t *realloc_mbstowcs(char *s)
467 {
468     wchar_t *result = malloc_mbstowcs(s);
469     free(s);
470     return result;
471 }
472
473
474 #undef Size_max
475
476 #endif /* YASH_STRBUF_H */
477
478
479 /* vim: set ts=8 sts=4 sw=4 noet tw=80: */