/* 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 : %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 : %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 : %s\n", argv0, strerror(errno)); exit(1); } free(buf); free(cps); return 0; }