FMEMOPEN(3)

ИМЯ
fmemopen - открывает память как поток

ОБЗОР
#include <stdio.h>

FILE *fmemopen(void *buf, size_t size, const char *mode);

Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)):

fmemopen():
    Начиная с glibc 2.10:
        _POSIX_C_SOURCE >= 200809L
    До glibc 2.10:
        _GNU_SOURCE

ОПИСАНИЕ
Функция fmemopen() открывает поток, тип доступа указывается в mode. Поток позволяет выполнять операции ввода-вывода со строкой или буфером памяти, указанным в buf.

В аргументе mode задаётся семантика ввода-вывода потока и может указываться одно из следующих значений:

r   Поток открывается для чтения.

w   Поток открывается для записи.

a   Добавление; поток открывается для записи, начальное положение в буфере указывает на первый байт null.

r+  Поток открывается для чтения и записи.

w+  Поток открывается для чтения и записи. Содержимое буфера обрезается (то есть, в первый байт буфера помещается '\0').

a+  Добавление; поток открывается для чтения и записи, начальное положение в буфере указывает на первый байт null.

Для потока поддерживается понятие текущего положения — место, откуда будет выполнена следующая операция ввода-вывода. Текущее положение неявно обновляется операциями ввода-вывода. Оно может быть изменено явным образом с помощью fseek(3) и получено с помощью ftell(3). Во всех режимах кроме добавления, начальное положение указывает на начало буфера. В режиме добавления, если в буфере нет байта null, то начальное положение равно size+1.

Если значение buf равно NULL, то fmemopen() выделяет буфер длиной size байт. Это полезно для приложений, которым нужно записать данные во временный буфер и прочитать из него. Начальное положение указывает на начало буфера. Буфер автоматически освобождается при закрытии потока. Заметим, что вызывающий никогда не получит указатель на временный буфер, выделенный этим вызовом (но смотрите open_memstream(3)).

Если buf не равно NULL, то значение должно указывать на буфер длиной не менее len байт, выделенный вызывающим.

Когда поток, открытый на запись, сбрасывается (flushed) (fflush(3)) или закрывается (fclose(3)), то в конец буфера записывается байт null, если есть место. Вызывающий должен быть уверен, что в буфере есть место для дополнительного байта (и в size учитывается этот байт), чтобы это произошло.

В потоке, открытом на чтение, при обнаружении байтов null («\0») в буфере операции чтения не возвращают конец файла. Чтение из буфера будет возвращать конец файла, только когда текущее положение в буфере достигнет size байт от начала буфера.

Операции записи выполняются, или по текущему положению (для всех режимов, кроме добавления), или по текущему размеру потока (в режимах добавления).

Попытка записать более size байт в буфер приводит к ошибке. По умолчанию, такие ошибки будут видимы (по отсутствию данных) только в момент сброса буфера stdio. Следующий вызов отключает буферизацию, что может быть полезно для обнаружения ошибок в момент операции вывода:

    setbuf(stream, NULL);

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении fmemopen() возвращается указатель FILE. В противном случае возвращается NULL и errno присваивается код ошибки.

ВЕРСИИ
Функция fmemopen() была доступна уже в glibc 1.0.x.

АТРИБУТЫ
Описание терминов данного раздела смотрите в attributes(7).
Интерфейс | Атрибут | Значение
fmemopen(), | Безвредность в нитях | MT-Safe

СООТВЕТСТВИЕ СТАНДАРТАМ
POSIX.1-2008. Эта функция не определена в POSIX.1-2001 и широко не распространена среди других систем.

В POSIX.1-2008 указано, что символ «b» в mode должен игнорироваться. Однако в Technical Corrigendum 1 изменили стандарт, позволив реализации решать что делать; это позволяет glibc учитывать «b».

ЗАМЕЧАНИЯ
У файлового потока, возвращаемого этой функцией, отсутствует файловый дескриптор (т. е., если файловый поток передать в fileno(3), то произойдёт ошибка).

