diff --git a/meson.build b/meson.build index 4d3b6213..68c3bf19 100644 --- a/meson.build +++ b/meson.build @@ -331,6 +331,7 @@ endif subdir('completions') subdir('icons') +subdir('utils') if (get_option('tests')) subdir('tests') diff --git a/utils/meson.build b/utils/meson.build new file mode 100644 index 00000000..2836788c --- /dev/null +++ b/utils/meson.build @@ -0,0 +1 @@ +executable('xtgettcap', 'xtgettcap.c') diff --git a/utils/xtgettcap.c b/utils/xtgettcap.c new file mode 100644 index 00000000..e21de7c0 --- /dev/null +++ b/utils/xtgettcap.c @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct termios orig_termios; + +static void +disable_raw_mode(void) +{ + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios) < 0) + exit(__LINE__); +} + +static void +enable_raw_mode(void) +{ + if (tcgetattr(STDIN_FILENO, &orig_termios) < 0) + exit(__LINE__); + + atexit(disable_raw_mode); + + struct termios raw = orig_termios; + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + raw.c_oflag &= ~(OPOST); + raw.c_cflag |= (CS8); + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 1; + + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) < 0) + exit(__LINE__); +} + +static const char * +hexlify(const char *s) +{ + static char buf[1024]; + + const size_t len = strlen(s); + for (size_t i = 0; i < len; i++) + sprintf(&buf[i * 2], "%02x", s[i]); + buf[len * 2 + 1] = '\0'; + + return buf; +} + +static size_t +unhexlify(char *dst, const char *src) +{ + size_t count = 0; + for (const char *p = src; *p != '\0'; p += 2, dst++, count++) + sscanf(p, "%02hhx", (unsigned char *)dst); + + *dst = '\0'; + return count; +} + +int +main(int argc, const char *const *argv) +{ + enable_raw_mode(); + + const size_t query_count = argc - 1; + + printf("\x1bP+q"); + for (int i = 1; i < argc; i++) + printf("%s%s", i > 1 ? ";" : "", hexlify(argv[i])); + printf("\033\\"); + + fflush(NULL); + + size_t replies = 0; + while (replies < query_count) { + struct pollfd fds[] = {{.fd = STDIN_FILENO, .events = POLLIN}}; + int r = poll(fds, sizeof(fds) / sizeof(fds[0]), -1); + if (r < 0) + exit(__LINE__); + + char buf[1024] = {0}; + ssize_t count = read(STDIN_FILENO, buf, sizeof(buf)); + + if (count < 0) + exit(__LINE__); + + if (count == 1 && buf[0] == 'q') + break; + + printf("reply: (%zd chars): ", count); + + for (size_t i = 0; i < (size_t)count; i++) { + if (isprint(buf[i])) + printf("%c", buf[i]); + else if (buf[i] == '\033') + printf("\033[1;31m\\E\033[m"); + else + printf("%02x", (uint8_t)buf[i]); + } + printf("\r\n"); + + const char *p = buf; + const char *end = buf + count; + + while (p < end) { + + const char *ST = strstr(p, "\033\\"); + if (ST == NULL) + break; + + if (count < 5 || + (strncmp(p, "\033P1+r", 5) != 00 && + strncmp(p, "\033P0+r", 5) != 0)) + { + break; + } + + const bool success = p[2] == '1'; + + char decoded[1024]; + char copy[ST - &p[5] + 1]; + strncpy(copy, &p[5], ST - &p[5]); + copy[ST - &p[5]] = '\0'; + + char *saveptr = NULL; + for (char *key_value = strtok_r(copy, "; ", &saveptr); + key_value != NULL; + key_value = strtok_r(NULL, "; ", &saveptr)) + { + // printf("key-value=%s\n", key_value); + const char *key = strtok(key_value, "="); + const char *value = strtok(NULL, "="); + +#if 0 + assert((success && value != NULL) || + (!success && value == NULL)); +#endif + + //printf("key=%s, value=%s\n", key, value); + size_t len = unhexlify(decoded, key); + + if (value != NULL) { + decoded[len++] = '='; + len += unhexlify(&decoded[len], value); + } + + const int color = success ? 39 : 31; + + printf(" \033[%dm", color); + for (size_t i = 0 ; i < len; i++) { + if (isprint(decoded[i])) + printf("%c", decoded[i]); + else if (decoded[i] == '\033') + printf("\033[1;31m\\E\033[22;%dm", color); + else + printf("\033[1m%02x\033[22m", (uint8_t)decoded[i]); + } + printf("\033[m\r\n"); + replies++; + } + + p = ST + 2; + } + + } + + return 0; +}