#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <uchar.h>
#include <err.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include "fake-lib.h"

char *ascii(const char16_t *wstr, bool translate_slashes)
{
    size_t len = 0;
    for (const char16_t *wp = wstr; *wp; wp++)
        len++;
    char *ret = malloc(len + 1);
    char *p = ret;
    for (const char16_t *wp = wstr; *wp; wp++)
        *p++ = (*wp == '\\' && translate_slashes ? '/' :
                *wp < 0x80 ? *wp :
                '?');
    *p = '\0';
    return ret;
}

void system_argv_array(char **args)
{
    pid_t pid = fork();
    if (pid < 0)
        err(1, "fork");

    if (pid == 0) {
        execvp(args[0], args);
        warn("execvp");
        _exit(127);
    }

    int status;
    if (waitpid(pid, &status, 0) != pid)
        err(1, "waitpid");
    if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0))
        errx(1, "subcommand failed");
}

void system_argv(const char *cmd, ...)
{
    int nargs, nchars;
    const char *word;
    va_list ap;

    va_start(ap, cmd);
    nargs = 1;                         /* terminating NULL */
    nchars = 0;
    for (word = cmd; word; word = va_arg(ap, const char *)) {
        nargs++;
        nchars += 1 + strlen(word);
    }
    va_end(ap);

    char *args[nargs], chars[nchars];
    char **argp = args, *charp = chars;
    va_start(ap, cmd);
    for (word = cmd; word; word = va_arg(ap, const char *)) {
        *argp++ = charp;
        strcpy(charp, word);
        charp += 1 + strlen(word);
    }
    va_end(ap);
    *argp++ = NULL;

    system_argv_array(args);
}

void c16cpy(char16_t *out, uint32_t *outsize, char *s)
{
    uint32_t retlen = 0;
    while (retlen < *outsize) {
        char16_t c = (out[retlen] = (unsigned char)*s++);
        if (!c)
            break;
        retlen++;
    }
    *outsize = retlen;
}

void *smalloc(size_t size)
{
    void *toret = malloc(size);
    if (!toret)
        errx(1, "out of memory");
    return toret;
}

void *srealloc(void *ptr, size_t size)
{
    void *toret = realloc(ptr, size);
    if (!toret)
        errx(1, "out of memory");
    return toret;
}

char *dupcat(const char *str, ...)
{
    va_list ap;
    const char *p;
    char *out, *outp;
    size_t len;

    len = 1;
    va_start(ap, str);
    for (p = str; p; p = va_arg(ap, const char *))
        len += strlen(p);
    va_end(ap);

    out = snewn(len, char);
    outp = out;
    va_start(ap, str);
    for (p = str; p; p = va_arg(ap, const char *)) {
        strcpy(outp, p);
        outp += strlen(p);
    }
    va_end(ap);

    return out;
}

unsigned le(const unsigned char *buf, size_t len, size_t off, size_t nbytes)
{
    unsigned toret = 0;
    off += nbytes;
    while (nbytes-- > 0) {
        toret <<= 8;
        if (--off < len)
            toret |= buf[off];
    }
    return toret;
}