commit: 69cbb114a926327ac256e2007d4208ba734e1fad
parent 51df2fb18fab83c6ccae6e4ddeb9e00b40740e64
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Thu, 5 Feb 2026 11:37:55 +0100
cmd/tee: Use read/write instead of fgetc/fputc
Prevents buffering of the output (short read means immediate write)
and potentially increase performance (reduced processing,
better buffer size).
Diffstat:
| M | cmd/tee.c | 43 | +++++++++++++++++++++++++++---------------- |
1 file changed, 27 insertions(+), 16 deletions(-)
diff --git a/cmd/tee.c b/cmd/tee.c
@@ -8,8 +8,9 @@
#include <assert.h> /* assert() */
#include <errno.h> /* errno */
+#include <fcntl.h>
#include <signal.h> /* signal() */
-#include <stdio.h> /* fprintf(), fgetc(), fputc(), fclose(), fopen() */
+#include <stdio.h> /* fprintf() */
#include <stdlib.h> /* calloc(), free(), abort() */
#include <string.h> /* strerror() */
#include <unistd.h> /* getopt(), opt… */
@@ -20,19 +21,16 @@
const char *argv0 = "tee";
static void
-cleanup(FILE **fds)
+cleanup(int *fds)
{
- if(fds != NULL)
- {
- free(fds);
- }
+ free(fds);
}
int
main(int argc, char *argv[])
{
- const char *mode = "w";
- FILE **fds = {NULL}; // Shut up GCC
+ mode_t mode = O_WRONLY | O_NOCTTY | O_CREAT;
+ int *fds = NULL;
int c;
#ifdef HAS_GETOPT_LONG
@@ -54,7 +52,7 @@ main(int argc, char *argv[])
switch(c)
{
case 'a':
- mode = "a";
+ mode |= O_APPEND;
break;
case 'i': /* ignore SIGINT */;
signal(SIGINT, SIG_IGN);
@@ -88,9 +86,9 @@ main(int argc, char *argv[])
assert(argv[argi]);
// POSIX: implementations shouldn't treat '-' as stdin
- fds[argi] = fopen(argv[argi], mode);
+ fds[argi] = open(argv[argi], mode);
- if(fds[argi] == NULL)
+ if(fds[argi] < 0)
{
fprintf(stderr, "tee: error: Failed opening file ‘%s’: %s\n", argv[argi], strerror(errno));
cleanup(fds);
@@ -101,9 +99,21 @@ main(int argc, char *argv[])
// main loop, note that failed writes shouldn't make tee exit
int err = 0;
- while((c = fgetc(stdin)) != EOF)
+ while(true)
{
- if(fputc(c, stdout) == EOF)
+ static char buf[BUFSIZ];
+ ssize_t sread = read(STDIN_FILENO, buf, sizeof(buf));
+ if(sread == 0) break;
+ if(sread < 0)
+ {
+ fprintf(stderr, "tee: error: Failed reading from stdin: %s\n", strerror(errno));
+ cleanup(fds);
+ return 1;
+ }
+
+ size_t nread = (size_t)sread;
+
+ if(write(STDOUT_FILENO, buf, nread) < 0)
{
fprintf(stderr, "tee: error: Failed writing to stdout: %s\n", strerror(errno));
err = 1;
@@ -112,9 +122,10 @@ main(int argc, char *argv[])
for(int argi = 0; argi < argc; argi++)
{
- if(fputc(c, fds[argi]) == EOF)
+ if(write(fds[argi], buf, nread) < 0)
{
- fprintf(stderr, "tee: error: Failed writing to argument %d: %s\n", argi, strerror(errno));
+ fprintf(
+ stderr, "tee: error: Failed writing to file '%s': %s\n", argv[argi], strerror(errno));
err = 1;
errno = 0;
}
@@ -124,7 +135,7 @@ main(int argc, char *argv[])
// cleanup
for(int argi = 0; argi < argc; argi++)
{
- if(fclose(fds[argi]) != 0)
+ if(close(fds[argi]) != 0)
{
fprintf(stderr, "tee: error: Failed closing file '%s': %s\n", argv[argi], strerror(errno));
err++;