Начиная с версии 2.22 двоичный режим (смотрите ниже) был удалён, было исправлено много дефектов в реализации fmemopen() и для этого интерфейса был создан новый символ с версией.

  Двоичный режим
В версиях с 2.9 по 2.21 реализация fmemopen() в glibc поддерживает «двоичный» режим, включаемый в mode указанием вторым символа 'b'. В этом режиме при записи не выполняется неявное добавление конечного байта null и fseek(3) SEEK_END считается относительно конца буфера (т. е., значения, указанного в аргументе size), а не длины текущей строки.

Дефект программного интерфейса перешёл и в реализацию двоичного режима: для задания двоичного режима 'b' должен указываться вторым символом в mode. То есть, например, «wb+» сработает, а «w+b» — нет. Это не совпадает с трактовкой mode в fopen(3).

Двоичный режим удалён в glibc 2.22; указание 'b' в mode игнорируется.

ДЕФЕКТЫ
В glibc до версии 2.22, если size равно нулю, то fmemopen() завершается с ошибкой EINVAL. Было бы логичней, если бы в этом случае успешно создавался поток, который затем бы возвращал конец файла при первой попытке его чтения; начиная с версии 2.22 реализация glibc поступает именно так.

В glibc до версии 2.22 указание в fmemopen() режима добавления («a» или «a+») устанавливает начальное положение в буфере на первый байт null, но (если положение сбрасывается в расположение, отличное от конца потока) не заставляет последующие операции записи выполнять добавление в конец потока. Этот дефект исправлен в glibc 2.22.

В glibc до версии 2.22, если в аргументе mode в fmemopen() включено добавление («a» или «a+») и аргумент size не учитывает байт null в buf, то согласно POSIX.1-2008 начальное положение буфера должно указывать на следующий байт за концом буфера. Однако, в этом случае glibc fmemopen() присваивает положению буфера значение -1. Этот дефект исправлен в glibc 2.22.

В glibc до версии 2.22 при вызове fseek(3) со значением whence, равным SEEK_END, и для потока, созданного fmemopen(), значение offset вычитается из положения конца потока, а не добавляется. Этот дефект исправлен в glibc 2.22.

При дополнении fmemopen() в glibc 2.9 «двоичным» режимом было потихоньку изменено ABI: раньше fmemopen() игнорировала «b» в mode.

ПРИМЕР
Программа, показанная ниже, использует fmemopen() для открытия входного буфера и open_memstream(3) для открытия выходного буфера с динамически изменяющимся размером. Программа сканирует входную строку (первый аргумент командной строки программы), читая целые числа, и записывает квадраты этих чисел в выходной буфер. Пример результата работы программы:

    $ ./a.out '1 23 43'
    размер=11; ptr=1 529 1849

  Исходный код программы
    #define _GNU_SOURCE
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #define handle_error(msg) \
        do { perror(msg); exit(EXIT_FAILURE); } while (0)
    int
    main(int argc, char *argv[])
    {
        FILE *out, *in;
        int v, s;
        size_t size;
        char *ptr;
        if (argc != 2) {
            fprintf(stderr, "Использование: %s '<num>...'\n", argv[0]);
            exit(EXIT_FAILURE);
        }
        in = fmemopen(argv[1], strlen(argv[1]), "r");
        if (in == NULL)
            handle_error("fmemopen");
        out = open_memstream(&ptr, &size);
        if (out == NULL)
            handle_error("open_memstream");
        for (;;) {
            s = fscanf(in, "%d", &v);
            if (s <= 0)
                break;
            s = fprintf(out, "%d ", v * v);
            if (s == -1)
                handle_error("fprintf");
        }
        fclose(in);
        fclose(out);
        printf("размер=%zu; ptr=%s\n", size, ptr);
        free(ptr);
        exit(EXIT_SUCCESS);
    }

СМОТРИТЕ ТАКЖЕ
fopen(3), fopencookie(3), open_memstream(3)