From e00c42a9b96ed3b8e8796c3583543587cb59d939 Mon Sep 17 00:00:00 2001 From: CismonX Date: Wed, 2 Dec 2020 17:53:59 +0800 Subject: [PATCH] Update tcsgrep. --- src/ctlseqs.c | 16 ++-- tests/tcsgrep.c | 197 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 166 insertions(+), 47 deletions(-) diff --git a/src/ctlseqs.c b/src/ctlseqs.c index e2f198e..7512946 100644 --- a/src/ctlseqs.c +++ b/src/ctlseqs.c @@ -331,13 +331,6 @@ ctlseqs_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *match state = ctlseqs_state_transit(state, buf[idx]); if (state == ctlseqs_state_err) { retval = CTLSEQS_NOSEQ; - for (; idx < len; ++idx) { - if (buf[idx] == 0x1b) { - break; - } - } - reader->buffer[0].num = idx; - reader->buffer[1].str = buf; break; } if (state == ctlseqs_state_done) { @@ -345,6 +338,15 @@ ctlseqs_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *match break; } } + if (retval == CTLSEQS_PARTIAL || retval == CTLSEQS_NOSEQ) { + for (; idx < len; ++idx) { + if (buf[idx] == 0x1b) { + break; + } + } + reader->buffer[0].num = idx; + reader->buffer[1].str = buf; + } reader->state = state; if (!reader->retain_partial || retval != CTLSEQS_PARTIAL) { reader->buf_start += idx; diff --git a/tests/tcsgrep.c b/tests/tcsgrep.c index 5b8fa3c..f4dd66d 100644 --- a/tests/tcsgrep.c +++ b/tests/tcsgrep.c @@ -19,11 +19,14 @@ #include "ctlseqs.h" +#include #include -#include #include #include #include + +#include +#include #include #define TCSGREP_NINTH_ARG_(a1, a2, a3, a4, a5, a6, a7, a8, a9, ...) a9 @@ -64,44 +67,105 @@ print_error(struct tcsgrep_ctx const *ctx, char const *msg) } static inline bool -parse_int(char const *str, int *dest) { +parse_int(char const *str, int *dest) +{ errno = 0; - long result = strtol(str, NULL, 10); - if (errno || result < -1 || result > INT_MAX) { + unsigned long result = strtoul(str, NULL, 10); + if (errno || result > 4096) { return false; } *dest = result; return true; } -static bool -parse_options(struct tcsgrep_ctx *ctx, int argc, char **argv) { - while (true) { - int result = getopt(argc, argv, "t:l:"); - switch (result) { - case 't': - if (!parse_int(optarg, &ctx->timeout)) { - print_error(ctx, "invalid timeout option"); - return false; - } - break; - case 'l': - if (!parse_int(optarg, &ctx->limit)) { - print_error(ctx, "invalid limit option"); - return false; - } - break; - case '?': - default: - return false; +static void +print_generic_seq(char const *header, union ctlseqs_value *buffer) +{ + size_t length = buffer[0].num; + char const *seq = buffer[1].str; + printf("%s %zu", header, length); + for (size_t idx = 0; idx < length; ++idx) { + char ch = seq[idx]; + if (isprint(ch)) { + printf(" %c", ch); + } else if (ch == 0x1b) { + printf(" ESC"); + } else { + printf(" \\x%02x", ch); } } - return true; + printf("\n"); +} + +static void +print_matching_seq(struct tcsgrep_sequence *seq, union ctlseqs_value *buffer) +{ + printf("OK %s", seq->name); + for (int idx = 0; idx < 8; ++idx) { + char placeholder = seq->pattern[idx]; + switch (placeholder) { + case 0x0e: // CTLSEQS_PH_NUM + printf(" %lu", buffer[idx].num); + break; + case 0x10: // CTLSEQS_PH_STR + printf(" %.*s", (int)buffer[idx].num, buffer[idx + 1].str); + break; + case 0x0f: // CTLSEQS_PH_NUMS + for (size_t i = 1; i <= buffer[idx].num; ++i) { + printf(" %lu", buffer[idx + i].num); + } + break; + } + } + printf("\n"); } int main(int argc, char **argv) { + struct tcsgrep_ctx ctx = { + .prog_name = argv[0], + .timeout = -1, + .limit = 4096, + }; + + int opt; + while (-1 != (opt = getopt(argc, argv, "t:l:"))) { + switch (opt) { + case 't': + if (!parse_int(optarg, &ctx.timeout)) { + print_error(&ctx, "invalid timeout option"); + return 1; + } + break; + case 'l': + if (!parse_int(optarg, &ctx.limit)) { + print_error(&ctx, "invalid limit option"); + return 1; + } + break; + case '?': + default: + print_error(&ctx, "invalid arguments"); + return 1; + } + } + + int flags = fcntl(STDIN_FILENO, F_GETFL); + if (flags == -1) { + print_error(&ctx, "failed to get file status flags"); + return 1; + } + if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) == -1) { + print_error(&ctx, "failed to set file status flags"); + return 1; + } + + struct ctlseqs_matcher *matcher = ctlseqs_matcher_init(); + if (matcher == NULL) { + print_error(&ctx, "failed to initialize matcher"); + return 1; + } static struct tcsgrep_sequence seqs[] = { TCSGREP_DEFSEQ_NOARGS(S7C1T), TCSGREP_DEFSEQ_NOARGS(S8C1T), @@ -252,19 +316,6 @@ main(int argc, char **argv) TCSGREP_DEFSEQ_NOARGS(KEY_F11), TCSGREP_DEFSEQ_NOARGS(KEY_F12), }; - struct tcsgrep_ctx ctx = { - .prog_name = argv[0], - .timeout = -1, - .limit = -1, - }; - if (!parse_options(&ctx, argc, argv)) { - return 1; - } - struct ctlseqs_matcher *matcher = ctlseqs_matcher_init(); - if (matcher == NULL) { - print_error(&ctx, "failed to initialize matcher"); - return 1; - } size_t npatterns = sizeof(seqs) / sizeof(struct tcsgrep_sequence); char const **patterns = malloc(sizeof(char const *) * npatterns); if (patterns == NULL) { @@ -278,13 +329,79 @@ main(int argc, char **argv) .patterns = patterns, .npatterns = npatterns, }; - if (!ctlseqs_matcher_setopt(matcher, &matcher_opts)) { + if (ctlseqs_matcher_setopt(matcher, &matcher_opts) != CTLSEQS_OK) { print_error(&ctx, "matcher setopt failed"); return 1; } + struct ctlseqs_reader *reader = ctlseqs_reader_init(); if (reader == NULL) { print_error(&ctx, "failed to initialize reader"); } - return 0; + union ctlseqs_value buffer[16]; + struct ctlseqs_reader_opts reader_opts = { + .fd = STDIN_FILENO, + .maxlen = ctx.limit, + .buffer = buffer + }; + if (ctlseqs_reader_setopt(reader, &reader_opts) != CTLSEQS_OK) { + print_error(&ctx, "reader setopt failed"); + return 1; + } + + if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) { + print_error(&ctx, "not a tty"); + return false; + } + struct termios old_termios; + if (tcgetattr(STDIN_FILENO, &old_termios) != 0) { + print_error(&ctx, "failed to get terminal attributes"); + return 1; + } + struct termios new_termios = old_termios; + new_termios.c_lflag &= ~(ICANON | ISIG | ECHO); + if (tcsetattr(STDIN_FILENO, TCSANOW, &new_termios) != 0) { + print_error(&ctx, "failed to set terminal attributes"); + return 1; + } + + int status = 0; + while (true) { + ssize_t result = ctlseqs_read(reader, matcher, ctx.timeout); + switch (result) { + case CTLSEQS_NOMEM: + print_error(&ctx, "failed to allocate memory"); + status = 1; + goto terminate; + case CTLSEQS_ERROR: + print_error(&ctx, "unexpected error"); + status = 1; + goto terminate; + case CTLSEQS_TIMEOUT: + printf("TIMEOUT\n"); + break; + case CTLSEQS_INTR: + printf("INTERRUPTED\n"); + break; + case CTLSEQS_EOF: + printf("EOF\n"); + break; + case CTLSEQS_PARTIAL: + print_generic_seq("PARTIAL", buffer); + break; + case CTLSEQS_NOSEQ: + print_generic_seq("NOSEQ", buffer); + if (buffer[1].str[0] == 0x03) { + goto terminate; + } + break; + default: + print_matching_seq(&seqs[result], buffer); + break; + } + } + + terminate: + tcsetattr(STDIN_FILENO, TCSANOW, &old_termios); + return status; }