#define _POSIX_C_SOURCE 200809L
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>

int
main(int argc, char **argv)
{
    struct stat source, destination;
    char buf[8192];
    int input, output = -1, status = 1;
    if (argc != 3) { fprintf(stderr, "usage: %s source destination\n", argv[0]); return 1; }
    input = open(argv[1], O_RDONLY | O_NONBLOCK);
    if (input < 0) { perror(argv[1]); return 1; }
    if (fstat(input, &source) < 0) { perror(argv[1]); goto done; }
    if (!S_ISREG(source.st_mode)) { fprintf(stderr, "cp: source must be a regular file\n"); goto done; }
    output = open(argv[2], O_WRONLY | O_CREAT | O_NONBLOCK, source.st_mode & 0777);
    if (output < 0) { perror(argv[2]); goto done; }
    if (fstat(output, &destination) < 0) { perror(argv[2]); goto done; }
    if (!S_ISREG(destination.st_mode)) { fprintf(stderr, "cp: destination must be a regular file\n"); goto done; }
    if (source.st_dev == destination.st_dev && source.st_ino == destination.st_ino) {
        fprintf(stderr, "cp: source and destination are the same file\n"); goto done;
    }
    if (ftruncate(output, 0) < 0) { perror(argv[2]); goto done; }
    for (;;) {
        ssize_t n = read(input, buf, sizeof(buf));
        size_t used = 0;
        if (n < 0 && errno == EINTR) continue;
        if (n < 0) { perror(argv[1]); goto done; }
        if (n == 0) break;
        while (used < (size_t)n) {
            ssize_t written = write(output, buf + used, (size_t)n - used);
            if (written < 0 && errno == EINTR) continue;
            if (written <= 0) { perror(argv[2]); goto done; }
            used += (size_t)written;
        }
    }
    status = 0;
done:
    if (output >= 0 && close(output) < 0) { perror(argv[2]); status = 1; }
    if (close(input) < 0) { perror(argv[1]); status = 1; }
    return status;
}
