FOPENCOOKIE(3) | Руководство программиста Linux | FOPENCOOKIE(3) |
fopencookie - открывает нестандартный поток
#define _GNU_SOURCE /* см. feature_test_macros(7) */ #include <stdio.h>
FILE *fopencookie(void *cookie, const char *mode, cookie_io_functions_t io_funcs);
Функция fopencookie() позволяет программисту создать нестандартную реализацию стандартного потока ввода-вывода. Эта реализация может хранить данные потока в расположении по своему выбору; например, fopencookie() используется для реализации функции fmemopen(3), которая предоставляет потоковый интерфейс для данных, хранящихся в буфере в памяти.
Для создания нестандартного потока программист должен:
Функция 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;
Поля:
При успешном выполнении fopencookie() возвращает указатель на новый поток. При ошибке возвращается NULL.
Описание терминов данного раздела смотрите в attributes(7).
Интерфейс | Атрибут | Значение |
fopencookie() | Безвредность в нитях | MT-Safe |
Эта функция является нестандартным расширением GNU.
Программа, представленная ниже, реализует нестандартный поток, свойства которого похожи (но не одинаковы) на свойство потока, получаемого от fmemopen(3). Она реализует поток, данные которого хранятся в буфере памяти. Программа записывает свои аргументы командной строки в поток, а затем перемещается по потоку, читая два из каждых пяти символов и записывая их в стандартный вывод. Сеанс оболочки, демонстрирующий использование программы:
$ ./a.out 'hello world' /he/ / w/ /d/ Достигнут конец файла
Заметим, что представленную версию можно сильно улучшить, добавив обработку ошибок (например, открытие потока с cookie, которая уже имеет открытый поток; закрытие потока, который уже был закрыт).
#define _GNU_SOURCE #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define INIT_BUF_SIZE 4 struct memfile_cookie { char *buf; /* динамически изменяемый буфер для данных */ size_t allocated; /* размер буфера */ size_t endpos; /* количество символов в буфере */ off_t offset; /* текущее файловое смещение в буфере */ }; ssize_t memfile_write(void *c, const char *buf, size_t size) { char *new_buff; struct memfile_cookie *cookie = c; /* Буфер мал? Удваиваем размер, пока не станет достаточным */ while (size + cookie->offset > cookie->allocated) { new_buff = realloc(cookie->buf, cookie->allocated * 2); if (new_buff == NULL) { return -1; } else { 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; /* Выбираем минимум запрашиваемых и доступных байт */ xbytes = size; if (cookie->offset + size > cookie->endpos) xbytes = cookie->endpos - cookie->offset; if (xbytes < 0) /* смещение может быть за endpos */ xbytes = 0; memcpy(buf, cookie->buf + cookie->offset, xbytes); cookie->offset += xbytes; return xbytes; } int memfile_seek(void *c, off64_t *offset, int whence) { off64_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; ssize_t nread; long p; int j; char buf[1000]; /* Устанавливаем cookie перед вызовом 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); } /* Записываем аргументы командной строки в файл */ for (j = 1; j < argc; j++) if (fputs(argv[j], stream) == EOF) { perror("fputs"); exit(EXIT_FAILURE); } /* Читаем два байта из пяти пока не получим EOF */ for (p = 0; ; p += 5) { if (fseek(stream, p, SEEK_SET) == -1) { perror("fseek"); exit(EXIT_FAILURE); } nread = fread(buf, 1, 2, stream); if (nread == -1) { perror("fread"); exit(EXIT_FAILURE); } if (nread == 0) { printf("Достигнут конец файла\n"); break; } printf("/%.*s/\n", nread, buf); } exit(EXIT_SUCCESS); }
2019-03-06 | Linux |