/* * _file.h — внутренности буферизованного FILE* (вариант B+, НЕ * публичный заголовок). Дизайн: docs/file-buffering-design.md. * * Инварианты: * - слот таблицы свободен, когда flags == 0; * - level < BUFSIZ на входе в любую операцию записи (конвенция * «сброс ПЕРЕД записью в буфер»); * - hold != EOF означает отложенный байт ungetc; логическая позиция * потока на 1 меньше учтённой в level; * - консольные псевдопотоки (_F_CONIN/_F_CONOUT) буфер не заводят * и в _file_sync не попадают. */ #ifndef _FILE_INTERNAL_H #define _FILE_INTERNAL_H #include /* Таблица потоков (_file_slots.c); свободный слот: flags == 0. */ extern FILE _file_slots[OPEN_MAX]; /* Найти свободный слот и взвести atexit(_fclosall) при первом * использовании. Слот НЕ помечается занятым — вызывающий заполняет * поля и последним ставит flags. NULL + errno=EMFILE, если все * заняты. (_file_slot.c; используют fopen и fdopen.) */ FILE *_file_slot_take(void); /* Транслировать строку режима fopen ("r/w/a", '+' в любом месте * хвоста, 'b'/'t' игнорируются) в флаги open() (возврат) и наши * _F_-флаги (*ff). -1 + errno=EINVAL при плохом режиме. * (_file_mode.c; используют fopen, fdopen, freopen.) */ int _file_mode_flags(const char *mode, uint8_t *ff); /* Обеспечить буфер: вернуть fp->buf, при необходимости malloc(BUFSIZ). * NULL = malloc не дал — вызывающий уходит на небуферизованный путь * (прямой syscall на 1 байт), поток продолжает работать. */ uint8_t *_file_buf(FILE *fp); /* Нейтрализовать буфер (перед seek/tell/close и при смене направления): * - направление «запись»: write() несброшенного хвоста; * - направление «чтение»: lseek(fd, -(level + hold), SEEK_CUR) — * откат непрочитанного readahead и ungetc-байта. * После вызова: level = 0, hold = EOF, _F_DIROUT снят, curp = buf. * Возврат 0 или EOF (ставит _F_ERROR). */ int _file_sync(FILE *fp); /* fclose всех открытых слотов; вешается на atexit первым fopen. */ void _fclosall(void); #endif