/**
* coopgammad -- Cooperative gamma server
* Copyright (C) 2016 Mattias Andrée (maandree@kth.se)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "util.h"
#include <libclut.h>
#include <sys/stat.h>
#include <errno.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 == NULL)
return NULL;
memcpy(dest, src, n);
return dest;
}
/**
* Read an entire file
*
* @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 = NULL;
char* new;
int saved_errno;
*n = 0;
if (!fstat(fd, &st))
size = st.st_size <= 0 ? 32 : (size_t)(st.st_size);
buf = malloc(size + 1);
if (buf == NULL)
return NULL;
for (;;)
{
if (*n == size)
{
new = realloc(buf, (size <<= 1) + 1);
if (new == NULL)
goto fail;
buf = new;
}
got = read(fd, buf + *n, size - *n);
if (got < 0)
{
if (errno == EINTR)
continue;
goto fail;
}
if (got == 0)
break;
*n += (size_t)got;
}
buf[*n] = '\0';
return buf;
fail:
saved_errno = errno;
free(buf);
errno = saved_errno;
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;
}
/**
* Duplicate a file descriptor an make sure
* the new file descriptor's index as a
* specified minimum value
*
* @param fd The file descriptor
* @param atleast The least acceptable new file descriptor
* @return The new file descriptor, -1 on error
*/
int dup2atleast(int fd, int atleast)
{
int* stack = malloc((size_t)(atleast + 1) * sizeof(int));
size_t stack_ptr = 0;
int new = -1, saved_errno;
if (stack == NULL)
goto fail;
for (;;)
{
new = dup(fd);
if (new < 0)
goto fail;
if (new >= atleast)
break;
}
fail:
saved_errno = errno;
while (stack_ptr--)
close(stack[stack_ptr]);
free(stack);
errno = saved_errno;
return new;
}
/**
* 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 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 == 0)
{
/* 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 ? 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;
}