Refactor code.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
CismonX 2021-10-30 22:43:54 +08:00
parent b2c5b019cc
commit e45395bbdf
Signed by: cismonx
GPG Key ID: 3094873E29A482FB
15 changed files with 797 additions and 475 deletions

3
.gitignore vendored
View File

@ -25,8 +25,10 @@ autoscan.log
compile compile
config.* config.*
configure configure
configure~
configure.scan configure.scan
install-sh install-sh
m4/
Makefile Makefile
Makefile.in Makefile.in
missing missing
@ -37,4 +39,5 @@ stamp-h1
# build # build
*.o *.o
build/
ctlseqs-*.tar.gz ctlseqs-*.tar.gz

View File

@ -1,38 +1,36 @@
<!-- <!--
Copyright (C) 2020,2021 CismonX <admin@cismon.net> Copyright (C) 2020,2021 CismonX <admin@cismon.net>
Copying and distribution of this file, with or without modification, are Copying and distribution of this file, with or without modification, are
permitted in any medium without royalty, provided the copyright notice and permitted in any medium without royalty, provided the copyright notice and
this notice are preserved. This file is offered as-is, without any warranty. this notice are preserved. This file is offered as-is, without any warranty.
--> -->
# Installing ctlseqs Installing ctlseqs
==================
## Copy Code to Your Project Copy Code to Your Project
-------------------------
The code of ctlseqs is simple, and can be used out-of-the-box. Just copy The code of ctlseqs is simple, and can be used out-of-the-box. Just copy
[ctlseqs.h](include/ctlseqs.h) and [ctlseqs.c](src/ctlseqs.c) to your project ctlseqs.h and ctlseqs.c to your project and build it with other code.
and build it alongside with other code.
Requires an ISO C99 and POSIX.1-2001 compliant C implementation. Requires an ISO C99 and POSIX.1-2001 compliant C implementation.
## Build and Install From Source Build and Install From Source
-----------------------------
Alternatively, ctlseqs can be built into a shared/static library with Alternatively, ctlseqs can be built into a shared/static library with
GNU Autotools (Autoconf, Automake, Libtool and Autoconf Archive). GNU Autotools (Autoconf, Automake, Libtool and Autoconf Archive).
```shell autoreconf --install
autoreconf --install ./configure
./configure make
make
```
Optionally, you can run tests (requires DejaGnu) and install the library. Optionally, you can run tests (requires DejaGnu) and install the library.
```shell make check
make check make install
make install
```
Unix man pages are not automatically installed with `make install`, and should Unix man pages are not automatically installed with `make install`, and
be installed manually with `make install-man`. should be installed manually with `make install-man`.

View File

@ -6,4 +6,6 @@
# this notice are preserved. This file is offered as-is, without any warranty. # this notice are preserved. This file is offered as-is, without any warranty.
# #
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = include src doc man tests examples SUBDIRS = include src doc man tests examples

View File

