From 808c6d5033ee99c7a4fcbac4aeab27898a68f10a Mon Sep 17 00:00:00 2001 From: CismonX Date: Wed, 9 Dec 2020 17:48:19 +0800 Subject: [PATCH] Update documentation. Fix bugs. --- README.org | 2 +- configure.ac | 2 -- man/ctlseqs_read.3 | 6 ++++++ src/ctlseqs.c | 42 +++++++++++++++++++++++++++--------------- tests/tcsgrep.c | 2 +- 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/README.org b/README.org index 6551fff..001c0b8 100644 --- a/README.org +++ b/README.org @@ -18,7 +18,7 @@ The ctlseqs library provides low-level C API for manipulating terminal emulators with control sequences. - This library is free software. you can redistribute it and/or modify it + Ctlseqs is free software. you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. diff --git a/configure.ac b/configure.ac index 7a8be14..997ca1f 100644 --- a/configure.ac +++ b/configure.ac @@ -41,8 +41,6 @@ AC_TYPE_SIZE_T AC_TYPE_SSIZE_T # Checks for library functions. -AC_FUNC_MALLOC -AC_FUNC_REALLOC AC_CHECK_FUNCS([dprintf memset strtoul]) AC_CONFIG_FILES([Makefile man/Makefile src/Makefile tests/Makefile examples/Makefile]) diff --git a/man/ctlseqs_read.3 b/man/ctlseqs_read.3 index 59b49e6..66c9d6a 100644 --- a/man/ctlseqs_read.3 +++ b/man/ctlseqs_read.3 @@ -51,6 +51,7 @@ A control sequence is successfully read, but fails to match any pattern in .TP .B CTLSEQS_PARTIAL Data is read successfully and can be recognized as part of a control sequence, but is not yet terminated. +Partial sequence is not consumed from the read buffer. .TP .B CTLSEQS_NOSEQ Data is read successfully, but cannot be recognized as a valid control sequence. @@ -60,6 +61,11 @@ The specified .I timeout has expired, and no data is read. .TP +.B CTLSEQS_NOMEM +Like +.BR CTLSEQS_PARTIAL , +but the internal read buffer is full, and no data can be further read. +.TP .B CTLSEQS_EOF End-of-file is encountered, and no data is read. .TP diff --git a/src/ctlseqs.c b/src/ctlseqs.c index be6f120..517e2f0 100644 --- a/src/ctlseqs.c +++ b/src/ctlseqs.c @@ -162,8 +162,8 @@ ctlseqs_poll(struct pollfd *pollfd, int timeout) CTLSEQS_HOT static inline int ctlseqs_do_read(struct ctlseqs_reader *reader) { - char *buf = reader->rbuf + reader->buf_start + reader->last_idx; - ssize_t nbytes = read(reader->pollfd.fd, buf, reader->readlen - reader->buf_start); + 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)) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return CTLSEQS_TIMEOUT; @@ -331,6 +331,7 @@ ctlseqs_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *match for (idx = reader->last_idx; idx < len; ++idx) { state = ctlseqs_state_transition(state, buf[idx]); if (state == ctlseqs_state_err) { + // Anything before next ESC is definitely not a control sequence. for (; idx < len; ++idx) { if (buf[idx] == 0x1b) { break; @@ -341,24 +342,39 @@ ctlseqs_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *match } if (state == ctlseqs_state_done) { retval = ctlseqs_match_pattern(reader, matcher); + ++idx; break; } } - reader->state = state >= ctlseqs_state_done ? ctlseqs_state_none : state; - if (retval == CTLSEQS_NOMATCH || retval == CTLSEQS_PARTIAL || retval == CTLSEQS_NOSEQ) { + if (retval < 0) { reader->buffer[0].num = idx; reader->buffer[1].str = buf; } if (retval == CTLSEQS_PARTIAL) { reader->last_idx = idx; + if (CTLSEQS_UNLIKELY(reader->buf_start + 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, + // 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, idx); + reader->buf_start = 0; + reader->buf_end = 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. + retval = CTLSEQS_NOMEM; + } + } } else { reader->buf_start += idx; reader->last_idx = 0; + if (reader->buf_start == reader->buf_end) { + reader->buf_start = 0; + reader->buf_end = 0; + } } - if (reader->buf_start == reader->buf_end) { - reader->buf_start = 0; - reader->buf_end = 0; - } + reader->state = state >= ctlseqs_state_done ? ctlseqs_state_none : state; return retval; } @@ -459,14 +475,10 @@ ctlseqs_reader_setopt(struct ctlseqs_reader *reader, struct ctlseqs_reader_opts size_t readlen = options->maxlen; if (reader->readlen != readlen) { char *rbuf; - if (reader->rbuf == NULL) { - rbuf = malloc(readlen); - } else { - if (readlen < reader->buf_end) { - return CTLSEQS_ERROR; - } - rbuf = realloc(reader->rbuf, readlen); + if (readlen < reader->buf_end) { + return CTLSEQS_ERROR; } + rbuf = realloc(reader->rbuf, readlen); if (rbuf == NULL) { return CTLSEQS_NOMEM; } diff --git a/tests/tcsgrep.c b/tests/tcsgrep.c index c710a84..eed020f 100644 --- a/tests/tcsgrep.c +++ b/tests/tcsgrep.c @@ -381,7 +381,7 @@ main(int argc, char **argv) printf("TIMEOUT\n"); break; case CTLSEQS_INTR: - printf("INTERRUPTED\n"); + printf("INTR\n"); break; case CTLSEQS_EOF: printf("EOF\n");