Keep truncated status fields valid UTF-8

4c9a1f134acd84479cef0f91f6444fe4d3acc28c
Alexis Sellier committed ago 1 parent 183d43a8
swm.c +15 -2
1464 1464
void swm_sanitize_field(char *dst, size_t size, const char *src, const char *fallback) {
1465 1465
    size_t i;
1466 1466
1467 1467
    if (!size)
1468 1468
        return;
1469 -
1470 1469
    if (!src || !*src)
1471 1470
        src = fallback ? fallback : "";
1472 1471
1473 1472
    for (i = 0; src[i] && i + 1 < size; i++) {
1474 1473
        unsigned char ch = (unsigned char)src[i];
1475 1474
1476 1475
        dst[i] = ch < ' ' || ch == 0x7f ? ' ' : src[i];
1477 1476
    }
1478 -
1477 +
    /* Wayland strings are UTF-8. Drop a partial final codepoint after truncation. */
1478 +
    if (src[i] && i) {
1479 +
        size_t        start = i - 1, len;
1480 +
        unsigned char ch;
1481 +
1482 +
        while (start && ((unsigned char)dst[start] & 0xc0) == 0x80)
1483 +
            start--;
1484 +
        ch  = (unsigned char)dst[start];
1485 +
        len = ch >= 0xc2 && ch <= 0xdf   ? 2
1486 +
              : ch >= 0xe0 && ch <= 0xef ? 3
1487 +
              : ch >= 0xf0 && ch <= 0xf4 ? 4
1488 +
                                         : 1;
1489 +
        if (i - start < len)
1490 +
            i = start;
1491 +
    }
1479 1492
    dst[i] = '\0';
1480 1493
}
1481 1494
1482 1495
/* Parse one persisted window-state record. */
1483 1496
bool swm_parse_window_state(
test/unit.c +2 -0
157 157
    assert(swm_rule_matches("*", nullptr, "anything", "title"));
158 158
    assert(swm_rule_matches("term", "shell", "my-terminal", "a shell"));
159 159
    assert(!swm_rule_matches("browser", nullptr, "terminal", "browser"));
160 160
    swm_sanitize_field(small, sizeof(small), "a\tb\x1b", nullptr);
161 161
    assert(!strcmp(small, "a b "));
162 +
    swm_sanitize_field(small, sizeof(small), "abc\xc3\xa9", nullptr);
163 +
    assert(!strcmp(small, "abc"));
162 164
    assert(swm_parse_window_state(
163 165
        "org.app\tWindow\t1\t-2\t300\t200\n", appid, sizeof(appid), title, sizeof(title), &geometry
164 166
    ));
165 167
    assert(!strcmp(appid, "org.app"));
166 168
    assert(!strcmp(title, "Window"));