aboutsummaryrefslogtreecommitdiff
path: root/src/tr.c
diff options
context:
space:
mode:
authorCharles Cabergs <me@cacharle.xyz>2020-08-22 19:43:03 +0200
committerCharles Cabergs <me@cacharle.xyz>2020-08-22 19:43:03 +0200
commitc40f79737a4ce6ae0cd8bd6ea7f302217333e486 (patch)
tree5afe1af26880bd7f8b16ee33fb8e14f99f665938 /src/tr.c
parentf204fe59bd5fa537bf84cb522339c92b16f5a909 (diff)
downloadcoreutils-c40f79737a4ce6ae0cd8bd6ea7f302217333e486.tar.gz
coreutils-c40f79737a4ce6ae0cd8bd6ea7f302217333e486.tar.bz2
coreutils-c40f79737a4ce6ae0cd8bd6ea7f302217333e486.zip
Added Makefile
Diffstat (limited to 'src/tr.c')
-rw-r--r--src/tr.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/src/tr.c b/src/tr.c
new file mode 100644
index 0000000..a423794
--- /dev/null
+++ b/src/tr.c
@@ -0,0 +1,230 @@
+#define _POSIX_C_SOURCE 2
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+
+typedef enum
+{
+ FLAG_COMPLEMENT = 1 << 0,
+ FLAG_DELETE = 1 << 1,
+ FLAG_SQUEEZE = 1 << 2,
+} t_flags;
+
+
+typedef enum
+{
+ MTAG_CHAR,
+ MTAG_CLASS,
+ MTAG_RANGE,
+ /* MTAG_REPEAT, */
+ /* MTAG_EQUIVALENT, */ // e.g [=e=] == eéèê
+} t_matcher_tag;
+
+typedef struct
+{
+ t_matcher_tag tag;
+ union
+ {
+ char c;
+ int (*class)(int c);
+ char range[2];
+ struct
+ {
+ char c;
+ size_t count;
+ } repeat;
+ };
+} t_matcher;
+
+struct char_class_entry {
+ char *id;
+ int (*func)(int c);
+};
+
+struct char_class_entry char_classes[] = {
+ {"[:alnum:]", isalnum},
+ {"[:alpha:]", isalpha},
+ {"[:blank:]", isblank},
+ {"[:cntrl:]", iscntrl},
+ {"[:digit:]", isdigit},
+ {"[:graph:]", isgraph},
+ {"[:lower:]", islower},
+ {"[:print:]", isprint},
+ {"[:punct:]", ispunct},
+ {"[:space:]", isspace},
+ {"[:upper:]", isupper},
+ {"[:xdigit:]", isxdigit},
+};
+
+typedef struct
+{
+ t_matcher *array;
+ size_t count;
+} t_matchers;
+
+void matchers_push(t_matchers *matchers, t_matcher_tag tag, void *data)
+{
+ t_matcher *pushed;
+
+ matchers->count++;
+ matchers->array = realloc(matchers->array, matchers->count);
+ assert(matchers->array != NULL);
+ pushed = &matchers->array[matchers->count - 1];
+ pushed->tag = tag;
+ switch (tag)
+ {
+ case MTAG_CHAR:
+ pushed->c = *(char*)data;
+ break;
+ case MTAG_CLASS:
+ pushed->class = (int (*)(int))data;
+ break;
+ case MTAG_RANGE:
+ pushed->range[0] = ((short)data & 0x00ff) >> 0;
+ pushed->range[1] = ((short)data & 0xff00) >> 8;
+ break;
+ default:
+ abort();
+ }
+}
+
+bool isodigit(char c) { return c >= '0' && c <= '7'; }
+
+void parse(t_matchers *matchers, char *s)
+{
+ for (size_t i = 0; s[i] != '\0'; i++)
+ {
+ if (s[i] == '\\')
+ {
+ memmove(&s[i], &s[i + 1], strlen(&s[i + 1]));
+
+ switch (s[i])
+ {
+ case 'a': s[i] = '\a'; break;
+ case 'b': s[i] = '\b'; break;
+ case 'f': s[i] = '\f'; break;
+ case 'n': s[i] = '\n'; break;
+ case 'r': s[i] = '\r'; break;
+ case 't': s[i] = '\t'; break;
+ case 'v': s[i] = '\v'; break;
+ }
+
+ if (isodigit(s[i]))
+ {
+ char num[4] = {'\0'};
+ strncpy(num, &s[i], 3);
+ char *end;
+ s[i] = strtol(num, &end, 8);
+ memmove(&s[i + 1], &s[i + 1 + (end - num)], strlen(&s[i + 1 + (end - num)]));
+ }
+ matchers_push(matchers, MTAG_CHAR, (void*)s[i]);
+ }
+ else if (s[i + 1] == '-' && s[i + 2] != '\0')
+ matchers_push(matchers, MTAG_RANGE, (void*)(s[i] | (s[i] << 8)));
+ else if (s[i] == '[' && s[i + 1] == ':')
+ {
+ /* for (size_t j = 0; j < sizeof(char_classes) / sizeof(char_class_entry); j++) */
+ /* { */
+ /* if (strncmp( */
+ /* } */
+ }
+ else
+ matchers_push(matchers, MTAG_CHAR, (void*)s[i]);
+ }
+}
+
+char *g_name = "tr";
+
+void fatal(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ fprintf(stderr, "%s: ", g_name);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+#define BUFFER_SIZE 256
+
+/*
+** no 't' flag, truncate is the default behavior
+*/
+
+int main(int argc, char **argv)
+{
+ int option;
+ t_flags flags = 0;
+
+ while ((option = getopt(argc, argv, "cCds")) != -1)
+ {
+ switch (option)
+ {
+ case 'c':
+ case 'C':
+ flags |= FLAG_COMPLEMENT;
+ break;
+ case 'd':
+ flags |= FLAG_DELETE;
+ break;
+ case 's':
+ flags |= FLAG_SQUEEZE;
+ break;
+ }
+ }
+ if (optind == argc)
+ fatal("missing operand\n");
+ if (argc - optind == 3)
+ fatal("extra operand '%s'\n", argv[argc - 1]);
+
+ if (flags & FLAG_DELETE && flags & FLAG_SQUEEZE && argc - optind != 2)
+ fatal("missing operand after '%s'\n"
+ "Two strings must be given when both deleting and squezzing repeats.\n",
+ argv[optind]);
+
+ if (flags & FLAG_DELETE && argc - optind != 1)
+ fatal("extra operand '%s'\n",
+ "Only one string may be given when deleting without squeezing repeats.\n",
+ argv[optind + 1]);
+
+ t_matchers set1 = { .array = NULL, .count = 0 };
+ t_matchers set2 = { .array = NULL, .count = 0 };
+
+ char buf[BUFFER_SIZE + 1] = {'\0'};
+ size_t read_size;
+
+ while ((read_size = fread(buf, sizeof(char), BUFFER_SIZE, stdin)) > 0)
+ {
+ for (size_t i = 0; i < read_size; i++)
+ {
+ /* if (flags & FLAG_DELETE && flags & FLAG_SQUEEZE) */
+ /* { */
+ /* */
+ /* } */
+ /* else if (flags & FLAG_DELETE && match()) */
+ /* { */
+ /* memmove(&buf[i], &buf[i + 1], read_size - i); */
+ /* */
+ /* } */
+ /* else if (flags & FLAG_SQUEEZE) */
+ /* { */
+ /* while (match(s[i + 1])) */
+ /* */
+ /* memmove(&buf[i + 1], &buf[i + 2], read_size - i); */
+ /* } */
+ /* else */
+ /* { */
+ /* if (match()) */
+ /* buf[i] = set2 */
+ /* } */
+ }
+ }
+
+ return EXIT_SUCCESS;
+}