diff options
Diffstat (limited to 'util.c')
-rw-r--r-- | util.c | 274 |
1 files changed, 274 insertions, 0 deletions
@@ -0,0 +1,274 @@ +/* See LICENSE file for copyright and license details. */ +#include "util.h" + +#include <libclut.h> + +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + + +/** + * Duplicate a memory segment + * + * @param src The memory segment, must not be `NULL` + * @param n The size of the memory segment, must not be zero + * @return The duplicate of the memory segment, + * `NULL` on error + */ +void * +memdup(const void *restrict src, size_t n) +{ + void *dest = malloc(n); + if (!dest) + return NULL; + memcpy(dest, src, n); + return dest; +} + + +/** + * Read an entire file + * + * Not cancelled by `EINTR` + * + * @param fd The file descriptor + * @param n Output for the size of the file + * @return The read content, plus a NUL byte at + * the end (not counted in `*n`) + */ +void * +nread(int fd, size_t *restrict n) +{ + size_t size = 32; + ssize_t got; + struct stat st; + char *buf, *new; + + *n = 0; + + if (!fstat(fd, &st)) + size = st.st_size <= 0 ? 32 : (size_t)(st.st_size); + + buf = malloc(size + 1); + if (!buf) + return NULL; + + for (;;) { + if (*n == size) { + new = realloc(buf, (size <<= 1) + 1); + if (!new) + goto fail; + buf = new; + } + + got = read(fd, buf + *n, size - *n); + if (got <= 0) { + if (!got) + break; + if (errno == EINTR) + continue; + goto fail; + } + *n += (size_t)got; + } + + buf[*n] = '\0'; + return buf; + +fail: + free(buf); + return NULL; +} + + +/** + * Write an entire buffer to a file + * + * Not cancelled by `EINTR` + * + * @param fd The file descriptor + * @param buf The buffer which shall be written to the fail + * @param n The size of the buffer + * @return The number of written bytes, less than `n` + * on error, cannot exceed `n` + */ +size_t +nwrite(int fd, const void *restrict buf, size_t n) +{ + const char *restrict bs = buf; + ssize_t wrote; + size_t ptr = 0; + + while (ptr < n) { + wrote = write(fd, bs + ptr, n - ptr); + if (wrote <= 0) { + if (wrote < 0 && errno == EINTR) + continue; + return ptr; + } + ptr += (size_t)wrote; + } + + return ptr; +} + + +/** + * Perform a timed suspention of the process. + * The process resumes when the timer expires, + * or when it is interrupted. + * + * @param ms The number of milliseconds to sleep, + * must be less than 1000 + */ +void +msleep(unsigned ms) +{ + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = (long)ms * 1000000L; + if (clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL) == ENOTSUP) + nanosleep(&ts, NULL); +} + + +/** + * Check whether a NUL-terminated string is encoded in UTF-8 + * + * @param string The string + * @return Zero if good, -1 on encoding error + */ +int +verify_utf8(const char *restrict string) +{ + static long BYTES_TO_MIN_BITS[] = {0, 0, 8, 12, 17, 22, 37}; + static long BYTES_TO_MAX_BITS[] = {0, 7, 11, 16, 21, 26, 31}; + long int bytes = 0, read_bytes = 0, bits = 0, c, character = 0; + + /* min bits max bits + 0....... 0 7 + 110..... 10...... 8 11 + 1110.... 10...... 10...... 12 16 + 11110... 10...... 10...... 10...... 17 21 + 111110.. 10...... 10...... 10...... 10...... 22 26 + 1111110. 10...... 10...... 10...... 10...... 10...... 27 31 + */ + + while ((c = (long)(*string++))) { + if (!read_bytes) { + /* First byte of the character. */ + + if ((c & 0x80) == 0x00) { + /* Single-byte character. */ + continue; + } + + if ((c & 0xC0) == 0x80) { + /* Single-byte character marked as multibyte, or + a non-first byte in a multibyte character. */ + return -1; + } + + /* Multibyte character. */ + while ((c & 0x80)) { + bytes++; + c <<= 1; + } + read_bytes = 1; + character = c & 0x7F; + if (bytes > 6) { + /* 31-bit characters can be encoded with 6-bytes, + and UTF-8 does not cover higher code points. */ + return -1; + } + } else { + /* Not first byte of the character. */ + + if ((c & 0xC0) != 0x80) { + /* Beginning of new character before a + multibyte character has ended. */ + return -1; + } + + character = (character << 6) | (c & 0x7F); + + if (++read_bytes < bytes) { + /* Not at last byte yet. */ + continue; + } + + /* Check that the character is not unnecessarily long. */ + while (character) { + character >>= 1, bits++; + } + if ((bits < BYTES_TO_MIN_BITS[bytes]) || (BYTES_TO_MAX_BITS[bytes] < bits)) { + return -1; + } + + read_bytes = bytes = bits = 0; + } + } + + /* Make sure we did not stop at the middle of a multibyte character. */ + return !read_bytes ? 0 : -1; +} + + +/** + * Make identity mapping ramps + * + * @param ramps Output parameter for the ramps + * @param output The output for which the ramps shall be configured + * @return Zero on success, -1 on error + */ +int +make_plain_ramps(union gamma_ramps *restrict ramps, struct output *restrict output) +{ + COPY_RAMP_SIZES(&ramps->u8, output); + switch (output->depth) { + case 8: + if (libgamma_gamma_ramps8_initialise(&(ramps->u8))) + return -1; + libclut_start_over(&ramps->u8, UINT8_MAX, uint8_t, 1, 1, 1); + break; + + case 16: + if (libgamma_gamma_ramps16_initialise(&(ramps->u16))) + return -1; + libclut_start_over(&ramps->u16, UINT16_MAX, uint16_t, 1, 1, 1); + break; + + case 32: + if (libgamma_gamma_ramps32_initialise(&(ramps->u32))) + return -1; + libclut_start_over(&ramps->u32, UINT32_MAX, uint32_t, 1, 1, 1); + break; + + case 64: + if (libgamma_gamma_ramps64_initialise(&(ramps->u64))) + return -1; + libclut_start_over(&ramps->u64, UINT64_MAX, uint64_t, 1, 1, 1); + break; + + case -1: + if (libgamma_gamma_rampsf_initialise(&(ramps->f))) + return -1; + libclut_start_over(&ramps->f, 1.0f, float, 1, 1, 1); + break; + + case -2: + if (libgamma_gamma_rampsd_initialise(&(ramps->d))) + return -1; + libclut_start_over(&ramps->d, (double)1, double, 1, 1, 1); + break; + + default: + abort(); + } + return 0; +} |