about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2016-02-13 22:30:31 +0100
committerChristian Neukirchen <chneukirchen@gmail.com>2016-02-13 22:30:31 +0100
commit23fdbc8400fc01e977753bc357ec9f4679a60290 (patch)
tree984e4f2cf775156ac368b152a163c4ad61754906
parentd7852457e1d9eafb4b0a6e93c3e26070420c45a1 (diff)
downloadarr-23fdbc8400fc01e977753bc357ec9f4679a60290.tar.gz
arr-23fdbc8400fc01e977753bc357ec9f4679a60290.tar.xz
arr-23fdbc8400fc01e977753bc357ec9f4679a60290.zip
replace C prototype with Ruby script
-rw-r--r--Makefile19
-rwxr-xr-xarr119
-rw-r--r--arr.c311
3 files changed, 119 insertions, 330 deletions
diff --git a/Makefile b/Makefile
deleted file mode 100644
index db9df32..0000000
--- a/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-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 b/arr
new file mode 100755
index 0000000..8d5ba5a
--- /dev/null
+++ b/arr
@@ -0,0 +1,119 @@
+#!/usr/bin/env ruby
+# arr EXPR [FILES...] - (re)arrange and select fields on each line
+#
+# To the extent possible under law,
+# Christian Neukirchen <chneukirchen@gmail.com>
+# has waived all copyright and related or neighboring rights to this work.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+USAGE = <<'EOF'
+Usage: arr [-0] [-P|-p PADDING] EXPR [FILES...]
+  EXPR   ::= FIELDS (("|" CHAR | "*") FIELDS)*  # | split on char, * split bytes
+  FIELDS ::= "~"? FIELD ("," FIELD)*            # ~ negates
+  FIELD  ::= "-"? "\d"+                         # negative fields count from back
+           | ("-"? "\d"+)? ":" ("-"? "\d"+)?    # range ends default to 1:-1
+EOF
+
+require 'strscan'
+require 'optparse'
+
+def fmt(ss, d)
+  last_split = " "
+
+  loop {
+    fields = []
+    neg = false
+
+    begin
+      i = j = nil
+      neg = true  if ss.scan(/~/)
+      if ss.scan(/:|-?\d+/)
+        i = Integer(ss.matched) rescue 1
+        i = i < 0 ? d.size + i : i - 1
+        if ss.matched == ":" || ss.scan(/:/)
+          if ss.scan(/-?\d+/)
+            j = Integer(ss.matched)
+          else
+            j = -1
+          end
+          j = j < 0 ? d.size + j : j - 1
+
+          if j > i
+            fields.concat (i..j).to_a
+          else
+            fields.concat (j..i).to_a.reverse
+          end
+        else
+          fields << i
+        end
+      else
+        abort "parse error at #{ss.inspect}"
+      end
+    end while ss.scan(/,/)
+    
+    d = d.values_at(*if neg
+                       (0..d.size).to_a - fields
+                     else
+                       fields
+                     end)
+        
+    if ss.scan(/\|(.)/)
+      d = d.join(last_split)
+      last_split = ss[1]
+      d = d.split(last_split)
+    elsif ss.scan(/\*/)
+      d = d.join(last_split)
+      last_split = ""
+      d = d.split('')
+    else
+      break
+    end
+  }
+
+  unless ss.scan(/\}/)
+    abort "parse error at #{ss.inspect}"
+  end
+    
+  d.compact.join(last_split)
+end
+
+def fmt2(str, arr)
+  ss = StringScanner.new(str)
+  
+  r = ""
+
+  while ss.scan(/(.*?)%\{/)
+    r << ss[1]
+    r << fmt(ss, arr)
+  end
+
+  r << ss.rest
+end
+
+begin
+  params = ARGV.getopts('0Pp:')
+  nl = params["0"] ? "\0" : "\n"
+  padding = params["p"] || ""
+  padding = nil  if params["P"]
+  
+  expr = ARGV.shift  or raise OptionParser::MissingArgument, "no EXPR given"
+
+  ARGV << "-"  if ARGV.empty?
+
+  files = ARGV.map { |name|
+    if name == "-"
+      STDIN
+    else
+      File.open(name, "rb")
+    end
+  }
+
+  until files.all?(&:eof?)
+    lines = files.map { |f| f.gets(nl).chomp(nl) rescue padding }
+    break  if lines.include?(nil)
+    print fmt2(expr, lines), nl
+  end
+rescue OptionParser::ParseError
+  STDERR.puts $!
+  STDERR.puts USAGE
+end
diff --git a/arr.c b/arr.c
deleted file mode 100644
index 552cbf4..0000000
--- a/arr.c
+++ /dev/null
@@ -1,311 +0,0 @@
-// arr - (re)arrange and select fields on each line
-
-/*
-##% gcc -Os -Wall -g -o $STEM $FILE -Wextra -Wwrite-strings
-*/
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-char lastsplit;
-static char *s;
-
-//  "%{" FIELD (CHOP FIELD)* (("|" | "*") FIELD ((, | :) FIELD)*)? "}"
-
-void
-fmt_range(char **args, int bytewise, size_t argsnum)
-{
-	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);
-			if (errno != 0) {
-				fprintf(stderr, "can't parse number at '%s'.\n", s);
-				exit(1);
-			}
-			s = end;
-			if (bytewise) {
-				if (n < 0)
-					n += argsnum + 1;
-				if (n < 0)
-					n = 0;
-				if (n >= 1 && n <= argsnum)
-					printf("%c", args[0][n-1]);
-			} else {
-				if (n < 0)
-					n += argsnum;
-				if (printed++)
-					printf("%c", lastsplit);
-				if (n > 0 && n < argsnum)
-					printf("%s", args[n]);
-			}
-			break;
-		case ',':
-			n = 0;
-			s++;
-			break;
-		case ':': {
-			s++;
-			long l = n;
-			errno = 0;
-			n = strtol(s, &end, 10);
-			if (errno != 0) {
-				fprintf(stderr, "can't parse number at '%s'.\n", s);
-				exit(1);
-			}
-			if (s == end)  // default to -1
-				n = -1;
-			s = end;
-			if (bytewise) {
-				if (n < 0)
-					n += argsnum + 1;
-				if (n < 0)
-					n = 0;
-				if (n > argsnum)
-					n = argsnum;
-				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 (n < 0)
-					n += argsnum;
-				if (n >= argsnum)
-					n = argsnum-1;
-				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, size_t argsnum)
-{
-	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);
-			if (errno != 0) {
-				fprintf(stderr, "can't parse number at '%s'.\n", s);
-				exit(1);
-			}
-			if (s == end && *s == '-')
-				goto split;
-			s = end;
-			if (newfield < 0)
-				newfield += argsnum;
-			field = newfield;
-			break;
-		case '|':
-			s++;
-			fmt_range(args, 0, argsnum);
-			return;
-		case '*':
-			s++;
-			if (field >= 0 && field < argsnum)
-				fmt_range(args+field, 1, strlen(args[field]));
-			else
-				fmt_range(args, 1, 0);
-			return;
-		case '}':
-			s++;
-			if (field >= 1 && field < argsnum)
-				printf("%s", args[field]);
-			return;
-		case '"':
-			s++;
-			lastsplit = *s;
-			s++;
-			if (*s != '"') {
-				fprintf(stderr, "invalid syntax '\"%c%s'\n",
-				    lastsplit, s);
-				exit(1);
-			}
-			s++;
-			goto split2;
-		default: { /* split at char, recurse */
-			split:
-			lastsplit = *s;
-			s++;
-			split2:;
-			char *t;
-			if (field >= 1 && field < argsnum)
-				t = strdup(args[field]);
-			else
-				t = strdup("");
-			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;
-			argsnum = i;
-			field = 0;
-		}
-		}
-	}
-}
-
-void
-fmt(const char *pattern, char **args, size_t argsnum)
-{
-	s = pattern;
-	lastsplit = '\t';
-
-	while (*s) {
-		switch (*s) {
-		case '%':
-			s++;
-			switch (*s) {
-			case '%':
-				s++;
-				putchar('%');
-				break;
-			case '{':
-				s++;
-				fmt_inner(args, argsnum);
-				break;
-			default:
-				putchar('%');
-			}
-			break;
-		default:
-			putchar(*s);
-			s++;
-		}
-	}
-}
-
-int
-main(int argc, char *argv[]) {
-        // number of implicit stdin arguments
-        int stdins = 0;
-
-	char c;
-	char *padding = "";
-	int delim = '\n';
-
-	while ((c = getopt(argc, argv, "0Pp:")) != -1)
-		switch(c) {
-		case '0': delim = '\0'; break;
-		case 'p': padding = optarg; break;
-		case 'P': padding = 0; break;
-                default: goto usage;
-		}
-
-	if (optind >= argc) {
-usage:
-		fprintf(stderr, "Usage: %s FMT FILES...\n", argv[0]);
-		exit(1);
-	}
-
-	int argnum = argc - optind;
-	char **arglist = argv + optind;  // starts counting at 1
-
-	// default to stdin when no file arguments are given
-	if (argnum <= 1)
-		stdins++;
-
-	FILE **files = calloc(argnum+stdins, sizeof (FILE *));
-	char **lines = calloc(argnum+stdins, sizeof (char *));
-	int i;
-	for (i = 1; i < argnum+stdins; i++) {
-		if (i >= argnum || strcmp(arglist[i], "-") == 0) {
-			files[i] = stdin;
-		} else {
-			files[i] = fopen(arglist[i], "r");
-			if (!files[i]) {
-				fprintf(stderr, "%s: %s: %s\n",
-				    argv[0], arglist[i], strerror(errno));
-				exit(1);
-			}
-		}
-	}
-
-	int eof;
-	size_t len;
-	while (1) {
-		eof = 0;
-		for (i = 1; i < argnum+stdins; i++) {
-			int read = getdelim(lines+i, &len, delim, files[i]);
-			if (read == -1) {
-				if (feof(files[i])) {
-					if (!padding)
-						exit(0);
-					lines[i] = strdup(padding);
-					eof++;
-				} else {
-					exit(1);
-				}
-			} else {
-				// strip delimiter
-				if (lines[i][read-1] == delim)
-					lines[i][read-1] = 0;
-			}
-		}
-		if (eof >= argnum+stdins-1)
-			break;
-		fmt(arglist[0], lines, argnum+stdins);
-		printf("%c", delim);
-	}
-
-	return 0;
-}