From 184dab97de6573e88ef989da4c6b569e9227abdd Mon Sep 17 00:00:00 2001 From: Christian Neukirchen Date: Sat, 14 Nov 2015 16:08:05 +0100 Subject: initial commit of arr --- Makefile | 19 +++++ arr.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 282 insertions(+) create mode 100644 Makefile create mode 100644 arr.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..db9df32 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +ALL=arr + +CFLAGS=-g -O2 -Wall -Wextra -Wwrite-strings + +DESTDIR= +PREFIX=/usr/local +BINDIR=$(PREFIX)/bin +MANDIR=$(PREFIX)/share/man + +all: $(ALL) + +clean: FRC + rm -f $(ALL) + +install: FRC all + mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1 + install -m0755 $(ALL) $(DESTDIR)$(BINDIR) + +FRC: diff --git a/arr.c b/arr.c new file mode 100644 index 0000000..ba896ce --- /dev/null +++ b/arr.c @@ -0,0 +1,263 @@ +// arr - (re)arrange and select fields on each line + +/* +##% gcc -Os -Wall -g -o $STEM $FILE -Wextra -Wwrite-strings +*/ + +#include +#include +#include +#include +#include + +long range[32]; +long rangemax; +char lastsplit; +static char *s; + +// TODO: negative numbers count from end +// TODO: C-style literals +// %FIELD shortcut? +// default to as many - as there are references to fields? + +// "%{" FIELD (CHOP FIELD)* (("|" | "*") FIELD ((, | :) FIELD)*)? "}" + +void +fmt_range(char **args, int bytewise) +{ + long n = 0; + char *end; + + int printed = 0; + + while (*s) { + switch (*s) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '-': + errno = 0; + n = strtol(s, &end, 10); + // XXX error handling + s = end; + if (bytewise) { + printf("%c", args[0][n-1]); + } else { + if (printed++) + printf("%c", lastsplit); + printf("%s", args[n]); + } + break; + case ',': + n = 0; + s++; + break; + case ':': { + s++; + long l = n; + errno = 0; + n = strtol(s, &end, 10); + // XXX error handling + s = end; + if (bytewise) { + if (l <= n) + for (l++; l <= n; l++) + printf("%c", args[0][l-1]); + else + for (l--; l >= n; l--) + printf("%c", args[0][l-1]); + } else { + if (l <= n) + for (l++; l <= n; l++) { + if (printed++) + printf("%c", lastsplit); + printf("%s", args[l]); + } + else + for (l--; l >= n; l--) { + if (printed++) + printf("%c", lastsplit); + printf("%s", args[l]); + } + } + break; + } + case '}': + s++; + return; + default: + fprintf(stderr, "invalid range at '%s'\n", s); + exit(1); + } + } +} + +void +fmt_inner(char **args) +{ + long field, newfield; + char *end; + int i; + + field = 0; + + while (*s) { + switch (*s) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '-': + errno = 0; + newfield = strtol(s, &end, 10); + // XXX error handling + if (s == end && *s == '-') + goto split; + s = end; + field = newfield; + break; + case '|': + s++; + fmt_range(args, 0); + return; + case '*': + s++; + fmt_range(args+field, 1); + return; + case '}': + s++; + if (field == 0) + abort(); + printf("%s", args[field]); + return; + case '"': + s++; + lastsplit = *s; + // XXX check for second " + s++; + s++; + goto split2; + default: { /* split at char, recurse */ + split: + /* TODO "|" */ + if (field == 0) + abort(); + lastsplit = *s; + s++; + split2:; + char *t = strdup(args[field]); + char **newargs = calloc(32 /*XXX*/, sizeof (char *)); + if (lastsplit == ' ') { // split on any ws + while (isspace((unsigned char)*t)) + t++; + newargs[1] = t; + for (i = 2; *t; t++) { + if (isspace((unsigned char)*t)) { + while (isspace((unsigned char)*(t+1))) + *t++ = 0; + newargs[i++] = t + 1; + } + } + } else { + newargs[1] = t; + for (i = 2; *t; t++) { + if (*t == lastsplit) { + *t = 0; + newargs[i++] = t + 1; + } + } + } + args = newargs; + field = 0; + } + } + } +} + +void +fmt(const char *pattern, char **args) +{ + s = pattern; + lastsplit = '\t'; + rangemax = 0; + + while (*s) { + switch (*s) { + case '%': + s++; + switch (*s) { + case '%': + s++; + putchar('%'); + break; + case '{': + s++; + fmt_inner(args); + break; + default: + putchar('%'); + } + break; + default: + putchar(*s); + s++; + } + } +} + +int +main(int argc, char *argv[]) { + char s[] = "foo,bar,baz,quux"; + char t[] = "bo/ing;zi/ng;zo/ng"; + char v[] = " words wordss\t wordsss"; + char *a[] = { 0, s, t, v }; + /* fmt("hello\n", a); */ + /* fmt("%{1}\n", a); */ + /* fmt("%{2}\n", a); */ + /* fmt("%{1,2}\n", a); */ + /* fmt(">>%{2}<<\n", a); */ + /* fmt(">>%{2;|1:3}<<\n", a); */ + /* fmt(">>%{2;|3:1}<<\n", a); */ + + /* fmt(">>%{2;|1:3} %{2;|3:1} %{2;|1,3}<<\n", a); */ + /* fmt(">>%{2;|1,2,3} %{2;|3,2,1} %{2;|1,3}<<\n", a); */ + /* fmt(">>%{2;2}<<\n", a); */ + /* fmt(">>%{2;2/1}<<\n", a); */ + /* fmt(">>%{3 |1:3}<<\n", a); */ + /* fmt(">>%{3 1*1}<<\n", a); */ + /* fmt(">>%{3 1*1:4}<<\n", a); */ + /* fmt("%zz%%zz\n", a); */ + + // default to stdin when no file arguments are given + if (argc == 2) { + argv[argc] = "-"; + argc++; + } + + FILE **files = calloc(argc, sizeof (FILE *)); + char **lines = calloc(argc, sizeof (char *)); + int i; + for (i = 1; i < argc-1; i++) + if (strcmp(argv[i+1], "-") == 0) + files[i] = stdin; + else + files[i] = fopen(argv[i], "r"); // XXX error handling + + int eof; + size_t len; + int delim = '\n'; + while (1) { + eof = 0; + for (i = 1; i < argc-1; i++) { + int read = getdelim(lines+i, &len, delim, files[i]); + // XXX error handling + if (lines[i][read-1] == delim) // strip delimiter + lines[i][read-1] = 0; + if (feof(files[i])) + eof++; + } + if (eof) + break; + fmt(argv[1], lines); + printf("%c", delim); + } + + return 0; +} -- cgit 1.4.1