#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <cstdio>

#include <wordexp.h>

extern "C" {

int wordexp(const char *words, wordexp_t *pwordexp, int)
{
    if (!words || !pwordexp) {
        return WRDE_BADCHAR;
    }

    char **wordv = static_cast<char **>(std::calloc(2, sizeof(char *)));
    if (!wordv) {
        return WRDE_NOSPACE;
    }

    wordv[0] = static_cast<char *>(std::malloc(std::strlen(words) + 1));
    if (!wordv[0]) {
        std::free(wordv);
        return WRDE_NOSPACE;
    }

    std::strcpy(wordv[0], words);
    pwordexp->we_wordc = 1;
    pwordexp->we_wordv = wordv;
    pwordexp->we_offs = 0;
    return 0;
}

void wordfree(wordexp_t *pwordexp)
{
    if (!pwordexp || !pwordexp->we_wordv) {
        return;
    }

    for (size_t index = 0; index < pwordexp->we_wordc; ++index) {
        std::free(pwordexp->we_wordv[index]);
    }
    std::free(pwordexp->we_wordv);
    pwordexp->we_wordc = 0;
    pwordexp->we_wordv = nullptr;
    pwordexp->we_offs = 0;
}

int link(const char *oldpath, const char *newpath)
{
    FILE *source = std::fopen(oldpath, "rb");
    if (!source) {
        return -1;
    }

    FILE *target = std::fopen(newpath, "rb");
    if (target) {
        std::fclose(target);
        std::fclose(source);
        errno = EEXIST;
        return -1;
    }

    target = std::fopen(newpath, "wb");
    if (!target) {
        const int saved_errno = errno;
        std::fclose(source);
        errno = saved_errno;
        return -1;
    }

    char buffer[4096];
    while (true) {
        const std::size_t read = std::fread(buffer, 1, sizeof(buffer), source);
        if (read > 0 && std::fwrite(buffer, 1, read, target) != read) {
            const int saved_errno = errno;
            std::fclose(target);
            std::fclose(source);
            errno = saved_errno;
            return -1;
        }
        if (read < sizeof(buffer)) {
            if (std::ferror(source)) {
                const int saved_errno = errno;
                std::fclose(target);
                std::fclose(source);
                errno = saved_errno;
                return -1;
            }
            break;
        }
    }

    if (std::fclose(target) != 0) {
        const int saved_errno = errno;
        std::fclose(source);
        errno = saved_errno;
        return -1;
    }
    std::fclose(source);
    return 0;
}

}  // extern "C"
