logo

utils

~/.local/bin tools and git-hooks git clone https://hacktivis.me/git/utils.git
commit: d63f75d6def87d5b2d5a602bf04759eb2168b1cd
parent d918b06f27b8a2dff1ea3cc292b00a1d533a358a
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Fri,  3 Dec 2021 23:57:39 +0100

bin/echo: Make it posix-compliant and plan9-style

Diffstat:

Mbin/Makefile.config2+-
Abin/echo.132++++++++++++++++++++++++++++++++
Mbin/echo.c52++++++++++++++++++++++++++++++++++++++++++++--------
Mtest-bin/Kyuafile7++++---
Atest-bin/echo22++++++++++++++++++++++
5 files changed, 103 insertions(+), 12 deletions(-)

diff --git a/bin/Makefile.config b/bin/Makefile.config @@ -1,2 +1,2 @@ EXE = args basename cat date del dirname echo false humanize lolcat mdate pwd range sizeof sname strings tee true tty xcd -MAN1 = basename.1 date.1 del.1 dirname.1 humanize.1 lolcat.1 sname.1 +MAN1 = basename.1 date.1 del.1 dirname.1 echo.1 humanize.1 lolcat.1 sname.1 diff --git a/bin/echo.1 b/bin/echo.1 @@ -0,0 +1,32 @@ +.\" Collection of Unix tools, comparable to coreutils +.\" Copyright 2017-2021 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +.\" SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only +.Dd 2021-04-05 +.Dt ECHO 1 +.Os +.Sh NAME +.Nm echo +.Nd write arguments to standard output +.Sh SYNOPSIS +.Nm +.Op Ar string ... +.Sh DESCRIPTION +.Nm +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 +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. +See +.Xr printf 1 +for such an utility. +.Sh EXIT STATUS +.Ex -std +.Sh STANDARDS +Not XSI-compliant but should be compliant to +.St -p1003.1-2008 +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact@hacktivis.me diff --git a/bin/echo.c b/bin/echo.c @@ -3,19 +3,55 @@ // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only #define _POSIX_C_SOURCE 200809L -#include <stdio.h> /* printf(), putchar() */ +#include <stdio.h> /* perror */ +#include <stdlib.h> /* malloc */ +#include <string.h> /* strlen */ +#include <unistd.h> /* write */ -// Note: maybe consider buffering to write(2) only once like in Plan9 -// https://web.archive.org/web/20150519100236/https://gist.github.com/dchest/1091803#comment-1398629 int main(int argc, char *argv[]) { - int i; + size_t arg_len = 0; + char *buffer, *buffer_p; + int err = 0; - for(i = 1; i < argc; i++) - printf("%s ", argv[i]); + if(argc < 2) + { + arg_len++; + } - putchar('\n'); + for(int i = 1; i < argc; i++) + { + arg_len += strlen(argv[i]) + 1; // str + space|newline + } - return 0; + buffer = malloc(arg_len); + if(buffer == NULL) + { + perror("malloc(arg_len)"); + return 1; + } + + buffer_p = buffer; + for(int i = 1; i < argc; i++) + { + buffer_p = strcpy(buffer_p, argv[i]); + buffer_p += strlen(buffer_p); + + if(i < argc - 1) + { + *buffer_p++ = ' '; + } + } + *buffer_p++ = '\n'; + + if(write(1, buffer, arg_len) < (ssize_t)arg_len) + { + perror("write(1, buffer, arg_len)"); + err++; + } + + free(buffer); + + return err; } diff --git a/test-bin/Kyuafile b/test-bin/Kyuafile @@ -4,12 +4,13 @@ test_suite("utils") -- /BEGIN/,$ | LC_ALL=C.UTF-8 sort atf_test_program{name="args"} -atf_test_program{name="cat"} -atf_test_program{name="tee"} atf_test_program{name="basename"} +atf_test_program{name="cat"} atf_test_program{name="dirname"} +atf_test_program{name="echo"} +atf_test_program{name="false"} atf_test_program{name="humanize"} atf_test_program{name="sname"} +atf_test_program{name="tee"} atf_test_program{name="true"} -atf_test_program{name="false"} atf_test_program{name="xcd"} diff --git a/test-bin/echo b/test-bin/echo @@ -0,0 +1,22 @@ +#!/usr/bin/env atf-sh +atf_test_case empty +empty_body() { + atf_check -o "inline:\n" ../bin/echo +} + +atf_test_case hello +hello_body() { + atf_check -o "inline:hello world\n" ../bin/echo hello world +} + +atf_test_case doubledash +doubledash_body() { + atf_check -o "inline:-- hello\n" ../bin/echo -- hello +} + +atf_init_test_cases() { + cd "$(atf_get_srcdir)" + atf_add_test_case empty + atf_add_test_case hello + atf_add_test_case doubledash +}