forked from drath/libratbag
367 lines
7.7 KiB
C
367 lines
7.7 KiB
C
/*
|
|
* Copyright © 2008 Kristian Høgsberg
|
|
* Copyright © 2013-2015 Red Hat, Inc.
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
* the above copyright notice appear in all copies and that both that copyright
|
|
* notice and this permission notice appear in supporting documentation, and
|
|
* that the name of the copyright holders not be used in advertising or
|
|
* publicity pertaining to distribution of the software without specific,
|
|
* written prior permission. The copyright holders make no representations
|
|
* about the suitability of this software for any purpose. It is provided "as
|
|
* is" without express or implied warranty.
|
|
*
|
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
* OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <libudev.h>
|
|
|
|
#include "shared-macro.h"
|
|
|
|
/*
|
|
* This list data structure is a verbatim copy from wayland-util.h from the
|
|
* Wayland project; except that wl_ prefix has been removed.
|
|
*/
|
|
|
|
struct list {
|
|
struct list *prev;
|
|
struct list *next;
|
|
};
|
|
|
|
void list_init(struct list *list);
|
|
void list_insert(struct list *list, struct list *elm);
|
|
void list_append(struct list *list, struct list *elm);
|
|
void list_remove(struct list *elm);
|
|
int list_empty(const struct list *list);
|
|
|
|
#define list_for_each(pos, head, member) \
|
|
for (pos = 0, pos = container_of((head)->next, pos, member); \
|
|
&pos->member != (head); \
|
|
pos = container_of(pos->member.next, pos, member))
|
|
|
|
#define list_for_each_safe(pos, tmp, head, member) \
|
|
for (pos = 0, tmp = 0, \
|
|
pos = container_of((head)->next, pos, member), \
|
|
tmp = container_of((pos)->member.next, tmp, member); \
|
|
&pos->member != (head); \
|
|
pos = tmp, \
|
|
tmp = container_of(pos->member.next, tmp, member))
|
|
|
|
int mkdir_p(char *dir, mode_t mode);
|
|
|
|
static inline char*
|
|
strncpy_safe(char *dest, const char *src, size_t n)
|
|
{
|
|
strncpy(dest, src, n);
|
|
dest[n - 1] = '\0';
|
|
return dest;
|
|
}
|
|
|
|
#define LIBRATBAG_EXPORT __attribute__ ((visibility("default")))
|
|
|
|
static inline void *
|
|
zalloc(size_t size)
|
|
{
|
|
void *p = calloc(1, size);
|
|
|
|
if (!p)
|
|
abort();
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* returns NULL if str is NULL, otherwise guarantees a successful strdup.
|
|
*/
|
|
static inline char *
|
|
strdup_safe(const char *str)
|
|
{
|
|
char *s;
|
|
|
|
if (!str)
|
|
return NULL;
|
|
|
|
s = strdup(str);
|
|
if (!s)
|
|
abort();
|
|
|
|
return s;
|
|
}
|
|
|
|
static inline int
|
|
snprintf_safe(char *buf, size_t n, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
int rc;
|
|
|
|
va_start(args, fmt);
|
|
rc = vsnprintf(buf, n, fmt, args);
|
|
va_end(args);
|
|
|
|
if (rc < 0 || n < (size_t)rc)
|
|
abort();
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Returns a strdup'd string with all non-ascii characters replaced with a
|
|
* space.
|
|
*/
|
|
static inline char *
|
|
strdup_ascii_only(const char *str_in)
|
|
{
|
|
char *str, *ascii_only;
|
|
|
|
ascii_only = strdup_safe(str_in);
|
|
str = ascii_only;
|
|
while (str && *str) {
|
|
unsigned char c = *str;
|
|
if (c > 127)
|
|
*str = ' ';
|
|
|
|
str++;
|
|
}
|
|
return ascii_only;
|
|
}
|
|
|
|
#define sprintf_safe(buf, fmt, ...) \
|
|
snprintf_safe(buf, ARRAY_LENGTH(buf), fmt, __VA_ARGS__)
|
|
|
|
__attribute__((format(printf, 1, 2)))
|
|
static inline char *
|
|
asprintf_safe(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
int rc;
|
|
char *result;
|
|
|
|
va_start(args, fmt);
|
|
rc = vasprintf(&result, fmt, args);
|
|
va_end(args);
|
|
|
|
if (rc < 0)
|
|
abort();
|
|
|
|
return result;
|
|
}
|
|
|
|
static inline void
|
|
msleep(unsigned int ms)
|
|
{
|
|
usleep(ms * 1000);
|
|
}
|
|
|
|
static inline int
|
|
long_bit_is_set(const unsigned long *array, int bit)
|
|
{
|
|
return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS)));
|
|
}
|
|
|
|
static inline void
|
|
long_set_bit(unsigned long *array, int bit)
|
|
{
|
|
array[bit / LONG_BITS] |= (1LL << (bit % LONG_BITS));
|
|
}
|
|
|
|
static inline void
|
|
long_clear_bit(unsigned long *array, int bit)
|
|
{
|
|
array[bit / LONG_BITS] &= ~(1LL << (bit % LONG_BITS));
|
|
}
|
|
|
|
static inline void
|
|
long_set_bit_state(unsigned long *array, int bit, int state)
|
|
{
|
|
if (state)
|
|
long_set_bit(array, bit);
|
|
else
|
|
long_clear_bit(array, bit);
|
|
}
|
|
|
|
const char *
|
|
udev_prop_value(struct udev_device *device,
|
|
const char *property_name);
|
|
|
|
/**
|
|
* Converts a string from UTF-8 to the encoding specified. Returns the number
|
|
* of bytes written to buf on success, or negative errno value on failure.
|
|
*/
|
|
ssize_t
|
|
ratbag_utf8_to_enc(char *buf, size_t buf_len, const char *to_enc,
|
|
const char *format, ...) __attribute__((format(printf, 4, 5)));
|
|
/**
|
|
* Converts a string from the given encoding into UTF-8. The memory for the
|
|
* result is allocated and a pointer to the result is placed in *out. Returns
|
|
* the number of bytes in the UTF-8 version of the string on success, negative
|
|
* errno value on failure.
|
|
*/
|
|
ssize_t
|
|
ratbag_utf8_from_enc(char *in_buf, size_t in_len, const char *from_enc,
|
|
char **out);
|
|
|
|
__attribute__((format(printf, 2, 3)))
|
|
static inline int
|
|
xasprintf(char **strp, const char *fmt, ...)
|
|
{
|
|
int rc = 0;
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
rc = vasprintf(strp, fmt, args);
|
|
va_end(args);
|
|
if ((rc == -1) && strp)
|
|
*strp = NULL;
|
|
|
|
return rc;
|
|
}
|
|
|
|
struct dpi_range {
|
|
unsigned int min;
|
|
unsigned int max;
|
|
float step;
|
|
};
|
|
|
|
/* Parse a string in the form min:max@step to a dpi_range */
|
|
static inline struct dpi_range *
|
|
dpi_range_from_string(const char *str)
|
|
{
|
|
float min, max, step;
|
|
int rc;
|
|
struct dpi_range *range;
|
|
|
|
rc = sscanf(str, "%f:%f@%f", &min, &max, &step);
|
|
if (rc != 3 || min < 0 || max <= min || step < 20)
|
|
return NULL;
|
|
|
|
range = zalloc(sizeof(*range));
|
|
range->min = min;
|
|
range->max = max;
|
|
range->step = step;
|
|
|
|
return range;
|
|
}
|
|
|
|
struct dpi_list {
|
|
int *entries;
|
|
size_t nentries;
|
|
};
|
|
|
|
static inline void
|
|
dpi_list_free(struct dpi_list *list)
|
|
{
|
|
if (list == NULL)
|
|
return;
|
|
free(list->entries);
|
|
free(list);
|
|
}
|
|
|
|
DEFINE_TRIVIAL_CLEANUP_FUNC(struct dpi_list *, dpi_list_free);
|
|
|
|
/* Parse a string in the form "100;200;400"to a dpi_list */
|
|
static inline struct dpi_list *
|
|
dpi_list_from_string(const char *str)
|
|
{
|
|
struct dpi_list *list;
|
|
unsigned int i, count, index;
|
|
int nread, dpi = 0;
|
|
int rc = 0;
|
|
char c;
|
|
void *tmp;
|
|
|
|
if (!str || str[0] == '\0')
|
|
return NULL;
|
|
|
|
/* first, count how many elements do we have in the table */
|
|
count = 1;
|
|
i = 0;
|
|
while (str[i] != 0) {
|
|
c = str[i++];
|
|
if (c == ';')
|
|
count++;
|
|
}
|
|
|
|
list = zalloc(sizeof *list);
|
|
list->nentries = count;
|
|
list->entries = zalloc(count * sizeof(list->entries[0]));
|
|
|
|
index = 0;
|
|
|
|
while (*str != 0 && index < count) {
|
|
if (*str == ';') {
|
|
str++;
|
|
continue;
|
|
}
|
|
|
|
nread = 0;
|
|
rc = sscanf(str, "%d%n", &dpi, &nread);
|
|
if (rc != 1 || !nread || dpi < 0)
|
|
goto err;
|
|
|
|
list->entries[index] = dpi;
|
|
str += nread;
|
|
index++;
|
|
}
|
|
|
|
if (index == 0)
|
|
goto err;
|
|
|
|
/* Drop empty entries for trailing semicolons */
|
|
list->nentries = index;
|
|
tmp = realloc(list->entries, index * sizeof(list->entries[0]));
|
|
if (tmp)
|
|
list->entries = tmp;
|
|
|
|
return list;
|
|
|
|
err:
|
|
dpi_list_free(list);
|
|
return NULL;
|
|
}
|
|
|
|
static inline uint16_t
|
|
get_unaligned_be_u16(const uint8_t *buf)
|
|
{
|
|
return (buf[0] << 8) | buf[1];
|
|
}
|
|
|
|
static inline void
|
|
set_unaligned_be_u16(uint8_t *buf, uint16_t value)
|
|
{
|
|
buf[0] = value >> 8;
|
|
buf[1] = value & 0xFF;
|
|
}
|
|
|
|
static inline uint16_t
|
|
get_unaligned_le_u16(const uint8_t *buf)
|
|
{
|
|
return (buf[1] << 8) | buf[0];
|
|
}
|
|
|
|
static inline void
|
|
set_unaligned_le_u16(uint8_t *buf, uint16_t value)
|
|
{
|
|
buf[0] = value & 0xFF;
|
|
buf[1] = value >> 8;
|
|
}
|
|
|
|
static inline uint32_t
|
|
get_unaligned_be_u32(const uint8_t *buf)
|
|
{
|
|
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
|
}
|