about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2015-11-14 16:08:05 +0100
committerChristian Neukirchen <chneukirchen@gmail.com>2015-11-14 16:08:05 +0100
commit184dab97de6573e88ef989da4c6b569e9227abdd (patch)
tree53d936c04226d74c13b882149234882d683e163c
downloadarr-184dab97de6573e88ef989da4c6b569e9227abdd.tar.gz
arr-184dab97de6573e88ef989da4c6b569e9227abdd.tar.xz
arr-184dab97de6573e88ef989da4c6b569e9227abdd.zip
initial commit of arr
-rw-r--r--Makefile19
-rw-r--r--arr.c263
2 files changed, 282 insertions, 0 deletions
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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+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;
+}