Browse Source

Refactor code.

primary
CismonX 1 month ago
parent
commit
e45395bbdf
Signed by: CismonX GPG Key ID: 3094873E29A482FB
  1. 3
      .gitignore
  2. 50
      INSTALL.md
  3. 2
      Makefile.am
  4. 35
      README.md
  5. 41
      configure.ac
  6. 9
      examples/Makefile.am
  7. 86
      examples/sixdraw.c
  8. 411
      include/ctlseqs.h
  9. 9
      src/Makefile.am
  10. 384
      src/ctlseqs.c
  11. 10
      tests/Makefile.am
  12. 19
      tests/ctlseqs/match.exp
  13. 5
      tests/ctlseqs/partial.exp
  14. 4
      tests/init.exp
  15. 204
      tests/tcsgrep.c

3
.gitignore

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

50
INSTALL.md

@ -1,38 +1,36 @@
<!--
Copyright (C) 2020,2021 CismonX <admin@cismon.net>
Copying and distribution of this file, with or without modification, are
permitted in any medium without royalty, provided the copyright notice and
this notice are preserved. This file is offered as-is, without any warranty.
Copyright (C) 2020,2021 CismonX <admin@cismon.net>
Copying and distribution of this file, with or without modification, are
permitted in any medium without royalty, provided the copyright notice and
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
[ctlseqs.h](include/ctlseqs.h) and [ctlseqs.c](src/ctlseqs.c) to your project
and build it alongside with other code.
The code of ctlseqs is simple, and can be used out-of-the-box. Just copy
ctlseqs.h and ctlseqs.c to your project and build it 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
GNU Autotools (Autoconf, Automake, Libtool and Autoconf Archive).
Alternatively, ctlseqs can be built into a shared/static library with
GNU Autotools (Autoconf, Automake, Libtool and Autoconf Archive).
```shell
autoreconf --install
./configure
make
```
autoreconf --install
./configure
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 install
```
make check
make install
Unix man pages are not automatically installed with `make install`, and should
be installed manually with `make install-man`.
Unix man pages are not automatically installed with `make install`, and
should be installed manually with `make install-man`.

2
Makefile.am

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

35
README.md

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

41
configure.ac

@ -1,24 +1,25 @@
dnl
dnl Copyright (C) 2020,2021 CismonX <admin@cismon.net>
dnl
dnl Copying and distribution of this file, with or without modification, are
dnl permitted in any medium without royalty, provided the copyright notice and
dnl this notice are preserved. This file is offered as-is, without any warranty.
dnl Copying and distribution of this file, with or without modification,
dnl are permitted in any medium without royalty,
dnl provided the copyright notice and this notice are preserved.
dnl This file is offered as-is, without any warranty.
dnl
AC_PREREQ([2.60])
AC_PREREQ([2.69])
AC_INIT([ctlseqs], [0.1.0], [bug-report@cismon.net])
AC_CONFIG_SRCDIR([src/ctlseqs.c])
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])
LT_PREREQ([2.4.0])
LT_PREREQ([2.4.2])
LT_INIT
# Checks for programs.
AC_PROG_CC_C99
AC_PROG_CXX
AC_PROG_AWK
AC_PROG_CC
AC_PROG_CPP
AC_PROG_INSTALL
AC_PROG_LN_S
@ -28,12 +29,14 @@ AC_PROG_MAKE_SET
m4_ifdef([AX_GCC_BUILTIN], [
AX_GCC_BUILTIN(__builtin_expect)
AX_GCC_BUILTIN(__builtin_unreachable)
])
m4_ifdef([AX_GCC_FUNC_ATTRIBUTE], [
AX_GCC_FUNC_ATTRIBUTE(cold)
AX_GCC_FUNC_ATTRIBUTE(hot)
])
# 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.
AC_CHECK_HEADER_STDBOOL
@ -42,13 +45,23 @@ AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
# 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.
m4_define([CTLSEQS_LT_CURRENT], [0])
m4_define([CTLSEQS_LT_REVISION], [0])
m4_define([CTLSEQS_LT_CUR], [0])
m4_define([CTLSEQS_LT_REV], [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

9
examples/Makefile.am

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

86
examples/sixdraw.c

@ -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.
* These features are not widely supported. To save yourself from trouble,
@ -48,9 +48,8 @@
# define DEFAULT_TIMEOUT_MILLIS 500
#endif // !DEFAULT_TIMEOUT_MILLIS
#define DECRQM_UNREC 0
#define DECRQM_SET 1
#define DECRQM_RST 2
#define DECRQM_SET 1
#define DECRQM_RST 2
#define DECTCEM 25
#define DECSDM 80
@ -126,7 +125,11 @@ terminate(struct sixdraw_ctx *ctx)
// Restore original sixel mode.
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.
@ -168,7 +171,9 @@ decrqm(struct sixdraw_ctx *ctx, unsigned mode, char const *name)
print_error(ctx, "failed to get %s status", name);
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);
return false;
}
@ -250,7 +255,8 @@ init(struct sixdraw_ctx *ctx, int argc, char **argv)
break;
case '?':
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;
}
}
@ -264,7 +270,8 @@ init(struct sixdraw_ctx *ctx, int argc, char **argv)
char const *patterns[] = {
CTLSEQS_RESP_PRIMARY_DA(CTLSEQS_PH_NUMS),
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),
};
struct ctlseqs_matcher_options matcher_options = {
@ -339,7 +346,7 @@ prepare(struct sixdraw_ctx *ctx)
return false;
}
// Check terminal support for sixel graphics and DEC locator.
// Check terminal support for sixel graphics.
fprintf(ctx->out_file, CTLSEQS_PRIMARY_DA());
ssize_t retval;
do {
@ -384,9 +391,14 @@ prepare(struct sixdraw_ctx *ctx)
}
// Before patch #369, XTerm implemented DECSDM incorrectly.
// 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) {
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.
@ -418,9 +430,14 @@ prepare(struct sixdraw_ctx *ctx)
// Build the immutable part of sixel sequence.
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->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);
ctx->sixel_init_size += sprintf(
sixel_seq + ctx->sixel_init_size,
"%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;
}
@ -428,27 +445,34 @@ prepare(struct sixdraw_ctx *ctx)
static bool
draw(struct sixdraw_ctx *ctx)
{
fprintf(ctx->out_file, 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);
fprintf(
ctx->out_file,
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;
while (true) {
switch (ctlseqs_read(ctx->reader, ctx->matcher, -1)) {
case 2: // CTLSEQS_RESP_SGR_MOUSE
// Pixel coordinates start at 1 instead of 0.
print_sixel_dot(ctx, result[1].num - 1, result[2].num - 1);
break;
case CTLSEQS_NOSEQ:
// Press Ctrl+C to exit.
if (result[1].str[0] == 0x03) {
return true;
}
break;
case CTLSEQS_ERROR:
case CTLSEQS_NOMEM:
case CTLSEQS_EOF:
return false;
case 2: // CTLSEQS_RESP_SGR_MOUSE
// Pixel coordinates start at 1 instead of 0.
print_sixel_dot(ctx, result[1].num - 1, result[2].num - 1);
break;
case CTLSEQS_NOSEQ:
// Press Ctrl+C to exit.
if (result[1].str[0] == 0x03) {
return true;
}
break;
case CTLSEQS_ERROR:
case CTLSEQS_NOMEM:
case CTLSEQS_EOF:
return false;
}
}
}

411
include/ctlseqs.h

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

9
src/Makefile.am

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

384
src/ctlseqs.c

@ -159,142 +159,196 @@ struct ctlseqs_reader {
};
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);
if (nevents == -1) {
return errno == EINTR ? CTLSEQS_INTR : CTLSEQS_ERROR;
}
if (nevents == 0) {
switch (nevents) {
case 0:
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_do_read(struct ctlseqs_reader *reader)
{
ctlseqs_do_read(
struct ctlseqs_reader *reader
) {
size_t offset = reader->buf_start + reader->last_idx;
ssize_t nbytes = read(reader->pollfd.fd, reader->rbuf + offset, reader->readlen - offset);
if (ctlseqs_unlikely(nbytes == -1)) {
ssize_t nbytes = read(
reader->pollfd.fd,
reader->rbuf + offset,
reader->readlen - offset
);
switch (nbytes) {
case -1:
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return CTLSEQS_TIMEOUT;
} else if (errno == EINTR) {
return CTLSEQS_INTR;
} else {
return CTLSEQS_ERROR;
}
return errno == EINTR ? CTLSEQS_INTR : CTLSEQS_ERROR;
}
if (ctlseqs_unlikely(nbytes == 0)) {
case 0:
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_state_transition(enum ctlseqs_state state, char ch)
{
ctlseqs_state_transition(
enum ctlseqs_state state,
char ch
) {
switch (state) {
case ctlseqs_state_none:
return ch == 0x1b ? ctlseqs_state_esc : ctlseqs_state_err;
case ctlseqs_state_esc:
switch (ch) {
case '[':
return ctlseqs_state_csi;
case 'N':
case 'O':
return ctlseqs_state_ss;
case 'P':
case ']':
case '_':
case '^':
return ctlseqs_state_cmdstr;
case 'X':
return ctlseqs_state_ctlstr;
default:
return (ch >= ' ' && ch <= '~') ? ctlseqs_state_done : ctlseqs_state_err;
}
case ctlseqs_state_csi:
if (ch >= '0' && ch <= '?') {
return state;
} else if (ch >= ' ' && ch <= '/') {
return ctlseqs_state_csi_intmd;
}
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 < ' ')) {
case ctlseqs_state_none:
if (ch == 0x1b) {
return ctlseqs_state_esc;
} else {
return ctlseqs_state_err;
}
case ctlseqs_state_esc:
switch (ch) {
case '[':
return ctlseqs_state_csi;
case 'N':
case 'O':
return ctlseqs_state_ss;
case 'P':
case ']':
case '_':
case '^':
return ctlseqs_state_cmdstr;
case 'X':
return ctlseqs_state_ctlstr;
default:
if (ch >= ' ' && ch <= '~') {
return ctlseqs_state_done;
} else {
return ctlseqs_state_err;
}
}
case ctlseqs_state_csi:
if (ch >= '0' && ch <= '?') {
return state;
} else if (ch >= ' ' && ch <= '/') {
return ctlseqs_state_csi_intmd;
} else if (ch >= '@' && ch <= '~') {
return ctlseqs_state_done;
} else {
return ctlseqs_state_err;
}
case ctlseqs_state_csi_intmd:
if (ch >= ' ' && ch <= '/') {
return state;
case ctlseqs_state_ss:
return (ch >= ' ' && ch <= '~') ? ctlseqs_state_done : ctlseqs_state_err;
case ctlseqs_state_ctlstr:
return ch == 0x1b ? ctlseqs_state_str_end : state;
case ctlseqs_state_str_end:
return ch == '\\' ? ctlseqs_state_done : ctlseqs_state_err;
default:
ctlseqs_unreachable();
} 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_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;
unsigned long num;
char *endptr = NULL;
union ctlseqs_value *buf_val = *buf;
switch (type) {
case ctlseqs_ph_num:
CTLSEQS_VALUE_NUM(10);
case ctlseqs_ph_nums:
for (cnt = 1; ; ++cnt) {
errno = 0;
num = strtoul(seq, &endptr, 10);
if (errno || seq == endptr) {
return NULL;
}
buf_val[cnt].num = num;
if (endptr[0] != ';') {
break;
}
seq = endptr + 1;
case ctlseqs_ph_num:
CTLSEQS_VALUE_NUM(10);
case ctlseqs_ph_nums:
for (cnt = 1; ; ++cnt) {
errno = 0;
num = strtoul(seq, &endptr, 10);
if (errno || seq == endptr) {
return NULL;
}
buf_val[cnt].num = num;
if (endptr[0] != ';') {
break;
}
buf_val[0].len = cnt;
*buf += cnt + 1;
return endptr;
case ctlseqs_ph_str:
CTLSEQS_VALUE_STR(num < ' ' || num > '~');
case ctlseqs_ph_cmdstr:
CTLSEQS_VALUE_STR(num < 0x08 || num > '~' || (num > 0x0d && num < ' '));
case ctlseqs_ph_csi_param:
CTLSEQS_VALUE_STR(num < '0' || num > '?');
case ctlseqs_ph_csi_intmd:
CTLSEQS_VALUE_STR(num < ' ' || num > '/');
case ctlseqs_ph_hexnum:
CTLSEQS_VALUE_NUM(16);
case ctlseqs_ph_chrstr:
CTLSEQS_VALUE_STR(num > 0x7f);
default:
ctlseqs_unreachable();
return NULL;
seq = endptr + 1;
}
buf_val[0].len = cnt;
*buf += cnt + 1;
return endptr;
case ctlseqs_ph_str:
CTLSEQS_VALUE_STR(num < ' ' || num > '~');
case ctlseqs_ph_cmdstr:
CTLSEQS_VALUE_STR(num < 0x08 || num > '~' || num > 0x0d && num < ' ');
case ctlseqs_ph_csi_param:
CTLSEQS_VALUE_STR(num < '0' || num > '?');
case ctlseqs_ph_csi_intmd:
CTLSEQS_VALUE_STR(num < ' ' || num > '/');
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_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_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,
.seq = args->seq + 1,
.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) {
match_ctx.value = match_ctx.node->value;
if (match_ctx.value == -1) {
match_character:
match_ctx.node = match_ctx.node->children[(unsigned)match_ctx.seq++[0]];
match_character:
match_ctx.node
= match_ctx.node->children[(unsigned)match_ctx.seq++[0]];
if (match_ctx.node == NULL) {
break;
}
} else if (match_ctx.value < -1) {
match_placeholder:
match_placeholder:
old_node = match_ctx.node;
match_ctx.node = match_ctx.node->children[-match_ctx.value];
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,
.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) {
break;
}
@ -340,8 +399,10 @@ ctlseqs_match_pattern(struct ctlseqs_matcher const *matcher, struct ctlseqs_matc
}
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;
char const *seq = args->seq;
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_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 = {
.seq = reader->rbuf + 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);
if (retval == CTLSEQS_PARTIAL) {
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.
// 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.
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_end = args.result_idx;
} else {
// We could memmove() here, but having a buffer no larger than twice the size of a sequence
// is hardly what a normal program would desire.
// We could memmove() here, but having a buffer no larger than
// twice the size of a sequence is hardly what a normal program
// would desire.
retval = CTLSEQS_NOMEM;
}
}
@ -409,7 +480,11 @@ ctlseqs_reader_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const
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;
}
@ -428,10 +503,13 @@ ctlseqs_matcher_init()
}
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;
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 };
for (size_t i = 0; i < options->npatterns; ++i) {
char const *pattern = options->patterns[i];
@ -452,10 +530,14 @@ ctlseqs_matcher_config(struct ctlseqs_matcher *matcher, struct ctlseqs_matcher_o
continue;
}
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;
}
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)) {
return CTLSEQS_NOMEM;
}
@ -464,16 +546,23 @@ ctlseqs_matcher_config(struct ctlseqs_matcher *matcher, struct ctlseqs_matcher_o
matcher->pool_size *= 2;
}
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) {
.value = -1, // Value -1 indicates that there's no match on current node.
.placeholder = ch < ctlseqs_ph_begin || ch >= ctlseqs_ph_end ? 0 : ch,
// Value -1 indicates that there's no match on current node.
.value = -1,
.placeholder = placeholder,
};
if (node->placeholder == 0) {
if (placeholder == 0) {
continue;
}
if (old_node->value < -1) {
// Node with multiple placeholders contains negated offset of the child node
// which is the head of the linked list.
// Node with multiple placeholders contains negated offset of
// the child node which is the head of the linked list.
node->next = old_node->children[-old_node->value];
}
old_node->value = -ch;
@ -484,8 +573,12 @@ ctlseqs_matcher_config(struct ctlseqs_matcher *matcher, struct ctlseqs_matcher_o
}
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 = {
.seq = str,
.seq_len = str_len,
@ -493,7 +586,7 @@ ctlseqs_match(struct ctlseqs_matcher const *matcher, char const *str, size_t str
.save_seq = true,
};
ssize_t retval;
try_match:
try_match:
retval = ctlseqs_do_match(matcher, &args);
if (retval == CTLSEQS_NOSEQ) {
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_matcher_free(struct ctlseqs_matcher *matcher)
{
ctlseqs_matcher_free(
struct ctlseqs_matcher *matcher
) {
if (ctlseqs_likely(matcher != NULL)) {
for (size_t idx = 1; idx <= matcher->pool_idx; ++idx) {
free(matcher->node_pools[idx]);
@ -528,8 +622,10 @@ ctlseqs_reader_init()
}
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;
if (reader->readlen != readlen) {
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_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;
// Whether we have read more than we could match in the preview call.
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);
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);
}
void
ctlseqs_purge(struct ctlseqs_reader *reader, size_t nbytes)
{
ctlseqs_purge(
struct ctlseqs_reader *reader,
size_t nbytes
) {
if (ctlseqs_unlikely(nbytes == 0)) {
return;
}
@ -592,8 +697,9 @@ ctlseqs_purge(struct ctlseqs_reader *reader, size_t nbytes)
}
ctlseqs_cold void
ctlseqs_reader_free(struct ctlseqs_reader *reader)
{
ctlseqs_reader_free(
struct ctlseqs_reader *reader
) {
if (ctlseqs_likely(reader != NULL)) {
free(reader->rbuf);
free(reader);

10
tests/Makefile.am

@ -7,12 +7,12 @@
#
AUTOMAKE_OPTIONS = dejagnu
AM_CPPFLAGS = -I$(top_srcdir)/include
EXTRA_DIST = ctlseqs/*.exp init.exp
noinst_PROGRAMS = tcsgrep
tcsgrep_SOURCES = tcsgrep.c
tcsgrep_LDADD = $(top_builddir)/src/libctlseqs.la
noinst_PROGRAMS = tcsgrep
tcsgrep_CPPFLAGS = -I$(top_srcdir)/include
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

19