/* * fopen — открыть файл как буферизованный поток FILE* (вариант B+). * * FILE берётся из статической таблицы _file_slots (свободный слот: * flags == 0), без malloc — сам буфер выделится лениво при первой * операции. Первый успешный fopen вешает _fclosall на atexit, чтобы * exit() сбрасывал несброшенную запись всех потоков. * NULL при: плохом режиме (EINVAL), занятой таблице (EMFILE), ошибке * open (errno от него). */ #include #include /* atexit */ #include #include #include #include "_file.h" /* Транслировать строку режима fopen() в подмножество флагов open(), * которое понимает libc/io/open.c. Поддержаны r, w, a, "+" в любом * месте хвоста; "b"/"t" игнорируются — весь I/O бинарный. */ static int mode_to_flags(const char *mode, uint8_t *file_flags) { if (!mode || !*mode) { errno = EINVAL; return -1; } int oflags = 0; uint8_t ff = 0; char base = *mode; int plus = 0; for (const char *p = mode + 1; *p; p++) { if (*p == '+') plus = 1; } switch (base) { case 'r': oflags = plus ? O_RDWR : O_RDONLY; ff = _F_READ | (plus ? _F_WRITE : 0); break; case 'w': oflags = (plus ? O_RDWR : O_WRONLY) | O_CREAT | O_TRUNC; ff = _F_WRITE | (plus ? _F_READ : 0); break; case 'a': oflags = (plus ? O_RDWR : O_WRONLY) | O_CREAT | O_APPEND; ff = _F_WRITE | _F_APPEND | (plus ? _F_READ : 0); break; default: errno = EINVAL; return -1; } *file_flags = ff; return oflags; } FILE *fopen(const char *path, const char *mode) { static uint8_t atexit_armed; uint8_t ff; int oflags = mode_to_flags(mode, &ff); if (oflags < 0) return NULL; /* Свободный слот таблицы — ДО open, чтобы не открывать зря. */ FILE *fp = _file_slots; for (uint8_t i = 0; ; i++, fp++) { if (i >= OPEN_MAX) { errno = EMFILE; return NULL; } if (fp->flags == 0) break; } int fd = open(path, oflags); if (fd < 0) return NULL; fp->fd = fd; fp->buf = NULL; fp->curp = NULL; fp->level = 0; fp->hold = EOF; fp->flags = ff; /* последним — занимает слот */ if (!atexit_armed) { atexit_armed = 1; atexit(_fclosall); /* exit() сбросит все потоки */ } return fp; }