@@ -6,3 +6,4 @@ src/false/false
src/true/true
src/rm/rm
src/cat/cat
+src/sort/sort
--- /dev/null
+.POSIX:
+
+.PHONY: all clean install uninstall
+
+include config.mk
+
+SRCS != echo *.c
+OBJS = ${SRCS:.c=.o}
+
+all: ${PROG}
+
+${PROG}: ${OBJS}
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDLIBS}
+
+${PROG}.o: ${PROG}.c
+
+clean:
+ ${RM} -f ${OBJS} ${PROG}
+
+install: ${PROG} ${PROG}.1
+ mkdir -p ${DESTDIR}${PREFIX}/bin
+ install -m 755 ${PROG} ${DESTDIR}${PREFIX}/bin
+ mkdir -p ${DESTDIR}${MANPREFIX}/man1
+ gzip < ${PROG}.1 > ${DESTDIR}${MANPREFIX}/man1/${PROG}.1.gz
+
+uninstall:
+ ${RM} -f ${DESTDIR}${PREFIX}/bin/${PROG}
+ ${RM} -f ${DESTDIR}${MANPREFIX}/man1/${PROG}.1.gz
--- /dev/null
+PROG = sort
+
+# compiler
+CC = cc
+
+# flags
+CFLAGS = -std=c99 -O2 -Wall -Wextra -Wpedantic -Werror \
+ -Walloca -Wcast-qual -Wconversion -Wformat=2 -Wformat-security \
+ -Wnull-dereference -Wstack-protector -Wvla -Warray-bounds \
+ -Wbad-function-cast -Wconversion -Wshadow -Wstrict-overflow=4 -Wundef \
+ -Wstrict-prototypes -Wswitch-default -Wfloat-equal -Wimplicit-fallthrough \
+ -Wpointer-arith -Wswitch-enum \
+ -D_FORTIFY_SOURCE=2 \
+ -fstack-protector-strong -fPIE -fstack-clash-protection
+
+LDFLAGS = -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -Wl,-z,separate-code
+
+# libs
+LDLIBS =
+
+# paths
+PREFIX = /usr/local
+MANPREFIX = ${PREFIX}/share/man
+
+RM = rm
+
--- /dev/null
+.\" SPDX-License-Identifier: BSD-2-Clause
+
+.\" Copyright (c) 2022 Alessio Chiapperini
+
+.\" 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.
+
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 April 15, 2022
+.Dt SORT 1
+.Os
+.Sh NAME
+.Nm sort
+.Nd don't sort or merge records (lines) of text and binary files.
+.Sh SYNOPSIS
+.Nm
+.Op Fl \&h
+.Op Fl o Ar outfile
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility does't sort text and binary files by line, it reads files sequentially,
+writing them to 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 standard input.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl h
+Print usage information and exit.
+.It Fl o
+Print the output to
+.Ar outfile
+instead of the standard output.
+.El
+.Sh "EXIT STATUS"
+The
+.Nm
+utility exits 0 on success, and 1 if an error occurs.
+.Sh EXAMPLES
+Print the contents of the file named
+.Ar file
+to standard output unsorted:
+.Dl $ sort file
+.Pp
+Sequentially print the contents of the files
+.Ar file1
+and
+.Ar file2
+to the file
+.Ar file3
+without sorting them:
+.Dl $ sort file1 file2 > file3
+.Pp
+Print data it receives from standard input until it receives an EOF
+.Pq ^D
+character to standard output:
+.Dl $ sort -
+.Sh SEE ALSO
+.Xr cat 1
+.Sh AUTHOR
+.An Alessio Chiapperini Aq Mt alessio.chiapperini@\:nullbuffer.com
+
--- /dev/null
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 Alessio Chiapperini
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200112L
+#elif _POSIX_C_SOURCE < 200112L
+# error incompatible _POSIX_C_SOURCE level
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static char *ofile = 0;
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "usage: sort [-o outfile] [file...]\n");
+ exit(1);
+}
+
+static int
+sort(const char *src, const char *dst)
+{
+ char buf[BUFSIZ];
+ ssize_t n;
+ ssize_t offs, o;
+ int ifd, ofd;
+ int ret;
+
+ ret = 0;
+ ifd = STDIN_FILENO;
+ if (src[0] != '-' || src[1] != '\0') {
+ ifd = open(src, O_RDONLY);
+ if (ifd < 0) {
+ ret = 1;
+ perror("sort");
+ goto in_open_err;
+ }
+ }
+
+ ofd = STDOUT_FILENO;
+ if (dst != 0) {
+ ofd = open(dst, O_WRONLY | O_CREAT | O_TRUNC);
+ if (ofd < 0) {
+ ret = 1;
+ perror("sort");
+ goto out_open_err;
+ }
+ }
+
+ while ((n = read(ifd, buf, sizeof(buf))) > 0) {
+ offs = 0;
+ while (offs < n) {
+ o = write(ofd, buf, (size_t)n);
+ if (o < 0) {
+ ret = 1;
+ perror("sort");
+ goto write_err;
+ }
+ offs += o;
+ }
+ }
+
+ if (n < 0) {
+ ret = 1;
+ perror("sort");
+ }
+
+write_err:
+ if (ofd != STDOUT_FILENO) {
+ close(ofd);
+ }
+out_open_err:
+ close(ifd);
+in_open_err:
+ return (ret);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ char *def[] = { "-" };
+ int opt;
+
+ while ((opt = getopt(argc, argv, "ho:")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage();
+ break;
+ case 'o':
+ ofile = optarg;
+ break;
+ case '?':
+ /* FALLTHROUGH */
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ argc = 1;
+ argv = def;
+ }
+
+ for (int i = 0; i < argc; i++) {
+ if (sort(argv[i], ofile) > 0) {
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
@@ -60,6 +60,7 @@ run_tests() {
. $(dirname "$0")/true.sh
. $(dirname "$0")/rm.sh
. $(dirname "$0")/cat.sh
+. $(dirname "$0")/sort.sh
setup
run_tests \
@@ -73,4 +74,9 @@ run_tests \
cat_should_handle_two_files \
cat_should_handle_stdin \
cat_should_handle_u_flag \
- cat_should_handle_dash
+ cat_should_handle_dash \
+ sort_should_handle_one_file \
+ sort_should_handle_two_files \
+ sort_should_handle_stdin \
+ sort_should_handle_o_flag \
+ sort_should_handle_dash
--- /dev/null
+echo -n "this is a test file" > "$TMPDIR"/test-file-1
+echo -n "this is another test file" > "$TMPDIR"/test-file-2
+echo -n "dashed" > ${TMPDIR}/-dashed
+
+sort_should_handle_one_file() {
+ tool=`find ../src -type f -name "sort"`
+ ct="$(${tool} ${TMPDIR}/test-file-1)"
+ ref=$(cat ${TMPDIR}/test-file-1)
+
+ [ "$ct" = "$ref" ]
+}
+
+sort_should_handle_two_files() {
+ tool=`find ../src -type f -name "sort"`
+ ct="$(${tool} ${TMPDIR}/test-file-1 ${TMPDIR}/test-file-2)"
+ ref1=$(cat ${TMPDIR}/test-file-1)
+ ref2=$(cat ${TMPDIR}/test-file-2)
+ ref=$(echo -n "$ref1$ref2")
+
+ [ "$ct" = "$ref" ]
+}
+
+sort_should_handle_stdin() {
+ tool=`find ../src -type f -name "sort"`
+ ct="$(echo -n "this is from stdin" | ${tool} -)"
+ ref=$(echo -n "this is from stdin")
+ [ "$ct" = "$ref" ] || return 1
+ ct="$(echo -n "this is from stdin" | ${tool})"
+ [ "$ct" = "$ref" ]
+}
+
+sort_should_handle_o_flag() {
+ # The default behavior is to flush at every write
+ $(echo -n "this is from stdin" | ${tool} -o ${TMPDIR}/out)
+ ct=$(cat ${TMPDIR}/out)
+ [ "$ct" = "this is from stdin" ]
+}
+
+sort_should_handle_dash() {
+ tool=`find ../src -type f -name "sort"`
+ ct="$(${tool} -- ${TMPDIR}/-dashed)"
+ ref=$(echo -n "dashed")
+ [ "$ct" = "$ref" ]
+}