Add sort
authorAlessio Chiapperini <[email protected]>
Fri, 15 Apr 2022 12:40:40 +0000 (15 14:40 +0200)
committerAlessio Chiapperini <[email protected]>
Fri, 15 Apr 2022 12:40:40 +0000 (15 14:40 +0200)
.gitignore
src/sort/Makefile [new file with mode: 0644]
src/sort/config.mk [new file with mode: 0644]
src/sort/sort.1 [new file with mode: 0644]
src/sort/sort.c [new file with mode: 0644]
test/harness.sh
test/sort.sh [new file with mode: 0644]

index c40060f..d839122 100644 (file)
@@ -6,3 +6,4 @@ src/false/false
 src/true/true
 src/rm/rm
 src/cat/cat
+src/sort/sort
diff --git a/src/sort/Makefile b/src/sort/Makefile
new file mode 100644 (file)
index 0000000..c184601
--- /dev/null
@@ -0,0 +1,28 @@
+.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
diff --git a/src/sort/config.mk b/src/sort/config.mk
new file mode 100644 (file)
index 0000000..62a6292
--- /dev/null
@@ -0,0 +1,26 @@
+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
+
diff --git a/src/sort/sort.1 b/src/sort/sort.1
new file mode 100644 (file)
index 0000000..5dd84a2
--- /dev/null
@@ -0,0 +1,87 @@
+.\" 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 
+
diff --git a/src/sort/sort.c b/src/sort/sort.c
new file mode 100644 (file)
index 0000000..addf51d
--- /dev/null
@@ -0,0 +1,145 @@
+/*-
+ * 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);
+}
+
index 5df2b8b..24d85f6 100755 (executable)
@@ -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
diff --git a/test/sort.sh b/test/sort.sh
new file mode 100644 (file)
index 0000000..2455d7f
--- /dev/null
@@ -0,0 +1,44 @@
+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" ]
+}