summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile32
-rw-r--r--README38
-rw-r--r--TODO120
-rwxr-xr-xscripts/false2
-rwxr-xr-xscripts/true1
-rw-r--r--src/basename.c33
-rw-r--r--src/cat.c82
-rw-r--r--src/dirname.c28
-rw-r--r--src/false.c4
-rw-r--r--src/fold.c123
-rw-r--r--src/iconv.c110
-rw-r--r--src/link.c23
-rw-r--r--src/ls.c80
-rw-r--r--src/pwd.c90
-rw-r--r--src/strings.c132
-rw-r--r--src/tr.c118
-rw-r--r--src/true.c4
-rw-r--r--src/wc.c116
19 files changed, 1139 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0cc99a1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.o
+bin/*
+config.mak
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..259f782
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,32 @@
+
+bindir = /bin
+usrbindir = /usr/bin
+
+CFLAGS = -D_XOPEN_SOURCE=700 -Os -s -Wall -Wno-format -Wno-char-subscripts -Wno-unused -Wno-parentheses
+LDFLAGS = -s
+
+-include config.mak
+
+B = cat true false pwd
+UB = strings basename dirname link wc fold iconv
+
+ALL = $(B:%=bin/%) $(UB:%=bin/%)
+
+all: $(ALL)
+
+clean:
+ rm -f $(ALL)
+
+bin/%: src/%.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+install: $(patsubst bin/%,$(bindir)/%,$(B)) $(patsubst bin/%,$(usrbindir)/%,$(UB))
+
+size: $(ALL)
+ size -t $(ALL)
+
+$(bindir)/%: bin/%
+ install $< $(bindir)
+
+$(usrbindir)/%: bin/%
+ install $< $(usrbindir)
diff --git a/README b/README
new file mode 100644
index 0000000..99298cd
--- /dev/null
+++ b/README
@@ -0,0 +1,38 @@
+noXCUse: New Open XCU Simple Edition
+ or, No Excuse for bloat and brokenness
+
+This package is a collection of implementation of some (but by no
+means all) of the utilities specified in the SUS/POSIX "XCU" (Shell &
+Utilities) volume. They are designed to be as simple as possible while
+meeting the requirements of the standard and general usability
+considerations (like not being horrendously slow or imposing excessive
+limitations). Unlike other size-oriented implementations such as
+Busybox and Toybox, noXCUse does not use a unified binary or
+significant amounts of shared-across-tools library code, making it
+easy to cherry-pick the set of tools you need for a particular system.
+
+Development priority has been given to the tools whose implementations
+in Busybox are inadequate; in most cases, it's a matter of Busybox
+failing to support multibyte characters (UTF-8) in utilities whose
+primary function is character processing (such as fold and wc).
+
+In addition to the "priority" utilities, some low-hanging fruit (that
+is, utterly trivial utilities) has been included simply "because it's
+easy".
+
+These utilities were developed and tested using musl as the C library,
+and while they aim to be conforming programs with respect to SUS, they
+make some assumptions: for example that multibyte character encoding
+is stateless and that resynchronization is possible. The iconv utility
+also has a hard-coded list of (as subset of) the charsets supported by
+musl, since there is no portable way to query the C iconv interface
+for this information.
+
+Finally, note that aside from the iconv utility, no development has
+been done on these utilities since 2007 (pre-SUSv4/POSIX-2008). I may
+gradually add more utilities to the collection, but this is completely
+a side project.
+
+Cheers,
+
+Rich Felker / dalias
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..bad0bfb
--- /dev/null
+++ b/TODO
@@ -0,0 +1,120 @@
+DONE
+
+:
+basename
+cat
+dirname
+false
+link
+pwd
+true
+wc
+
+TODO
+
+ar
+awk
+bc
+cal
+chgrp
+chmod
+chown
+cksum
+cmp
+comm
+compress
+cp
+csplit
+cut
+date
+dd
+df
+diff
+du
+echo
+ed
+env
+ex
+expand
+expr
+file
+find
+fold
+fuser
+gencat
+getconf
+getopts
+grep
+head
+iconv
+id
+ipcrm
+ipcs
+join
+kill
+lex
+ln
+locale
+localedef
+logger
+logname
+ls
+m4
+mailx
+make
+man
+mesg
+mkdir
+mkfifo
+more
+mv
+newgrp
+nice
+nl
+nm
+nohup
+od
+paste
+patch
+pathchk
+pax
+pr
+printf
+prs
+ps
+renice
+rm
+rmdel
+rmdir
+sed
+sh
+sleep
+sort
+split
+strings
+stty
+tabs
+tail
+tee
+test
+time
+times
+touch
+tput
+tr
+trap
+tsort
+tty
+type
+uname
+uncompress
+unexpand
+uniq
+unlink
+uudecode
+uuencode
+who
+write
+xargs
+yacc
+zcat
diff --git a/scripts/false b/scripts/false
new file mode 100755
index 0000000..ecdbef9
--- /dev/null
+++ b/scripts/false
@@ -0,0 +1,2 @@
+#!/bin/sh
+exit 1
diff --git a/scripts/true b/scripts/true
new file mode 100755
index 0000000..1a24852
--- /dev/null
+++ b/scripts/true
@@ -0,0 +1 @@
+#!/bin/sh
diff --git a/src/basename.c b/src/basename.c
new file mode 100644
index 0000000..25b8b24
--- /dev/null
+++ b/src/basename.c
@@ -0,0 +1,33 @@
+#include <unistd.h>
+#include <string.h>
+#include <libgen.h>
+
+static int my_write(int fd, const char *s, size_t l)
+{
+ if (!l) l = strlen(s);
+ while (l) {
+ ssize_t n = write(fd, s, l);
+ if (n<0) return -1;
+ s += n; l -= n;
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char *s;
+ if ((unsigned)argc - 2 > 1) {
+ my_write(2, argv[0], 0);
+ my_write(2, ": missing operand\n", 0);
+ return 1;
+ }
+ s = basename(argv[1]);
+ if (argc > 2) {
+ size_t k = strlen(s);
+ size_t l = strlen(argv[2]);
+ if (k>l && !strcmp(s+(k-l), argv[2])) s[k-l]=0;
+ }
+ if (my_write(1, s, 0)<0 || my_write(1, "\n", 1)<0)
+ return 1;
+ return 0;
+}
diff --git a/src/cat.c b/src/cat.c
new file mode 100644
index 0000000..7901a89
--- /dev/null
+++ b/src/cat.c
@@ -0,0 +1,82 @@
+/*
+ * cat.c
+ * Implementation of SUSv3 XCU cat utility
+ * Copyright © 2007 Rich Felker
+ * Licensed under the terms of the GNU General Public License, v2 or later
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+
+static int my_write(int fd, const char *s, size_t l)
+{
+ while (l) {
+ ssize_t n = write(fd, s, l);
+ if (n<0) return -1;
+ s += n; l -= n;
+ }
+ return 0;
+}
+
+static void my_perror(char *prog, char *msg)
+{
+ char *err = strerror(errno);
+ write(2, prog, strlen(prog));
+ write(2, ": ", 2);
+ write(2, msg, strlen(msg));
+ write(2, ": ", 2);
+ write(2, err, strlen(err));
+ write(2, "\n", 1);
+}
+
+int main(int argc, char *argv[])
+{
+ char buf[4096];
+ char **v = argv+1;
+ int n = argc-1;
+ ssize_t i, k, l;
+ int err = 0;
+ int fd;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ for (; n && v[0][0] == '-' && v[0][1]; v++, n--) {
+ if (v[0][1]=='-' && !v[0][2]) { /* -- */
+ v++; n--;
+ break;
+ }
+ if (v[0][1]!='u' || v[0][2]) { /* not -u */
+ errno = EINVAL;
+ my_perror(argv[0], v[0]);
+ exit(1);
+ }
+ }
+ if (!n) {
+ n = 1;
+ v = (char *[]){ "-", NULL };
+ }
+ for (; n; v++, n--) {
+ if (v[0][0] == '-' && !v[0][1]) fd = 0;
+ else if ((fd = open(v[0], O_RDONLY)) < 0) {
+ my_perror(argv[0], v[0]);
+ err = 1;
+ continue;
+ }
+ while ((l = read(fd, buf, sizeof buf)) > 0) {
+ if (my_write(1, buf, l) < 0) {
+ my_perror(argv[0], "write error");
+ exit(1);
+ }
+ }
+ if (l) {
+ my_perror(argv[0], v[0]);
+ err = 1;
+ }
+ if (fd) close(fd);
+ }
+ return err;
+}
diff --git a/src/dirname.c b/src/dirname.c
new file mode 100644
index 0000000..982606a
--- /dev/null
+++ b/src/dirname.c
@@ -0,0 +1,28 @@
+#include <unistd.h>
+#include <string.h>
+#include <libgen.h>
+
+static int my_write(int fd, const char *s, size_t l)
+{
+ if (!l) l = strlen(s);
+ while (l) {
+ ssize_t n = write(fd, s, l);
+ if (n<0) return -1;
+ s += n; l -= n;
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char *s;
+ if (argc != 2) {
+ my_write(2, argv[0], 0);
+ my_write(2, ": missing operand\n", 0);
+ return 1;
+ }
+ s = dirname(argv[1]);
+ if (my_write(1, s, 0)<0 || my_write(1, "\n", 1)<0)
+ return 1;
+ return 0;
+}
diff --git a/src/false.c b/src/false.c
new file mode 100644
index 0000000..f37cfc4
--- /dev/null
+++ b/src/false.c
@@ -0,0 +1,4 @@
+int main()
+{
+ return 1;
+}
diff --git a/src/fold.c b/src/fold.c
new file mode 100644
index 0000000..64e4534
--- /dev/null
+++ b/src/fold.c
@@ -0,0 +1,123 @@
+/*
+ * fold.c
+ * Implementation of SUSv3 XCU fold utility
+ * Copyright © 2007 Rich Felker
+ * Licensed under the terms of the GNU General Public License, v2 or later
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <unistd.h>
+#include <limits.h>
+#include <locale.h>
+#include <errno.h>
+
+static void my_perror(char *prog, char *msg)
+{
+ char *err = strerror(errno);
+ write(2, prog, strlen(prog));
+ write(2, ": ", 2);
+ write(2, msg, strlen(msg));
+ write(2, ": ", 2);
+ write(2, err, strlen(err));
+ write(2, "\n", 1);
+}
+
+int main(int argc, char *argv[])
+{
+ int b;
+ char *name;
+ char dummy[MB_LEN_MAX];
+ size_t i, j, len=127;
+ wchar_t wc, *buf = malloc(sizeof wc * len);
+ long col, out_col, width=80;
+ int w;
+ int splitable;
+ int byte=0, space=0;
+ FILE *f;
+
+ if (!setlocale(LC_CTYPE, ""))
+ my_perror(argv[0], "setlocale");
+
+ while ((b = getopt(argc, argv, "bsw:")) != EOF) switch(b) {
+ case 'b': byte=1; break;
+ case 's': space=1; break;
+ case 'w':
+ width=strtoul(optarg, NULL, 10);
+ break;
+ default:
+ return 1;
+ }
+
+ if (optind == argc) {
+ f = stdin;
+ name = "-";
+ goto nofiles;
+ }
+
+ while (optind < argc) {
+ f = fopen(name=argv[optind++], "rb");
+ if (!f) {
+ my_perror(argv[0], name);
+ continue;
+ }
+nofiles:
+ i = col = out_col = 0;
+ splitable = -1;
+ while ((wc = getwc(f))>=0) {
+ if (i == len) {
+ buf = realloc(buf, sizeof wc * (len=len<<1|1));
+ if (!buf) {
+ my_perror(argv[0], "realloc");
+ return 1;
+ }
+ }
+ buf[i++] = wc;
+
+ if (wc == '\n') {
+ col = 0;
+ splitable = -1;
+ goto output;
+ }
+ else if (byte) w = wctomb(dummy, wc);
+ else if (wc == '\t') {
+ w = 8-(col&7);
+ if (w > width - col) w = 8;
+ }
+ else if (wc == '\r') col = w = 0;
+ else if (wc == '\b') {
+ if (col) col--;
+ w = 0;
+ }
+ else w = wcwidth(wc);
+
+ if (col && w > width - col) {
+ if (putwchar('\n')==WEOF) goto werr;
+ col -= out_col;
+ splitable = -1;
+ }
+ col += w;
+
+ if (space && iswblank(wc))
+ splitable = 1;
+ if (splitable) {
+output:
+ for (j=0; j<i; j++)
+ if (putwchar(buf[j])==WEOF) goto werr;
+ i = 0;
+ out_col = col;
+ splitable >>= 1;
+ }
+ }
+ if (ferror(f)) my_perror(argv[0], name);
+ if (f != stdin) fclose(f);
+ }
+ return 0;
+werr:
+ my_perror(argv[0], "write error");
+ return 1;
+}
diff --git a/src/iconv.c b/src/iconv.c
new file mode 100644
index 0000000..a0938d6
--- /dev/null
+++ b/src/iconv.c
@@ -0,0 +1,110 @@
+/*
+ * iconv.c
+ * Implementation of SUSv4 XCU iconv utility
+ * Copyright © 2011 Rich Felker
+ * Licensed under the terms of the GNU General Public License, v2 or later
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <iconv.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+ const char *from=0, *to=0;
+ int b;
+ iconv_t cd;
+ char buf[BUFSIZ];
+ char outbuf[BUFSIZ*4];
+ char *in, *out;
+ size_t inb;
+ size_t l;
+ size_t unitsize=0;
+ int err=0;
+ FILE *f;
+
+ while ((b = getopt(argc, argv, "f:t:csl")) != EOF) switch(b) {
+ case 'l':
+ puts("UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, UTF32-LE, UCS-2BE, UCS-2LE, WCHAR_T,\n"
+ "US_ASCII, ISO8859-1, ISO8859-2, ISO8859-3, ISO8859-4, ISO8859-5,\n"
+ "ISO8859-6, ISO8859-7, ...");
+ exit(0);
+ case 'c': case 's': break;
+ case 'f': from=optarg; break;
+ case 't': to=optarg; break;
+ default: exit(1);
+ }
+
+ if (!from || !to) {
+ setlocale(LC_CTYPE, "");
+ if (!to) to = nl_langinfo(CODESET);
+ if (!from) from = nl_langinfo(CODESET);
+ }
+ cd = iconv_open(to, from);
+ if (cd == (iconv_t)-1) {
+ if (iconv_open(to, "WCHAR_T") == (iconv_t)-1)
+ fprintf(stderr, "iconv: destination charset %s", to);
+ else
+ fprintf(stderr, "iconv: source charset %s", from);
+ perror("");
+ exit(1);
+ }
+ if (optind == argc) argv[argc++] = "-";
+
+ for (; optind < argc; optind++) {
+ if (argv[optind][0]=='-' && !argv[optind][1]) {
+ f = stdin;
+ argv[optind] = "(stdin)";
+ } else if (!(f = fopen(argv[optind], "rb"))) {
+ fprintf(stderr, "iconv: %s", argv[optind]);
+ perror("");
+ err = 1;
+ continue;
+ }
+ inb = 0;
+ for (;;) {
+ in = buf;
+ out = outbuf;
+ l = fread(buf+inb, 1, sizeof(buf)-inb, f);
+ inb += l;
+ if (!inb) break;
+ if (iconv(cd, &in, &inb, &out, (size_t [1]){sizeof outbuf})==-1
+ && errno == EILSEQ) {
+ if (!unitsize) {
+ wchar_t wc='0';
+ char dummy[4], *dummyp=dummy;
+ iconv_t cd2 = iconv_open(from, "WCHAR_T");
+ if (cd == (iconv_t)-1) {
+ unitsize = 1;
+ } else {
+ iconv(cd2,
+ (char *[1]){(char *)&wc},
+ (size_t[1]){1},
+ &dummyp, (size_t[1]){4});
+ unitsize = dummyp-dummy;
+ if (!unitsize) unitsize=1;
+ }
+ }
+ inb-=unitsize;
+ in+=unitsize;
+ }
+ if (inb && !l && errno==EINVAL) break;
+ if (out>outbuf && !fwrite(outbuf, out-outbuf, 1, stdout)) {
+ perror("iconv: write error");
+ exit(1);
+ }
+ if (inb) memmove(buf, in, inb);
+ }
+ if (ferror(f)) {
+ fprintf(stderr, "iconv: %s", argv[optind]);
+ perror("");
+ err = 1;
+ }
+ }
+ return err;
+}
diff --git a/src/link.c b/src/link.c
new file mode 100644
index 0000000..fd00ae0
--- /dev/null
+++ b/src/link.c
@@ -0,0 +1,23 @@
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#define CSTR(s) (s), sizeof(s)-1
+
+int main(int argc, char *argv[])
+{
+ if (argc != 3) {
+ write(2, argv[0], strlen(argv[0]));
+ write(2, CSTR(": incorrect usage\n"));
+ exit(1);
+ }
+ if (link(argv[1], argv[2])) {
+ write(2, argv[0], strlen(argv[0]));
+ write(2, CSTR(": "));
+ write(2, strerror(errno), strlen(strerror(errno)));
+ write(2, CSTR("\n"));
+ exit(1);
+ }
+ return 0;
+}
diff --git a/src/ls.c b/src/ls.c
new file mode 100644
index 0000000..defee1e
--- /dev/null
+++ b/src/ls.c
@@ -0,0 +1,80 @@
+/*
+ * ls.c
+ * Implementation of SUSv3 XCU ls utility
+ * Copyright © 2007 Rich Felker
+ * Licensed under the terms of the GNU General Public License, v2 or later
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <unistd.h>
+#include <limits.h>
+#include <locale.h>
+#include <errno.h>
+
+static size_t mbswidth(const char *s)
+{
+ size_t l, w=0;
+ int v;
+ wchar_t wc;
+ mbstate_t mbs = { 0 };
+ for (w=0; *s; s+=l, w+=v)
+ if ((l=mbrtowc(&wc, s, MB_LEN_MAX, &mbs))+2<2
+ || (v=wcwidth(wc)) < 0)
+ return -1;
+ return w;
+}
+
+static void my_perror(char *prog, char *msg)
+{
+ char *err = strerror(errno);
+ write(2, prog, strlen(prog));
+ write(2, ": ", 2);
+ write(2, msg, strlen(msg));
+ write(2, ": ", 2);
+ write(2, err, strlen(err));
+ write(2, "\n", 1);
+}
+
+static int cmp(const void *a, const void *b)
+{
+ return strcoll(*(const char **)a, *(const char **)b);
+}
+
+int main(int argc, char *argv[])
+{
+ struct stat st;
+ int (*xstat)(const char *, struct stat *) = stat;
+
+ if (!setlocale(LC_ALL, ""))
+ my_perror(argv[0], "setlocale");
+
+ while ((b=getopt(argc,argv,"CFHLRacdfgilmnoprstux1"))!=EOF) switch(b) {
+ default:
+ return 1;
+ }
+
+ if (optind == argc) {
+ }
+
+ for (i=optind; i<argc; ) {
+ if (xstat(argv[i], &st)) {
+ my_perror(argv[0], argv[i]);
+ memmove(&argv[i], &argv[i+1], argc-i-1);
+ argc--;
+ } else if (S_ISDIR(st.st_mode) && !opt_d) {
+ char *tmp = argv[i];
+ argv[i] = argv[--firstdir];
+ argv[fisrtdir] = tmp;
+ if (i == firstdir) break;
+ } else i++;
+ }
+ qsort(argv+optind, firstdir-optind, sizeof(char *), cmp);
+ qsort(argv+firstdir, argc-firstdir, sizeof(char *), cmp);
+}
+
+
diff --git a/src/pwd.c b/src/pwd.c
new file mode 100644
index 0000000..8ccd28b
--- /dev/null
+++ b/src/pwd.c
@@ -0,0 +1,90 @@
+/*
+ * pwd.c
+ * Implementation of SUSv3 XCU pwd utility
+ * Copyright © 2007 Rich Felker
+ * Licensed under the terms of the GNU General Public License, v2 or later
+ */
+
+/* NOTE: This implementation assumes PATH_MAX is defined and correct. */
+
+#include <limits.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+static int my_write(int fd, const char *s, size_t l)
+{
+ if (!l) l = strlen(s);
+ while (l) {
+ ssize_t n = write(fd, s, l);
+ if (n<0) return -1;
+ s += n; l -= n;
+ }
+ return 0;
+}
+
+static void my_perror(char *prog, char *msg)
+{
+ char *err = strerror(errno);
+ write(2, prog, strlen(prog));
+ write(2, ": ", 2);
+ write(2, msg, strlen(msg));
+ write(2, ": ", 2);
+ write(2, err, strlen(err));
+ write(2, "\n", 1);
+}
+
+static int is_rel(const char *s)
+{
+ unsigned dots=0, slash=1;
+ if (*s++ != '/') return 1;
+ for (; *s; s++) {
+ if (*s == '/') {
+ if (dots-1<2) return 1;
+ dots=0;
+ }
+ else slash=1;
+ if (slash && *s == '.') dots++;
+ else dots=slash=0;
+ }
+ return dots-1<2;
+}
+
+int main(int argc, char *argv[])
+{
+ int i, j;
+ int p=0;
+ char buf[PATH_MAX+2];
+ struct stat st1, st2;
+ char *pwd;
+
+ for (i=1; i<argc && argv[i][0]=='-'; i++)
+ for (j=1; argv[i][j]; j++) {
+ switch (argv[i][j]) {
+ case 'L': p=0; continue;
+ case 'P': p=1;
+ case 0: continue;
+ }
+invalid:
+ errno = EINVAL;
+ my_perror(argv[0], argv[i]);
+ return 1;
+ }
+ if (i<argc) goto invalid;
+ if (p || !(pwd=getenv("PWD")) || is_rel(pwd)
+ || stat(pwd, &st1) || stat(".", &st2)
+ || st1.st_dev != st2.st_dev
+ || st1.st_ino != st2.st_ino) {
+ if (!getcwd(buf, sizeof buf)) {
+ my_perror(argv[0], "getcwd");
+ return 1;
+ }
+ } else {
+ /* safe because stat succeeded */
+ strcpy(buf, pwd);
+ }
+ strcat(buf, "\n");
+ return my_write(1, buf, 0) != 0;
+}
diff --git a/src/strings.c b/src/strings.c
new file mode 100644
index 0000000..5a1781b
--- /dev/null
+++ b/src/strings.c
@@ -0,0 +1,132 @@
+/*
+ * strings.c
+ * Implementation of SUSv3 XCU strings utility
+ * Copyright © 2007 Rich Felker
+ * Licensed under the terms of the GNU General Public License, v2 or later
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <unistd.h>
+#include <limits.h>
+#include <locale.h>
+
+static int my_mbrtowc(wchar_t *wc, int b, mbstate_t *st)
+{
+ char c = b;
+ int retry = 1;
+retry:
+ switch ((int)mbrtowc(wc, &c, 1, st)) {
+ case -2:
+ return -2;
+ case -1:
+ memset(st, 0, sizeof(mbstate_t));
+ if (retry--) goto retry;
+ return retry; /* yes this is lame */
+ }
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ mbstate_t st;
+ wchar_t *buf;
+ size_t i, min = 4;
+ int b;
+ off_t start, ofs;
+ int l;
+ char fmtbuf[] = "%ll? ", *fmt=fmtbuf+5;
+ int ret = 0;
+ int in_str;
+ FILE *f = stdin;
+ char *name = "(stdin)";
+
+ if (!setlocale(LC_CTYPE, "")) {
+ fprintf(stderr, "%s: warning: cannot set LC_CTYPE", argv[0]);
+ perror("");
+ }
+
+ while ((b = getopt(argc, argv, "an:t:")) != EOF) switch(b) {
+ case 'a':
+ /* no-op: we always search entire file */
+ break;
+ case 'n':
+ min = strtoul(optarg, NULL, 10);
+ if (min < 1 || min >= SIZE_MAX) {
+ fprintf(stderr, "%s: invalid argument to -%c: %s\n",
+ argv[0], b, optarg);
+ exit(1);
+ }
+ break;
+ case 't':
+ if (!strchr("dox", optarg[0]) || optarg[1]) {
+ fprintf(stderr, "%s: invalid argument to -%c: %s\n",
+ argv[0], b, optarg);
+ exit(1);
+ }
+ fmt = fmtbuf;
+ fmt[3] = optarg[0];
+ break;
+ default:
+ exit(1);
+ }
+
+ /* min+1 does not overflow because of above check */
+ if (!(buf = calloc(sizeof(wchar_t), min+1))) {
+ fprintf(stderr, "%s: ", argv[0]);
+ perror("calloc failed");
+ exit(1);
+ }
+
+ if (optind < argc) goto nextfile;
+begin:
+ memset(&st, 0, sizeof(mbstate_t));
+ start = ofs = i = in_str = 0;
+ while ((b=getc(f)) >= 0) {
+ ofs++;
+ l = my_mbrtowc(buf+i, b, &st);
+ if (l == -2) continue;
+ if (l == -1 || !iswprint(buf[i])) {
+ if (in_str) putchar('\n');
+ in_str = 0;
+ i = 0;
+ start = ofs;
+ continue;
+ }
+ if (i<min-1) {
+ i++;
+ continue;
+ }
+ if (!in_str) {
+ in_str = 1;
+ printf(fmt, (long long)start);
+ printf("%ls", buf);
+ continue;
+ }
+ printf("%lc", buf[i]);
+ }
+ if (ferror(f)) {
+ fprintf(stderr, "%s: error reading file %s", argv[0], name);
+ perror("");
+ ret = 1;
+ }
+ fclose(f);
+nextfile:
+ if (optind < argc) {
+ name = argv[optind++];
+ if (!(f = fopen(name, "rb"))) {
+ fprintf(stderr, "%s: error opening file %s",
+ argv[0], name);
+ perror("");
+ ret = 1;
+ goto nextfile;
+ }
+ goto begin;
+ }
+
+ return ret;
+}
diff --git a/src/tr.c b/src/tr.c
new file mode 100644
index 0000000..5dad823
--- /dev/null
+++ b/src/tr.c
@@ -0,0 +1,118 @@
+
+#include <stdio.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <unistd.h>
+#include <assert.h>
+
+
+int index_of(wchar_t c, char *s)
+{
+ mbstate_t mbst, mbst_init = { };
+ int i=0;
+ char *z;
+
+ while (*s) {
+ if (s[0] == '[' && s[1] == ':') {
+ wctype_t type;
+ char typename[32];
+ z = strchr(s, ':');
+ if (!z || z[1] != ']' || z-s >= sizeof typename) {
+ ERROR;
+ }
+ memcpy(typename, s, z-s);
+ typename[z-s] = 0;
+ type = wctype(typename);
+ if (!type)
+ ERROR;
+ if (iswctype(c, type))
+ return i;
+ }
+
+ if (mbrtowc(&wc, s, MB_LEN_MAX, &mbst) >= (size_t)-2)
+ return ERROR;
+ }
+}
+
+
+char *parse_repl(char *s1, char *s2)
+{
+ struct map *head, *tail;
+ int i;
+ for (i=j=0; s1[i]; i++) {
+ tail->next = malloc(sizeof(struct map));
+ tail = tail->next;
+
+ if (!strncmp(s1, "[:upper:]", 9)
+ && !strncmp(s2, "[:lower:]", 9)) {
+ tail->from = tail->to = 0;
+ s1 += 9; s2 += 9;
+ }
+ if (!strncmp(s1, "[:lower:]", 9)
+ && !strncmp(s2, "[:upper:]", 9)) {
+ tail->from = tail->to = 1;
+ s1 += 9; s2 += 9;
+ }
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ int o;
+ int cmpl = 0, del = 0, sqez = 0, repl = 1;
+ wchar_t ch, pv = WEOF;
+ char b[MB_LEN_MAX];
+ int i, j;
+ mbstate_t mbst, mbst_init = { };
+
+ setlocale(LC_CTYPE, "");
+
+ while ((o = getopt(argc, argv, "Ccds")) != EOF)
+ {
+ switch (o)
+ {
+ case 'C':
+ case 'c':
+ cmpl = 1;
+ break;
+ case 'd':
+ del = 1;
+ break;
+ case 's':
+ sqez = 1;
+ break;
+ default:
+ return 1;
+ }
+ }
+ string1 = argc-optind >= 1 ? argv[optind] : "";
+ string2 = argc-optind >= 2 ? argv[optind+1] : "";
+ if (!string2 || del) repl = 0;
+
+ i=0;
+ while (b[i] = getchar() != EOF) {
+retry:
+ switch (mbrtowc(&wc, b+i, 1, &mbst)) {
+ case -1:
+ if (!i) {
+ putchar(b[0]);
+ mbst = mbst_init;
+ continue;
+ }
+ for (j=0; j<i; j++)
+ putchar(b[j]);
+ b[i=0] = b[j];
+ mbst = mbst_init;
+ goto retry;
+ case -2:
+ i++;
+ continue;
+ }
+ b[++i] = 0;
+ }
+
+
+
+ return 0;
+}
diff --git a/src/true.c b/src/true.c
new file mode 100644
index 0000000..a46866d
--- /dev/null
+++ b/src/true.c
@@ -0,0 +1,4 @@
+int main()
+{
+ return 0;
+}
diff --git a/src/wc.c b/src/wc.c
new file mode 100644
index 0000000..3689595
--- /dev/null
+++ b/src/wc.c
@@ -0,0 +1,116 @@
+/*
+ * wc.c
+ * Implementation of SUSv3 XCU wc utility
+ * Copyright © 2007 Rich Felker
+ * Licensed under the terms of the GNU General Public License, v2 or later
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <locale.h>
+
+static int my_mbrtowc(wchar_t *wc, int b, mbstate_t *st)
+{
+ char c = b;
+ int retry = 1;
+retry:
+ switch ((int)mbrtowc(wc, &c, 1, st)) {
+ case -2:
+ return -2;
+ case -1:
+ memset(st, 0, sizeof(mbstate_t));
+ if (retry--) goto retry;
+ return retry; /* yes this is lame */
+ }
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int b;
+ int pc=0, pm=0, pw=0, pl=0;
+ unsigned long long c, m, w, l;
+ unsigned long long tc=0, tm=0, tw=0, tl=0;
+ int sp;
+ mbstate_t st;
+ wchar_t wc;
+ char *file;
+ FILE *f;
+ int err = 0;
+ int totals = 0, noname = 0;
+
+ setlocale(LC_CTYPE, "");
+
+ while ((b = getopt(argc, argv, "cmwl")) != EOF) switch(b) {
+ case 'c': pc=1; break;
+ case 'm': pm=1; break;
+ case 'w': pw=1; break;
+ case 'l': pl=1; break;
+ default: exit(1);
+ }
+ if (!(pc|pm|pw|pl)) pc=pw=pl=1;
+ if (pc&pm) {
+ fprintf(stderr, "%s: -c and -m are not valid together\n",
+ argv[0]);
+ exit(1);
+ }
+ if (optind == argc) {
+ file = "(stdin)";
+ f = stdin;
+ noname = 1;
+ goto use_stdin;
+ } else if (argc-optind > 1) totals=1;
+ for (; optind < argc; optind++) {
+ file = argv[optind];
+ f = fopen(file, "rb");
+ if (!f) {
+ fprintf(stderr, "%s: %s: ", argv[0], file);
+ perror("");
+ err = 1;
+ continue;
+ }
+use_stdin:
+ memset(&st, 0, sizeof st);
+ for (sp=1, c=m=w=l=0; (b=getc(f)) >= 0; c++) {
+ if ((pm|pw) && my_mbrtowc(&wc, b, &st) >= 0) {
+ m++;
+ if (iswspace(wc)) sp=1;
+ else if (sp) sp=0, w++;
+ }
+ if (b == '\n') l++;
+ }
+ if (ferror(f)) {
+ fprintf(stderr, "%s: %s: ", argv[0], file);
+ perror("");
+ err = 1;
+ }
+ tl += l; tw += w; tm += m; tc += c;
+totals:
+ if (pl) printf("%7llu", l);
+ if (pl&pw) putchar(' ');
+ if (pw) printf("%7llu", w);
+ if ((pl|pw)&(pc|pm)) putchar(' ');
+ if (pc|pm) printf("%7llu", pm?m:c);
+ if (!noname) printf(" %s", file);
+ putchar('\n');
+ fclose(f);
+ }
+ if (totals) {
+ l = tl;
+ w = tw;
+ m = tm;
+ c = tc;
+ file = "total";
+ f = stdin;
+ totals = 0;
+ goto totals;
+ }
+ return err;
+}