Unix Tools

Implementations of common Unix command-line utilities written in C.


unix-tools / cp.c

cp.c raw | download
1#define _POSIX_C_SOURCE 200809L
2#include <sys/stat.h>
3#include <fcntl.h>
4#include <unistd.h>
5#include <errno.h>
6#include <stdio.h>
7
8int
9main(int argc, char **argv)
10{
11 struct stat source, destination;
12 char buf[8192];
13 int input, output = -1, status = 1;
14 if (argc != 3) { fprintf(stderr, "usage: %s source destination\n", argv[0]); return 1; }
15 input = open(argv[1], O_RDONLY | O_NONBLOCK);
16 if (input < 0) { perror(argv[1]); return 1; }
17 if (fstat(input, &source) < 0) { perror(argv[1]); goto done; }
18 if (!S_ISREG(source.st_mode)) { fprintf(stderr, "cp: source must be a regular file\n"); goto done; }
19 output = open(argv[2], O_WRONLY | O_CREAT | O_NONBLOCK, source.st_mode & 0777);
20 if (output < 0) { perror(argv[2]); goto done; }
21 if (fstat(output, &destination) < 0) { perror(argv[2]); goto done; }
22 if (!S_ISREG(destination.st_mode)) { fprintf(stderr, "cp: destination must be a regular file\n"); goto done; }
23 if (source.st_dev == destination.st_dev && source.st_ino == destination.st_ino) {
24 fprintf(stderr, "cp: source and destination are the same file\n"); goto done;
25 }
26 if (ftruncate(output, 0) < 0) { perror(argv[2]); goto done; }
27 for (;;) {
28 ssize_t n = read(input, buf, sizeof(buf));
29 size_t used = 0;
30 if (n < 0 && errno == EINTR) continue;
31 if (n < 0) { perror(argv[1]); goto done; }
32 if (n == 0) break;
33 while (used < (size_t)n) {
34 ssize_t written = write(output, buf + used, (size_t)n - used);
35 if (written < 0 && errno == EINTR) continue;
36 if (written <= 0) { perror(argv[2]); goto done; }
37 used += (size_t)written;
38 }
39 }
40 status = 0;
41done:
42 if (output >= 0 && close(output) < 0) { perror(argv[2]); status = 1; }
43 if (close(input) < 0) { perror(argv[1]); status = 1; }
44 return status;
45}