-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
.compare = catalog_compare_func
};
+static bool next_header(const char **s) {
+ const char *e;
+
+ e = strchr(*s, '\n');
+
+ /* Unexpected end */
+ if (!e)
+ return false;
+
+ /* End of headers */
+ if (e == *s)
+ return false;
+
+ *s = e + 1;
+ return true;
+}
+
+static const char *skip_header(const char *s) {
+ while (next_header(&s))
+ ;
+ return s;
+}
+
+static char *combine_entries(const char *one, const char *two) {
+ const char *b1, *b2;
+ size_t l1, l2, n;
+ char *dest, *p;
+
+ /* Find split point of headers to body */
+ b1 = skip_header(one);
+ b2 = skip_header(two);
+
+ l1 = strlen(one);
+ l2 = strlen(two);
+ dest = new(char, l1 + l2 + 1);
+ if (!dest) {
+ log_oom();
+ return NULL;
+ }
+
+ p = dest;
+
+ /* Headers from @one */
+ n = b1 - one;
+ p = mempcpy(p, one, n);
+
+ /* Headers from @two, these will only be found if not present above */
+ n = b2 - two;
+ p = mempcpy(p, two, n);
+
+ /* Body from @one */
+ n = l1 - (b1 - one);
+ if (n > 0) {
+ memcpy(p, b1, n);
+ p += n;
+
+ /* Body from @two */
+ } else {
+ n = l2 - (b2 - two);
+ memcpy(p, b2, n);
+ p += n;
+ }
+
+ assert(p - dest <= (ptrdiff_t)(l1 + l2));
+ p[0] = '\0';
+ return dest;
+}
+
static int finish_item(
Hashmap *h,
- struct strbuf *sb,
sd_id128_t id,
const char *language,
- const char *payload) {
+ char *payload, size_t payload_size) {
- ssize_t offset;
_cleanup_free_ CatalogItem *i = NULL;
- int r;
+ _cleanup_free_ char *prev = NULL, *combined = NULL;
assert(h);
- assert(sb);
assert(payload);
-
- offset = strbuf_add_string(sb, payload, strlen(payload));
- if (offset < 0)
- return log_oom();
+ assert(payload_size > 0);
i = new0(CatalogItem, 1);
if (!i)
assert(strlen(language) > 1 && strlen(language) < 32);
strcpy(i->language, language);
}
- i->offset = htole64((uint64_t) offset);
- r = hashmap_put(h, i, i);
- if (r == -EEXIST) {
- log_warning("Duplicate entry for " SD_ID128_FORMAT_STR ".%s, ignoring.",
- SD_ID128_FORMAT_VAL(id), language ? language : "C");
- return 0;
- } else if (r < 0)
- return r;
+ prev = hashmap_get(h, i);
+ if (prev) {
+ /* Already have such an item, combine them */
+ combined = combine_entries(payload, prev);
+ if (!combined)
+ return log_oom();
+
+ if (hashmap_update(h, i, combined) < 0)
+ return log_oom();
+ combined = NULL;
+ } else {
+ /* A new item */
+ combined = memdup(payload, payload_size + 1);
+ if (!combined)
+ return log_oom();
+
+ if (hashmap_put(h, i, combined) < 0)
+ return log_oom();
+ i = NULL;
+ combined = NULL;
+ }
- i = NULL;
return 0;
}
return 0;
beg = end - 1;
- while (beg > filename && *beg != '.' && *beg != '/' && end - beg < 32)
- beg --;
+ while (beg > filename && !IN_SET(*beg, '.', '/') && end - beg < 32)
+ beg--;
if (*beg != '.' || end <= beg + 1)
return 0;
return 0;
}
-int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) {
+int catalog_import_file(Hashmap *h, const char *path) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *payload = NULL;
+ size_t payload_size = 0, payload_allocated = 0;
unsigned n = 0;
sd_id128_t id;
_cleanup_free_ char *deflang = NULL, *lang = NULL;
int r;
assert(h);
- assert(sb);
assert(path);
f = fopen(path, "re");
for (;;) {
char line[LINE_MAX];
- size_t a, b, c;
- char *t;
+ size_t line_len;
if (!fgets(line, sizeof(line), f)) {
if (feof(f))
line[0] == '-' &&
line[1] == '-' &&
line[2] == ' ' &&
- (line[2+1+32] == ' ' || line[2+1+32] == '\0')) {
+ IN_SET(line[2+1+32], ' ', '\0')) {
bool with_language;
sd_id128_t jd;
if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) {
if (got_id) {
- r = finish_item(h, sb, id, lang ?: deflang, payload);
+ if (payload_size == 0) {
+ log_error("[%s:%u] No payload text.", path, n);
+ return -EINVAL;
+ }
+
+ r = finish_item(h, id, lang ?: deflang, payload, payload_size);
if (r < 0)
return r;
lang = mfree(lang);
+ payload_size = 0;
}
if (with_language) {
- t = strstrip(line + 2 + 1 + 32 + 1);
+ char *t;
+ t = strstrip(line + 2 + 1 + 32 + 1);
r = catalog_entry_lang(path, n, t, deflang, &lang);
if (r < 0)
return r;
empty_line = false;
id = jd;
- if (payload)
- payload[0] = '\0';
-
continue;
}
}
return -EINVAL;
}
- a = payload ? strlen(payload) : 0;
- b = strlen(line);
-
- c = a + (empty_line ? 1 : 0) + b + 1 + 1;
- t = realloc(payload, c);
- if (!t)
+ line_len = strlen(line);
+ if (!GREEDY_REALLOC(payload, payload_allocated,
+ payload_size + (empty_line ? 1 : 0) + line_len + 1 + 1))
return log_oom();
- if (empty_line) {
- t[a] = '\n';
- memcpy(t + a + 1, line, b);
- t[a+b+1] = '\n';
- t[a+b+2] = 0;
- } else {
- memcpy(t + a, line, b);
- t[a+b] = '\n';
- t[a+b+1] = 0;
- }
+ if (empty_line)
+ payload[payload_size++] = '\n';
+ memcpy(payload + payload_size, line, line_len);
+ payload_size += line_len;
+ payload[payload_size++] = '\n';
+ payload[payload_size] = '\0';
- payload = t;
empty_line = false;
}
if (got_id) {
- r = finish_item(h, sb, id, lang ?: deflang, payload);
+ if (payload_size == 0) {
+ log_error("[%s:%u] No payload text.", path, n);
+ return -EINVAL;
+ }
+
+ r = finish_item(h, id, lang ?: deflang, payload, payload_size);
if (r < 0)
return r;
}
_cleanup_strv_free_ char **files = NULL;
char **f;
struct strbuf *sb = NULL;
- _cleanup_hashmap_free_free_ Hashmap *h = NULL;
+ _cleanup_hashmap_free_free_free_ Hashmap *h = NULL;
_cleanup_free_ CatalogItem *items = NULL;
+ ssize_t offset;
+ char *payload;
CatalogItem *i;
Iterator j;
unsigned n;
goto finish;
}
- r = conf_files_list_strv(&files, ".catalog", root, dirs);
+ r = conf_files_list_strv(&files, ".catalog", root, 0, dirs);
if (r < 0) {
log_error_errno(r, "Failed to get catalog files: %m");
goto finish;
STRV_FOREACH(f, files) {
log_debug("Reading file '%s'", *f);
- r = catalog_import_file(h, sb, *f);
+ r = catalog_import_file(h, *f);
if (r < 0) {
log_error_errno(r, "Failed to import file '%s': %m", *f);
goto finish;
} else
log_debug("Found %u items in catalog.", hashmap_size(h));
- strbuf_complete(sb);
-
items = new(CatalogItem, hashmap_size(h));
if (!items) {
r = log_oom();
}
n = 0;
- HASHMAP_FOREACH(i, h, j) {
+ HASHMAP_FOREACH_KEY(payload, i, h, j) {
log_debug("Found " SD_ID128_FORMAT_STR ", language %s",
SD_ID128_FORMAT_VAL(i->id),
isempty(i->language) ? "C" : i->language);
+
+ offset = strbuf_add_string(sb, payload, strlen(payload));
+ if (offset < 0) {
+ r = log_oom();
+ goto finish;
+ }
+ i->offset = htole64((uint64_t) offset);
items[n++] = *i;
}
assert(n == hashmap_size(h));
qsort_safe(items, n, sizeof(CatalogItem), catalog_compare_func);
+ strbuf_complete(sb);
+
sz = write_catalog(database, sb, items, n);
if (sz < 0)
r = log_error_errno(sz, "Failed to write %s: %m", database);
static char *find_header(const char *s, const char *header) {
for (;;) {
- const char *v, *e;
+ const char *v;
v = startswith(s, header);
if (v) {
return strndup(v, strcspn(v, NEWLINE));
}
- /* End of text */
- e = strchr(s, '\n');
- if (!e)
+ if (!next_header(&s))
return NULL;
-
- /* End of header */
- if (e == s)
- return NULL;
-
- s = e + 1;
}
}