Escape terminal controls in window listings
ed6e245fd085f5c69e1d430d851fa86af0a2985f
1 parent
886699c1
swmctl.c
+11 -9
| 410 | 410 | if (workspaces[i].seen) |
|
| 411 | 411 | printf("%" PRIu32 "\n", i + 1); |
|
| 412 | 412 | } |
|
| 413 | 413 | } |
|
| 414 | 414 | ||
| 415 | - | /* Write a field with line and column separators escaped. */ |
|
| 416 | - | static void print_window_field(const char *value) { |
|
| 415 | + | /* Write a field with separators and terminal control bytes escaped. */ |
|
| 416 | + | static void print_window_field(FILE *stream, const char *value) { |
|
| 417 | 417 | const unsigned char *p; |
|
| 418 | 418 | ||
| 419 | 419 | for (p = (const unsigned char *)(value ? value : ""); *p; p++) { |
|
| 420 | 420 | if (*p == '\\') |
|
| 421 | - | fputs("\\\\", stdout); |
|
| 421 | + | fputs("\\\\", stream); |
|
| 422 | 422 | else if (*p == '\t') |
|
| 423 | - | fputs("\\t", stdout); |
|
| 423 | + | fputs("\\t", stream); |
|
| 424 | 424 | else if (*p == '\n') |
|
| 425 | - | fputs("\\n", stdout); |
|
| 425 | + | fputs("\\n", stream); |
|
| 426 | 426 | else if (*p == '\r') |
|
| 427 | - | fputs("\\r", stdout); |
|
| 427 | + | fputs("\\r", stream); |
|
| 428 | + | else if (*p < ' ' || *p == 0x7f) |
|
| 429 | + | fprintf(stream, "\\x%02x", *p); |
|
| 428 | 430 | else |
|
| 429 | - | putchar(*p); |
|
| 431 | + | fputc(*p, stream); |
|
| 430 | 432 | } |
|
| 431 | 433 | } |
|
| 432 | 434 | ||
| 433 | 435 | /* Print mapped toplevels as title, application ID, and identifier fields. */ |
|
| 434 | 436 | static void print_windows(void) { |
|
| 435 | 437 | window_t *window; |
|
| 436 | 438 | ||
| 437 | 439 | wl_list_for_each(window, &windows, link) { |
|
| 438 | 440 | if (window->closed || !window->identifier[0]) |
|
| 439 | 441 | continue; |
|
| 440 | - | print_window_field(window->title); |
|
| 442 | + | print_window_field(stdout, window->title); |
|
| 441 | 443 | putchar('\t'); |
|
| 442 | - | print_window_field(window->app_id); |
|
| 444 | + | print_window_field(stdout, window->app_id); |
|
| 443 | 445 | putchar('\t'); |
|
| 444 | 446 | puts(window->identifier); |
|
| 445 | 447 | } |
|
| 446 | 448 | } |
|
| 447 | 449 |
test/ctl_unit.c
+10 -1
| 7 | 7 | #include "swmctl.c" |
|
| 8 | 8 | #undef main |
|
| 9 | 9 | ||
| 10 | 10 | static void expect_invalid_color(const char *value) { |
|
| 11 | 11 | pid_t child = fork(); |
|
| 12 | - | int status; |
|
| 12 | + | int status; |
|
| 13 | 13 | ||
| 14 | 14 | assert(child >= 0); |
|
| 15 | 15 | if (!child) { |
|
| 16 | 16 | int nullfd = open("/dev/null", O_WRONLY); |
|
| 17 | 17 |
| 25 | 25 | assert(WIFEXITED(status)); |
|
| 26 | 26 | assert(WEXITSTATUS(status) == EXIT_FAILURE); |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | 29 | int main(void) { |
|
| 30 | + | char escaped[64] = {}; |
|
| 31 | + | FILE *stream = tmpfile(); |
|
| 32 | + | ||
| 33 | + | assert(stream); |
|
| 34 | + | print_window_field(stream, "title\x1b[2J"); |
|
| 35 | + | rewind(stream); |
|
| 36 | + | assert(fread(escaped, 1, sizeof(escaped) - 1, stream) > 0); |
|
| 37 | + | assert(!strcmp(escaped, "title\\x1b[2J")); |
|
| 38 | + | assert(fclose(stream) == 0); |
|
| 30 | 39 | assert(parse_color("#012345") == 0x012345ff); |
|
| 31 | 40 | assert(parse_color("#01234567") == 0x01234567); |
|
| 32 | 41 | expect_invalid_color("# 12345"); |
|
| 33 | 42 | expect_invalid_color("#+12345"); |
|
| 34 | 43 | return EXIT_SUCCESS; |