fopencookie(3)

НАИМЕНОВАНИЕ
fopencookie - open a custom stream

БИБЛИОТЕКА
Standard C library (libc, -lc)

ОБЗОР
#define _GNU_SOURCE         /* See feature_test_macros(7) */
#define _FILE_OFFSET_BITS 64
#include <stdio.h>

FILE *fopencookie(void *restrict cookie, const char *restrict mode,
                  cookie_io_functions_t io_funcs);

ОПИСАНИЕ
Функция fopencookie() позволяет программисту создать нестандартную реализацию стандартного потока ввода-вывода. Эта реализация может хранить данные потока в расположении по своему выбору; например, fopencookie() используется для реализации функции fmemopen(3), которая предоставляет потоковый интерфейс для данных, хранящихся в буфере в памяти.

Для создания нестандартного потока программист должен:

•   Реализовать четыре «обрабатывающих» (hook) функции, которые используются внутри стандартной библиотеки ввода-вывода при операциях ввода-вывода над потоком.

•   Define a "cookie" data type, a structure that provides bookkeeping information (e.g., where to store data) used by the aforementioned hook functions. The standard I/O package knows nothing about the contents of this cookie (thus it is typed as void * when passed to fopencookie()), but automatically supplies the cookie as the first argument when calling the hook functions.

•   Вызвать fopencookie() для открытия нового потока и связывания cookie и обрабатывающими функциями с этим потоком.

Функция fopencookie() подобна fopen(3): она открывает новый поток и возвращает указатель на объект FILE, который используется для работы с потоком.

Аргумент cookie — это указатель на структуру cookie вызывающего, которая связывается с новым потоком. Данный указатель передаётся в первом аргументе в моменты, когда стандартная библиотека ввода-вывода вызывает одну из обрабатывающих функций, описанных ниже.

Аргумент mode служит той же цели что и для fopen(3). Поддерживаются следующие режимы: r, w, a, r+, w+ и a+. Подробности смотрите в fopen(3).

Аргумент io_funcs — это структура, которая содержит четыре поля с задаваемыми программистом обрабатывающими функциями, которые используются для реализации этого потока. Структура определена как

    typedef struct {
        cookie_read_function_t  *read;
        cookie_write_function_t *write;
        cookie_seek_function_t  *seek;
        cookie_close_function_t *close;
    } cookie_io_functions_t;

Поля:

cookie_read_function_t *read
    Эта функция реализует операции чтения из потока. Она вызывается с тремя аргументами:

        ssize_t read(void *cookie, char *buf, size_t size);

    Аргументы buf и size — буфер для получаемых данных и его размер. В качестве результата функция read возвращает количество байт, скопированных в buf, 0 — при окончании файла и -1 при ошибке. Функция read обновляет смещение в потоке соответствующим образом.

    Если значение *read равно null, то при чтении из нестандартного потока всегда возвращается конец файла.

cookie_write_function_t *write
    Эта функция реализует операции записи в поток. Она вызывается с тремя аргументами:

        ssize_t write(void *cookie, const char *buf, size_t size);

    Аргументы buf и size — буфер для выходных данных и его размер (данные, записываемые в поток). В качестве результата функция write возвращает количество байт, скопированных из buf, и 0 при ошибке (функция не должна возвращать отрицательное значение). Функция write обновляет смещение в потоке соответствующим образом.

    Если значение *write равно null, то вывод в поток отбрасывается.

cookie_seek_function_t *seek
    Эта функция реализует операции смещения в потоке. Она вызывается с тремя аргументами:

        int seek(void *cookie, off_t *offset, int whence);

    В аргументе *offset указывается новое файловое смещение, зависящее от значения whence:

    SEEK_SET
        Значение смещения приравнивается *offset байт от начала потока.

    SEEK_CUR
        Значение *offset должно быть добавлено к текущему смещению в потоке.

    SEEK_END
        Значение смещения приравнивается к размеру потока плюс *offset.

    Перед возвратом функция seek должна обновить *offset, чтобы показать новое смещение в потоке.

    В качестве результата функция seek должна возвращать 0 при успешном выполнении и -1 при ошибке.

    Если значение *seek равно null, то операции смещения в потоке выполнить невозможно.

cookie_close_function_t *close
    Эта функция закрывает поток. Обрабатывающая функция может выполнить такие операции как освобождение буферов, выделенных для потока. При вызове она принимает один аргумент:

        int close(void *cookie);

    Аргумент cookie — это cookie, которую программист передал при вызове fopencookie().

    В качестве результата функция close должна возвращать 0 при успешном выполнении и EOF при ошибке.

    Если значение *close равно NULL, то при закрытии потока не выполняется никаких действий.

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

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

СТАНДАРТЫ
GNU.

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

    $ ./a.out 'hello world'
    /he/
    / w/
    /d/
    Reached end of file

