aboutsummaryrefslogtreecommitdiff
path: root/src/track_remove_parser.c
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-05-26 03:39:06 +0200
committerdec05eba <dec05eba@protonmail.com>2021-05-26 03:39:06 +0200
commit3757fe80b30801119bda41ab0445e915271129fc (patch)
tree62d282f929d02b96656ec3f8fab5ccef1f839a14 /src/track_remove_parser.c
parent4fabc85d2a59383c67239c488fb2c717657b5954 (diff)
Allow use of multiple ranges when using cleanup command
Diffstat (limited to 'src/track_remove_parser.c')
-rw-r--r--src/track_remove_parser.c133
1 files changed, 133 insertions, 0 deletions
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 <stdio.h>
+
+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;
+}