diff --git a/tests/tcsgrep.c b/tests/tcsgrep.c index bfff391..2227eb3 100644 --- a/tests/tcsgrep.c +++ b/tests/tcsgrep.c @@ -63,6 +63,9 @@ struct tcsgrep_sequence { struct tcsgrep_ctx { char const *prog_name; + FILE *out_file; + FILE *err_file; + int in_fd; int timeout; int limit; bool purge_long_seqs; @@ -73,7 +76,7 @@ struct tcsgrep_ctx { static inline void print_error(struct tcsgrep_ctx const *ctx, char const *msg) { - fprintf(stderr, "%s: [error] %s.\n", ctx->prog_name, msg); + fprintf(ctx->err_file, "%s: [error] %s.\n", ctx->prog_name, msg); } static inline bool @@ -89,7 +92,7 @@ parse_int(char const *str, int *dest) } static inline void -print_char(int ch) +print_char(struct tcsgrep_ctx const *ctx, int ch) { static char const *ascii_table[] = { "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", @@ -98,68 +101,75 @@ print_char(int ch) "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" }; if (ch == ' ') { - printf(" SP"); + fprintf(ctx->out_file, " SP"); } else if (isprint(ch)) { - printf(" %c", ch); + fprintf(ctx->out_file, " %c", ch); } else if (ch == 0x7f) { - printf(" DEL"); + fprintf(ctx->out_file, " DEL"); } else if (!iscntrl(ch)) { - printf(" \\x%02x", (unsigned char)ch); + fprintf(ctx->out_file, " \\x%02x", (unsigned char)ch); } else { - printf(" %s", ascii_table[ch]); + fprintf(ctx->out_file, " %s", ascii_table[ch]); } } static void -print_generic_seq(char const *header, union ctlseqs_value *result, bool newline) +print_generic_seq(struct tcsgrep_ctx const *ctx, char const *header, union ctlseqs_value const *result, bool newline) { size_t length = result[0].len; char const *seq = result[1].str; - printf("%s %zu", header, length); + fprintf(ctx->out_file, "%s %zu", header, length); for (size_t idx = 0; idx < length; ++idx) { - print_char((unsigned)seq[idx]); + print_char(ctx, (unsigned)seq[idx]); } if (newline) { - printf("\n"); + fprintf(ctx->out_file, "\n"); } } static void -print_matching_seq(struct tcsgrep_sequence *seq, union ctlseqs_value *result, bool verbose) -{ +print_matching_seq( + struct tcsgrep_ctx const *ctx, + struct tcsgrep_sequence const *seq, + union ctlseqs_value const *result, + bool verbose +) { if (verbose) { - print_generic_seq("OK", result, false); + print_generic_seq(ctx, "OK", result, false); result += 2; } else { - printf("OK"); + fprintf(ctx->out_file, "OK"); } - printf(" %s", seq->name); + fprintf(ctx->out_file, " %s", seq->name); for (int idx = 0; idx < 8; ++idx) { char placeholder = seq->args[idx]; switch (placeholder) { case 0x0e: // CTLSEQS_PH_NUM - printf(" %lu", result[idx].num); + fprintf(ctx->out_file, " %lu", result[idx].num); break; case 0x10: // CTLSEQS_PH_STR - printf(" %.*s", (int)result[idx].len, result[idx + 1].str); + fprintf(ctx->out_file, " %.*s", (int)result[idx].len, result[idx + 1].str); break; case 0x0f: // CTLSEQS_PH_NUMS for (size_t i = 1; i <= result[idx].len; ++i) { - printf(" %lu", result[idx + i].num); + fprintf(ctx->out_file, " %lu", result[idx + i].num); } break; } } - printf("\n"); + fprintf(ctx->out_file, "\n"); } int main(int argc, char **argv) { struct tcsgrep_ctx ctx = { - .prog_name = argv[0], - .timeout = -1, - .limit = DEFAULT_MAX_BUFFER_LEN, + .prog_name = argv[0], + .out_file = stdout, + .err_file = stderr, + .in_fd = STDIN_FILENO, + .timeout = -1, + .limit = DEFAULT_MAX_BUFFER_LEN, }; int opt; @@ -190,12 +200,12 @@ main(int argc, char **argv) } } - int flags = fcntl(STDIN_FILENO, F_GETFL); + int flags = fcntl(ctx.in_fd, 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) { + if (fcntl(ctx.in_fd, F_SETFL, flags | O_NONBLOCK) == -1) { print_error(&ctx, "failed to set file status flags"); return 1; } @@ -379,7 +389,7 @@ main(int argc, char **argv) } static union ctlseqs_value result[4096]; struct ctlseqs_reader_options reader_options = { - .fd = STDIN_FILENO, + .fd = ctx.in_fd, .maxlen = ctx.limit, .result = result, .flags = ctx.verbose ? CTLSEQS_READER_SAVE_MATCHED_SEQS : 0, @@ -390,14 +400,14 @@ main(int argc, char **argv) } struct termios old_termios; - if (tcgetattr(STDIN_FILENO, &old_termios) != 0) { + if (tcgetattr(ctx.in_fd, &old_termios) != 0) { ctx.not_tty = true; } else { struct termios new_termios = old_termios; new_termios.c_cc[VMIN] = 0; new_termios.c_cc[VTIME] = 0; new_termios.c_lflag &= ~(ICANON | ISIG | ECHO); - if (tcsetattr(STDIN_FILENO, TCSANOW, &new_termios) != 0) { + if (tcsetattr(ctx.in_fd, TCSANOW, &new_termios) != 0) { print_error(&ctx, "failed to set terminal attributes"); return 1; } @@ -412,25 +422,25 @@ main(int argc, char **argv) status = 1; goto terminate; case CTLSEQS_TIMEOUT: - printf("TIMEOUT\n"); + fprintf(ctx.out_file, "TIMEOUT\n"); status = 1; goto terminate; case CTLSEQS_INTR: - printf("INTR\n"); + fprintf(ctx.out_file, "INTR\n"); break; case CTLSEQS_EOF: - printf("EOF\n"); + fprintf(ctx.out_file, "EOF\n"); goto terminate; case CTLSEQS_PARTIAL: if (ctx.verbose) { - print_generic_seq("PARTIAL", result, true); + print_generic_seq(&ctx, "PARTIAL", result, true); } break; case CTLSEQS_NOMATCH: - print_generic_seq("NOMATCH", result, true); + print_generic_seq(&ctx, "NOMATCH", result, true); break; case CTLSEQS_NOMEM: - print_generic_seq("NOMEM", result, true); + print_generic_seq(&ctx, "NOMEM", result, true); if (ctx.purge_long_seqs) { ctlseqs_purge(reader, result[0].len); break; @@ -439,13 +449,13 @@ main(int argc, char **argv) goto terminate; } case CTLSEQS_NOSEQ: - print_generic_seq("NOSEQ", result, true); + print_generic_seq(&ctx, "NOSEQ", result, true); if (!ctx.not_tty && result[1].str[0] == 0x04) { goto terminate; } break; default: - print_matching_seq(&seqs[retval], result, ctx.verbose); + print_matching_seq(&ctx, &seqs[retval], result, ctx.verbose); break; } } @@ -454,7 +464,7 @@ main(int argc, char **argv) ctlseqs_matcher_free(matcher); ctlseqs_reader_free(reader); if (!ctx.not_tty) { - tcsetattr(STDIN_FILENO, TCSANOW, &old_termios); + tcsetattr(ctx.in_fd, TCSANOW, &old_termios); } return status; }