Заметим, что представленную версию можно сильно улучшить, добавив обработку ошибок (например, открытие потока с cookie, которая уже имеет открытый поток; закрытие потока, который уже был закрыт).

  Исходный код программы
    #define _GNU_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <unistd.h>

    #define INIT_BUF_SIZE 4

    struct memfile_cookie {
        char   *buf;        /* Dynamically sized buffer for data */
        size_t  allocated;  /* Size of buf */
        size_t  endpos;     /* Number of characters in buf */
        off_t   offset;     /* Current file offset in buf */
    };

    ssize_t
    memfile_write(void *c, const char *buf, size_t size)
    {
        char *new_buff;
        struct memfile_cookie *cookie = c;

        /* Buffer too small? Keep doubling size until big enough. */

        while (size + cookie->offset > cookie->allocated) {
            new_buff = realloc(cookie->buf, cookie->allocated * 2);
            if (new_buff == NULL)
                return -1;
            cookie->allocated *= 2;
            cookie->buf = new_buff;
        }

        memcpy(cookie->buf + cookie->offset, buf, size);

        cookie->offset += size;
        if (cookie->offset > cookie->endpos)
            cookie->endpos = cookie->offset;

        return size;
    }

    ssize_t
    memfile_read(void *c, char *buf, size_t size)
    {
        ssize_t xbytes;
        struct memfile_cookie *cookie = c;

        /* Fetch minimum of bytes requested and bytes available. */

        xbytes = size;
        if (cookie->offset + size > cookie->endpos)
            xbytes = cookie->endpos - cookie->offset;
        if (xbytes < 0)     /* offset may be past endpos */
            xbytes = 0;

        memcpy(buf, cookie->buf + cookie->offset, xbytes);

        cookie->offset += xbytes;
        return xbytes;
    }

    int
    memfile_seek(void *c, off_t *offset, int whence)
    {
        off_t new_offset;
        struct memfile_cookie *cookie = c;

        if (whence == SEEK_SET)
            new_offset = *offset;
        else if (whence == SEEK_END)
            new_offset = cookie->endpos + *offset;
        else if (whence == SEEK_CUR)
            new_offset = cookie->offset + *offset;
        else
            return -1;

        if (new_offset < 0)
            return -1;

        cookie->offset = new_offset;
        *offset = new_offset;
        return 0;
    }

    int
    memfile_close(void *c)
    {
        struct memfile_cookie *cookie = c;

        free(cookie->buf);
        cookie->allocated = 0;
        cookie->buf = NULL;

        return 0;
    }

    int
    main(int argc, char *argv[])
    {
        cookie_io_functions_t  memfile_func = {
            .read  = memfile_read,
            .write = memfile_write,
            .seek  = memfile_seek,
            .close = memfile_close
        };
        FILE *stream;
        struct memfile_cookie mycookie;
        size_t nread;
        char buf[1000];

        /* Set up the cookie before calling fopencookie(). */

        mycookie.buf = malloc(INIT_BUF_SIZE);
        if (mycookie.buf == NULL) {
            perror("malloc");
            exit(EXIT_FAILURE);
        }

        mycookie.allocated = INIT_BUF_SIZE;
        mycookie.offset = 0;
        mycookie.endpos = 0;

        stream = fopencookie(&mycookie, "w+", memfile_func);
        if (stream == NULL) {
            perror("fopencookie");
            exit(EXIT_FAILURE);
        }

        /* Write command-line arguments to our file. */

        for (size_t j = 1; j < argc; j++)
            if (fputs(argv[j], stream) == EOF) {
                perror("fputs");
                exit(EXIT_FAILURE);
            }

        /* Read two bytes out of every five, until EOF. */

        for (long p = 0; ; p += 5) {
            if (fseek(stream, p, SEEK_SET) == -1) {
                perror("fseek");
                exit(EXIT_FAILURE);
            }
            nread = fread(buf, 1, 2, stream);
            if (nread == 0) {
                if (ferror(stream) != 0) {
                    fprintf(stderr, "fread failed\n");
                    exit(EXIT_FAILURE);
                }
                printf("Reached end of file\n");
                break;
            }

            printf("/%.*s/\n", (int) nread, buf);
        }

        free(mycookie.buf);

        exit(EXIT_SUCCESS);
    }

ПРИМЕЧАНИЯ
_FILE_OFFSET_BITS should be defined to be 64 in code that uses non-null seek or that takes the address of fopencookie, if the code is intended to be portable to traditional 32-bit x86 and ARM platforms where off_t's width defaults to 32 bits.

СМОТРИТЕ ТАКЖЕ
fclose(3), fmemopen(3), fopen(3), fseek(3)

ПЕРЕВОД
Русский перевод этой страницы руководства разработал(и) Azamat Hackimov <azamat.hackimov@gmail.com>, Dmitry Bolkhovskikh <d20052005@yandex.ru>, Yuri Kozlov <yuray@komyakino.ru>, Иван Павлов <pavia00@gmail.com> и Kirill Rekhov <krekhov.dev@gmail.com>

Этот перевод является свободной программной документацией; он распространяется на условиях общедоступной лицензии GNU (GNU General Public License - GPL, https://www.gnu.org/licenses/gpl-3.0.html версии 3 или более поздней) в отношении авторского права, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ.

Если вы обнаружите какие-либо ошибки в переводе этой страницы руководства, пожалуйста, сообщите об этом разработчику(ам) по его(их) адресу(ам) электронной почты или по адресу списка рассылки русских переводчиков (<debian-l10n-russian@lists.debian.org>).