From: Ahmad Fatoum <a.fatoum@pengutronix.de>
To: barebox@lists.infradead.org
Cc: Ahmad Fatoum <a.fatoum@pengutronix.de>
Subject: [PATCH] commands: cat: add support for concatenating into file
Date: Mon, 26 Jan 2026 11:59:52 +0100 [thread overview]
Message-ID: <20260126105952.2155950-1-a.fatoum@pengutronix.de> (raw)
For testing initrd concatenation, it would be useful if barebox could
repeat the operation on the shell and then it can compare it against
the result, so add cat -o and -a options, like we already got for
echo.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
commands/cat.c | 76 ++++++++++++++++++++++++++++++++++---------
test/py/test_shell.py | 32 ++++++++++++++++++
2 files changed, 92 insertions(+), 16 deletions(-)
diff --git a/commands/cat.c b/commands/cat.c
index 503520dc64a5..aa77b19907e0 100644
--- a/commands/cat.c
+++ b/commands/cat.c
@@ -14,6 +14,8 @@
#include <errno.h>
#include <xfuncs.h>
#include <malloc.h>
+#include <getopt.h>
+#include <libfile.h>
#define BUFSIZE 1024
@@ -23,20 +25,46 @@
*/
static int do_cat(int argc, char *argv[])
{
+ const char *outfile = NULL;
int ret;
- int fd, i;
+ int fd, outfd = -1, i;
char *buf;
int err = 0;
- int args = 1;
+ int oflags = O_WRONLY | O_CREAT;
+ int opt;
- if (argc < 2) {
- perror("cat");
- return 1;
+ while ((opt = getopt(argc, argv, "a:o:")) > 0) {
+ switch(opt) {
+ case 'a':
+ oflags |= O_APPEND;
+ outfile = optarg;
+ break;
+ case 'o':
+ oflags |= O_TRUNC;
+ outfile = optarg;
+ break;
+ default:
+ return COMMAND_ERROR_USAGE;
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc == 0)
+ return COMMAND_ERROR_USAGE;
+
+ if (outfile) {
+ outfd = open(outfile, oflags);
+ if (outfd < 0) {
+ perror("open");
+ return 1;
+ }
}
buf = xmalloc(BUFSIZE);
- while (args < argc) {
+ for (int args = 0; args < argc; args++) {
fd = open(argv[args], O_RDONLY);
if (fd < 0) {
err = 1;
@@ -44,24 +72,36 @@ static int do_cat(int argc, char *argv[])
goto out;
}
- while((ret = read(fd, buf, BUFSIZE)) > 0) {
- for(i = 0; i < ret; i++) {
- if (isprint(buf[i]) || buf[i] == '\n' || buf[i] == '\t')
- putchar(buf[i]);
- else
- putchar('.');
- }
- if(ctrlc()) {
+
+ if (outfd >= 0) {
+ ret = copy_fd(fd, outfd, 0);
+ if (ret < 0) {
+ perror("copy_fd");
err = 1;
close(fd);
goto out;
}
+ } else {
+ while((ret = read(fd, buf, BUFSIZE)) > 0) {
+ for (i = 0; i < ret; i++) {
+ if (isprint(buf[i]) || buf[i] == '\n' || buf[i] == '\t')
+ putchar(buf[i]);
+ else
+ putchar('.');
+ }
+ if(ctrlc()) {
+ err = 1;
+ close(fd);
+ goto out;
+ }
+ }
}
+
close(fd);
- args++;
}
out:
+ close(outfd);
free(buf);
return err;
@@ -69,12 +109,16 @@ static int do_cat(int argc, char *argv[])
BAREBOX_CMD_HELP_START(cat)
BAREBOX_CMD_HELP_TEXT("Currently only printable characters and NL, TAB are printed.")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-a FILE", "append to FILE instead of using stdout")
+BAREBOX_CMD_HELP_OPT ("-o FILE", "overwrite FILE instead of using stdout")
BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(cat)
.cmd = do_cat,
BAREBOX_CMD_DESC("concatenate file(s) to stdout")
- BAREBOX_CMD_OPTS("FILE...")
+ BAREBOX_CMD_OPTS("[-ao] FILE...")
BAREBOX_CMD_GROUP(CMD_GRP_FILE)
BAREBOX_CMD_HELP(cmd_cat_help)
BAREBOX_CMD_END
diff --git a/test/py/test_shell.py b/test/py/test_shell.py
index 50ae497fc6c3..9010270e0e63 100644
--- a/test/py/test_shell.py
+++ b/test/py/test_shell.py
@@ -32,6 +32,38 @@ def test_shell_quoting(barebox):
assert barebox.run_check('source /tmp/script && echo "$var"') == "A B"
+def test_barebox_echo_cat(barebox, barebox_config):
+ skip_disabled(barebox_config, "CONFIG_CMD_ECHO", "CONFIG_CMD_CAT")
+
+ barebox.run_check('rm -f stanza*')
+
+ barebox.run_check('echo -o stanza-1 meow')
+ barebox.run_check('echo -o stanza-1 mau')
+ barebox.run_check('echo -o stanza-2 meow')
+ barebox.run_check('echo -a stanza-2 meow')
+ barebox.run_check('cat -o stanza-3 /dev/null')
+ barebox.run_check('echo -a stanza-3 mau')
+ barebox.run_check('echo -a stanza-3 ma')
+
+ expected = ["mau", "meow", "meow", "mau", "ma"]
+
+ stdout = barebox.run_check('ls -l stanza-*')
+
+ assert len(stdout) == 3
+
+ assert barebox.run_check('cat stanza-*') == expected
+
+ barebox.run_check('rm -f stanza')
+
+ stdout = barebox.run_check('cat -o stanza stanza-*')
+ assert stdout == []
+ assert barebox.run_check('cat stanza') == expected
+
+ stdout = barebox.run_check('cat -a stanza stanza-*')
+ assert stdout == []
+ assert barebox.run_check('cat stanza') == expected + expected
+
+
def test_barebox_md5sum(barebox, barebox_config):
skip_disabled(barebox_config, "CONFIG_CMD_MD5SUM", "CONFIG_CMD_ECHO")
--
2.47.3
next reply other threads:[~2026-01-26 11:00 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-26 10:59 Ahmad Fatoum [this message]
2026-01-27 7:33 ` Sascha Hauer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260126105952.2155950-1-a.fatoum@pengutronix.de \
--to=a.fatoum@pengutronix.de \
--cc=barebox@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox