From 342ff4d8f03acff6adebc938c16b280249404985 Mon Sep 17 00:00:00 2001 From: CismonX Date: Tue, 8 Dec 2020 17:19:02 +0800 Subject: [PATCH] Fix bugs. Properly handle partial match. --- man/ctlseqs_reader_setopt.3 | 18 +++------- src/ctlseqs.c | 70 +++++++++++++++++++------------------ src/ctlseqs.h | 3 +- tests/tcsgrep.c | 3 ++ 4 files changed, 44 insertions(+), 50 deletions(-) diff --git a/man/ctlseqs_reader_setopt.3 b/man/ctlseqs_reader_setopt.3 index 92b1709..8cfc6b1 100644 --- a/man/ctlseqs_reader_setopt.3 +++ b/man/ctlseqs_reader_setopt.3 @@ -120,13 +120,6 @@ before You may want this option enabled if .I fd is maintained in an event loop. -.TP -.B CTLSEQS_READER_RETAIN_PARTIAL -When -.BR ctlseqs_read () -returns -.BR CTLSEQS_PARTIAL , -preserve the partially matched sequence in the internal buffer for future calls. . .SH RETURN VALUE .TP @@ -135,14 +128,11 @@ Success. .TP .B CTLSEQS_NOMEM Fails to allocate sufficient memory. -.SH BUGS -After a successful -.BR ctlseqs_read () -call, changing +.TP +.B CTLSEQS_ERROR +Attempts to change .I maxlen -to a smaller value on the same -.I reader -may result in data loss due to truncation of the internal read buffer. +value, but data in the internal read buffer will be lost due to truncation, if done so. . .SH SEE ALSO .BR ctlseqs_read (3) diff --git a/src/ctlseqs.c b/src/ctlseqs.c index 427fee3..be6f120 100644 --- a/src/ctlseqs.c +++ b/src/ctlseqs.c @@ -32,7 +32,6 @@ #include #include -#include #include #include @@ -136,15 +135,15 @@ struct ctlseqs_reader { union ctlseqs_value *buffer; size_t readlen; struct pollfd pollfd; - bool no_poll; - bool retain_partial; char *rbuf; size_t buf_start; size_t buf_end; + size_t last_idx; enum ctlseqs_state state; + bool no_poll; }; -CTLSEQS_HOT static int +CTLSEQS_HOT static inline int ctlseqs_poll(struct pollfd *pollfd, int timeout) { int nevents = poll(pollfd, 1, timeout); @@ -160,10 +159,11 @@ ctlseqs_poll(struct pollfd *pollfd, int timeout) return pollfd->revents & POLLHUP ? CTLSEQS_EOF : CTLSEQS_ERROR; } -CTLSEQS_HOT static int +CTLSEQS_HOT static inline int ctlseqs_do_read(struct ctlseqs_reader *reader) { - ssize_t nbytes = read(reader->pollfd.fd, reader->rbuf + reader->buf_start, reader->readlen - reader->buf_start); + char *buf = reader->rbuf + reader->buf_start + reader->last_idx; + ssize_t nbytes = read(reader->pollfd.fd, buf, reader->readlen - reader->buf_start); if (CTLSEQS_UNLIKELY(nbytes == -1)) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return CTLSEQS_TIMEOUT; @@ -231,7 +231,7 @@ ctlseqs_state_transition(enum ctlseqs_state state, char ch) } } -CTLSEQS_HOT bool +CTLSEQS_HOT static bool ctlseqs_fetch(char **seq, int type, union ctlseqs_value *buf, size_t *buf_offset) { unsigned long cnt, num; @@ -322,13 +322,13 @@ ctlseqs_match_pattern(struct ctlseqs_reader *reader, struct ctlseqs_matcher cons } CTLSEQS_HOT static ssize_t -ctlseqs_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *matcher, bool retain_partial) +ctlseqs_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *matcher) { ssize_t retval = CTLSEQS_PARTIAL; char const *buf = reader->rbuf + reader->buf_start; size_t idx, len = reader->buf_end - reader->buf_start; enum ctlseqs_state state = reader->state; - for (idx = 0; idx < len; ++idx) { + for (idx = reader->last_idx; idx < len; ++idx) { state = ctlseqs_state_transition(state, buf[idx]); if (state == ctlseqs_state_err) { for (; idx < len; ++idx) { @@ -344,13 +344,16 @@ ctlseqs_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *match break; } } - if (retval == CTLSEQS_PARTIAL || retval == CTLSEQS_NOSEQ) { + reader->state = state >= ctlseqs_state_done ? ctlseqs_state_none : state; + if (retval == CTLSEQS_NOMATCH || retval == CTLSEQS_PARTIAL || retval == CTLSEQS_NOSEQ) { reader->buffer[0].num = idx; reader->buffer[1].str = buf; } - reader->state = state; - if (!retain_partial || retval != CTLSEQS_PARTIAL) { + if (retval == CTLSEQS_PARTIAL) { + reader->last_idx = idx; + } else { reader->buf_start += idx; + reader->last_idx = 0; } if (reader->buf_start == reader->buf_end) { reader->buf_start = 0; @@ -443,7 +446,9 @@ ctlseqs_reader_init() { struct ctlseqs_reader *reader = malloc(sizeof(struct ctlseqs_reader)); if (CTLSEQS_LIKELY(reader != NULL)) { - *reader = (struct ctlseqs_reader) { 0 }; + *reader = (struct ctlseqs_reader) { + .pollfd.events = POLLIN, + }; } return reader; } @@ -451,13 +456,15 @@ ctlseqs_reader_init() int ctlseqs_reader_setopt(struct ctlseqs_reader *reader, struct ctlseqs_reader_opts const *options) { - reader->buffer = options->buffer; 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 (rbuf == NULL) { @@ -469,39 +476,34 @@ ctlseqs_reader_setopt(struct ctlseqs_reader *reader, struct ctlseqs_reader_opts reader->buf_end = reader->readlen; } } - reader->pollfd = (struct pollfd) { .fd = options->fd, .events = POLLIN }; + reader->buffer = options->buffer; + reader->pollfd.fd = options->fd; reader->no_poll = options->flags & CTLSEQS_READER_NO_POLL; - reader->retain_partial = options->flags & CTLSEQS_READER_RETAIN_PARTIAL; return CTLSEQS_OK; } CTLSEQS_HOT ssize_t ctlseqs_read(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *matcher, int timeout) { - ssize_t match_result; - if (reader->buf_start != 0) { - match_result = ctlseqs_match(reader, matcher, true); - if (match_result != CTLSEQS_PARTIAL) { - goto terminate; + 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) { + result = ctlseqs_match(reader, matcher); + if (result != CTLSEQS_PARTIAL) { + return result; } } if (!reader->no_poll) { - match_result = ctlseqs_poll(&reader->pollfd, timeout); - if (match_result < 0) { - goto terminate; + result = ctlseqs_poll(&reader->pollfd, timeout); + if (result < 0) { + return result; } } - match_result = ctlseqs_do_read(reader); - if (CTLSEQS_UNLIKELY(match_result < 0)) { - if (match_result == CTLSEQS_TIMEOUT && reader->state != ctlseqs_state_none) { - match_result = CTLSEQS_PARTIAL; - } - goto terminate; + result = ctlseqs_do_read(reader); + if (CTLSEQS_UNLIKELY(result < 0)) { + return reader->state == ctlseqs_state_none ? result : CTLSEQS_PARTIAL; } - match_result = ctlseqs_match(reader, matcher, reader->retain_partial); - terminate: - reader->state = ctlseqs_state_none; - return match_result; + return ctlseqs_match(reader, matcher); } CTLSEQS_COLD void diff --git a/src/ctlseqs.h b/src/ctlseqs.h index cd54d8d..cb5b97b 100644 --- a/src/ctlseqs.h +++ b/src/ctlseqs.h @@ -349,8 +349,7 @@ /* Reader option flags */ -#define CTLSEQS_READER_NO_POLL (1 << 0) // Do not poll() before read() -#define CTLSEQS_READER_RETAIN_PARTIAL (1 << 1) // Retain data after a partial match +#define CTLSEQS_READER_NO_POLL (1 << 0) // Do not poll() before read() /* Function return status codes */ diff --git a/tests/tcsgrep.c b/tests/tcsgrep.c index f4dd66d..c710a84 100644 --- a/tests/tcsgrep.c +++ b/tests/tcsgrep.c @@ -389,6 +389,9 @@ main(int argc, char **argv) case CTLSEQS_PARTIAL: print_generic_seq("PARTIAL", buffer); break; + case CTLSEQS_NOMATCH: + print_generic_seq("NOMATCH", buffer); + break; case CTLSEQS_NOSEQ: print_generic_seq("NOSEQ", buffer); if (buffer[1].str[0] == 0x03) {