| 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 |