/*
 * _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 <stdio.h>

/* Таблица потоков (_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
