Skip to content

Commit 3ed113d

Browse files
authored
Merge pull request #112 from fischerling/add-utils
Add cp and head programs
2 parents dcde0dc + 3a8aed0 commit 3ed113d

File tree

4 files changed

+208
-24
lines changed

4 files changed

+208
-24
lines changed

files/usr/share/man/head.man

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
SYNOPSIS
2+
head [-NUM] [FILE]...
3+
4+
DESCRIPTION
5+
Output the first part of each FILE.
6+
7+
Print the first 10 lines of each FILE to standard output.
8+
With more than one FILE, precede each with a header giving the file name.
9+
10+
With no FILE read standard input.
11+
12+
OPTIONS
13+
-h, --help shows command help.
14+
-NUM print only the first NUM lines.

programs/CMakeLists.txt

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,42 @@
11
# List of programs.
22
set(PROGRAM_LIST
3-
runtests.c
4-
more.c
3+
cat.c
54
chmod.c
65
chown.c
7-
id.c
8-
stat.c
6+
clear.c
7+
cp.c
8+
cpuid.c
9+
date.c
10+
echo.c
11+
edit.c
12+
env.c
913
false.c
10-
nice.c
11-
ls.c
12-
mkdir.c
14+
head.c
15+
id.c
16+
init.c
1317
ipcrm.c
18+
ipcs.c
19+
kill.c
1420
login.c
15-
man.c
1621
logo.c
17-
shell.c
18-
cpuid.c
19-
clear.c
20-
rmdir.c
22+
ls.c
23+
man.c
24+
mkdir.c
25+
more.c
26+
nice.c
2127
poweroff.c
22-
rm.c
23-
cat.c
24-
init.c
2528
ps.c
26-
kill.c
27-
edit.c
28-
sleep.c
29-
date.c
30-
echo.c
29+
pwd.c
30+
rm.c
31+
rmdir.c
32+
runtests.c
33+
shell.c
3134
showpid.c
32-
uname.c
33-
ipcs.c
35+
sleep.c
36+
stat.c
3437
touch.c
38+
uname.c
3539
uptime.c
36-
pwd.c
37-
env.c
3840
)
3941

4042
# Set the directory where the compiled binaries will be placed.

programs/cp.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/// @file cp.c
2+
/// @brief `cp` program.
3+
/// @copyright (c) 2024 This file is distributed under the MIT License.
4+
/// See LICENSE.md for details.
5+
6+
#include <err.h>
7+
#include <fcntl.h>
8+
#include <stddef.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
#include <strerror.h>
12+
#include <string.h>
13+
#include <sys/stat.h>
14+
#include <sys/unistd.h>
15+
16+
int main(int argc, char **argv)
17+
{
18+
if (argc < 3) {
19+
printf("%s: missing file operand.\n", argv[0]);
20+
printf("Try 'cp --help' for more information.\n");
21+
return EXIT_FAILURE;
22+
}
23+
// Check if `--help` is provided.
24+
for (int i = 1; i < argc; ++i) {
25+
if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-h") == 0)) {
26+
printf("%s - copy files\n", argv[0]);
27+
printf("Usage: %s SOURCE DEST\n", argv[0]);
28+
return EXIT_SUCCESS;
29+
}
30+
}
31+
// Prepare the buffer for reading.
32+
ssize_t bytes_read = 0;
33+
char buffer[BUFSIZ];
34+
char *src = argv[1];
35+
char *dest = argv[2];
36+
37+
int srcfd = open(src, O_RDONLY, 0);
38+
if (srcfd < 0) {
39+
err(EXIT_FAILURE, "%s: %s", argv[0], src);
40+
}
41+
42+
int destfd = creat(dest, 0600);
43+
if (destfd < 0) {
44+
err(EXIT_FAILURE, "%s: %s", argv[0], dest);
45+
}
46+
47+
// Write the content read from srcfd to destfd
48+
while ((bytes_read = read(srcfd, buffer, sizeof(buffer))) > 0) {
49+
if (write(destfd, buffer, bytes_read) != bytes_read) {
50+
err(EXIT_FAILURE, "%s: %s", argv[0], dest);
51+
}
52+
}
53+
// Close the file descriptors.
54+
close(srcfd);
55+
close(destfd);
56+
return 0;
57+
}

