/*
 * open — открыть/создать файл по POSIX-флагам.
 *
 * Машина состояний флагов: экспонируем POSIX-биты (O_RDONLY/O_WRONLY/
 * O_RDWR + O_CREAT/O_EXCL/O_TRUNC/O_APPEND) и диспетчеризуем на три
 * входа ESTEX:
 *
 *     $11 OPEN        — открыть существующий файл
 *     $0A CREATE      — создать (усечь, если существует), вернуть хендл
 *     $0B CREATE_NEW  — создать, только если файла нет
 *
 * Три приватных __naked-обёртки делают сырые RST 10h; публичный open()
 * — обычный C-оркестратор.  IX сохраняется через каждый RST.
 * При ошибке errno установлен, возврат -1.
 */

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "_fd_guard.h"

/* ---- сырые ESTEX-обёртки ---------------------------------------------- */

/*  ESTEX $11 OPEN: A=режим (1=R, 2=W, 0=R/W), HL=путь → A=хендл, CF=err. */
static int _estex_open_raw(const char *path, int posix_mode) __naked
{
    (void)path; (void)posix_mode;
    __asm
        push  ix
        ;; HL = путь, DE = posix_mode.  Перевести в нумерацию ESTEX.
        ld    a, e
        and   a, #0x03
        ld    c, #1
        or    a, a
        jr    Z, _oopen_real
        ld    c, #2
        dec   a
        jr    Z, _oopen_real
        ld    c, #0
    _oopen_real:
        ld    a, c
        ld    c, #0x11                 ; ESTEX OPEN
        rst   #0x10
        pop   ix
        jr    c, _oopen_err
        ld    e, a
        ld    d, #0
        ret
    _oopen_err:
        call  __errno_set
        ld    de, #-1
        ret
    __endasm;
}

/*  ESTEX $0A CREATE: A=атрибут, HL=путь → A=хендл, CF=err.
 *  Усекает существующий файл. */
static int _estex_create_raw(const char *path) __naked
{
    (void)path;
    __asm
        push  ix
        xor   a, a                     ; A = 0 (обычный атрибут)
        ld    c, #0x0A
        rst   #0x10
        pop   ix
        jr    c, _ocreat_err
        ld    e, a
        ld    d, #0
        ret
    _ocreat_err:
        call  __errno_set
        ld    de, #-1
        ret
    __endasm;
}

/*  ESTEX $0B CREATE_NEW: A=атрибут, HL=путь → A=хендл, CF=err.
 *  Падает (errno=EEXIST), если файл уже существует. */
static int _estex_create_new_raw(const char *path) __naked
{
    (void)path;
    __asm
        push  ix
        xor   a, a
        ld    c, #0x0B
        rst   #0x10
        pop   ix
        jr    c, _ocreatn_err
        ld    e, a
        ld    d, #0
        ret
    _ocreatn_err:
        call  __errno_set
        ld    de, #-1
        ret
    __endasm;
}

/* ---- публичная поверхность --------------------------------------------- */

int open(const char *path, int flags)
{
    int fd;

    /* Предохранитель: 9-й OPEN вешает DSS (см. _fd_guard.h) —
     * отказать самим, не доводя до syscall. */
    if (_fd_open_count >= _FD_OPEN_MAX) {
        errno = EMFILE;
        return -1;
    }

    if (flags & O_CREAT) {
        if (flags & O_EXCL) {
            /* Не должен существовать. */
            fd = _estex_create_new_raw(path);
        } else if (flags & O_TRUNC) {
            /* Всегда создать или усечь. */
            fd = _estex_create_raw(path);
        } else {
            /* Открыть, если есть, иначе создать. */
            fd = _estex_open_raw(path, flags);
            if (fd < 0 && errno == ENOENT) {
                fd = _estex_create_raw(path);
            }
        }
    } else {
        fd = _estex_open_raw(path, flags);
    }

    if (fd < 0) {
        return -1;
    }
    _fd_open_count++;

    if (flags & O_APPEND) {
        /* Встать на конец файла — дальнейшие записи дописывают. */
        (void)lseek(fd, 0L, SEEK_END);
    }
    return fd;
}
