Keep truncated status fields valid UTF-8
4c9a1f134acd84479cef0f91f6444fe4d3acc28c
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")); |