/* SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-2.0-only
**  Copyright (c) 1996,1997,1998 Ralf S. Engelschall <rse@engelschall.com>
*/

#include "eperl.h"
#include <errno.h>
#include <fcntl.h>


void putenvf(const char *fmt, ...)
{
    char *cp;
    va_list ap;
    va_start(ap, fmt);
    vasprintf(&cp, fmt, ap);
    va_end(ap);
    putenv(cp);
}


/*
**  I/O handle redirection
*/
#define IO_REDIRECT_STD(outerr, num)          \
static int IO_redirected_std##outerr = -1;    \
void IO_redirect_std##outerr(int fd)          \
{                                             \
    IO_redirected_std##outerr = fcntl(num, F_DUPFD_CLOEXEC, 0); \
    dup2(fd, num);                            \
}                                             \
void IO_restore_std##outerr(void)             \
{                                             \
    if (IO_redirected_std##outerr != -1) {    \
        dup2(IO_redirected_std##outerr, num); \
        close(IO_redirected_std##outerr);     \
        IO_redirected_std##outerr = -1;       \
    }                                         \
}
IO_REDIRECT_STD(out, 1)
IO_REDIRECT_STD(err, 2)


/*
**  Temporary filename support
*/
static char *mytmpfiles[tmpfile_cnt];
struct tmpfile mytmpfile(enum tmpfile_id id)
{
    asprintf(&mytmpfiles[id], "%s/%cPerl.XXXXXXXXXX", getenv("TMPDIR") ?: "/tmp", 'e' + id);
    int fd = mkostemp(mytmpfiles[id], O_CLOEXEC);
    if (fd == -1)
        mytmpfiles[id] = NULL;
    return (struct tmpfile){mytmpfiles[id], fd};
}

void remove_mytmpfiles(void)
{
    for (size_t i = 0; i != tmpfile_cnt; ++i)
        if (mytmpfiles[i])
            unlink(mytmpfiles[i]);
}


bool ePerl_CopyFILE(FILE *from, FILE *to)
{
    char buf[64 * 1024];
    for (size_t rd = sizeof(buf); rd == sizeof(buf); ) {
        rd = fread(buf, 1, sizeof(buf), from);
        fwrite(buf, 1, rd, to);
    }
    return !ferror(from);
}

/*
**  read source file into internal buffer
*/
bool ePerl_ReadSourceFile(const char *filename, char **cpBufC, size_t *nBufC)
{
    bool rc = true;
    FILE *fp = fopen(filename, "r"), *out = NULL;
    if (fp == NULL) {
        ePerl_SetError("Cannot open source file %s for reading", filename);
        CU(false);
    }

    out = open_memstream(cpBufC, nBufC);
    if (!ePerl_CopyFILE(fp, out)) {
        ePerl_SetError("Cannot read from file %s", filename);
        CU(false);
    }

CUS:
    if (fp)
        fclose(fp);
    if (out)
        if (fclose(out)) {
            ePerl_SetError("Cannot allocate for %s: %s", filename, strerror(errno));
            rc = false;
        }
    return rc;
}

/*
**  read an error file to internal buffer and substitute the filename
*/
void ePerl_SubstErrorLog(char **cpBuf, size_t *nBuf, const char *replace, const char *with)
{
    size_t replace_len = strlen(replace);
    size_t with_len    = strlen(with);
    ++*nBuf;
    for (size_t cur = 0; ;) {
        char *path = memmem(*cpBuf + cur, *nBuf - cur, replace, replace_len);
        if (!path)
            break;
        cur = path - *cpBuf;

        char *newBuf = with_len > replace_len ? realloc(*cpBuf, *nBuf + with_len - replace_len) : *cpBuf;
        if (!newBuf)
            break;
        memmove(newBuf + cur + with_len, newBuf + cur + replace_len, *nBuf - cur - replace_len);
        *cpBuf = newBuf;
        *nBuf += with_len - replace_len;

        memcpy(*cpBuf + cur, with, with_len);
        cur += with_len;
    }
    --*nBuf;
}
