commit: 85ca7f6ac5438486be9fd7bfe2eec2fdaa529b57
parent c6ff4177c2e8da51ce66f35c8ca705931a4bea07
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Wed, 31 May 2023 03:51:29 +0200
cmd/echo: Add support for the common -n option
Diffstat:
3 files changed, 57 insertions(+), 17 deletions(-)
diff --git a/cmd/echo.1 b/cmd/echo.1
@@ -1,7 +1,7 @@
.\" Collection of Unix tools, comparable to coreutils
-.\" Copyright 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+.\" Copyright 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
.\" SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
-.Dd 2021-04-05
+.Dd 2023-05-31
.Dt ECHO 1
.Os
.Sh NAME
@@ -15,11 +15,23 @@
buffers all of it's arguments and writes them all at once to standard output, followed by a newline.
If there is no arguments, only the newline is written.
.Pp
+The
+.Fl -
+operand, which generally denotes an end to option processing, is treated as part of
+.Ar string .
+.Pp
+.Nm
+supports the following options:
+.Bl -tag -width Ds
+.It Fl n
+Do not print the trailing newline character.
+.El
+.Pp
Should also be noted that this version of
.Nm
-isn't XSI-compliant as arguments like
-.Fl Ar n
-are parsed as strings and backslash operators aren't supported.
+isn't XSI-compliant as
+.Fl n
+is parsed as an option and backslash operators aren't supported.
See
.Xr printf 1
for such an utility.
diff --git a/cmd/echo.c b/cmd/echo.c
@@ -1,23 +1,27 @@
// Collection of Unix tools, comparable to coreutils
-// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-FileCopyrightText: 2017-2023 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
#define _POSIX_C_SOURCE 200809L
-#include <stdio.h> /* perror */
-#include <stdlib.h> /* malloc */
-#include <string.h> /* strlen */
-#include <unistd.h> /* write */
+#include <stdbool.h> /* bool */
+#include <stdio.h> /* perror */
+#include <stdlib.h> /* malloc */
+#include <string.h> /* strlen */
+#include <unistd.h> /* write */
int
main(int argc, char *argv[])
{
size_t arg_len = 0;
char *buffer, *buffer_p;
- int err = 0;
+ int err = 0;
+ bool opt_n = false;
- if(argc < 2)
+ if((argc >= 2) && (strncmp(argv[1], "-n", 3) == 0))
{
- arg_len++;
+ opt_n = true;
+ argc--;
+ argv++;
}
for(int i = 1; i < argc; i++)
@@ -25,6 +29,21 @@ main(int argc, char *argv[])
arg_len += strlen(argv[i]) + 1; // str + space|newline
}
+ if(arg_len == 0)
+ {
+ if(opt_n) return 0;
+
+ if(write(1, "\n", 1) < 1)
+ {
+ perror("echo: write(1, \"\n\", 1)");
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if(opt_n) arg_len--; // no newline
+
buffer = malloc(arg_len);
if(buffer == NULL)
{
@@ -35,16 +54,16 @@ main(int argc, char *argv[])
buffer_p = buffer;
for(int i = 1; i < argc; i++)
{
- /* flawfinder: ignore, consider that arguments are safely separated by NULL */
- buffer_p = strcpy(buffer_p, argv[i]);
- buffer_p += strlen(buffer_p);
+ size_t len = strlen(argv[i]);
+ memcpy(buffer_p, argv[i], len);
+ buffer_p += len;
if(i < argc - 1)
{
*buffer_p++ = ' ';
}
}
- *buffer_p++ = '\n';
+ if(!opt_n) *buffer_p++ = '\n';
if(write(1, buffer, arg_len) < (ssize_t)arg_len)
{
diff --git a/test-cmd/echo b/test-cmd/echo
@@ -22,10 +22,19 @@ devfull_body() {
atf_check -s exit:1 -e 'inline:echo: write(1, buffer, arg_len): No space left on device\n' sh -c '../cmd/echo hello world >/dev/full'
}
+atf_test_case opt_n
+opt_n_body() {
+ atf_check -o "inline:" ../cmd/echo -n
+ atf_check -o "inline:foo" ../cmd/echo -n foo
+ atf_check -o "inline:foo bar" ../cmd/echo -n foo bar
+ atf_check -o "inline:-- foo" ../cmd/echo -n -- foo
+}
+
atf_init_test_cases() {
cd "$(atf_get_srcdir)" || exit 1
atf_add_test_case empty
atf_add_test_case hello
atf_add_test_case doubledash
atf_add_test_case devfull
+ atf_add_test_case opt_n
}