2021-11-13 21:56:53 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2025-07-28 01:02:01 -04:00
|
|
|
#include "common/buf.h"
|
2024-03-16 04:39:45 +01:00
|
|
|
#include <assert.h>
|
2021-06-30 19:56:31 +01:00
|
|
|
#include <ctype.h>
|
2024-08-19 21:22:50 +01:00
|
|
|
#include <stdarg.h>
|
2023-09-21 22:51:54 +01:00
|
|
|
#include <stdbool.h>
|
2024-08-19 21:22:50 +01:00
|
|
|
#include <stdio.h>
|
2024-04-14 14:20:57 -04:00
|
|
|
#include <string.h>
|
2025-10-15 16:36:01 +09:00
|
|
|
#include <wlr/util/log.h>
|
2024-04-14 14:20:57 -04:00
|
|
|
#include "common/macros.h"
|
2022-09-16 18:41:02 -04:00
|
|
|
#include "common/mem.h"
|
2024-04-16 20:45:16 -04:00
|
|
|
#include "common/string-helpers.h"
|
2020-06-09 21:40:46 +01:00
|
|
|
|
2023-09-21 23:09:05 +01:00
|
|
|
void
|
|
|
|
|
buf_expand_tilde(struct buf *s)
|
|
|
|
|
{
|
2025-07-04 00:17:18 -04:00
|
|
|
struct buf tmp = BUF_INIT;
|
2023-09-21 23:09:05 +01:00
|
|
|
for (int i = 0 ; i < s->len ; i++) {
|
2024-04-16 23:36:32 -04:00
|
|
|
if (s->data[i] == '~') {
|
2025-07-04 00:17:18 -04:00
|
|
|
buf_add(&tmp, getenv("HOME"));
|
2023-09-21 23:09:05 +01:00
|
|
|
} else {
|
2025-07-04 00:17:18 -04:00
|
|
|
buf_add_char(&tmp, s->data[i]);
|
2023-09-21 23:09:05 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-07-04 00:17:18 -04:00
|
|
|
buf_move(s, &tmp);
|
2023-09-21 23:09:05 +01:00
|
|
|
}
|
|
|
|
|
|
2021-10-11 22:31:38 +01:00
|
|
|
static void
|
|
|
|
|
strip_curly_braces(char *s)
|
|
|
|
|
{
|
|
|
|
|
size_t len = strlen(s);
|
|
|
|
|
if (s[0] != '{' || s[len - 1] != '}') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
len -= 2;
|
2023-09-23 15:16:57 +01:00
|
|
|
memmove(s, s + 1, len);
|
2021-10-11 22:31:38 +01:00
|
|
|
s[len] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-21 22:51:54 +01:00
|
|
|
static bool
|
|
|
|
|
isvalid(char p)
|
|
|
|
|
{
|
|
|
|
|
return isalnum(p) || p == '_' || p == '{' || p == '}';
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-30 19:56:31 +01:00
|
|
|
void
|
|
|
|
|
buf_expand_shell_variables(struct buf *s)
|
|
|
|
|
{
|
2025-07-04 00:17:18 -04:00
|
|
|
struct buf tmp = BUF_INIT;
|
2024-04-14 14:20:57 -04:00
|
|
|
struct buf environment_variable = BUF_INIT;
|
2021-06-30 19:56:31 +01:00
|
|
|
|
|
|
|
|
for (int i = 0 ; i < s->len ; i++) {
|
2024-04-16 23:36:32 -04:00
|
|
|
if (s->data[i] == '$' && isvalid(s->data[i+1])) {
|
2021-06-30 19:56:31 +01:00
|
|
|
/* expand environment variable */
|
2024-03-16 19:03:06 +01:00
|
|
|
buf_clear(&environment_variable);
|
2024-04-16 23:36:32 -04:00
|
|
|
buf_add(&environment_variable, s->data + i + 1);
|
|
|
|
|
char *p = environment_variable.data;
|
2023-09-21 22:51:54 +01:00
|
|
|
while (isvalid(*p)) {
|
2021-06-30 19:56:31 +01:00
|
|
|
++p;
|
|
|
|
|
}
|
|
|
|
|
*p = '\0';
|
2024-04-16 23:36:32 -04:00
|
|
|
i += strlen(environment_variable.data);
|
|
|
|
|
strip_curly_braces(environment_variable.data);
|
|
|
|
|
p = getenv(environment_variable.data);
|
2021-10-11 22:28:06 +01:00
|
|
|
if (p) {
|
2025-07-04 00:17:18 -04:00
|
|
|
buf_add(&tmp, p);
|
2021-06-30 19:56:31 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
2025-07-04 00:17:18 -04:00
|
|
|
buf_add_char(&tmp, s->data[i]);
|
2021-06-30 19:56:31 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-04-14 14:20:57 -04:00
|
|
|
buf_reset(&environment_variable);
|
2025-07-04 00:17:18 -04:00
|
|
|
buf_move(s, &tmp);
|
2021-06-30 19:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
2024-04-14 14:20:57 -04:00
|
|
|
static void
|
|
|
|
|
buf_expand(struct buf *s, int new_alloc)
|
2020-06-09 21:40:46 +01:00
|
|
|
{
|
2024-04-14 14:20:57 -04:00
|
|
|
/*
|
|
|
|
|
* "s->alloc &&" ensures that s->data is always allocated after
|
|
|
|
|
* returning (even if new_alloc == 0). The extra check is not
|
|
|
|
|
* really necessary but makes it easier for analyzers to see
|
|
|
|
|
* that we never overwrite a string literal.
|
|
|
|
|
*/
|
|
|
|
|
if (s->alloc && new_alloc <= s->alloc) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
new_alloc = MAX(new_alloc, 256);
|
|
|
|
|
new_alloc = MAX(new_alloc, s->alloc * 3 / 2);
|
|
|
|
|
if (s->alloc) {
|
2024-04-16 23:36:32 -04:00
|
|
|
assert(s->data);
|
|
|
|
|
s->data = xrealloc(s->data, new_alloc);
|
2024-04-14 14:20:57 -04:00
|
|
|
} else {
|
|
|
|
|
assert(!s->len);
|
2024-04-16 23:36:32 -04:00
|
|
|
s->data = xmalloc(new_alloc);
|
|
|
|
|
s->data[0] = '\0';
|
2024-04-14 14:20:57 -04:00
|
|
|
}
|
|
|
|
|
s->alloc = new_alloc;
|
2020-06-09 21:40:46 +01:00
|
|
|
}
|
|
|
|
|
|
2024-08-19 21:22:50 +01:00
|
|
|
void
|
|
|
|
|
buf_add_fmt(struct buf *s, const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
if (string_null_or_empty(fmt)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2024-08-23 20:09:47 +02:00
|
|
|
int n = vsnprintf(NULL, 0, fmt, ap);
|
2024-08-19 21:22:50 +01:00
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
if (n < 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 20:09:47 +02:00
|
|
|
size_t size = (size_t)n + 1;
|
2024-08-19 21:22:50 +01:00
|
|
|
buf_expand(s, s->len + size);
|
|
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
n = vsnprintf(s->data + s->len, size, fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
if (n < 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s->len += n;
|
|
|
|
|
s->data[s->len] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-22 18:32:42 +01:00
|
|
|
void
|
|
|
|
|
buf_add_hex_color(struct buf *s, float color[4])
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* In theme.c parse_hexstr() colors are pre-multiplied (by alpha) as
|
|
|
|
|
* expected by wlr_scene(). We therefore need to reverse that here.
|
|
|
|
|
*
|
|
|
|
|
* For details, see https://github.com/labwc/labwc/pull/1685
|
|
|
|
|
*/
|
|
|
|
|
float alpha = color[3];
|
|
|
|
|
|
|
|
|
|
/* Avoid division by zero */
|
|
|
|
|
if (alpha == 0.0f) {
|
|
|
|
|
buf_add(s, "#00000000");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf_add_fmt(s, "#%02x%02x%02x%02x",
|
|
|
|
|
(int)(color[0] / alpha * 255),
|
|
|
|
|
(int)(color[1] / alpha * 255),
|
|
|
|
|
(int)(color[2] / alpha * 255),
|
|
|
|
|
(int)(alpha * 255));
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-28 20:41:41 +01:00
|
|
|
void
|
|
|
|
|
buf_add(struct buf *s, const char *data)
|
2020-06-09 21:40:46 +01:00
|
|
|
{
|
2024-04-16 20:45:16 -04:00
|
|
|
if (string_null_or_empty(data)) {
|
2020-06-09 21:40:46 +01:00
|
|
|
return;
|
2020-09-28 20:41:41 +01:00
|
|
|
}
|
2020-06-09 21:40:46 +01:00
|
|
|
int len = strlen(data);
|
2024-04-14 14:20:57 -04:00
|
|
|
buf_expand(s, s->len + len + 1);
|
2024-04-16 23:36:32 -04:00
|
|
|
memcpy(s->data + s->len, data, len);
|
2020-06-09 21:40:46 +01:00
|
|
|
s->len += len;
|
2024-04-16 23:36:32 -04:00
|
|
|
s->data[s->len] = 0;
|
2020-06-09 21:40:46 +01:00
|
|
|
}
|
2024-03-16 04:39:45 +01:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
buf_add_char(struct buf *s, char ch)
|
|
|
|
|
{
|
2024-11-07 21:08:09 +00:00
|
|
|
buf_expand(s, s->len + 2);
|
2024-04-16 23:36:32 -04:00
|
|
|
s->data[s->len++] = ch;
|
|
|
|
|
s->data[s->len] = '\0';
|
2024-03-16 04:39:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
buf_clear(struct buf *s)
|
|
|
|
|
{
|
2024-04-14 14:20:57 -04:00
|
|
|
if (s->alloc) {
|
2024-04-16 23:36:32 -04:00
|
|
|
assert(s->data);
|
2024-04-14 14:20:57 -04:00
|
|
|
s->len = 0;
|
2024-04-16 23:36:32 -04:00
|
|
|
s->data[0] = '\0';
|
2024-04-14 14:20:57 -04:00
|
|
|
} else {
|
|
|
|
|
*s = BUF_INIT;
|
|
|
|
|
}
|
2024-03-16 04:39:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
buf_reset(struct buf *s)
|
|
|
|
|
{
|
2024-04-14 14:20:57 -04:00
|
|
|
if (s->alloc) {
|
2024-04-16 23:36:32 -04:00
|
|
|
free(s->data);
|
2024-04-14 14:20:57 -04:00
|
|
|
}
|
|
|
|
|
*s = BUF_INIT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
buf_move(struct buf *dst, struct buf *src)
|
|
|
|
|
{
|
|
|
|
|
if (dst->alloc) {
|
2024-04-16 23:36:32 -04:00
|
|
|
free(dst->data);
|
2024-04-14 14:20:57 -04:00
|
|
|
}
|
|
|
|
|
*dst = *src;
|
|
|
|
|
*src = BUF_INIT;
|
2024-03-16 04:39:45 +01:00
|
|
|
}
|
2025-10-15 16:36:01 +09:00
|
|
|
|
|
|
|
|
struct buf
|
|
|
|
|
buf_from_file(const char *filename)
|
|
|
|
|
{
|
|
|
|
|
struct buf buf = BUF_INIT;
|
|
|
|
|
FILE *stream = fopen(filename, "r");
|
|
|
|
|
if (!stream) {
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fseek(stream, 0, SEEK_END) == -1) {
|
|
|
|
|
wlr_log_errno(WLR_ERROR, "fseek(%s)", filename);
|
|
|
|
|
fclose(stream);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
long size = ftell(stream);
|
|
|
|
|
if (size == -1) {
|
|
|
|
|
wlr_log_errno(WLR_ERROR, "ftell(%s)", filename);
|
|
|
|
|
fclose(stream);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
rewind(stream);
|
|
|
|
|
|
|
|
|
|
buf_expand(&buf, size + 1);
|
|
|
|
|
if (fread(buf.data, 1, size, stream) == (size_t)size) {
|
|
|
|
|
buf.len = size;
|
|
|
|
|
buf.data[size] = '\0';
|
|
|
|
|
} else {
|
|
|
|
|
wlr_log_errno(WLR_ERROR, "fread(%s)", filename);
|
|
|
|
|
buf_reset(&buf);
|
|
|
|
|
}
|
|
|
|
|
fclose(stream);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|