From 466dd62ba30b8270379a79238bc85e21a75ebcbe Mon Sep 17 00:00:00 2001 From: Leah Neukirchen Date: Thu, 19 Oct 2017 15:15:04 +0200 Subject: initial import of necho --- Makefile | 36 ++++++++++++++++ README | 55 +++++++++++++++++++++++++ necho.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 necho.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6d4328b --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +CFLAGS=-g -O2 -Wall -Wno-switch -Wextra + +DESTDIR= +PREFIX=/usr/local +BINDIR=$(PREFIX)/bin +MANDIR=$(PREFIX)/share/man + +ALL = necho +LINKS = zecho qecho jecho secho + +all: $(ALL) $(LINKS) $(LINKS:=.1) + +README: necho.1 + mandoc -Tutf8 $< | col -bx >$@ + +necho: necho.c + +$(LINKS): necho + ln -sf $< $@ + +$(LINKS:=.1): necho.1 + ln -sf $< $@ + +install: FRC all + mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1 + install -m0755 $(ALL) $(DESTDIR)$(BINDIR) + install -m0644 $(ALL:=.1) $(DESTDIR)$(MANDIR)/man1 + for l in $(LINKS); do \ + ln -sf necho $(DESTDIR)$(BINDIR)/$$l; \ + ln -sf necho.1 $(DESTDIR)$(MANDIR)/man1/$$l.1; \ + done + +clean: FRC + rm -f $(ALL) $(LINKS) $(LINKS:=.1) + +FRC: diff --git a/README b/README new file mode 100644 index 0000000..52be312 --- /dev/null +++ b/README @@ -0,0 +1,55 @@ +NECHO(1) General Commands Manual NECHO(1) + +NAME + necho, zecho, qecho, jecho, secho – minimal, sensible alternatives to + echo(1) + +SYNOPSIS + necho [string ...] + zecho [string ...] + qecho [string ...] + jecho [string ...] + secho [string ...] + +DESCRIPTION + The necho utility writes its arguments, one per line, to standard output. + + The zecho utility writes its arguments, terminated by NUL bytes, to + standard output. + + The qecho utility writes its arguments, surrounded by unicode quotation + marks, to standard output. + + The jecho utility writes its arguments joined together to standard + output. + + The necho utility writes its arguments, separated by spaces, to standard + output, followed by a . If there are no arguments, only the + is written. + +OPTIONS + Implementations do not support any options, nor special handling of a + “--” argument. + +EXIT STATUS + These utilities exit 0 on success, and >0 if an error occurs. + +SEE ALSO + echo(1), printf(1) + +STANDARDS + secho is a POSIX-conforming, but not XSI-conforming implementation of + echo(1). + +AUTHORS + Leah Neukirchen + +LICENSE + These utilities are in the public domain. + + To the extent possible under law, the creator of this work has waived all + copyright and related or neighboring rights to this work. + + http://creativecommons.org/publicdomain/zero/1.0/ + +Void Linux October 19, 2017 Void Linux diff --git a/necho.c b/necho.c new file mode 100644 index 0000000..56fdf4d --- /dev/null +++ b/necho.c @@ -0,0 +1,142 @@ +/* + * minimal, sensible alternatives to echo(1) + * + * necho - print arguments, one per line + * zecho - print arguments, nul-terminated + * qecho - print arguments surrounded in unicode quotation marks + * jecho - print arguments joined together + * secho - print arguments joined with spaces, and a newline + * + * These commands print all arguments as-is, and try to use + * - one syscall per argument for necho, zecho, qecho + * - few writev syscalls for jecho, secho + * + * secho is a POSIX-conforming, but not XSI-conforming implementation of echo(1). + * + * To the extent possible under law, the creator of this work has waived + * all copyright and related or neighboring rights to this work. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#define _XOPEN_SOURCE 700 + +#include + +#include +#include +#include +#include +#include + +/* struct iovec[IOV_MAX] should fit on the stack */ +#if IOV_MAX > 1024 +#define SENSIBLE_IOV_MAX 1024 +#else +#define SENSIBLE_IOV_MAX IOV_MAX +#endif + +/* like writev(2), but deal with short writes */ +ssize_t +safe_writev(int fd, struct iovec *iov, int iovcnt) +{ + ssize_t len, wr; + int i; + + for (i = 0, len = 0; i < iovcnt; i++) + len += iov[i].iov_len; + + while (len > 0) { + if ((wr = writev(fd, iov, iovcnt)) < 0) + return -1; + + len -= wr; + if (len <= 0) + return 0; + + /* short write, clear emitted prefix of iovec */ + for (i = 0; i < iovcnt && wr < (ssize_t)iov[i].iov_len; i++) { + wr -= iov[i].iov_len; + iov[i].iov_base = 0; + iov[i].iov_len = 0; + } + if (i < iovcnt) { + iov[i].iov_base = (char *)iov[i].iov_base + wr; + iov[i].iov_len -= wr; + } + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + int i; + + char *style = strrchr(argv[0], '/'); + if (style) + style++; + else + style = argv[0]; + + errno = 0; + + if (*style == 'q') { + for (i = 1; i < argc; i++) { + ssize_t len = strlen(argv[i]); + struct iovec iov[] = { +/* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */ + { "\302\273", 2 }, + { argv[i], len }, +/* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */ + { "\302\253", 2 }, + { i == argc - 1 ? "\n" : " ", 1 } + }; + if (safe_writev(1, iov, 4) < 0) + break; + } + } else if (*style == 'j' || (*style == 's' || *style == 'e')) { + struct iovec iov[SENSIBLE_IOV_MAX]; + int j; + + for (i = 1; i < argc; ) { + for (j = 0; i < argc && j < SENSIBLE_IOV_MAX - 1; j++) { + iov[j].iov_base = argv[i]; + iov[j].iov_len = strlen(argv[i]); + i++; + if (*style != 'j') { + j++; + iov[j].iov_base = i == argc ? "\n" : " "; + iov[j].iov_len = 1; + } + } + if (safe_writev(1, iov, j) < 0) + break; + } + } else { /* default to necho */ + for (i = 1; i < argc; i++) { + struct iovec iov[] = { + { argv[i], strlen(argv[i]) }, + { *style == 'z' ? "\0" : "\n", 1 } + }; + if (safe_writev(1, iov, 2) < 0) + break; + } + } + + if (errno) { +#ifndef TINY + char *msg = strerror(errno); + struct iovec iov[] = { + { style, strlen(style) }, + { ": write error: ", 15 }, + { msg, strlen(msg) }, + { "\n", 1 }, + }; + (void)writev(2, iov, 4); +#endif + return 1; + } + + return 0; +} -- cgit 1.4.1