diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 32 | ||||
-rw-r--r-- | README | 38 | ||||
-rw-r--r-- | TODO | 120 | ||||
-rwxr-xr-x | scripts/false | 2 | ||||
-rwxr-xr-x | scripts/true | 1 | ||||
-rw-r--r-- | src/basename.c | 33 | ||||
-rw-r--r-- | src/cat.c | 82 | ||||
-rw-r--r-- | src/dirname.c | 28 | ||||
-rw-r--r-- | src/false.c | 4 | ||||
-rw-r--r-- | src/fold.c | 123 | ||||
-rw-r--r-- | src/iconv.c | 110 | ||||
-rw-r--r-- | src/link.c | 23 | ||||
-rw-r--r-- | src/ls.c | 80 | ||||
-rw-r--r-- | src/pwd.c | 90 | ||||
-rw-r--r-- | src/strings.c | 132 | ||||
-rw-r--r-- | src/tr.c | 118 | ||||
-rw-r--r-- | src/true.c | 4 | ||||
-rw-r--r-- | src/wc.c | 116 |
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) @@ -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 @@ -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; +} |