From 3757fe80b30801119bda41ab0445e915271129fc Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 26 May 2021 03:39:06 +0200 Subject: Allow use of multiple ranges when using cleanup command --- src/track_remove_parser.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/track_remove_parser.c (limited to 'src/track_remove_parser.c') diff --git a/src/track_remove_parser.c b/src/track_remove_parser.c new file mode 100644 index 0000000..61c2375 --- /dev/null +++ b/src/track_remove_parser.c @@ -0,0 +1,133 @@ +#include "track_remove_parser.h" +#include + +typedef enum { + TR_TOK_RANGE, + TR_TOK_END_OF_FILE, + TR_TOK_INVALID +} TrackRemoveToken; + +typedef struct { + const char *str; + size_t size; + size_t offset; + Range range; +} TrackRemoveTokenizer; + +static int is_number(char c) { + return c >= '0' && c <= '9'; +} + +/* Returns non-0 value on overflow */ +static int string_to_num_unchecked(const char *str, size_t size, int *number) { + int result = 0; + for(size_t i = 0; i < size; ++i) { + const int result_before = result; + result *= 10; + result += (str[i] - '0'); + /* overflow */ + if(result <= result_before) + return 1; + } + *number = result; + return 0; +} + +static void track_remover_tokenizer_init(TrackRemoveTokenizer *self, const char *str, size_t size) { + self->str = str; + self->size = size; + self->offset = 0; + self->range.start = 0; + self->range.end = 0; +} + +static int track_remover_tokenizer_get_char(TrackRemoveTokenizer *self) { + if(self->offset < self->size) + return self->str[self->offset]; + else + return '\0'; +} + +static TrackRemoveToken track_remover_tokenizer_next(TrackRemoveTokenizer *self) { + char c; + for(;;) { + c = track_remover_tokenizer_get_char(self); + if(c != ' ' && c != '\t') + break; + ++self->offset; + } + + if(c >= '1' && c <= '9') { + size_t number_start = self->offset; + ++self->offset; + + for(;;) { + c = track_remover_tokenizer_get_char(self); + if(!is_number(c)) + break; + ++self->offset; + } + + if(string_to_num_unchecked(self->str + number_start, self->offset - number_start, &self->range.start) != 0) + return TR_TOK_INVALID; + + self->range.end = self->range.start; + if(c != '-') + return TR_TOK_RANGE; + + ++self->offset; + c = track_remover_tokenizer_get_char(self); + if(!is_number(c)) + return TR_TOK_INVALID; + + number_start = self->offset; + ++self->offset; + + for(;;) { + c = track_remover_tokenizer_get_char(self); + if(!is_number(c)) + break; + ++self->offset; + } + + if(string_to_num_unchecked(self->str + number_start, self->offset - number_start, &self->range.end) != 0) + return TR_TOK_INVALID; + + return TR_TOK_RANGE; + } else if(c == '\0') { + return TR_TOK_END_OF_FILE; + } else { + return TR_TOK_INVALID; + } +} + +int track_remove_parser_parse(const char *str, size_t size, Buffer /*Range*/ *ranges) { + int num_ranges = 0; + TrackRemoveTokenizer tokenizer; + track_remover_tokenizer_init(&tokenizer, str, size); + + for(;;) { + TrackRemoveToken token = track_remover_tokenizer_next(&tokenizer); + if(token == TR_TOK_RANGE) { + if(tokenizer.range.start > tokenizer.range.end) { + fprintf(stderr, "Invalid number range. The start range can't be past the end range\n"); + return 1; + } + + buffer_append(ranges, &tokenizer.range, sizeof(tokenizer.range)); + ++num_ranges; + } else if(token == TR_TOK_END_OF_FILE) { + break; + } else { + fprintf(stderr, "Invalid input. Expected numbers and/or number ranges\n"); + return 1; + } + } + + if(num_ranges == 0) { + fprintf(stderr, "Invalid input. Expected numbers and/or number ranges\n"); + return 1; + } + + return 0; +} -- cgit v1.2.3