summaryrefslogtreecommitdiff
path: root/corebinutils
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:23:12 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:23:12 +0300
commitd857ba85ce6b1c2b1309df6f39a5a2a57aa335de (patch)
treec94820a6cd4cf2bb2269b29408c0923d9842f4ad /corebinutils
parent43d9b4ac189e87a06216924fd6e417a0e0f62c6c (diff)
parent7f792c1c56a38c0d39a7e0b99d1636b642d78efd (diff)
downloadProject-Tick-d857ba85ce6b1c2b1309df6f39a5a2a57aa335de.tar.gz
Project-Tick-d857ba85ce6b1c2b1309df6f39a5a2a57aa335de.zip
Add 'corebinutils/cat/' from commit '7f792c1c56a38c0d39a7e0b99d1636b642d78efd'
git-subtree-dir: corebinutils/cat git-subtree-mainline: 43d9b4ac189e87a06216924fd6e417a0e0f62c6c git-subtree-split: 7f792c1c56a38c0d39a7e0b99d1636b642d78efd
Diffstat (limited to 'corebinutils')
-rw-r--r--corebinutils/cat/.gitignore45
-rw-r--r--corebinutils/cat/GNUmakefile32
-rw-r--r--corebinutils/cat/LICENSE32
-rw-r--r--corebinutils/cat/LICENSES/BSD-3-Clause.txt11
-rw-r--r--corebinutils/cat/README.md27
-rw-r--r--corebinutils/cat/cat.1214
-rw-r--r--corebinutils/cat/cat.c585
-rw-r--r--corebinutils/cat/tests/test.sh91
8 files changed, 1037 insertions, 0 deletions
diff --git a/corebinutils/cat/.gitignore b/corebinutils/cat/.gitignore
new file mode 100644
index 0000000000..4df974f9b5
--- /dev/null
+++ b/corebinutils/cat/.gitignore
@@ -0,0 +1,45 @@
+*.a
+*.core
+*.lo
+*.nossppico
+*.o
+*.orig
+*.pico
+*.pieo
+*.po
+# Don't ignore translation files under `contrib/...`.
+!contrib/**/po
+*.rej
+*.so
+*.so.[0-9]*
+*.sw[nop]
+*~
+_.tinderbox.*
+_.universe-toolchain
+_.amd64.*
+_.arm.*
+_.arm64.*
+_.i386.*
+_.powerpc.*
+_.riscv.*
+.*DS_Store
+.depend*
+GPATH
+GRTAGS
+GTAGS
+ID
+cscope.files
+cscope.in.out
+cscope.out
+cscope.po.out
+compile_commands.json
+compile_commands.events.json
+tags
+.cache
+.clangd
+.ccls-cache
+sys/*/compile
+/src.conf
+build/
+out/
+.linux-obj/
diff --git a/corebinutils/cat/GNUmakefile b/corebinutils/cat/GNUmakefile
new file mode 100644
index 0000000000..e3dde0f99a
--- /dev/null
+++ b/corebinutils/cat/GNUmakefile
@@ -0,0 +1,32 @@
+.DEFAULT_GOAL := all
+
+CC ?= cc
+CPPFLAGS += -D_GNU_SOURCE
+CFLAGS ?= -O2
+CFLAGS += -g -Wall -Wextra -Wno-unused-parameter
+LDFLAGS ?=
+LDLIBS ?=
+
+OBJDIR := $(CURDIR)/build
+OUTDIR := $(CURDIR)/out
+TARGET := $(OUTDIR)/cat
+OBJS := $(OBJDIR)/cat.o
+
+.PHONY: all clean dirs test
+
+all: $(TARGET)
+
+dirs:
+ @mkdir -p "$(OBJDIR)" "$(OUTDIR)"
+
+$(TARGET): $(OBJS) | dirs
+ $(CC) $(LDFLAGS) -o "$@" $(OBJS) $(LDLIBS)
+
+$(OBJDIR)/cat.o: $(CURDIR)/cat.c | dirs
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c "$(CURDIR)/cat.c" -o "$@"
+
+test: $(TARGET)
+ CAT_BIN="$(TARGET)" sh "$(CURDIR)/tests/test.sh"
+
+clean:
+ @rm -rf "$(OBJDIR)" "$(OUTDIR)"
diff --git a/corebinutils/cat/LICENSE b/corebinutils/cat/LICENSE
new file mode 100644
index 0000000000..4181bb7a7a
--- /dev/null
+++ b/corebinutils/cat/LICENSE
@@ -0,0 +1,32 @@
+Copyright (c) 1989, 1993
+ The Regents of the University of California. All rights reserved.
+
+Copyright (c) 2026
+ Project Tick. All rights reserved.
+
+This code is derived from software contributed to Berkeley by
+Kevin Fall.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE. \ No newline at end of file
diff --git a/corebinutils/cat/LICENSES/BSD-3-Clause.txt b/corebinutils/cat/LICENSES/BSD-3-Clause.txt
new file mode 100644
index 0000000000..ea890afbc7
--- /dev/null
+++ b/corebinutils/cat/LICENSES/BSD-3-Clause.txt
@@ -0,0 +1,11 @@
+Copyright (c) <year> <owner>.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/corebinutils/cat/README.md b/corebinutils/cat/README.md
new file mode 100644
index 0000000000..c128da7d7a
--- /dev/null
+++ b/corebinutils/cat/README.md
@@ -0,0 +1,27 @@
+# cat
+
+Standalone musl-libc-based Linux port of FreeBSD `cat` for Project Tick BSD/Linux Distribution.
+
+## Build
+
+```sh
+gmake -f GNUmakefile
+```
+
+Binary output:
+
+```sh
+out/cat
+```
+
+## Test
+
+```sh
+gmake -f GNUmakefile test
+```
+
+## Notes
+
+- This project is intentionally decoupled from the top-level FreeBSD build.
+- Linux builds disable the FreeBSD Capsicum/Casper path.
+- No shared BSD compat shim is used; source is adjusted directly for Linux.
diff --git a/corebinutils/cat/cat.1 b/corebinutils/cat/cat.1
new file mode 100644
index 0000000000..35744858cd
--- /dev/null
+++ b/corebinutils/cat/cat.1
@@ -0,0 +1,214 @@
+.\"-
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Copyright (c) 2026
+.\" Project Tick. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd January 29, 2013
+.Dt CAT 1
+.Os
+.Sh NAME
+.Nm cat
+.Nd concatenate and print files
+.Sh SYNOPSIS
+.Nm
+.Op Fl belnstuv
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility reads files sequentially, writing them to the standard output.
+The
+.Ar file
+operands are processed in command-line order.
+If
+.Ar file
+is a single dash
+.Pq Sq Fl
+or absent,
+.Nm
+reads from the standard input.
+If
+.Ar file
+is a
+.Ux
+domain socket,
+.Nm
+connects to it and then reads it until
+.Dv EOF .
+This complements the
+.Ux
+domain binding capability available in
+.Xr inetd 8 .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl b
+Number the non-blank output lines, starting at 1.
+.It Fl e
+Display non-printing characters (see the
+.Fl v
+option), and display a dollar sign
+.Pq Ql \&$
+at the end of each line.
+.It Fl l
+Set an exclusive advisory lock on the standard output file descriptor.
+This lock is set using
+.Xr fcntl 2
+with the
+.Dv F_SETLKW
+command.
+If the output file is already locked,
+.Nm
+will block until the lock is acquired.
+.It Fl n
+Number the output lines, starting at 1.
+.It Fl s
+Squeeze multiple adjacent empty lines, causing the output to be
+single spaced.
+.It Fl t
+Display non-printing characters (see the
+.Fl v
+option), and display tab characters as
+.Ql ^I .
+.It Fl u
+Disable output buffering.
+.It Fl v
+Display non-printing characters so they are visible.
+Control characters print as
+.Ql ^X
+for control-X; the delete
+character (octal 0177) prints as
+.Ql ^? .
+.Pf Non- Tn ASCII
+characters (with the high bit set) are printed as
+.Ql M-
+(for meta) followed by the character for the low 7 bits.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+The command:
+.Pp
+.Dl "cat file1"
+.Pp
+will print the contents of
+.Pa file1
+to the standard output.
+.Pp
+The command:
+.Pp
+.Dl "cat file1 file2 > file3"
+.Pp
+will sequentially print the contents of
+.Pa file1
+and
+.Pa file2
+to the file
+.Pa file3 ,
+truncating
+.Pa file3
+if it already exists.
+See the manual page for your shell (e.g.,
+.Xr sh 1 )
+for more information on redirection.
+.Pp
+The command:
+.Pp
+.Dl "cat file1 - file2 - file3"
+.Pp
+will print the contents of
+.Pa file1 ,
+print data it receives from the standard input until it receives an
+.Dv EOF
+.Pq Sq ^D
+character, print the contents of
+.Pa file2 ,
+read and output contents of the standard input again, then finally output
+the contents of
+.Pa file3 .
+Note that if the standard input refers to a file, the second dash
+on the command-line would have no effect, since the entire contents of the file
+would have already been read and printed by
+.Nm
+when it encountered the first
+.Sq Fl
+operand.
+.Sh SEE ALSO
+.Xr head 1 ,
+.Xr more 1 ,
+.Xr pr 1 ,
+.Xr sh 1 ,
+.Xr tail 1 ,
+.Xr vis 1 ,
+.Xr zcat 1 ,
+.Xr fcntl 2 ,
+.Xr setbuf 3
+.Rs
+.%A Rob Pike
+.%T "UNIX Style, or cat -v Considered Harmful"
+.%J "USENIX Summer Conference Proceedings"
+.%D 1983
+.Re
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.2-92
+specification.
+.Pp
+The flags
+.Op Fl belnstv
+are extensions to the specification.
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v1 .
+.An Dennis Ritchie
+designed and wrote the first man page.
+It appears to have been for
+.Nm .
+.Sh BUGS
+Because of the shell language mechanism used to perform output
+redirection, the command
+.Dq Li cat file1 file2 > file1
+will cause the original data in
+.Pa file1
+to be destroyed!
+.Pp
+The
+.Nm
+utility does not recognize multibyte characters when the
+.Fl t
+or
+.Fl v
+option is in effect.
diff --git a/corebinutils/cat/cat.c b/corebinutils/cat/cat.c
new file mode 100644
index 0000000000..f6aaa15960
--- /dev/null
+++ b/corebinutils/cat/cat.c
@@ -0,0 +1,585 @@
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 2026
+ * Project Tick. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kevin Fall.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#ifndef __linux__
+#include <sys/capsicum.h>
+#endif
+#if !defined(NO_UDOM_SUPPORT) && !defined(__linux__)
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netdb.h>
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#ifndef __linux__
+#include <capsicum_helpers.h>
+#include <libcasper.h>
+#include <casper/cap_fileargs.h>
+#include <casper/cap_net.h>
+#endif
+
+static int bflag, eflag, lflag, nflag, sflag, tflag, vflag;
+static int rval;
+static const char *filename;
+#ifndef __linux__
+static fileargs_t *fa;
+#endif
+
+static void usage(void) __attribute__((noreturn));
+static void scanfiles(char *argv[], int cooked);
+#ifndef BOOTSTRAP_CAT
+static void cook_cat(FILE *);
+static ssize_t in_kernel_copy(int);
+#endif
+static void raw_cat(int);
+
+#ifndef MAXPHYS
+#define MAXPHYS (128 * 1024)
+#endif
+
+static inline bool
+is_wascii(wint_t wc)
+{
+ return ((wc & ~0x7f) == 0);
+}
+
+#ifdef __linux__
+#define init_casper(argc, argv) ((void)0)
+#define caph_cache_catpages() ((void)0)
+#define caph_enter_casper() (0)
+#endif
+
+#ifndef NO_UDOM_SUPPORT
+#ifndef __linux__
+static cap_channel_t *capnet;
+
+static int udom_open(const char *path, int flags);
+#endif
+#endif
+
+/*
+ * Memory strategy threshold, in pages: if physmem is larger than this,
+ * use a large buffer.
+ */
+#define PHYSPAGES_THRESHOLD (32 * 1024)
+
+/* Maximum buffer size in bytes - do not allow it to grow larger than this. */
+#define BUFSIZE_MAX (2 * 1024 * 1024)
+
+/*
+ * Small (default) buffer size in bytes. It's inefficient for this to be
+ * smaller than MAXPHYS.
+ */
+#define BUFSIZE_SMALL (MAXPHYS)
+
+
+/*
+ * For the bootstrapped cat binary (needed for locked appending to METALOG), we
+ * disable all flags except -l and -u to avoid non-portable function calls.
+ * In the future we may instead want to write a small portable bootstrap tool
+ * that locks the output file before writing to it. However, for now
+ * bootstrapping cat without multibyte support is the simpler solution.
+ */
+#ifdef BOOTSTRAP_CAT
+#define SUPPORTED_FLAGS "lu"
+#else
+#define SUPPORTED_FLAGS "belnstuv"
+#endif
+
+#if !defined(NO_UDOM_SUPPORT) && !defined(__linux__)
+static void
+init_casper_net(cap_channel_t *casper)
+{
+ cap_net_limit_t *limit;
+ int familylimit;
+
+ capnet = cap_service_open(casper, "system.net");
+ if (capnet == NULL)
+ err(EXIT_FAILURE, "unable to create network service");
+
+ limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR |
+ CAPNET_CONNECTDNS);
+ if (limit == NULL)
+ err(EXIT_FAILURE, "unable to create limits");
+
+ familylimit = AF_LOCAL;
+ cap_net_limit_name2addr_family(limit, &familylimit, 1);
+
+ if (cap_net_limit(limit) != 0)
+ err(EXIT_FAILURE, "unable to apply limits");
+}
+#endif
+
+#ifndef __linux__
+static void
+init_casper(int argc, char *argv[])
+{
+ cap_channel_t *casper;
+ cap_rights_t rights;
+
+ casper = cap_init();
+ if (casper == NULL)
+ err(EXIT_FAILURE, "unable to create Casper");
+
+ fa = fileargs_cinit(casper, argc, argv, O_RDONLY, 0,
+ cap_rights_init(&rights, CAP_READ, CAP_FSTAT, CAP_FCNTL, CAP_SEEK),
+ FA_OPEN | FA_REALPATH);
+ if (fa == NULL)
+ err(EXIT_FAILURE, "unable to create fileargs");
+
+#ifndef NO_UDOM_SUPPORT
+ init_casper_net(casper);
+#endif
+
+ cap_close(casper);
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ struct flock stdout_lock;
+
+ setlocale(LC_CTYPE, "");
+
+ while ((ch = getopt(argc, argv, SUPPORTED_FLAGS)) != -1)
+ switch (ch) {
+ case 'b':
+ bflag = nflag = 1; /* -b implies -n */
+ break;
+ case 'e':
+ eflag = vflag = 1; /* -e implies -v */
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 't':
+ tflag = vflag = 1; /* -t implies -v */
+ break;
+ case 'u':
+ setbuf(stdout, NULL);
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (lflag) {
+ stdout_lock.l_len = 0;
+ stdout_lock.l_start = 0;
+ stdout_lock.l_type = F_WRLCK;
+ stdout_lock.l_whence = SEEK_SET;
+ if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) != 0)
+ err(EXIT_FAILURE, "stdout");
+ }
+
+ init_casper(argc, argv);
+
+ caph_cache_catpages();
+
+ if (caph_enter_casper() != 0)
+ err(EXIT_FAILURE, "capsicum");
+
+ if (bflag || eflag || nflag || sflag || tflag || vflag)
+ scanfiles(argv, 1);
+ else
+ scanfiles(argv, 0);
+ if (fclose(stdout))
+ err(1, "stdout");
+ exit(rval);
+ /* NOTREACHED */
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: cat [-" SUPPORTED_FLAGS "] [file ...]\n");
+ exit(1);
+ /* NOTREACHED */
+}
+
+static void
+scanfiles(char *argv[], int cooked)
+{
+ int fd, i;
+ char *path;
+#ifndef BOOTSTRAP_CAT
+ FILE *fp;
+#endif
+
+ (void)cooked;
+
+ i = 0;
+ fd = -1;
+ while ((path = argv[i]) != NULL || i == 0) {
+ if (path == NULL || strcmp(path, "-") == 0) {
+ filename = "stdin";
+ fd = STDIN_FILENO;
+ } else {
+ filename = path;
+#ifdef __linux__
+ fd = open(path, O_RDONLY);
+#else
+ fd = fileargs_open(fa, path);
+#ifndef NO_UDOM_SUPPORT
+ if (fd < 0 && errno == EOPNOTSUPP)
+ fd = udom_open(path, O_RDONLY);
+#endif
+#endif
+ }
+ if (fd < 0) {
+ warn("%s", path);
+ rval = 1;
+#ifndef BOOTSTRAP_CAT
+ } else if (cooked) {
+ if (fd == STDIN_FILENO)
+ cook_cat(stdin);
+ else {
+ fp = fdopen(fd, "r");
+ cook_cat(fp);
+ fclose(fp);
+ }
+#endif
+ } else {
+#ifndef BOOTSTRAP_CAT
+ if (in_kernel_copy(fd) != 0) {
+ if (errno == EINVAL || errno == EBADF ||
+ errno == EISDIR)
+ raw_cat(fd);
+ else
+ err(1, "%s", filename);
+ }
+#else
+ raw_cat(fd);
+#endif
+ if (fd != STDIN_FILENO)
+ close(fd);
+ }
+ if (path == NULL)
+ break;
+ ++i;
+ }
+}
+
+#ifndef BOOTSTRAP_CAT
+static void
+cook_cat(FILE *fp)
+{
+ int ch, gobble, line, prev;
+#ifndef __linux__
+ wint_t wch;
+#endif
+
+ /* Reset EOF condition on stdin. */
+ if (fp == stdin && feof(stdin))
+ clearerr(stdin);
+
+ line = gobble = 0;
+ for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) {
+ if (prev == '\n') {
+ if (sflag) {
+ if (ch == '\n') {
+ if (gobble)
+ continue;
+ gobble = 1;
+ } else
+ gobble = 0;
+ }
+ if (nflag) {
+ if (!bflag || ch != '\n') {
+ (void)fprintf(stdout, "%6d\t", ++line);
+ if (ferror(stdout))
+ break;
+ } else if (eflag) {
+ (void)fprintf(stdout, "%6s\t", "");
+ if (ferror(stdout))
+ break;
+ }
+ }
+ }
+ if (ch == '\n') {
+ if (eflag && putchar('$') == EOF)
+ break;
+ } else if (ch == '\t') {
+ if (tflag) {
+ if (putchar('^') == EOF || putchar('I') == EOF)
+ break;
+ continue;
+ }
+ } else if (vflag) {
+#ifdef __linux__
+ unsigned char uch;
+
+ uch = (unsigned char)ch;
+ if (!isascii(uch) && !isprint(uch)) {
+ if (putchar('M') == EOF || putchar('-') == EOF)
+ break;
+ uch = toascii(uch);
+ }
+ if (iscntrl(uch)) {
+ ch = (uch == '\177') ? '?' : (uch | 0100);
+ if (putchar('^') == EOF || putchar(ch) == EOF)
+ break;
+ continue;
+ }
+ if (putchar(uch) == EOF)
+ break;
+ continue;
+#else
+ (void)ungetc(ch, fp);
+ /*
+ * Our getwc(3) doesn't change file position
+ * on error.
+ */
+ if ((wch = getwc(fp)) == WEOF) {
+ if (ferror(fp) && errno == EILSEQ) {
+ clearerr(fp);
+#ifdef __FreeBSD__
+ /* Resync attempt. */
+ memset(&fp->_mbstate, 0, sizeof(mbstate_t));
+#endif
+ if ((ch = getc(fp)) == EOF)
+ break;
+ wch = ch;
+ goto ilseq;
+ } else
+ break;
+ }
+ if (!is_wascii(wch) && !iswprint(wch)) {
+ilseq:
+ if (putchar('M') == EOF || putchar('-') == EOF)
+ break;
+ wch = toascii(wch);
+ }
+ if (iswcntrl(wch)) {
+ ch = toascii(wch);
+ ch = (ch == '\177') ? '?' : (ch | 0100);
+ if (putchar('^') == EOF || putchar(ch) == EOF)
+ break;
+ continue;
+ }
+ if (putwchar(wch) == WEOF)
+ break;
+ ch = -1;
+ continue;
+#endif
+ }
+ if (putchar(ch) == EOF)
+ break;
+ }
+ if (ferror(fp)) {
+ warn("%s", filename);
+ rval = 1;
+ clearerr(fp);
+ }
+ if (ferror(stdout))
+ err(1, "stdout");
+}
+
+static ssize_t
+in_kernel_copy(int rfd)
+{
+ int wfd;
+ ssize_t ret;
+
+ wfd = fileno(stdout);
+ ret = 1;
+
+ while (ret > 0)
+ ret = copy_file_range(rfd, NULL, wfd, NULL, SSIZE_MAX, 0);
+
+ return (ret);
+}
+#endif /* BOOTSTRAP_CAT */
+
+static void
+raw_cat(int rfd)
+{
+ long pagesize;
+ int off, wfd;
+ ssize_t nr, nw;
+ static size_t bsize;
+ static char *buf = NULL;
+ struct stat sbuf;
+
+ wfd = fileno(stdout);
+ if (buf == NULL) {
+ if (fstat(wfd, &sbuf))
+ err(1, "stdout");
+ if (S_ISREG(sbuf.st_mode)) {
+ /* If there's plenty of RAM, use a large copy buffer */
+ if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD)
+ bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
+ else
+ bsize = BUFSIZE_SMALL;
+ } else {
+ bsize = sbuf.st_blksize;
+ pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize > 0)
+ bsize = MAX(bsize, (size_t)pagesize);
+ }
+ if ((buf = malloc(bsize)) == NULL)
+ err(1, "malloc() failure of IO buffer");
+ }
+ while ((nr = read(rfd, buf, bsize)) > 0)
+ for (off = 0; nr; nr -= nw, off += nw)
+ if ((nw = write(wfd, buf + off, (size_t)nr)) < 0)
+ err(1, "stdout");
+ if (nr < 0) {
+ warn("%s", filename);
+ rval = 1;
+ }
+}
+
+#if !defined(NO_UDOM_SUPPORT) && !defined(__linux__)
+
+static int
+udom_open(const char *path, int flags)
+{
+ struct addrinfo hints, *res, *res0;
+ char rpath[PATH_MAX];
+ int error, fd, serrno;
+ cap_rights_t rights;
+
+ /*
+ * Construct the unix domain socket address and attempt to connect.
+ */
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_LOCAL;
+
+ if (fileargs_realpath(fa, path, rpath) == NULL)
+ return (-1);
+
+ error = cap_getaddrinfo(capnet, rpath, NULL, &hints, &res0);
+ if (error) {
+ warn("%s", gai_strerror(error));
+ errno = EINVAL;
+ return (-1);
+ }
+ cap_rights_init(&rights, CAP_CONNECT, CAP_READ, CAP_WRITE,
+ CAP_SHUTDOWN, CAP_FSTAT, CAP_FCNTL);
+
+ /* Default error if something goes wrong. */
+ serrno = EINVAL;
+
+ for (res = res0; res != NULL; res = res->ai_next) {
+ fd = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (fd < 0) {
+ serrno = errno;
+ freeaddrinfo(res0);
+ errno = serrno;
+ return (-1);
+ }
+ if (caph_rights_limit(fd, &rights) != 0) {
+ serrno = errno;
+ close(fd);
+ freeaddrinfo(res0);
+ errno = serrno;
+ return (-1);
+ }
+ error = cap_connect(capnet, fd, res->ai_addr, res->ai_addrlen);
+ if (error == 0)
+ break;
+ else {
+ serrno = errno;
+ close(fd);
+ }
+ }
+ freeaddrinfo(res0);
+
+ if (res == NULL) {
+ errno = serrno;
+ return (-1);
+ }
+
+ /*
+ * handle the open flags by shutting down appropriate directions
+ */
+
+ switch (flags & O_ACCMODE) {
+ case O_RDONLY:
+ cap_rights_clear(&rights, CAP_WRITE);
+ if (shutdown(fd, SHUT_WR) != 0)
+ warn(NULL);
+ break;
+ case O_WRONLY:
+ cap_rights_clear(&rights, CAP_READ);
+ if (shutdown(fd, SHUT_RD) != 0)
+ warn(NULL);
+ break;
+ default:
+ break;
+ }
+
+ cap_rights_clear(&rights, CAP_CONNECT, CAP_SHUTDOWN);
+ if (caph_rights_limit(fd, &rights) != 0) {
+ serrno = errno;
+ close(fd);
+ errno = serrno;
+ return (-1);
+ }
+ return (fd);
+}
+
+#endif
diff --git a/corebinutils/cat/tests/test.sh b/corebinutils/cat/tests/test.sh
new file mode 100644
index 0000000000..1e9b5bc820
--- /dev/null
+++ b/corebinutils/cat/tests/test.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+set -eu
+
+ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
+CAT_BIN=${CAT_BIN:-"$ROOT/out/cat"}
+TMPDIR=${TMPDIR:-/tmp}
+WORKDIR=$(mktemp -d "$TMPDIR/cat-test.XXXXXX")
+trap 'rm -rf "$WORKDIR"' EXIT INT TERM
+
+fail() {
+ printf '%s\n' "FAIL: $1" >&2
+ exit 1
+}
+
+assert_eq() {
+ name=$1
+ expected=$2
+ actual=$3
+ if [ "$expected" != "$actual" ]; then
+ printf '%s\n' "FAIL: $name" >&2
+ printf '%s\n' "--- expected ---" >&2
+ printf '%s' "$expected" >&2
+ printf '\n%s\n' "--- actual ---" >&2
+ printf '%s' "$actual" >&2
+ printf '\n' >&2
+ exit 1
+ fi
+}
+
+[ -x "$CAT_BIN" ] || fail "missing binary: $CAT_BIN"
+
+printf 'hello\nworld\n' > "$WORKDIR/basic.txt"
+assert_eq "file passthrough" \
+ "hello
+world" \
+ "$("$CAT_BIN" "$WORKDIR/basic.txt")"
+
+assert_eq "stdin passthrough" \
+ "stdin-check" \
+ "$(printf 'stdin-check\n' | "$CAT_BIN" -)"
+
+printf 'a\n\n\nb\t\177\n' > "$WORKDIR/fixture.txt"
+
+assert_eq "number all lines" \
+ " 1 a
+ 2
+ 3
+ 4 b " \
+ "$("$CAT_BIN" -n "$WORKDIR/fixture.txt")"
+
+assert_eq "number nonblank lines" \
+ " 1 a
+
+
+ 2 b " \
+ "$("$CAT_BIN" -b "$WORKDIR/fixture.txt")"
+
+assert_eq "squeeze blanks" \
+ "a
+
+b " \
+ "$("$CAT_BIN" -s "$WORKDIR/fixture.txt")"
+
+assert_eq "show ends" \
+ "a$
+$
+$
+b ^?$" \
+ "$("$CAT_BIN" -e "$WORKDIR/fixture.txt")"
+
+assert_eq "show tabs" \
+ "a
+
+
+b^I^?" \
+ "$("$CAT_BIN" -t "$WORKDIR/fixture.txt")"
+
+assert_eq "show nonprinting" \
+ "a
+
+
+b ^?" \
+ "$("$CAT_BIN" -v "$WORKDIR/fixture.txt")"
+
+usage_output=$("$CAT_BIN" -z 2>&1 || true)
+case $usage_output in
+ *"usage: cat [-belnstuv] [file ...]"*) ;;
+ *) fail "usage on bad flag" ;;
+esac
+
+printf '%s\n' "PASS"