From 23fdbc8400fc01e977753bc357ec9f4679a60290 Mon Sep 17 00:00:00 2001 From: Christian Neukirchen Date: Sat, 13 Feb 2016 22:30:31 +0100 Subject: replace C prototype with Ruby script --- Makefile | 19 ---- arr | 119 ++++++++++++++++++++++++ arr.c | 311 --------------------------------------------------------------- 3 files changed, 119 insertions(+), 330 deletions(-) delete mode 100644 Makefile create mode 100755 arr delete mode 100644 arr.c 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 +# 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 -#include -#include -#include -#include -#include - -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; -} -- cgit 1.4.1