diff options
| author | Mattias Andrée <m@maandree.se> | 2026-01-24 14:11:01 +0100 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-01-24 14:11:01 +0100 |
| commit | c8b7fdc7294329dc5eaf9f089f83184ece7d098c (patch) | |
| tree | 1059fd3ab69fb9219ef5a6be3f53cd8208015c40 /common.c | |
| download | charconv-c8b7fdc7294329dc5eaf9f089f83184ece7d098c.tar.gz charconv-c8b7fdc7294329dc5eaf9f089f83184ece7d098c.tar.bz2 charconv-c8b7fdc7294329dc5eaf9f089f83184ece7d098c.tar.xz | |
First commit
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'common.c')
| -rw-r--r-- | common.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/common.c b/common.c new file mode 100644 index 0000000..11d72ac --- /dev/null +++ b/common.c @@ -0,0 +1,169 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +void +writeall(const char *s, size_t n) +{ + ssize_t r; + while (n) { + r = write(STDOUT_FILENO, s, n); + if (r < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, "%s: write <stdout>: %s\n", argv0, strerror(errno)); + exit(1); + } + s = &s[r]; + n -= (size_t)r; + } +} + + +void +writechar(uint_least32_t cp) +{ + char buf[4]; + + if (cp < UINT32_C(0x80)) { + buf[0] = (char)cp; + writeall(buf, 1u); + } else if (cp < UINT32_C(0x800)) { + buf[0] = (char)(( cp >> 6) | 0xC0u); + buf[1] = (char)(((cp >> 0) & 0x3Fu) | 0x80u); + writeall(buf, 2u); + } else if (cp < UINT32_C(0x10000)) { + buf[0] = (char)(( cp >> 12) | 0xE0u); + buf[1] = (char)(((cp >> 6) & 0x3Fu) | 0x80u); + buf[2] = (char)(((cp >> 0) & 0x3Fu) | 0x80u); + writeall(buf, 3u); + } else if (cp < UINT32_C(0x110000)) { + buf[0] = (char)(( cp >> 18) | 0xF0u); + buf[1] = (char)(((cp >> 12) & 0x3Fu) | 0x80u); + buf[2] = (char)(((cp >> 6) & 0x3Fu) | 0x80u); + buf[3] = (char)(((cp >> 0) & 0x3Fu) | 0x80u); + writeall(buf, 4u); + } else { + abort(); + } +} + + +int +convert(enum libcharconv_result (*conv)(const char *, size_t, size_t *, uint_least32_t *, size_t *)) +{ + char *buf = NULL; + size_t bufsize = 0; + size_t head = 0; + size_t tail = 0; + ssize_t r; + enum libcharconv_result res; + size_t n; + uint_least32_t *cps; + size_t ncps; + size_t cps_size = 16u; + size_t i; + + cps = malloc(cps_size * sizeof(*cps)); + if (!cps) { + fprintf(stderr, "%s: out of memory\n", argv0); + exit(1); + } + + for (;;) { + if (head == bufsize) { + if (tail) { + memmove(&buf[0], &buf[tail], head -= tail); + tail = 0; + } else { + buf = realloc(buf, bufsize += 8 << 10); + if (!buf) { + fprintf(stderr, "%s: out of memory\n", argv0); + exit(1); + } + } + } + r = read(STDIN_FILENO, &buf[head], bufsize - head); + if (r <= 0) { + if (!r) + break; + if (errno == EINTR) + continue; + fprintf(stderr, "%s: read <stdin>: %s\n", argv0, strerror(errno)); + exit(1); + } + head += (size_t)r; + conv_again: + ncps = cps_size; + res = (*conv)(&buf[tail], head - tail, &n, cps, &ncps); + if (ncps > cps_size) { + cps_size = ncps; + cps = realloc(cps, cps_size * sizeof(*cps)); + if (!cps) { + fprintf(stderr, "%s: out of memory\n", argv0); + exit(1); + } + goto conv_again; + } + switch (res) { + case LIBCHARCONV_NO_CONVERT: + writeall(&buf[tail], n); + tail += n; + if (tail != head) + goto conv_again; + break; + case LIBCHARCONV_CONVERTED: + for (i = 0u; i < ncps; i++) + writechar(cps[i]); + tail += n; + if (tail != head) + goto conv_again; + break; + case LIBCHARCONV_INDETERMINATE: + case LIBCHARCONV_CONVERT_IF_END: + break; + default: + abort(); + } + } + + while (tail < head) { + ncps = cps_size; + res = (*conv)(&buf[tail], head - tail, &n, cps, &ncps); + if (ncps > cps_size) { + cps_size = ncps; + cps = realloc(cps, cps_size * sizeof(*cps)); + if (!cps) { + fprintf(stderr, "%s: out of memory\n", argv0); + exit(1); + } + continue; + } + switch (res) { + case LIBCHARCONV_INDETERMINATE: + n = head - tail; + /* fall through */ + case LIBCHARCONV_NO_CONVERT: + writeall(&buf[tail], n); + tail += n; + break; + case LIBCHARCONV_CONVERTED: + case LIBCHARCONV_CONVERT_IF_END: + for (i = 0u; i < ncps; i++) + writechar(cps[i]); + tail += n; + break; + default: + abort(); + } + } + + if (close(STDOUT_FILENO)) { + fprintf(stderr, "%s: write <stdout>: %s\n", argv0, strerror(errno)); + exit(1); + } + + free(buf); + free(cps); + return 0; +} |
