cat: rewrite to use the splice() syscall
[lab.git] / cat.c
blob995a1e131ec255c9f4c6cc38e253f6a7d8bab828
1 /* baseutils: `cat.c` - con`cat`enate and print files.
2 Copyright (c) 2022, Yusuf
3 See `LICENSE` for copyright and license details */
5 #define _POSIX_C_SOURCE 200809L
7 #ifdef __linux__
8 /* for splice() support */
9 #define _GNU_SOURCE
10 #endif
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <errno.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <sys/param.h>
19 #include <sys/stat.h>
21 static const char *usage = "cat [-u] [file...]";
23 /* amount of bytes to transfer in one iteration */
24 #define BUF_SIZE 16384
26 void
27 cat(int file_fd)
29 static char* buffer = NULL;
30 static int buf_sz = 0;
31 int n_bytes;
32 struct stat sbuf;
34 if (!buffer) {
35 if (fstat(file_fd, &sbuf) < 0) {
36 perror("uname");
37 exit(1);
40 buf_sz = MAX(BUF_SIZE, sbuf.st_blksize);
41 if ((buffer = malloc(buf_sz)) == NULL) {
42 perror("malloc");
43 exit(1);
48 for (;;) {
49 n_bytes = read(file_fd, (void *)buffer, buf_sz);
50 if (n_bytes <= 0)
51 break;
53 write(fileno(stdout), buffer, n_bytes);
57 #ifdef __linux__
58 void
59 fastcat(int file_fd)
61 int result;
63 for(;;) {
64 // we make the assumption here that
65 // STDOUT_FD == 1, which is true on linux...
66 result = splice(file_fd, NULL, 1, NULL, BUF_SIZE * 4, 0);
67 if (result >= 0)
68 break;
71 #else
72 #define fastcat(fd) cat(fd)
73 #endif
75 int
76 process_file(const char* name, int unbuf)
78 int fd;
80 if (strcmp(name, "-") == 0) {
81 if (unbuf)
82 if (setvbuf(stdin, NULL, _IONBF, 0) == EOF)
83 fputs("cat: -u: unbuffer request failed\n", stderr);
85 fd = fileno(stdin);
86 } else {
87 fd = open(name, O_RDONLY);
90 if (fd < 0) {
91 return EXIT_FAILURE;
92 } else if (!isatty(fd)) {
93 fastcat(fd);
94 } else {
95 cat(fd);
98 return EXIT_SUCCESS;
102 main(int argc, char *argv[])
104 char ch;
105 int status, i, u, fd;
107 status = EXIT_SUCCESS;
109 while ((ch = getopt(argc, argv, "u")) != -1) {
110 switch (ch) {
111 case 'u':
112 if (setvbuf(stdout, NULL, _IONBF, 0) == EOF)
113 fputs("cat: -u: unbuffer request failed\n", stderr);
114 u = 1;
115 break;
116 case '?':
117 default:
118 fprintf(stderr, "Usage:\n\t%s\n", usage);
119 return EXIT_FAILURE;
123 argv += optind;
124 argc -= optind;
126 if (argc < 1) {
127 // stdin only
128 return process_file("-", u);
131 for (i = 0; i < argc; i++) {
132 status = process_file(argv[i], u);
135 return status;