@ -1,25 +1,28 @@
<!-- <!--
Copyright (C) 2020,2021 CismonX <admin@cismon.net> Copyright (C) 2020,2021 CismonX <admin@cismon.net>
Copying and distribution of this file, with or without modification, are Copying and distribution of this file, with or without modification, are
permitted in any medium without royalty, provided the copyright notice and permitted in any medium without royalty, provided the copyright notice and
this notice are preserved. This file is offered as-is, without any warranty. this notice are preserved. This file is offered as-is, without any warranty.
--> -->
# ctlseqs ctlseqs
=======
[![Build Status](https://shields.io/drone/build/CismonX/ctlseqs?server=https%3A%2F%2Fdrone.cismon.net)](https://drone.cismon.net/CismonX/ctlseqs) The ctlseqs library provides C API for handling ECMA-35/ECMA-48 compatible
[![License](https://img.shields.io/badge/license-GPL--3.0--or--later-blue.svg)](COPYING) control functions, which is commonly used for communication between terminal
[![Savannah](https://img.shields.io/badge/hosted_on-Savannah-pink)](https://sv.gnu.org/p/ctlseqs) emulators and text-based programs.
## About ctlseqs is free software, licensed under the terms of the GNU General Public
License, either version 3, or any later version of the license. See COPYING
for details.
The ctlseqs library provides C API for handling ECMA-35/ECMA-48 compatible control functions, See INSTALL.md for instructions on how to build and install ctlseqs.
which is commonly used for communication between terminal emulators and text-based programs.
## Getting Started Visit the [project homepage] for documentation, mailing lists, releases,
and everything else about ctlseqs.
For installation instructions, see [INSTALL.md](INSTALL.md).
Documentation of the library can be read [online](https://nongnu.org/ctlseqs/manual), or read with `info ctlseqs` after installation. <!-- Reference Links -->
Unix man pages are also available.
[project homepage]: https://savannah.nongnu.org/projects/ctlseqs

View File

@ -1,24 +1,25 @@
dnl dnl
dnl Copyright (C) 2020,2021 CismonX <admin@cismon.net> dnl Copyright (C) 2020,2021 CismonX <admin@cismon.net>
dnl dnl
dnl Copying and distribution of this file, with or without modification, are dnl Copying and distribution of this file, with or without modification,
dnl permitted in any medium without royalty, provided the copyright notice and dnl are permitted in any medium without royalty,
dnl this notice are preserved. This file is offered as-is, without any warranty. dnl provided the copyright notice and this notice are preserved.
dnl This file is offered as-is, without any warranty.
dnl dnl
AC_PREREQ([2.60]) AC_PREREQ([2.69])
AC_INIT([ctlseqs], [0.1.0], [bug-report@cismon.net]) AC_INIT([ctlseqs], [0.1.0], [bug-report@cismon.net])
AC_CONFIG_SRCDIR([src/ctlseqs.c]) AC_CONFIG_SRCDIR([src/ctlseqs.c])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([foreign info-in-builddir])
AM_EXTRA_RECURSIVE_TARGETS([install-man uninstall-man]) AM_EXTRA_RECURSIVE_TARGETS([install-man uninstall-man])
LT_PREREQ([2.4.0]) LT_PREREQ([2.4.2])
LT_INIT LT_INIT
# Checks for programs. # Checks for programs.
AC_PROG_CC_C99
AC_PROG_CXX
AC_PROG_AWK AC_PROG_AWK
AC_PROG_CC
AC_PROG_CPP AC_PROG_CPP
AC_PROG_INSTALL AC_PROG_INSTALL
AC_PROG_LN_S AC_PROG_LN_S
@ -28,12 +29,14 @@ AC_PROG_MAKE_SET
m4_ifdef([AX_GCC_BUILTIN], [ m4_ifdef([AX_GCC_BUILTIN], [
AX_GCC_BUILTIN(__builtin_expect) AX_GCC_BUILTIN(__builtin_expect)
AX_GCC_BUILTIN(__builtin_unreachable) AX_GCC_BUILTIN(__builtin_unreachable)
])
m4_ifdef([AX_GCC_FUNC_ATTRIBUTE], [
AX_GCC_FUNC_ATTRIBUTE(cold) AX_GCC_FUNC_ATTRIBUTE(cold)
AX_GCC_FUNC_ATTRIBUTE(hot) AX_GCC_FUNC_ATTRIBUTE(hot)
]) ])
# Checks for header files. # Checks for header files.
AC_CHECK_HEADERS([fcntl.h limits.h stddef.h stdlib.h string.h sys/ioctl.h termios.h unistd.h]) AC_CHECK_HEADERS([fcntl.h sys/ioctl.h termios.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics. # Checks for typedefs, structures, and compiler characteristics.
AC_CHECK_HEADER_STDBOOL AC_CHECK_HEADER_STDBOOL
@ -42,13 +45,23 @@ AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T AC_TYPE_SSIZE_T
# Checks for library functions. # Checks for library functions.
AC_CHECK_FUNCS([memset strtoul]) AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([strtol strtoul tcgetattr tcsetattr ioctl])
# Specify libtool library version. # Specify libtool library version.
m4_define([CTLSEQS_LT_CURRENT], [0]) m4_define([CTLSEQS_LT_CUR], [0])
m4_define([CTLSEQS_LT_REVISION], [0]) m4_define([CTLSEQS_LT_REV], [0])
m4_define([CTLSEQS_LT_AGE], [0]) m4_define([CTLSEQS_LT_AGE], [0])
AC_SUBST([CTLSEQS_LT_VERSION], [CTLSEQS_LT_CURRENT:CTLSEQS_LT_REVISION:CTLSEQS_LT_AGE]) AC_SUBST([CTLSEQS_LT_VERSION], [CTLSEQS_LT_CUR:CTLSEQS_LT_REV:CTLSEQS_LT_AGE])
AC_CONFIG_FILES([Makefile include/Makefile doc/Makefile man/Makefile src/Makefile tests/Makefile examples/Makefile]) AC_CONFIG_FILES([
Makefile
include/Makefile
doc/Makefile
man/Makefile
src/Makefile
tests/Makefile
examples/Makefile
])
AC_OUTPUT AC_OUTPUT

View File

@ -6,8 +6,7 @@
# this notice are preserved. This file is offered as-is, without any warranty. # this notice are preserved. This file is offered as-is, without any warranty.
# #
AM_CPPFLAGS = -I$(top_srcdir)/include noinst_PROGRAMS = sixdraw
sixdraw_CPPFLAGS = -I$(top_srcdir)/include
noinst_PROGRAMS = sixdraw sixdraw_SOURCES = sixdraw.c
sixdraw_SOURCES = sixdraw.c sixdraw_LDADD = $(top_builddir)/src/libctlseqs.la
sixdraw_LDADD = $(top_builddir)/src/libctlseqs.la

View File

@ -1,5 +1,5 @@
/** /**
* sixdraw.c - draw lines on your terminal * sixdraw.c - draw on your terminal
* *
* Requires sixel graphics and 1016 mouse mode to run on your terminal. * Requires sixel graphics and 1016 mouse mode to run on your terminal.
* These features are not widely supported. To save yourself from trouble, * These features are not widely supported. To save yourself from trouble,
@ -48,9 +48,8 @@
# define DEFAULT_TIMEOUT_MILLIS 500 # define DEFAULT_TIMEOUT_MILLIS 500
#endif // !DEFAULT_TIMEOUT_MILLIS #endif // !DEFAULT_TIMEOUT_MILLIS
#define DECRQM_UNREC 0 #define DECRQM_SET 1
#define DECRQM_SET 1 #define DECRQM_RST 2
#define DECRQM_RST 2
#define DECTCEM 25 #define DECTCEM 25
#define DECSDM 80 #define DECSDM 80
@ -126,7 +125,11 @@ terminate(struct sixdraw_ctx *ctx)
// Restore original sixel mode. // Restore original sixel mode.
if (ctx->sixel_scroll) { if (ctx->sixel_scroll) {
fprintf(ctx->out_file, ctx->legacy_xterm ? CTLSEQS_DECRST("%d") : CTLSEQS_DECSET("%d"), DECSDM); fprintf(
ctx->out_file,
ctx->legacy_xterm ? CTLSEQS_DECRST("%d") : CTLSEQS_DECSET("%d"),
DECSDM
);
} }
// Restore original terminal modes. // Restore original terminal modes.
@ -168,7 +171,9 @@ decrqm(struct sixdraw_ctx *ctx, unsigned mode, char const *name)
print_error(ctx, "failed to get %s status", name); print_error(ctx, "failed to get %s status", name);
return false; return false;
} }
if (result[0].num != mode || result[1].num == DECRQM_UNREC) { if ( result[0].num != mode
|| (result[1].num != DECRQM_SET && result[1].num != DECRQM_RST)
) {
print_error(ctx, "%s status not recognizable", name); print_error(ctx, "%s status not recognizable", name);
return false; return false;
} }
@ -250,7 +255,8 @@ init(struct sixdraw_ctx *ctx, int argc, char **argv)
break; break;
case '?': case '?':
default: default:
fprintf(ctx->out_file, "%s\n", "Usage: sixdraw [-t timeout] [-c line-color]"); fprintf(ctx->out_file, "%s\n",
"Usage: sixdraw [-t timeout] [-c line-color]");
return false; return false;
} }
} }
@ -264,7 +270,8 @@ init(struct sixdraw_ctx *ctx, int argc, char **argv)
char const *patterns[] = { char const *patterns[] = {
CTLSEQS_RESP_PRIMARY_DA(CTLSEQS_PH_NUMS), CTLSEQS_RESP_PRIMARY_DA(CTLSEQS_PH_NUMS),
CTLSEQS_RESP_DECRQM(CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), CTLSEQS_RESP_DECRQM(CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
CTLSEQS_RESP_SGR_MOUSE(CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, "M"), CTLSEQS_RESP_SGR_MOUSE(
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, "M"),
CTLSEQS_RESP_XTVERSION(CTLSEQS_PH_STR), CTLSEQS_RESP_XTVERSION(CTLSEQS_PH_STR),
}; };
struct ctlseqs_matcher_options matcher_options = { struct ctlseqs_matcher_options matcher_options = {
@ -339,7 +346,7 @@ prepare(struct sixdraw_ctx *ctx)
return false; return false;
} }
// Check terminal support for sixel graphics and DEC locator. // Check terminal support for sixel graphics.
fprintf(ctx->out_file, CTLSEQS_PRIMARY_DA()); fprintf(ctx->out_file, CTLSEQS_PRIMARY_DA());
ssize_t retval; ssize_t retval;
do { do {
@ -384,9 +391,14 @@ prepare(struct sixdraw_ctx *ctx)
} }
// Before patch #369, XTerm implemented DECSDM incorrectly. // Before patch #369, XTerm implemented DECSDM incorrectly.
// See https://invisible-island.net/xterm/xterm.log.html#xterm_369 // See https://invisible-island.net/xterm/xterm.log.html#xterm_369
ctx->sixel_scroll = result[1].num == (ctx->legacy_xterm ? DECRQM_RST : DECRQM_SET); ctx->sixel_scroll
= result[1].num == (ctx->legacy_xterm ? DECRQM_RST : DECRQM_SET);
if (ctx->sixel_scroll) { if (ctx->sixel_scroll) {
fprintf(ctx->out_file, ctx->legacy_xterm ? CTLSEQS_DECSET("%d") : CTLSEQS_DECRST("%d"), DECSDM); fprintf(
ctx->out_file,
ctx->legacy_xterm ? CTLSEQS_DECSET("%d") : CTLSEQS_DECRST("%d"),
DECSDM
);
} }
// Enable button event tracking mode. // Enable button event tracking mode.
@ -418,9 +430,14 @@ prepare(struct sixdraw_ctx *ctx)
// Build the immutable part of sixel sequence. // Build the immutable part of sixel sequence.
ctx->sixel_init_size = sizeof(SIXEL_SEQ_HEAD) - 1; ctx->sixel_init_size = sizeof(SIXEL_SEQ_HEAD) - 1;
ctx->sixel_init_size += sprintf(sixel_seq + ctx->sixel_init_size, "%u;%u#0;2;%d;%d;%d#0", ctx->sixel_init_size += sprintf(
ctx->ch_width, ctx->ch_height, ((ctx->line_color >> 16) & 0xFF) * 100 / 0xFF, sixel_seq + ctx->sixel_init_size,
((ctx->line_color >> 8) & 0xFF) * 100 / 0xFF, ((ctx->line_color >> 0) & 0xFF) * 100 / 0xFF); "%u;%u#0;2;%d;%d;%d#0",
ctx->ch_width, ctx->ch_height,
((ctx->line_color >> 16) & 0xFF) * 100 / 0xFF,
((ctx->line_color >> 8) & 0xFF) * 100 / 0xFF,
((ctx->line_color >> 0) & 0xFF) * 100 / 0xFF
);
return true; return true;
} }
@ -428,27 +445,34 @@ prepare(struct sixdraw_ctx *ctx)
static bool static bool
draw(struct sixdraw_ctx *ctx) draw(struct sixdraw_ctx *ctx)
{ {
fprintf(ctx->out_file, CTLSEQS_CUP("%d", "1") "Canvas size: %ux%u. Line color: #%06X.", fprintf(
ctx->rows - 1, ctx->canvas_width, ctx->canvas_height, ctx->line_color); ctx->out_file,
fprintf(ctx->out_file, CTLSEQS_CUP("%d", "1") "Usage: Draw with mouse. Press Ctrl+C to exit.", ctx->rows); CTLSEQS_CUP("%d", "1") "Canvas size: %ux%u. Line color: #%06X.",
ctx->rows - 1, ctx->canvas_width, ctx->canvas_height, ctx->line_color
);
fprintf(
ctx->out_file,
CTLSEQS_CUP("%d", "1") "Usage: Draw with mouse. Press Ctrl+C to exit.",
ctx->rows
);
union ctlseqs_value *result = ctx->result; union ctlseqs_value *result = ctx->result;
while (true) { while (true) {
switch (ctlseqs_read(ctx->reader, ctx->matcher, -1)) { switch (ctlseqs_read(ctx->reader, ctx->matcher, -1)) {
case 2: // CTLSEQS_RESP_SGR_MOUSE case 2: // CTLSEQS_RESP_SGR_MOUSE
// Pixel coordinates start at 1 instead of 0. // Pixel coordinates start at 1 instead of 0.
print_sixel_dot(ctx, result[1].num - 1, result[2].num - 1); print_sixel_dot(ctx, result[1].num - 1, result[2].num - 1);
break; break;
case CTLSEQS_NOSEQ: case CTLSEQS_NOSEQ:
// Press Ctrl+C to exit. // Press Ctrl+C to exit.
if (result[1].str[0] == 0x03) { if (result[1].str[0] == 0x03) {
return true; return true;
} }
break; break;
case CTLSEQS_ERROR: case CTLSEQS_ERROR:
case CTLSEQS_NOMEM: case CTLSEQS_NOMEM:
case CTLSEQS_EOF: case CTLSEQS_EOF:
return false; return false;
} }
} }
} }

View File

@ -1,8 +1,8 @@
/** /**
* ctlseqs.h - helper library for control sequences * ctlseqs.h - helper library for control sequences
* *
* Copyright (C) 2020,2021 CismonX <admin@cismon.net> * Copyright (C) 2020,2021 CismonX <admin@cismon.net>
* *
* This file is part of the ctlseqs library. * This file is part of the ctlseqs library.
* *
* ctlseqs is free software: you can redistribute it and/or modify * ctlseqs is free software: you can redistribute it and/or modify
@ -30,89 +30,166 @@
/* C0 Control Functions */ /* C0 Control Functions */
#define CTLSEQS_NUL "\x00" // Null // Null
#define CTLSEQS_SOH "\x01" // Start of Heading #define CTLSEQS_NUL "\x00"
#define CTLSEQS_STX "\x02" // Start of Text // Start of Heading
#define CTLSEQS_ETX "\x03" // End of Text #define CTLSEQS_SOH "\x01"
#define CTLSEQS_EOT "\x04" // End of Transmission // Start of Text
#define CTLSEQS_ENQ "\x05" // Enquiry #define CTLSEQS_STX "\x02"
#define CTLSEQS_ACK "\x06" // Acknowledge // End of Text
#define CTLSEQS_BEL "\x07" // Bell #define CTLSEQS_ETX "\x03"
#define CTLSEQS_BS "\x08" // Backspace // End of Transmission
#define CTLSEQS_HT "\x09" // Horizontal Tab #define CTLSEQS_EOT "\x04"
#define CTLSEQS_LF "\x0a" // Line Feed // Enquiry
#define CTLSEQS_VT "\x0b" // Vertical Tab #define CTLSEQS_ENQ "\x05"
#define CTLSEQS_FF "\x0c" // Form Feed or New Page // Acknowledge
#define CTLSEQS_CR "\x0d" // Carriage Return #define CTLSEQS_ACK "\x06"
#define CTLSEQS_SO "\x0e" // Shift Out // Bell
#define CTLSEQS_SI "\x0f" // Shift In #define CTLSEQS_BEL "\x07"
#define CTLSEQS_DLE "\x10" // Data Link Escape // Backspace
#define CTLSEQS_DC1 "\x11" // Device Control 1 #define CTLSEQS_BS "\x08"
#define CTLSEQS_DC2 "\x12" // Device Control 2 // Horizontal Tab
#define CTLSEQS_DC3 "\x13" // Device Control 3 #define CTLSEQS_HT "\x09"
#define CTLSEQS_DC4 "\x14" // Device Control 4 // Line Feed
#define CTLSEQS_NAK "\x15" // Negative Acknowledgement #define CTLSEQS_LF "\x0a"
#define CTLSEQS_SYN "\x16" // Synchronous Idle // Vertical Tab
#define CTLSEQS_ETB "\x17" // End of Transmission Block #define CTLSEQS_VT "\x0b"
#define CTLSEQS_CAN "\x18" // Cancel // Form Feed or New Page
#define CTLSEQS_EM "\x19" // End of Medium #define CTLSEQS_FF "\x0c"
#define CTLSEQS_SUB "\x1a" // Substitute // Carriage Return
#define CTLSEQS_ESC "\x1b" // Escape #define CTLSEQS_CR "\x0d"
#define CTLSEQS_FS "\x1c" // File Separator // Shift Out
#define CTLSEQS_GS "\x1d" // Group Separator #define CTLSEQS_SO "\x0e"
#define CTLSEQS_RS "\x1e" // Record Separator // Shift In
#define CTLSEQS_US "\x1f" // Unit Separator #define CTLSEQS_SI "\x0f"
#define CTLSEQS_SP "\x20" // Space // Data Link Escape
#define CTLSEQS_DEL "\x7f" // Delete #define CTLSEQS_DLE "\x10"
// Device Control 1
#define CTLSEQS_DC1 "\x11"
// Device Control 2
#define CTLSEQS_DC2 "\x12"
// Device Control 3
#define CTLSEQS_DC3 "\x13"
// Device Control 4
#define CTLSEQS_DC4 "\x14"
// Negative Acknowledgement
#define CTLSEQS_NAK "\x15"
// Synchronous Idle
#define CTLSEQS_SYN "\x16"
// End of Transmission Block
#define CTLSEQS_ETB "\x17"
// Cancel
#define CTLSEQS_CAN "\x18"
// End of Medium
#define CTLSEQS_EM "\x19"
// Substitute
#define CTLSEQS_SUB "\x1a"
// Escape
#define CTLSEQS_ESC "\x1b"
// File Separator
#define CTLSEQS_FS "\x1c"
// Group Separator
#define CTLSEQS_GS "\x1d"
// Record Separator
#define CTLSEQS_RS "\x1e"
// Unit Separator
#define CTLSEQS_US "\x1f"
// Space
#define CTLSEQS_SP "\x20"
// Delete
#define CTLSEQS_DEL "\x7f"
/* C1 Control Functions (2-character 7-bit representation) */ /* C1 Control Functions (2-character 7-bit representation) */
#define CTLSEQS_BPH CTLSEQS_ESC "B" // Break Permitted Here // Break Permitted Here
#define CTLSEQS_NBH CTLSEQS_ESC "C" // No Break Here #define CTLSEQS_BPH CTLSEQS_ESC "B"
#define CTLSEQS_IND CTLSEQS_ESC "D" // Index // No Break Here
#define CTLSEQS_NEL CTLSEQS_ESC "E" // Next Line #define CTLSEQS_NBH CTLSEQS_ESC "C"
#define CTLSEQS_SSA CTLSEQS_ESC "F" // Start of Selected Area // Index
#define CTLSEQS_ESA CTLSEQS_ESC "G" // End of Selected Area #define CTLSEQS_IND CTLSEQS_ESC "D"
#define CTLSEQS_HTS CTLSEQS_ESC "H" // Character Tabulation Set // Next Line
#define CTLSEQS_VTS CTLSEQS_ESC "J" // Line Tabulation Set #define CTLSEQS_NEL CTLSEQS_ESC "E"
#define CTLSEQS_PLD CTLSEQS_ESC "K" // Partial Line Forward // Start of Selected Area
#define CTLSEQS_PLU CTLSEQS_ESC "L" // Partial Line Backward #define CTLSEQS_SSA CTLSEQS_ESC "F"
#define CTLSEQS_RI CTLSEQS_ESC "M" // Reverse Index // End of Selected Area
#define CTLSEQS_SS2 CTLSEQS_ESC "N" // Single Shift 2 #define CTLSEQS_ESA CTLSEQS_ESC "G"
#define CTLSEQS_SS3 CTLSEQS_ESC "O" // Single Shift 3 // Character Tabulation Set
#define CTLSEQS_DCS CTLSEQS_ESC "P" // Device Control String #define CTLSEQS_HTS CTLSEQS_ESC "H"
#define CYLSEQS_PU1 CTLSEQS_ESC "Q" // Private Use 1 // Line Tabulation Set
#define CYLSEQS_PU2 CTLSEQS_ESC "R" // Private Use 2 #define CTLSEQS_VTS CTLSEQS_ESC "J"
#define CYLSEQS_STS CTLSEQS_ESC "S" // Set Transmit State // Partial Line Forward
#define CTLSEQS_CCH CTLSEQS_ESC "T" // Cancel Character #define CTLSEQS_PLD CTLSEQS_ESC "K"
#define CTLSEQS_MW CTLSEQS_ESC "U" // Message Waiting // Partial Line Backward
#define CTLSEQS_SPA CTLSEQS_ESC "V" // Start of Guarded Area #define CTLSEQS_PLU CTLSEQS_ESC "L"
#define CTLSEQS_EPA CTLSEQS_ESC "W" // End of Guarded Area // Reverse Index
#define CTLSEQS_SOS CTLSEQS_ESC "X" // Start of String #define CTLSEQS_RI CTLSEQS_ESC "M"
#define CTLSEQS_HTJ CTLSEQS_ESC "Y" // Character Tabulation with Justification // Single Shift 2
#define CYLSEQS_SCI CTLSEQS_ESC "Z" // Single Character Introducer #define CTLSEQS_SS2 CTLSEQS_ESC "N"
#define CTLSEQS_CSI CTLSEQS_ESC "[" // Control Sequence Introducer // Single Shift 3
#define CTLSEQS_ST CTLSEQS_ESC "\\" // String Terminator #define CTLSEQS_SS3 CTLSEQS_ESC "O"
#define CTLSEQS_OSC CTLSEQS_ESC "]" // Operating System Command // Device Control String
#define CTLSEQS_PM CTLSEQS_ESC "^" // Privacy Message #define CTLSEQS_DCS CTLSEQS_ESC "P"
#define CTLSEQS_APC CTLSEQS_ESC "_" // Application Program Command // Private Use 1
#define CTLSEQS_PU1 CTLSEQS_ESC "Q"
// Private Use 2
#define CTLSEQS_PU2 CTLSEQS_ESC "R"
// Set Transmit State
#define CTLSEQS_STS CTLSEQS_ESC "S"
// Cancel Character
#define CTLSEQS_CCH CTLSEQS_ESC "T"
// Message Waiting
#define CTLSEQS_MW CTLSEQS_ESC "U"
// Start of Guarded Area
#define CTLSEQS_SPA CTLSEQS_ESC "V"
// End of Guarded Area
#define CTLSEQS_EPA CTLSEQS_ESC "W"
// Start of String
#define CTLSEQS_SOS CTLSEQS_ESC "X"
// Character Tabulation with Justification
#define CTLSEQS_HTJ CTLSEQS_ESC "Y"
// Single Character Introducer
#define CTLSEQS_SCI CTLSEQS_ESC "Z"
// Control Sequence Introducer
#define CTLSEQS_CSI CTLSEQS_ESC "["
// String Terminator
#define CTLSEQS_ST CTLSEQS_ESC "\\"
// Operating System Command
#define CTLSEQS_OSC CTLSEQS_ESC "]"
// Privacy Message
#define CTLSEQS_PM CTLSEQS_ESC "^"
// Application Program Command
#define CTLSEQS_APC CTLSEQS_ESC "_"
/* Controls beginning with ESC */ /* Controls beginning with ESC */
#define CTLSEQS_S7C1T() CTLSEQS_ESC " F" // 7-bit controls // 7-bit controls
#define CTLSEQS_S8C1T() CTLSEQS_ESC " G" // 8-bit controls #define CTLSEQS_S7C1T() CTLSEQS_ESC " F"
#define CTLSEQS_DECDHL_TOP() CTLSEQS_ESC "#3" // DEC double-height line, top half // 8-bit controls
#define CTLSEQS_DECDHL_BOTOM() CTLSEQS_ESC "#4" // DEC double-height line, bottom half #define CTLSEQS_S8C1T() CTLSEQS_ESC " G"
#define CTLSEQS_DECSWL() CTLSEQS_ESC "#5" // DEC single-width line // DEC double-height line, top half
#define CTLSEQS_DECDWL() CTLSEQS_ESC "#6" // DEC double-width line #define CTLSEQS_DECDHL_TOP() CTLSEQS_ESC "#3"
#define CTLSEQS_DECALN() CTLSEQS_ESC "#8" // DEC Screen Alignment Test // DEC double-height line, bottom half
#define CTLSEQS_DECBI() CTLSEQS_ESC "6" // Back Index #define CTLSEQS_DECDHL_BOTOM() CTLSEQS_ESC "#4"
#define CTLSEQS_DECSC() CTLSEQS_ESC "7" // Save Cursor // DEC single-width line
#define CTLSEQS_DECRC() CTLSEQS_ESC "8" // Restore Cursor #define CTLSEQS_DECSWL() CTLSEQS_ESC "#5"
#define CTLSEQS_DECFI() CTLSEQS_ESC "9" // Forward Index // DEC double-width line
#define CTLSEQS_DECKPAM() CTLSEQS_ESC "=" // Application Keypad #define CTLSEQS_DECDWL() CTLSEQS_ESC "#6"
#define CTLSEQS_DECKPNM() CTLSEQS_ESC ">" // Normal Keypad // DEC Screen Alignment Test
#define CTLSEQS_RIS() CTLSEQS_ESC "c" // Full Reset #define CTLSEQS_DECALN() CTLSEQS_ESC "#8"
// Back Index
#define CTLSEQS_DECBI() CTLSEQS_ESC "6"
// Save Cursor
#define CTLSEQS_DECSC() CTLSEQS_ESC "7"
// Restore Cursor
#define CTLSEQS_DECRC() CTLSEQS_ESC "8"
// Forward Index
#define CTLSEQS_DECFI() CTLSEQS_ESC "9"
// Application Keypad
#define CTLSEQS_DECKPAM() CTLSEQS_ESC "="
// Normal Keypad
#define CTLSEQS_DECKPNM() CTLSEQS_ESC ">"
// Full Reset
#define CTLSEQS_RIS() CTLSEQS_ESC "c"
/* Device-Control functions */ /* Device-Control functions */
@ -172,11 +249,13 @@
// Scroll up $n Lines // Scroll up $n Lines
#define CTLSEQS_SU(n) CTLSEQS_CSI n "S" #define CTLSEQS_SU(n) CTLSEQS_CSI n "S"
// Set or request graphics attribute // Set or request graphics attribute
#define CTLSEQS_XTSMGRAPHICS(i1, i2, is) CTLSEQS_CSI "?" i1 ";" i2 ";" is "S" #define CTLSEQS_XTSMGRAPHICS(i1, i2, is) \
CTLSEQS_CSI "?" i1 ";" i2 ";" is "S"
// Scroll Down $n Line(s) // Scroll Down $n Line(s)
#define CTLSEQS_SD(n) CTLSEQS_CSI n "T" #define CTLSEQS_SD(n) CTLSEQS_CSI n "T"
// Initiate highlight mouse tracking // Initiate highlight mouse tracking
#define CTLSEQS_XTHIMOUSE(f, x, y, fr, lr) CTLSEQS_CSI f ";" x ";" y ";" fr ";" lr "T" #define CTLSEQS_XTHIMOUSE(f, x, y, fr, lr) \
CTLSEQS_CSI f ";" x ";" y ";" fr ";" lr "T"
// Reset title mode features to default value // Reset title mode features to default value
#define CTLSEQS_XTRMTITLE(is) CTLSEQS_CSI ">" is "T" #define CTLSEQS_XTRMTITLE(is) CTLSEQS_CSI ">" is "T"
// Erase $n Character(s) // Erase $n Character(s)
@ -211,7 +290,7 @@
#define CTLSEQS_MC(i) CTLSEQS_CSI i "i" #define CTLSEQS_MC(i) CTLSEQS_CSI i "i"
// Media Copy, DEC-specific // Media Copy, DEC-specific
#define CTLSEQS_MC_DEC(i) CTLSEQS_CSI "?" i "i" #define CTLSEQS_MC_DEC(i) CTLSEQS_CSI "?" i "i"
// Reset Mode // Reset Mode
#define CTLSEQS_RM(i) CTLSEQS_CSI i "l" #define CTLSEQS_RM(i) CTLSEQS_CSI i "l"
// DEC Private Mode Reset // DEC Private Mode Reset
#define CTLSEQS_DECRST(i) CTLSEQS_CSI "?" i "l" #define CTLSEQS_DECRST(i) CTLSEQS_CSI "?" i "l"
@ -248,7 +327,8 @@
// Restore DEC Private Mode Values // Restore DEC Private Mode Values
#define CTLSEQS_XTRESTORE(is) CTLSEQS_CSI "?" is "r" #define CTLSEQS_XTRESTORE(is) CTLSEQS_CSI "?" is "r"
// Change Attributes in Rectangular Area // Change Attributes in Rectangular Area
#define CTLSEQS_DECCARA(t, l, b, r, i) CTLSEQS_CSI t ";" l ";" b ";" r ";" i "$r" #define CTLSEQS_DECCARA(t, l, b, r, i) \
CTLSEQS_CSI t ";" l ";" b ";" r ";" i "$r"
// Save cursor // Save cursor
#define CTLSEQS_SCOSC() CTLSEQS_CSI "s" #define CTLSEQS_SCOSC() CTLSEQS_CSI "s"
// Set left and right margins // Set left and right margins
@ -262,39 +342,47 @@
// Set warning-bell volume // Set warning-bell volume
#define CTLSEQS_DECSWBV(i) CTLSEQS_CSI i " t" #define CTLSEQS_DECSWBV(i) CTLSEQS_CSI i " t"
// Reverse Attributes in Rectangular Area // Reverse Attributes in Rectangular Area
#define CTLSEQS_DECRARA(t, l, b, r, i) CTLSEQS_CSI t ";" l ";" b ";" r ";" i "$t" #define CTLSEQS_DECRARA(t, l, b, r, i) \
CTLSEQS_CSI t ";" l ";" b ";" r ";" i "$t"
// Restore cursor // Restore cursor
#define CTLSEQS_SCORC() CTLSEQS_CSI "u" #define CTLSEQS_SCORC() CTLSEQS_CSI "u"
// Set margin-bell volume // Set margin-bell volume
#define CTLSEQS_DECSMBV(i) CTLSEQS_CSI i " u" #define CTLSEQS_DECSMBV(i) CTLSEQS_CSI i " u"
// Copy Rectangular Area // Copy Rectangular Area
#define CTLSEQS_DECCRA(t, l, b, r, sp, dt, dl, dp) CTLSEQS_CSI t ";" l ";" b ";" r ";" sp ";" dt ";" dl ";" dp "$v" #define CTLSEQS_DECCRA(t, l, b, r, sp, dt, dl, dp) \
CTLSEQS_CSI t ";" l ";" b ";" r ";" sp ";" dt ";" dl ";" dp "$v"
// Request presentation state report // Request presentation state report
#define CTLSEQS_DECRQPSR(i) CTLSEQS_CSI i "$w" #define CTLSEQS_DECRQPSR(i) CTLSEQS_CSI i "$w"
// Enable Filter Rectangle // Enable Filter Rectangle
#define CTLSEQS_DECEFR(t, l, b, r) CTLSEQS_CSI t ";" l ";" b ";" r "'w" #define CTLSEQS_DECEFR(t, l, b, r) \
CTLSEQS_CSI t ";" l ";" b ";" r "'w"
// Request Terminal Parameters // Request Terminal Parameters
#define CTLSEQS_DECREQTPARM(i) CTLSEQS_CSI i "x" #define CTLSEQS_DECREQTPARM(i) CTLSEQS_CSI i "x"
// Select Attribute Change Extent // Select Attribute Change Extent
#define CTLSEQS_DECSACE(i) CTLSEQS_CSI i "*x" #define CTLSEQS_DECSACE(i) CTLSEQS_CSI i "*x"
// Fill Rectangular Area // Fill Rectangular Area
#define CTLSEQS_DECFRA(c, t, l, b, r) CTLSEQS_CSI c ";" t ";" l ";" b ";" r "$x" #define CTLSEQS_DECFRA(c, t, l, b, r) \
CTLSEQS_CSI c ";" t ";" l ";" b ";" r "$x"
// Select checksum extension // Select checksum extension
#define CTLSEQS_XTCHECKSUM(i) CTLSEQS_CSI i "#y" #define CTLSEQS_XTCHECKSUM(i) CTLSEQS_CSI i "#y"
// Request Checksum of Rectangular Area // Request Checksum of Rectangular Area
#define CTLSEQS_DECRQCRA(id, p, t, l, b, r) CTLSEQS_CSI id ";" p ";" t ";" l ";" b ";" r "*y" #define CTLSEQS_DECRQCRA(id, p, t, l, b, r) \
CTLSEQS_CSI id ";" p ";" t ";" l ";" b ";" r "*y"
// Enable Locator Reporting // Enable Locator Reporting
#define CTLSEQS_DECELR(i1, i2) CTLSEQS_CSI i1 ";" i2 "'z" #define CTLSEQS_DECELR(i1, i2) CTLSEQS_CSI i1 ";" i2 "'z"
// Erase Rectangular Area // Erase Rectangular Area
#define CTLSEQS_DECERA(t, l, b, r) CTLSEQS_CSI t ";" l ";" b ";" r "$z" #define CTLSEQS_DECERA(t, l, b, r) \
CTLSEQS_CSI t ";" l ";" b ";" r "$z"
// Select Locator Events // Select Locator Events
#define CTLSEQS_DECSLE(is) CTLSEQS_CSI is "'{" #define CTLSEQS_DECSLE(is) CTLSEQS_CSI is "'{"
// Push video attributes onto stack // Push video attributes onto stack
#define CTLSEQS_XTPUSHSGR(is) CTLSEQS_CSI is "#{" #define CTLSEQS_XTPUSHSGR(is) CTLSEQS_CSI is "#{"
// Selective Erase Rectangular Area // Selective Erase Rectangular Area
#define CTLSEQS_DECSERA(t, l, b, r) CTLSEQS_CSI t ";" l ";" b ";" r "${" #define CTLSEQS_DECSERA(t, l, b, r) \
CTLSEQS_CSI t ";" l ";" b ";" r "${"
// Report selected graphic rendition // Report selected graphic rendition
#define CTLSEQS_XTREPORTSGR(t, l, b, r) CTLSEQS_CSI t ";" l ";" b ";" r "$|" #define CTLSEQS_XTREPORTSGR(t, l, b, r) \
CTLSEQS_CSI t ";" l ";" b ";" r "$|"
// Select columns per page // Select columns per page
#define CTLSEQS_DECSCPP(i) CTLSEQS_CSI i "$|" #define CTLSEQS_DECSCPP(i) CTLSEQS_CSI i "$|"
// Request Locator Position // Request Locator Position
@ -324,7 +412,8 @@
// Primary DA response message // Primary DA response message
#define CTLSEQS_RESP_PRIMARY_DA(ns) CTLSEQS_CSI "?" ns "c" #define CTLSEQS_RESP_PRIMARY_DA(ns) CTLSEQS_CSI "?" ns "c"
// Secondary DA response message // Secondary DA response message
#define CTLSEQS_RESP_SECONDARY_DA(n1, n2, n3) CTLSEQS_CSI ">" n1 ";" n2 ";" n3 "c" #define CTLSEQS_RESP_SECONDARY_DA(n1, n2, n3) \
CTLSEQS_CSI ">" n1 ";" n2 ";" n3 "c"
// DECLRP response message // DECLRP response message
#define CTLSEQS_RESP_DECXCPR(n1, n2) CTLSEQS_CSI "?" n1 ";" n2 "R" #define CTLSEQS_RESP_DECXCPR(n1, n2) CTLSEQS_CSI "?" n1 ";" n2 "R"
// DSR response message // DSR response message
@ -342,34 +431,58 @@
// DECRQCRA response message // DECRQCRA response message
#define CTLSEQS_RESP_DECRQCRA(n, s) CTLSEQS_DCS n "!~" s CTLSEQS_ST #define CTLSEQS_RESP_DECRQCRA(n, s) CTLSEQS_DCS n "!~" s CTLSEQS_ST
// DECRQLP response message // DECRQLP response message
#define CTLSEQS_RESP_DECRQLP(e, b, row, col, p) CTLSEQS_CSI e ";" b ";" row ";" col ";" p "&w" #define CTLSEQS_RESP_DECRQLP(e, b, row, col, p) \
CTLSEQS_CSI e ";" b ";" row ";" col ";" p "&w"
// Mouse response in SGR mouse mode // Mouse response in SGR mouse mode
#define CTLSEQS_RESP_SGR_MOUSE(n, col, row, c) CTLSEQS_CSI "<" n ";" col ";" row c #define CTLSEQS_RESP_SGR_MOUSE(n, col, row, c) \
CTLSEQS_CSI "<" n ";" col ";" row c
/// PC-Style Function Keys /// PC-Style Function Keys
#define CTLSEQS_KEY_UP() CTLSEQS_CSI "A" // Up arrow key // Up arrow key
#define CTLSEQS_KEY_DOWN() CTLSEQS_CSI "B" // Down arrow key #define CTLSEQS_KEY_UP() CTLSEQS_CSI "A"
#define CTLSEQS_KEY_RIGHT() CTLSEQS_CSI "C" // Right arrow key // Down arrow key
#define CTLSEQS_KEY_LEFT() CTLSEQS_CSI "D" // Left arrow key #define CTLSEQS_KEY_DOWN() CTLSEQS_CSI "B"
#define CTLSEQS_KEY_HOME() CTLSEQS_CSI "H" // Home key // Right arrow key
#define CTLSEQS_KEY_END() CTLSEQS_CSI "F" // End key #define CTLSEQS_KEY_RIGHT() CTLSEQS_CSI "C"
#define CTLSEQS_KEY_INSERT() CTLSEQS_CSI "2~" // Insert key // Left arrow key
#define CTLSEQS_KEY_DELETE() CTLSEQS_CSI "3~" // Delete key #define CTLSEQS_KEY_LEFT() CTLSEQS_CSI "D"
#define CTLSEQS_KEY_PGUP() CTLSEQS_CSI "5~" // Page Up key // Home key
#define CTLSEQS_KEY_PGDN() CTLSEQS_CSI "6~" // Page Down key #define CTLSEQS_KEY_HOME() CTLSEQS_CSI "H"
#define CTLSEQS_KEY_F1() CTLSEQS_SS3 "P" // F1 key // End key
#define CTLSEQS_KEY_F2() CTLSEQS_SS3 "Q" // F2 key #define CTLSEQS_KEY_END() CTLSEQS_CSI "F"
#define CTLSEQS_KEY_F3() CTLSEQS_SS3 "R" // F3 key // Insert key
#define CTLSEQS_KEY_F4() CTLSEQS_SS3 "S" // F4 key #define CTLSEQS_KEY_INSERT() CTLSEQS_CSI "2~"
#define CTLSEQS_KEY_F5() CTLSEQS_CSI "15~" // F5 key // Delete key
#define CTLSEQS_KEY_F6() CTLSEQS_CSI "17~" // F6 key #define CTLSEQS_KEY_DELETE() CTLSEQS_CSI "3~"
#define CTLSEQS_KEY_F7() CTLSEQS_CSI "18~" // F7 key // Page Up key
#define CTLSEQS_KEY_F8() CTLSEQS_CSI "19~" // F8 key #define CTLSEQS_KEY_PGUP() CTLSEQS_CSI "5~"
#define CTLSEQS_KEY_F9() CTLSEQS_CSI "20~" // F9 key // Page Down key
#define CTLSEQS_KEY_F10() CTLSEQS_CSI "21~" // F10 key #define CTLSEQS_KEY_PGDN() CTLSEQS_CSI "6~"
#define CTLSEQS_KEY_F11() CTLSEQS_CSI "23~" // F11 key // F1 key
#define CTLSEQS_KEY_F12() CTLSEQS_CSI "24~" // F12 key #define CTLSEQS_KEY_F1() CTLSEQS_SS3 "P"
// F2 key
#define CTLSEQS_KEY_F2() CTLSEQS_SS3 "Q"
// F3 key
#define CTLSEQS_KEY_F3() CTLSEQS_SS3 "R"
// F4 key
#define CTLSEQS_KEY_F4() CTLSEQS_SS3 "S"
// F5 key
#define CTLSEQS_KEY_F5() CTLSEQS_CSI "15~"
// F6 key
#define CTLSEQS_KEY_F6() CTLSEQS_CSI "17~"
// F7 key
#define CTLSEQS_KEY_F7() CTLSEQS_CSI "18~"
// F8 key
#define CTLSEQS_KEY_F8() CTLSEQS_CSI "19~"
// F9 key
#define CTLSEQS_KEY_F9() CTLSEQS_CSI "20~"
// F10 key
#define CTLSEQS_KEY_F10() CTLSEQS_CSI "21~"
// F11 key
#define CTLSEQS_KEY_F11() CTLSEQS_CSI "23~"
// F12 key
#define CTLSEQS_KEY_F12() CTLSEQS_CSI "24~"
#ifdef __cplusplus #ifdef __cplusplus
# include <cstddef> # include <cstddef>
@ -381,19 +494,29 @@
/* Placeholders */ /* Placeholders */
#define CTLSEQS_PH_NUM "\x0e" // CSI Parameter Bytes, numbers only // CSI Parameter Bytes, numbers only
#define CTLSEQS_PH_NUMS "\x0f" // CSI Parameter Bytes, multiple numbers separated by semicolon #define CTLSEQS_PH_NUM "\x0e"
#define CTLSEQS_PH_STR "\x10" // String, printable characters only // CSI Parameter Bytes, multiple numbers separated by semicolon
#define CTLSEQS_PH_CMDSTR "\x11" // Command String #define CTLSEQS_PH_NUMS "\x0f"
#define CTLSEQS_PH_CSI_PARAM "\x12" // CSI Parameter Bytes // String, printable characters only
#define CTLSEQS_PH_CSI_INTMD "\x13" // CSI Intermediate Bytes #define CTLSEQS_PH_STR "\x10"
#define CTLSEQS_PH_HEXNUM "\x14" // Printable characters representing a hexadecimal number // Command String
#define CTLSEQS_PH_CHRSTR "\x15" // Character String #define CTLSEQS_PH_CMDSTR "\x11"
// CSI Parameter Bytes
#define CTLSEQS_PH_CSI_PARAM "\x12"
// CSI Intermediate Bytes
#define CTLSEQS_PH_CSI_INTMD "\x13"
// Printable characters representing a hexadecimal number
#define CTLSEQS_PH_HEXNUM "\x14"
// Character String
#define CTLSEQS_PH_CHRSTR "\x15"
/* Reader option flags */ /* Reader option flags */
#define CTLSEQS_READER_NO_POLL (1 << 0) // Do not poll() before read() // Do not poll() before read()
#define CTLSEQS_READER_SAVE_MATCHED_SEQS (1 << 1) // Save successfully matched sequence to buffer #define CTLSEQS_READER_NO_POLL (1 << 0)
// Save successfully matched sequence to buffer
#define CTLSEQS_READER_SAVE_MATCHED_SEQS (1 << 1)
/* Function return status codes */ /* Function return status codes */
@ -439,28 +562,50 @@ struct ctlseqs_matcher *
ctlseqs_matcher_init(); ctlseqs_matcher_init();
int int
ctlseqs_matcher_config(struct ctlseqs_matcher *matcher, struct ctlseqs_matcher_options const *options); ctlseqs_matcher_config(
struct ctlseqs_matcher *matcher,
struct ctlseqs_matcher_options const *options
);
void void
ctlseqs_matcher_free(struct ctlseqs_matcher *matcher); ctlseqs_matcher_free(
struct ctlseqs_matcher *matcher
);
ssize_t ssize_t
ctlseqs_match(struct ctlseqs_matcher const *matcher, char const *str, size_t str_len, union ctlseqs_value *result); ctlseqs_match(
struct ctlseqs_matcher const *matcher,
char const *str,
size_t str_len,
union ctlseqs_value *result
);
struct ctlseqs_reader * struct ctlseqs_reader *
ctlseqs_reader_init(); ctlseqs_reader_init();
int int
ctlseqs_reader_config(struct ctlseqs_reader *reader, struct ctlseqs_reader_options const *options); ctlseqs_reader_config(
struct ctlseqs_reader *reader,
struct ctlseqs_reader_options const *options
);
ssize_t ssize_t
ctlseqs_read(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *matcher, int timeout); ctlseqs_read(
struct ctlseqs_reader *reader,
struct ctlseqs_matcher const *matcher,
int timeout
);
void void
ctlseqs_purge(struct ctlseqs_reader *reader, size_t nbytes); ctlseqs_purge(
struct ctlseqs_reader *reader,
size_t nbytes
);
void void
ctlseqs_reader_free(struct ctlseqs_reader *reader); ctlseqs_reader_free(
struct ctlseqs_reader *reader
);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -6,8 +6,7 @@
# this notice are preserved. This file is offered as-is, without any warranty. # this notice are preserved. This file is offered as-is, without any warranty.
# #
AM_CPPFLAGS = -I$(top_srcdir)/include lib_LTLIBRARIES = libctlseqs.la
AM_LDFLAGS = -version-info $(CTLSEQS_LT_VERSION) libctlseqs_la_CPPFLAGS = -I$(top_srcdir)/include
libctlseqs_la_SOURCES = ctlseqs.c
lib_LTLIBRARIES = libctlseqs.la libctlseqs_la_LDFLAGS = -version-info $(CTLSEQS_LT_VERSION)
libctlseqs_la_SOURCES = ctlseqs.c

View File

@ -159,142 +159,196 @@ struct ctlseqs_reader {
}; };
ctlseqs_hot static inline int ctlseqs_hot static inline int
ctlseqs_poll(struct pollfd *pollfd, int timeout) ctlseqs_poll(
{ struct pollfd *pollfd,
int timeout
) {
int nevents = poll(pollfd, 1, timeout); int nevents = poll(pollfd, 1, timeout);
if (nevents == -1) { switch (nevents) {
return errno == EINTR ? CTLSEQS_INTR : CTLSEQS_ERROR; case 0:
}
if (nevents == 0) {
return CTLSEQS_TIMEOUT; return CTLSEQS_TIMEOUT;
case 1:
if (ctlseqs_likely(pollfd->revents & POLLIN)) {
return CTLSEQS_OK;
} else if (pollfd->revents & POLLHUP) {
return CTLSEQS_EOF;
} else {
return CTLSEQS_ERROR;
}
default:
if (errno == EINTR) {
return CTLSEQS_INTR;
} else {
return CTLSEQS_ERROR;
}
} }
if (ctlseqs_likely(pollfd->revents & POLLIN)) {
return CTLSEQS_OK;
}
return pollfd->revents & POLLHUP ? CTLSEQS_EOF : CTLSEQS_ERROR;
} }
ctlseqs_hot static inline int ctlseqs_hot static inline int
ctlseqs_do_read(struct ctlseqs_reader *reader) ctlseqs_do_read(
{ struct ctlseqs_reader *reader
) {
size_t offset = reader->buf_start + reader->last_idx; size_t offset = reader->buf_start + reader->last_idx;
ssize_t nbytes = read(reader->pollfd.fd, reader->rbuf + offset, reader->readlen - offset); ssize_t nbytes = read(
if (ctlseqs_unlikely(nbytes == -1)) { reader->pollfd.fd,
reader->rbuf + offset,
reader->readlen - offset
);
switch (nbytes) {
case -1:
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
return CTLSEQS_TIMEOUT; return CTLSEQS_TIMEOUT;
} else if (errno == EINTR) {
return CTLSEQS_INTR;
} else {
return CTLSEQS_ERROR;
} }
return errno == EINTR ? CTLSEQS_INTR : CTLSEQS_ERROR; case 0:
}
if (ctlseqs_unlikely(nbytes == 0)) {
return CTLSEQS_EOF; return CTLSEQS_EOF;
default:
reader->buf_end += nbytes;
return CTLSEQS_OK;
} }
reader->buf_end += nbytes;
return CTLSEQS_OK;
} }
ctlseqs_hot static enum ctlseqs_state ctlseqs_hot static enum ctlseqs_state
ctlseqs_state_transition(enum ctlseqs_state state, char ch) ctlseqs_state_transition(
{ enum ctlseqs_state state,
char ch
) {
switch (state) { switch (state) {
case ctlseqs_state_none: case ctlseqs_state_none:
return ch == 0x1b ? ctlseqs_state_esc : ctlseqs_state_err; if (ch == 0x1b) {
case ctlseqs_state_esc: return ctlseqs_state_esc;
switch (ch) { } else {
case '[': return ctlseqs_state_err;
return ctlseqs_state_csi; }
case 'N': case ctlseqs_state_esc:
case 'O': switch (ch) {
return ctlseqs_state_ss; case '[':
case 'P': return ctlseqs_state_csi;
case ']': case 'N':
case '_': case 'O':
case '^': return ctlseqs_state_ss;
return ctlseqs_state_cmdstr; case 'P':
case 'X': case ']':
return ctlseqs_state_ctlstr; case '_':
default: case '^':
return (ch >= ' ' && ch <= '~') ? ctlseqs_state_done : ctlseqs_state_err; return ctlseqs_state_cmdstr;
} case 'X':
case ctlseqs_state_csi: return ctlseqs_state_ctlstr;
if (ch >= '0' && ch <= '?') { default:
return state; if (ch >= ' ' && ch <= '~') {
} else if (ch >= ' ' && ch <= '/') { return ctlseqs_state_done;
return ctlseqs_state_csi_intmd; } else {
}
return (ch >= '@' && ch <= '~') ? ctlseqs_state_done : ctlseqs_state_err;
case ctlseqs_state_csi_intmd:
if (ch >= ' ' && ch <= '/') {
return state;
}
return (ch >= '@' && ch <= '~') ? ctlseqs_state_done : ctlseqs_state_err;
case ctlseqs_state_cmdstr:
if (ch == 0x1b) {
return ctlseqs_state_str_end;
} else if (ch < 0x08 || ch > '~' || (ch > 0x0d && ch < ' ')) {
return ctlseqs_state_err; return ctlseqs_state_err;
} }
}
case ctlseqs_state_csi:
if (ch >= '0' && ch <= '?') {
return state; return state;
case ctlseqs_state_ss: } else if (ch >= ' ' && ch <= '/') {
return (ch >= ' ' && ch <= '~') ? ctlseqs_state_done : ctlseqs_state_err; return ctlseqs_state_csi_intmd;
case ctlseqs_state_ctlstr: } else if (ch >= '@' && ch <= '~') {
return ch == 0x1b ? ctlseqs_state_str_end : state; return ctlseqs_state_done;
case ctlseqs_state_str_end: } else {
return ch == '\\' ? ctlseqs_state_done : ctlseqs_state_err; return ctlseqs_state_err;
default: }
ctlseqs_unreachable(); case ctlseqs_state_csi_intmd:
if (ch >= ' ' && ch <= '/') {
return state; return state;
} else if (ch >= '@' && ch <= '~') {
return ctlseqs_state_done;
} else {
return ctlseqs_state_err;
}
case ctlseqs_state_cmdstr:
if (ch == 0x1b) {
return ctlseqs_state_str_end;
} else if (ch < 0x08 || ch > '~' || (ch > 0x0d && ch < ' ')) {
return ctlseqs_state_err;
} else {
return state;
}
case ctlseqs_state_ss:
if (ch >= ' ' && ch <= '~') {
return ctlseqs_state_done;
} else {
return ctlseqs_state_err;
}
case ctlseqs_state_ctlstr:
if (ch == 0x1b) {
return ctlseqs_state_str_end;
} else {
return state;
}
case ctlseqs_state_str_end:
if (ch == '\\') {
return ctlseqs_state_done;
} else {
return ctlseqs_state_err;
}
default:
ctlseqs_unreachable();
return state;
} }
} }
ctlseqs_hot static char const * ctlseqs_hot static char const *
ctlseqs_fetch_value(char const *seq, int type, union ctlseqs_value **buf) ctlseqs_fetch_value(
{ char const *seq,
int type,
union ctlseqs_value **buf
) {
size_t cnt; size_t cnt;
unsigned long num; unsigned long num;
char *endptr = NULL; char *endptr = NULL;
union ctlseqs_value *buf_val = *buf; union ctlseqs_value *buf_val = *buf;
switch (type) { switch (type) {
case ctlseqs_ph_num: case ctlseqs_ph_num:
CTLSEQS_VALUE_NUM(10); CTLSEQS_VALUE_NUM(10);
case ctlseqs_ph_nums: case ctlseqs_ph_nums:
for (cnt = 1; ; ++cnt) { for (cnt = 1; ; ++cnt) {
errno = 0; errno = 0;
num = strtoul(seq, &endptr, 10); num = strtoul(seq, &endptr, 10);
if (errno || seq == endptr) { if (errno || seq == endptr) {
return NULL; return NULL;
}
buf_val[cnt].num = num;
if (endptr[0] != ';') {
break;
}
seq = endptr + 1;
} }
buf_val[0].len = cnt; buf_val[cnt].num = num;
*buf += cnt + 1; if (endptr[0] != ';') {
return endptr; break;
case ctlseqs_ph_str: }
CTLSEQS_VALUE_STR(num < ' ' || num > '~'); seq = endptr + 1;
case ctlseqs_ph_cmdstr: }
CTLSEQS_VALUE_STR(num < 0x08 || num > '~' || (num > 0x0d && num < ' ')); buf_val[0].len = cnt;
case ctlseqs_ph_csi_param: *buf += cnt + 1;
CTLSEQS_VALUE_STR(num < '0' || num > '?'); return endptr;
case ctlseqs_ph_csi_intmd: case ctlseqs_ph_str:
CTLSEQS_VALUE_STR(num < ' ' || num > '/'); CTLSEQS_VALUE_STR(num < ' ' || num > '~');
case ctlseqs_ph_hexnum: case ctlseqs_ph_cmdstr:
CTLSEQS_VALUE_NUM(16); CTLSEQS_VALUE_STR(num < 0x08 || num > '~' || num > 0x0d && num < ' ');
case ctlseqs_ph_chrstr: case ctlseqs_ph_csi_param:
CTLSEQS_VALUE_STR(num > 0x7f); CTLSEQS_VALUE_STR(num < '0' || num > '?');
default: case ctlseqs_ph_csi_intmd:
ctlseqs_unreachable(); CTLSEQS_VALUE_STR(num < ' ' || num > '/');
return NULL; case ctlseqs_ph_hexnum:
CTLSEQS_VALUE_NUM(16);
case ctlseqs_ph_chrstr:
CTLSEQS_VALUE_STR(num > 0x7f);
default:
ctlseqs_unreachable();
return NULL;
} }
} }
ctlseqs_hot static ssize_t ctlseqs_hot static ssize_t
ctlseqs_match_pattern(struct ctlseqs_matcher const *matcher, struct ctlseqs_match_args const *args) ctlseqs_match_pattern(
{ struct ctlseqs_matcher const *matcher,
struct ctlseqs_match_args const *args
) {
struct ctlseqs_trie_node const *old_node, empty_node = { 0 }; struct ctlseqs_trie_node const *old_node, empty_node = { 0 };
struct ctlseqs_match_ctx match_stack[matcher->match_stack_size], match_ctx = { struct ctlseqs_match_ctx match_stack[matcher->match_stack_size];
struct ctlseqs_match_ctx match_ctx = {
.node = matcher == NULL ? &empty_node : &matcher->root, .node = matcher == NULL ? &empty_node : &matcher->root,
.seq = args->seq + 1, .seq = args->seq + 1,
.result = args->result + (args->save_seq ? 2 : 0), .result = args->result + (args->save_seq ? 2 : 0),
@ -303,13 +357,14 @@ ctlseqs_match_pattern(struct ctlseqs_matcher const *matcher, struct ctlseqs_matc
while (true) { while (true) {
match_ctx.value = match_ctx.node->value; match_ctx.value = match_ctx.node->value;
if (match_ctx.value == -1) { if (match_ctx.value == -1) {
match_character: match_character:
match_ctx.node = match_ctx.node->children[(unsigned)match_ctx.seq++[0]]; match_ctx.node
= match_ctx.node->children[(unsigned)match_ctx.seq++[0]];
if (match_ctx.node == NULL) { if (match_ctx.node == NULL) {
break; break;
} }
} else if (match_ctx.value < -1) { } else if (match_ctx.value < -1) {
match_placeholder: match_placeholder:
old_node = match_ctx.node; old_node = match_ctx.node;
match_ctx.node = match_ctx.node->children[-match_ctx.value]; match_ctx.node = match_ctx.node->children[-match_ctx.value];
struct ctlseqs_trie_node *next_node = match_ctx.node->next; struct ctlseqs_trie_node *next_node = match_ctx.node->next;
@ -319,7 +374,11 @@ ctlseqs_match_pattern(struct ctlseqs_matcher const *matcher, struct ctlseqs_matc
.seq = match_ctx.seq, .seq = match_ctx.seq,
.result = match_ctx.result, .result = match_ctx.result,
}; };
match_ctx.seq = ctlseqs_fetch_value(match_ctx.seq, -match_ctx.value, &match_ctx.result); match_ctx.seq = ctlseqs_fetch_value(
match_ctx.seq,
-match_ctx.value,
&match_ctx.result
);
if (match_ctx.seq == NULL) { if (match_ctx.seq == NULL) {
break; break;
} }
@ -340,8 +399,10 @@ ctlseqs_match_pattern(struct ctlseqs_matcher const *matcher, struct ctlseqs_matc
} }
ctlseqs_hot static inline ssize_t ctlseqs_hot static inline ssize_t
ctlseqs_do_match(struct ctlseqs_matcher const *matcher, struct ctlseqs_match_args *args) ctlseqs_do_match(
{ struct ctlseqs_matcher const *matcher,
struct ctlseqs_match_args *args
) {
ssize_t retval = CTLSEQS_PARTIAL; ssize_t retval = CTLSEQS_PARTIAL;
char const *seq = args->seq; char const *seq = args->seq;
size_t idx, len = args->seq_len; size_t idx, len = args->seq_len;
@ -374,8 +435,10 @@ ctlseqs_do_match(struct ctlseqs_matcher const *matcher, struct ctlseqs_match_arg
} }
ctlseqs_hot static ssize_t ctlseqs_hot static ssize_t
ctlseqs_reader_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *matcher) ctlseqs_reader_match(
{ struct ctlseqs_reader *reader,
struct ctlseqs_matcher const *matcher
) {
struct ctlseqs_match_args args = { struct ctlseqs_match_args args = {
.seq = reader->rbuf + reader->buf_start, .seq = reader->rbuf + reader->buf_start,
.seq_len = reader->buf_end - reader->buf_start, .seq_len = reader->buf_end - reader->buf_start,
@ -387,17 +450,25 @@ ctlseqs_reader_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const
ssize_t retval = ctlseqs_do_match(matcher, &args); ssize_t retval = ctlseqs_do_match(matcher, &args);
if (retval == CTLSEQS_PARTIAL) { if (retval == CTLSEQS_PARTIAL) {
reader->last_idx = args.result_idx; reader->last_idx = args.result_idx;
if (ctlseqs_unlikely(reader->buf_start + args.result_idx == reader->readlen)) { if (ctlseqs_unlikely(
reader->buf_start + args.result_idx == reader->readlen
)) {
// Buffer is full but a match is still pending. // Buffer is full but a match is still pending.
// This may happen when the reader's maxlen option is not large enough to hold a sequence, // This may happen when the reader's maxlen option is not
// large enough to hold a sequence,
// or when the the sequences are produced faster than consumed. // or when the the sequences are produced faster than consumed.
if (reader->buf_start > reader->readlen / 2) { if (reader->buf_start > reader->readlen / 2) {
memcpy(reader->rbuf, reader->rbuf + reader->buf_start, args.result_idx); memcpy(
reader->rbuf,
reader->rbuf + reader->buf_start,
args.result_idx
);
reader->buf_start = 0; reader->buf_start = 0;
reader->buf_end = args.result_idx; reader->buf_end = args.result_idx;
} else { } else {
// We could memmove() here, but having a buffer no larger than twice the size of a sequence // We could memmove() here, but having a buffer no larger than
// is hardly what a normal program would desire. // twice the size of a sequence is hardly what a normal program
// would desire.
retval = CTLSEQS_NOMEM; retval = CTLSEQS_NOMEM;
} }
} }
@ -409,7 +480,11 @@ ctlseqs_reader_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const
reader->buf_end = 0; reader->buf_end = 0;
} }
} }
reader->state = args.state >= ctlseqs_state_done ? ctlseqs_state_none : args.state; if (args.state >= ctlseqs_state_done) {
reader->state = ctlseqs_state_none;
} else {
reader->state = args.state;
}
return retval; return retval;
} }
@ -428,10 +503,13 @@ ctlseqs_matcher_init()
} }
int int
ctlseqs_matcher_config(struct ctlseqs_matcher *matcher, struct ctlseqs_matcher_options const *options) ctlseqs_matcher_config(
{ struct ctlseqs_matcher *matcher,
struct ctlseqs_matcher_options const *options
) {
size_t node_idx = 0, max_format_size = 0; size_t node_idx = 0, max_format_size = 0;
struct ctlseqs_trie_node *node_pool = matcher->node_pools[matcher->pool_idx]; struct ctlseqs_trie_node *node_pool
= matcher->node_pools[matcher->pool_idx];
matcher->root = (struct ctlseqs_trie_node) { .value = -1 }; matcher->root = (struct ctlseqs_trie_node) { .value = -1 };
for (size_t i = 0; i < options->npatterns; ++i) { for (size_t i = 0; i < options->npatterns; ++i) {
char const *pattern = options->patterns[i]; char const *pattern = options->patterns[i];
@ -452,10 +530,14 @@ ctlseqs_matcher_config(struct ctlseqs_matcher *matcher, struct ctlseqs_matcher_o
continue; continue;
} }
if (ctlseqs_unlikely(++node_idx >= matcher->pool_size)) { if (ctlseqs_unlikely(++node_idx >= matcher->pool_size)) {
if (ctlseqs_unlikely(matcher->pool_idx >= CTLSEQS_TRIE_NODE_POOL_MAX_NUM - 1)) { if (ctlseqs_unlikely(
matcher->pool_idx >= CTLSEQS_TRIE_NODE_POOL_MAX_NUM - 1
)) {
return CTLSEQS_NOMEM; return CTLSEQS_NOMEM;
} }
node_pool = malloc(sizeof(struct ctlseqs_trie_node) * matcher->pool_size * 2); node_pool = malloc(
sizeof(struct ctlseqs_trie_node) * matcher->pool_size * 2
);
if (ctlseqs_unlikely(node_pool == NULL)) { if (ctlseqs_unlikely(node_pool == NULL)) {
return CTLSEQS_NOMEM; return CTLSEQS_NOMEM;
} }
@ -464,16 +546,23 @@ ctlseqs_matcher_config(struct ctlseqs_matcher *matcher, struct ctlseqs_matcher_o
matcher->pool_size *= 2; matcher->pool_size *= 2;
} }
old_node->children[ch] = node = node_pool + node_idx; old_node->children[ch] = node = node_pool + node_idx;
ssize_t placeholder;
if (ch < ctlseqs_ph_begin || ch >= ctlseqs_ph_end) {
placeholder = 0;
} else {
placeholder = ch;
}
*node = (struct ctlseqs_trie_node) { *node = (struct ctlseqs_trie_node) {
.value = -1, // Value -1 indicates that there's no match on current node. // Value -1 indicates that there's no match on current node.
.placeholder = ch < ctlseqs_ph_begin || ch >= ctlseqs_ph_end ? 0 : ch, .value = -1,
.placeholder = placeholder,
}; };
if (node->placeholder == 0) { if (placeholder == 0) {
continue; continue;
} }
if (old_node->value < -1) { if (old_node->value < -1) {
// Node with multiple placeholders contains negated offset of the child node // Node with multiple placeholders contains negated offset of
// which is the head of the linked list. // the child node which is the head of the linked list.
node->next = old_node->children[-old_node->value]; node->next = old_node->children[-old_node->value];
} }
old_node->value = -ch; old_node->value = -ch;
@ -484,8 +573,12 @@ ctlseqs_matcher_config(struct ctlseqs_matcher *matcher, struct ctlseqs_matcher_o
} }
ctlseqs_hot ssize_t ctlseqs_hot ssize_t
ctlseqs_match(struct ctlseqs_matcher const *matcher, char const *str, size_t str_len, union ctlseqs_value *result) ctlseqs_match(
{ struct ctlseqs_matcher const *matcher,
char const *str,
size_t str_len,
union ctlseqs_value *result
) {
struct ctlseqs_match_args args = { struct ctlseqs_match_args args = {
.seq = str, .seq = str,
.seq_len = str_len, .seq_len = str_len,
@ -493,7 +586,7 @@ ctlseqs_match(struct ctlseqs_matcher const *matcher, char const *str, size_t str
.save_seq = true, .save_seq = true,
}; };
ssize_t retval; ssize_t retval;
try_match: try_match:
retval = ctlseqs_do_match(matcher, &args); retval = ctlseqs_do_match(matcher, &args);
if (retval == CTLSEQS_NOSEQ) { if (retval == CTLSEQS_NOSEQ) {
size_t result_len = args.result[0].len; size_t result_len = args.result[0].len;
@ -507,8 +600,9 @@ ctlseqs_match(struct ctlseqs_matcher const *matcher, char const *str, size_t str
} }
ctlseqs_cold void ctlseqs_cold void
ctlseqs_matcher_free(struct ctlseqs_matcher *matcher) ctlseqs_matcher_free(
{ struct ctlseqs_matcher *matcher
) {
if (ctlseqs_likely(matcher != NULL)) { if (ctlseqs_likely(matcher != NULL)) {
for (size_t idx = 1; idx <= matcher->pool_idx; ++idx) { for (size_t idx = 1; idx <= matcher->pool_idx; ++idx) {
free(matcher->node_pools[idx]); free(matcher->node_pools[idx]);
@ -528,8 +622,10 @@ ctlseqs_reader_init()
} }
int int
ctlseqs_reader_config(struct ctlseqs_reader *reader, struct ctlseqs_reader_options const *options) ctlseqs_reader_config(
{ struct ctlseqs_reader *reader,
struct ctlseqs_reader_options const *options
) {
size_t const readlen = options->maxlen; size_t const readlen = options->maxlen;
if (reader->readlen != readlen) { if (reader->readlen != readlen) {
if (readlen < reader->buf_end) { if (readlen < reader->buf_end) {
@ -553,8 +649,11 @@ ctlseqs_reader_config(struct ctlseqs_reader *reader, struct ctlseqs_reader_optio
} }
ctlseqs_hot ssize_t ctlseqs_hot ssize_t
ctlseqs_read(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *matcher, int timeout) ctlseqs_read(
{ struct ctlseqs_reader *reader,
struct ctlseqs_matcher const *matcher,
int timeout
) {
ssize_t result; ssize_t result;
// Whether we have read more than we could match in the preview call. // Whether we have read more than we could match in the preview call.
if (reader->state == ctlseqs_state_none && reader->buf_start != 0) { if (reader->state == ctlseqs_state_none && reader->buf_start != 0) {
@ -571,14 +670,20 @@ ctlseqs_read(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *matche
} }
result = ctlseqs_do_read(reader); result = ctlseqs_do_read(reader);
if (ctlseqs_unlikely(result < 0)) { if (ctlseqs_unlikely(result < 0)) {
return reader->state == ctlseqs_state_none ? result : CTLSEQS_PARTIAL; if (reader->state == ctlseqs_state_none) {
return result;
} else {
return CTLSEQS_PARTIAL;
}
} }
return ctlseqs_reader_match(reader, matcher); return ctlseqs_reader_match(reader, matcher);
} }
void void
ctlseqs_purge(struct ctlseqs_reader *reader, size_t nbytes) ctlseqs_purge(
{ struct ctlseqs_reader *reader,
size_t nbytes
) {
if (ctlseqs_unlikely(nbytes == 0)) { if (ctlseqs_unlikely(nbytes == 0)) {
return; return;
} }
@ -592,8 +697,9 @@ ctlseqs_purge(struct ctlseqs_reader *reader, size_t nbytes)
} }
ctlseqs_cold void ctlseqs_cold void
ctlseqs_reader_free(struct ctlseqs_reader *reader) ctlseqs_reader_free(
{ struct ctlseqs_reader *reader
) {
if (ctlseqs_likely(reader != NULL)) { if (ctlseqs_likely(reader != NULL)) {
free(reader->rbuf); free(reader->rbuf);
free(reader); free(reader);

View File

@ -7,12 +7,12 @@
# #
AUTOMAKE_OPTIONS = dejagnu AUTOMAKE_OPTIONS = dejagnu
AM_CPPFLAGS = -I$(top_srcdir)/include
EXTRA_DIST = ctlseqs/*.exp init.exp EXTRA_DIST = ctlseqs/*.exp init.exp
noinst_PROGRAMS = tcsgrep noinst_PROGRAMS = tcsgrep
tcsgrep_SOURCES = tcsgrep.c tcsgrep_CPPFLAGS = -I$(top_srcdir)/include
tcsgrep_LDADD = $(top_builddir)/src/libctlseqs.la tcsgrep_SOURCES = tcsgrep.c
tcsgrep_LDADD = $(top_builddir)/src/libctlseqs.la
RUNTESTDEFAULTFLAGS = TCSGREP_BIN=$(srcdir)/tcsgrep RUNTESTDEFAULTFLAGS = TCSGREP_BIN=$(builddir)/tcsgrep
EXTRA_DEJAGNU_SITE_CONFIG = $(srcdir)/init.exp EXTRA_DEJAGNU_SITE_CONFIG = $(srcdir)/init.exp

View File

@ -12,16 +12,19 @@ set timeout 2
set n1 [ random_int ] set n1 [ random_int ]
set n2 [ random_int ] set n2 [ random_int ]
set n3 [ random_int ] set n3 [ random_int ]
set n1_len [ string length $n1 ]
set input { } set input [ list \
lappend input "$CSI$n1;${n2}H" "$CSI$n1;${n2}H" \
lappend input "$DCS$n1;$n2|$n3$ST" "$DCS$n1;$n2|$n3$ST" \
lappend input "$CSI<?${n1}Z" "$CSI<?${n1}Z" \
]
set output { } set output [ list \
lappend output "OK CUP $n1 $n2" "OK CUP $n1 $n2" \
lappend output "OK DECUDK $n1 $n2 $n3" "OK DECUDK $n1 $n2 $n3" \
lappend output "NOMATCH [ expr [ string length $n1 ] + 5 ] ESC \[ < ? [ split $n1 {} ] Z" "NOMATCH [ expr { $n1_len + 5 } ] ESC \[ < ? [ split $n1 {} ] Z" \
]
tcsgrep_start tcsgrep_start

View File

@ -26,9 +26,10 @@ expect {
} }
for { set i 0 } { $i < $nlen } { incr i } { for { set i 0 } { $i < $nlen } { incr i } {
set substr [ string range $n1 0 $i ]
send [ string index $n1 $i ] send [ string index $n1 $i ]
expect { expect {
-ex "PARTIAL [ expr $i + 3 ] ESC \[ [ split [ string range $n1 0 $i ] {} ]\n" { -ex "PARTIAL [ expr { $i + 3 } ] ESC \[ [ split $substr {} ]\n" {
pass "pass" pass "pass"
} }
default { default {
@ -40,7 +41,7 @@ for { set i 0 } { $i < $nlen } { incr i } {
send "p" send "p"
expect { expect {
-ex "NOMATCH [ expr $nlen + 3 ] ESC \[ [ split $n1 {} ] p\n" { -ex "NOMATCH [ expr { $nlen + 3 } ] ESC \[ [ split $n1 {} ] p\n" {
pass "pass" pass "pass"
} }
default { default {

View File

@ -31,7 +31,7 @@ proc tcsgrep_start args {
proc tcsgrep_stop { } { proc tcsgrep_stop { } {
global spawn_id global spawn_id
send \x04 send "\x04"
expect { expect {
-ex "NOSEQ 1 EOT\n" { } -ex "NOSEQ 1 EOT\n" { }
default { default {
@ -46,5 +46,5 @@ proc tcsgrep_stop { } {
proc random_int { } { proc random_int { } {
set num [ exec od -An -td -N4 /dev/urandom ] set num [ exec od -An -td -N4 /dev/urandom ]
return [ expr abs(int($num)) ] return [ expr { abs(int($num)) } ]
} }

View File

@ -50,8 +50,8 @@
#define DEFARGS_(nargs, ...) CONCAT_(nargs)(__VA_ARGS__) #define DEFARGS_(nargs, ...) CONCAT_(nargs)(__VA_ARGS__)
#define DEFSEQ_NOARGS(name) { #name, CTLSEQS_##name(), "" } #define DEFSEQ_NOARGS(name) { #name, CTLSEQS_##name(), "" }
#define DEFSEQ(name, ...) \ #define DEFSEQ(name, ...) { #name, CTLSEQS_##name(__VA_ARGS__), \
{ #name, CTLSEQS_##name(__VA_ARGS__), DEFARGS_(COUNT_ARGS_(__VA_ARGS__), __VA_ARGS__) } DEFARGS_(COUNT_ARGS_(__VA_ARGS__), __VA_ARGS__) }
#define DEFAULT_MAX_BUFFER_LEN 4096 #define DEFAULT_MAX_BUFFER_LEN 4096
@ -74,14 +74,18 @@ struct tcsgrep_ctx {
}; };
static inline void static inline void
print_error(struct tcsgrep_ctx const *ctx, char const *msg) print_error(
{ struct tcsgrep_ctx const *ctx,
char const *msg
) {
fprintf(ctx->err_file, "%s: [error] %s.\n", ctx->prog_name, msg); fprintf(ctx->err_file, "%s: [error] %s.\n", ctx->prog_name, msg);
} }
static inline bool static inline bool
parse_int(char const *str, int *dest) parse_int(
{ char const *str,
int *dest
) {
errno = 0; errno = 0;
unsigned long result = strtoul(str, NULL, 10); unsigned long result = strtoul(str, NULL, 10);
if (errno || result > 4096) { if (errno || result > 4096) {
@ -92,8 +96,10 @@ parse_int(char const *str, int *dest)
} }
static inline void static inline void
print_char(struct tcsgrep_ctx const *ctx, int ch) print_char(
{ struct tcsgrep_ctx const *ctx,
int ch
) {
static char const *ascii_table[] = { static char const *ascii_table[] = {
"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
"BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
@ -114,8 +120,12 @@ print_char(struct tcsgrep_ctx const *ctx, int ch)
} }
static void static void
print_generic_seq(struct tcsgrep_ctx const *ctx, char const *header, union ctlseqs_value const *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; size_t length = result[0].len;
char const *seq = result[1].str; char const *seq = result[1].str;
fprintf(ctx->out_file, "%s %zu", header, length); fprintf(ctx->out_file, "%s %zu", header, length);
@ -129,9 +139,9 @@ print_generic_seq(struct tcsgrep_ctx const *ctx, char const *header, union ctlse
static void static void
print_matching_seq( print_matching_seq(
struct tcsgrep_ctx const *ctx, struct tcsgrep_ctx const *ctx,
struct tcsgrep_sequence const *seq, struct tcsgrep_sequence const *seq,
union ctlseqs_value const *result, union ctlseqs_value const *result,
bool verbose bool verbose
) { ) {
if (verbose) { if (verbose) {
@ -148,7 +158,8 @@ print_matching_seq(
fprintf(ctx->out_file, " %lu", result[idx].num); fprintf(ctx->out_file, " %lu", result[idx].num);
break; break;
case 0x10: // CTLSEQS_PH_STR case 0x10: // CTLSEQS_PH_STR
fprintf(ctx->out_file, " %.*s", (int)result[idx].len, result[idx + 1].str); fprintf(ctx->out_file, " %.*s",
(int)result[idx].len, result[idx + 1].str);
break; break;
case 0x0f: // CTLSEQS_PH_NUMS case 0x0f: // CTLSEQS_PH_NUMS
for (size_t i = 1; i <= result[idx].len; ++i) { for (size_t i = 1; i <= result[idx].len; ++i) {
@ -161,8 +172,10 @@ print_matching_seq(
} }
int int
main(int argc, char **argv) main(
{ int argc,
char **argv
) {
struct tcsgrep_ctx ctx = { struct tcsgrep_ctx ctx = {
.prog_name = argv[0], .prog_name = argv[0],
.out_file = stdout, .out_file = stdout,
@ -175,28 +188,29 @@ main(int argc, char **argv)
int opt; int opt;
while (-1 != (opt = getopt(argc, argv, "t:l:pv"))) { while (-1 != (opt = getopt(argc, argv, "t:l:pv"))) {
switch (opt) { switch (opt) {
case 't': case 't':
if (!parse_int(optarg, &ctx.timeout)) { if (!parse_int(optarg, &ctx.timeout)) {
print_error(&ctx, "invalid timeout option"); 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 'p':
ctx.purge_long_seqs = true;
break;
case 'v':
ctx.verbose = true;
break;
case '?':
default:
fprintf(ctx.out_file, "%s\n", "Usage: tcsgrep [-t timeout] [-l limit] [-pv]");
return 1; return 1;
}
break;
case 'l':
if (!parse_int(optarg, &ctx.limit)) {
print_error(&ctx, "invalid limit option");
return 1;
}
break;
case 'p':
ctx.purge_long_seqs = true;
break;
case 'v':
ctx.verbose = true;
break;
case '?':
default:
fprintf(ctx.out_file, "%s\n",
"Usage: tcsgrep [-t timeout] [-l limit] [-pv]");
return 1;
} }
} }
@ -257,7 +271,8 @@ main(int argc, char **argv)
DEFSEQ(SU, CTLSEQS_PH_NUM), DEFSEQ(SU, CTLSEQS_PH_NUM),
DEFSEQ(XTSMGRAPHICS, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUMS), DEFSEQ(XTSMGRAPHICS, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUMS),
DEFSEQ(SD, CTLSEQS_PH_NUM), DEFSEQ(SD, CTLSEQS_PH_NUM),
DEFSEQ(XTHIMOUSE, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(XTHIMOUSE, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(XTRMTITLE, CTLSEQS_PH_STR), DEFSEQ(XTRMTITLE, CTLSEQS_PH_STR),
DEFSEQ(ECH, CTLSEQS_PH_NUM), DEFSEQ(ECH, CTLSEQS_PH_NUM),
DEFSEQ(CBT, CTLSEQS_PH_NUM), DEFSEQ(CBT, CTLSEQS_PH_NUM),
@ -293,34 +308,43 @@ main(int argc, char **argv)
DEFSEQ(DECSCA, CTLSEQS_PH_NUM), DEFSEQ(DECSCA, CTLSEQS_PH_NUM),
DEFSEQ(DECSTBM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(DECSTBM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(XTRESTORE, CTLSEQS_PH_NUMS), DEFSEQ(XTRESTORE, CTLSEQS_PH_NUMS),
DEFSEQ(DECCARA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(DECCARA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ_NOARGS(SCOSC), DEFSEQ_NOARGS(SCOSC),
DEFSEQ(DECSLRM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(DECSLRM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(XTSAVE, CTLSEQS_PH_NUMS), DEFSEQ(XTSAVE, CTLSEQS_PH_NUMS),
DEFSEQ(XTWINOPS, CTLSEQS_PH_NUMS), DEFSEQ(XTWINOPS, CTLSEQS_PH_NUMS),
DEFSEQ(XTSMTITLE, CTLSEQS_PH_NUMS), DEFSEQ(XTSMTITLE, CTLSEQS_PH_NUMS),
DEFSEQ(DECSWBV, CTLSEQS_PH_NUM), DEFSEQ(DECSWBV, CTLSEQS_PH_NUM),
DEFSEQ(DECRARA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(DECRARA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ_NOARGS(SCORC), DEFSEQ_NOARGS(SCORC),
DEFSEQ(DECSMBV, CTLSEQS_PH_NUM), DEFSEQ(DECSMBV, CTLSEQS_PH_NUM),
DEFSEQ(DECCRA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, DEFSEQ(DECCRA,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(DECRQPSR, CTLSEQS_PH_NUM), DEFSEQ(DECRQPSR, CTLSEQS_PH_NUM),
DEFSEQ(DECEFR, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(DECEFR,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(DECREQTPARM, CTLSEQS_PH_NUM), DEFSEQ(DECREQTPARM, CTLSEQS_PH_NUM),
DEFSEQ(DECEFR, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(DECEFR,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(DECREQTPARM, CTLSEQS_PH_NUM), DEFSEQ(DECREQTPARM, CTLSEQS_PH_NUM),
DEFSEQ(DECSACE, CTLSEQS_PH_NUM), DEFSEQ(DECSACE, CTLSEQS_PH_NUM),
DEFSEQ(DECFRA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(DECFRA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM,
DEFSEQ(XTCHECKSUM, CTLSEQS_PH_NUM),
DEFSEQ(DECRQCRA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(XTCHECKSUM, CTLSEQS_PH_NUM),
DEFSEQ(DECRQCRA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(DECELR, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(DECELR, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(DECERA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(DECERA,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(DECSLE, CTLSEQS_PH_NUMS), DEFSEQ(DECSLE, CTLSEQS_PH_NUMS),
DEFSEQ(XTPUSHSGR, CTLSEQS_PH_NUMS), DEFSEQ(XTPUSHSGR, CTLSEQS_PH_NUMS),
DEFSEQ(DECSERA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(DECSERA,
DEFSEQ(XTREPORTSGR, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(XTREPORTSGR,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(DECSCPP, CTLSEQS_PH_NUM), DEFSEQ(DECSCPP, CTLSEQS_PH_NUM),
DEFSEQ_NOARGS(DECRQLP), DEFSEQ_NOARGS(DECRQLP),
DEFSEQ(DECSNLS, CTLSEQS_PH_NUM), DEFSEQ(DECSNLS, CTLSEQS_PH_NUM),
@ -332,7 +356,8 @@ main(int argc, char **argv)
DEFSEQ(RESP_XTGETXRES, CTLSEQS_PH_NUM, CTLSEQS_PH_STR), DEFSEQ(RESP_XTGETXRES, CTLSEQS_PH_NUM, CTLSEQS_PH_STR),
DEFSEQ(RESP_XTGETTCAP, CTLSEQS_PH_NUM, CTLSEQS_PH_STR), DEFSEQ(RESP_XTGETTCAP, CTLSEQS_PH_NUM, CTLSEQS_PH_STR),
DEFSEQ(RESP_PRIMARY_DA, CTLSEQS_PH_NUMS), DEFSEQ(RESP_PRIMARY_DA, CTLSEQS_PH_NUMS),
DEFSEQ(RESP_SECONDARY_DA, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(RESP_SECONDARY_DA,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(RESP_DECXCPR, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(RESP_DECXCPR, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ(RESP_DSR, CTLSEQS_PH_NUMS), DEFSEQ(RESP_DSR, CTLSEQS_PH_NUMS),
DEFSEQ(RESP_DECRQM_ANSI, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(RESP_DECRQM_ANSI, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
@ -341,7 +366,8 @@ main(int argc, char **argv)
DEFSEQ(RESP_DECCIR, CTLSEQS_PH_STR), DEFSEQ(RESP_DECCIR, CTLSEQS_PH_STR),
DEFSEQ(RESP_DECTABSR, CTLSEQS_PH_STR), DEFSEQ(RESP_DECTABSR, CTLSEQS_PH_STR),
DEFSEQ(RESP_DECRQCRA, CTLSEQS_PH_NUM, CTLSEQS_PH_STR), DEFSEQ(RESP_DECRQCRA, CTLSEQS_PH_NUM, CTLSEQS_PH_STR),
DEFSEQ(RESP_DECRQLP, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM), DEFSEQ(RESP_DECRQLP, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM, CTLSEQS_PH_NUM,
CTLSEQS_PH_NUM, CTLSEQS_PH_NUM),
DEFSEQ_NOARGS(KEY_UP), DEFSEQ_NOARGS(KEY_UP),
DEFSEQ_NOARGS(KEY_DOWN), DEFSEQ_NOARGS(KEY_DOWN),
DEFSEQ_NOARGS(KEY_RIGHT), DEFSEQ_NOARGS(KEY_RIGHT),
@ -413,50 +439,50 @@ main(int argc, char **argv)
while (true) { while (true) {
ssize_t retval = ctlseqs_read(reader, matcher, ctx.timeout); ssize_t retval = ctlseqs_read(reader, matcher, ctx.timeout);
switch (retval) { switch (retval) {
case CTLSEQS_ERROR: case CTLSEQS_ERROR:
print_error(&ctx, "unexpected error"); print_error(&ctx, "unexpected error");
status = 1;
goto terminate;
case CTLSEQS_TIMEOUT:
fprintf(ctx.out_file, "TIMEOUT\n");
status = 1;
goto terminate;
case CTLSEQS_INTR:
fprintf(ctx.out_file, "INTR\n");
break;
case CTLSEQS_EOF:
fprintf(ctx.out_file, "EOF\n");
goto terminate;
case CTLSEQS_PARTIAL:
if (ctx.verbose) {
print_generic_seq(&ctx, "PARTIAL", result, true);
}
break;
case CTLSEQS_NOMATCH:
print_generic_seq(&ctx, "NOMATCH", result, true);
break;
case CTLSEQS_NOMEM:
print_generic_seq(&ctx, "NOMEM", result, true);
if (ctx.purge_long_seqs) {
ctlseqs_purge(reader, result[0].len);
break;
} else {
status = 1; status = 1;
goto terminate; goto terminate;
case CTLSEQS_TIMEOUT: }
fprintf(ctx.out_file, "TIMEOUT\n"); case CTLSEQS_NOSEQ:
status = 1; print_generic_seq(&ctx, "NOSEQ", result, true);
if (!ctx.not_tty && result[1].str[0] == 0x04) {
goto terminate; goto terminate;
case CTLSEQS_INTR: }
fprintf(ctx.out_file, "INTR\n"); break;
break; default:
case CTLSEQS_EOF: print_matching_seq(&ctx, &seqs[retval], result, ctx.verbose);
fprintf(ctx.out_file, "EOF\n"); break;
goto terminate;
case CTLSEQS_PARTIAL:
if (ctx.verbose) {
print_generic_seq(&ctx, "PARTIAL", result, true);
}
break;
case CTLSEQS_NOMATCH:
print_generic_seq(&ctx, "NOMATCH", result, true);
break;
case CTLSEQS_NOMEM:
print_generic_seq(&ctx, "NOMEM", result, true);
if (ctx.purge_long_seqs) {
ctlseqs_purge(reader, result[0].len);
break;
} else {
status = 1;
goto terminate;
}
case CTLSEQS_NOSEQ:
print_generic_seq(&ctx, "NOSEQ", result, true);
if (!ctx.not_tty && result[1].str[0] == 0x04) {
goto terminate;
}
break;
default:
print_matching_seq(&ctx, &seqs[retval], result, ctx.verbose);
break;
} }
} }
terminate: terminate:
ctlseqs_matcher_free(matcher); ctlseqs_matcher_free(matcher);
ctlseqs_reader_free(reader); ctlseqs_reader_free(reader);
if (!ctx.not_tty) { if (!ctx.not_tty) {