/*
 * wnix, a Unix-like operating system for WebAssembly applications.
 * Copyright (C) 2025  Xavier Del Campo Romero
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <libc/file.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>

static int find_free(void)
{
    for (size_t i = 0; i < sizeof __files / sizeof *__files; i++)
        if (!__files[i])
            return i;

    return -1;
}

static int convert(const char *mode)
{
    int ret = 0;

    if (!mode)
        return -1;
    else if (*mode == 'r')
        ret |= O_RDONLY;
    else if (*mode == 'w')
        ret |= O_WRONLY | O_CREAT;
    else if (*mode == 'a')
        ret |= O_WRONLY | O_CREAT | O_APPEND;

    if (*++mode == '+')
        ret |= O_RDWR;

    if (!ret)
        return -1;

    return ret;
}

FILE *fopen(const char *const path, const char *const mode)
{
    FILE *ret = NULL;
    int i, flags, fd = -1;
    mode_t mask;

    if (!path || (flags = convert(mode)) < 0)
    {
        errno = EINVAL;
        goto failure;
    }
    else if ((i = find_free()) < 0)
    {
        errno = EMFILE;
        goto failure;
    }

    mask = umask(0);
    umask(mask);

    if ((fd = open(path, flags, 0666 & ~mask)) < 0)
        goto failure;
    else if (!(ret = malloc(sizeof *ret)))
        goto failure;

    *ret = (const struct __file){.fd = fd};
    __files[i] = ret;
    return ret;

failure:

    if (fd >= 0)
        close(fd);

    free(ret);
    return NULL;
}