programs/head.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/// @file head.c
2+
/// @brief `head` program.
3+
/// @copyright (c) 2014-2024 This file is distributed under the MIT License.
4+
/// See LICENSE.md for details.
5+
6+
#include <err.h>
7+
#include <fcntl.h>
8+
#include <stddef.h>
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
#include <strerror.h>
12+
#include <string.h>
13+
#include <sys/stat.h>
14+
#include <sys/unistd.h>
15+
16+
static int head(int fd, const char *fname, size_t n) {
17+
// Count the printed lines
18+
int i = 0;
19+
// Prepare the buffer for reading.
20+
char buffer[BUFSIZ];
21+
size_t leftover = 0;
22+
char *line = buffer;
23+
ssize_t bytes_read = 0;
24+
25+
while ((bytes_read = read(fd, buffer + leftover, sizeof(buffer) - leftover)) > 0) {
26+
char *lineend;
27+
while (i < n && (lineend = memchr(line, '\n', sizeof(buffer) - (line - buffer)))) {
28+
lineend++; // Include the newline
29+
write(STDOUT_FILENO, line, lineend-line);
30+
line = lineend;
31+
i++;
32+
}
33+
34+
if (i >= n)
35+
break;
36+
37+
leftover = (leftover + bytes_read) - (line - buffer); // Bytes left in the buffer
38+
memmove(buffer, line, leftover); // Move the leftover to the front of buffer
39+
line = buffer;
40+
}
41+
42+
close(fd);
43+
if (bytes_read < 0) {
44+
fprintf(STDERR_FILENO, "head: %s: %s\n", fname, strerror(errno));
45+
return EXIT_FAILURE;
46+
}
47+
48+
return 0;
49+
}
50+
51+
int main(int argc, char **argv)
52+
{
53+
int ret = 0;
54+
int n = 10;
55+
char **file_args_start = argv;
56+
57+
// Find help argument
58+
for (int i = 1; i < argc; ++i) {
59+
if ((strcmp(argv[i], "--help") == 0) || (strcmp(argv[i], "-h") == 0)) {
60+
printf("Print the first part of files.\n");
61+
printf("Usage:\n");
62+
printf(" head [-<num>] [FILE]...\n");
63+
return 0;
64+
}
65+
}
66+
67+
// Detect number of lines argument
68+
if (argv[1][0] == '-') {
69+
char *nptr = argv[1]+1;
70+
char *endptr;
71+
errno = 0;
72+
n = strtol(nptr, &endptr, 10);
73+
if (*endptr || errno == ERANGE) {
74+
errx(EXIT_FAILURE, "head: invalid number of lines: `%s`", nptr);
75+
}
76+
77+
if (endptr == nptr) {
78+
errx(EXIT_FAILURE, "head: line number option requires an argument");
79+
}
80+
file_args_start = &argv[1];
81+
argc--;
82+
}
83+
84+
// No argument was provided -> read from stdin
85+
if (argc == 1) {
86+
return head(STDIN_FILENO, "stdin", n);
87+
}
88+
89+
for (int i = 1; i < argc; i++) {
90+
const char* fname = file_args_start[i];
91+
int fd;
92+
if (strcmp(fname, "-") == 0) {
93+
fd = STDIN_FILENO;
94+
} else {
95+
fd = open(fname, O_RDONLY, 0);
96+
if (fd < 0) {
97+
fprintf(STDERR_FILENO, "head: %s: %s\n", fname, strerror(errno));
98+
ret = EXIT_FAILURE;
99+
continue;
100+
}
101+
}
102+
103+
// Print file header if multiple files are given
104+
if (argc > 2) {
105+
printf("==> %s <==\n", fname);
106+
}
107+
108+
ret = ret || head(fd, fname, n);
109+
}
110+
return ret;
111+
}

0 commit comments

Comments
 (0)