about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2017-10-19 15:15:04 +0200
committerLeah Neukirchen <leah@vuxu.org>2017-10-19 15:15:04 +0200
commit466dd62ba30b8270379a79238bc85e21a75ebcbe (patch)
tree312c59892368d64a8b91790a2a3c1dea4bcc688e
downloadnecho-466dd62ba30b8270379a79238bc85e21a75ebcbe.tar.gz
necho-466dd62ba30b8270379a79238bc85e21a75ebcbe.tar.xz
necho-466dd62ba30b8270379a79238bc85e21a75ebcbe.zip
initial import of necho
-rw-r--r--Makefile36
-rw-r--r--README55
-rw-r--r--necho.c142
3 files changed, 233 insertions, 0 deletions
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 <newline>.  If there are no arguments, only the
+     <newline> 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 <leah@vuxu.org>
+
+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 <sys/uio.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+/* 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;
+}