logo

utils

~/.local/bin tools and git-hooks git clone https://hacktivis.me/git/utils.git
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:

Mcmd/echo.122+++++++++++++++++-----
Mcmd/echo.c43+++++++++++++++++++++++++++++++------------
Mtest-cmd/echo9+++++++++
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 }