FOPENCOOKIE - Linux手册页

时间:2019-08-20 18:00:20  来源:igfitidea点击:

Linux程序员手册 第3部分
更新日期: 2020-04-11

名称

fopencookie-打开自定义流

语法

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <stdio.h>

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

说明

fopencookie()函数允许程序员为标准I / O流创建自定义实现。该实现可以将流的数据存储在其自己选择的位置。例如,fopencookie()用于实现fmemopen(3),它为存储在内存缓冲区中的数据提供流接口。

为了创建自定义流,程序员必须:

*
实现在流上执行I / O时由标准I / O库内部使用的四个"挂钩"函数。
*
定义" cookie"数据类型,该结构提供由上述挂钩函数使用的簿记信息(例如,存储数据的位置)。标准I / O程序包对此cookie的内容一无所知(因此在传递给fopencookie()时将其键入为void *),但是在调用hook函数时会自动将cookie作为第一个参数提供。
*
调用fopencookie()打开一个新流,并将cookie和hook函数与该流关联。

fopencookie()函数的作用类似于fopen(3):它打开一个新的流,并返回一个指向FILE对象的指针,该对象用于对该流进行操作。

cookie参数是指向要与新流关联的调用者cookie结构的指针。当标准I / O库调用下面描述的任何挂钩函数时,此指针将作为第一个参数提供。

mode参数的作用与fopen(3)相同。支持以下模式:rwa,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参数分别是一个可以放置输入数据的缓冲区和该缓冲区的大小。作为函数结果,读取函数应返回复制到buf中的字节数,在文件末尾返回0,在错误时返回-1。读取功能应适当更新流偏移量。
如果* read是空指针,则从自定义流中读取的内容始终返回文件末尾。
cookie_write_function_t *write
该函数实现流的写操作。调用时,它将接收三个参数:
ssize_twrite(void * cookie,const char * buf,size_t size);
buf和size参数分别是要输出到流中的数据缓冲区和该缓冲区的大小。作为函数结果,写函数应返回从buf复制的字节数,如果出错则返回0。 (该函数不得返回负值。)写函数应适当更新流偏移量。
如果* write为空指针,则丢弃流的输出。
cookie_seek_function_t *seek
此函数在流上实现查找操作。调用时,它将接收三个参数:
intseek(void * cookie,off64_t * offset,int);
The *offset

argument specifies the new file offset depending on which
of the following three values is supplied in
whence:

SEEK_SET
流偏移量应从流的开始处设置* offset字节。
SEEK_CUR
* offset应该添加到当前流的偏移量中。
SEEK_END
流偏移量应设置为流的大小加* offset。
返回之前,seek函数应更新* offset以指示新的流偏移量。
作为其函数结果,seek函数在成功时应返回0,在错误时应返回-1。
如果* seek是空指针,则无法在流上执行搜索操作。
cookie_close_function_t *close
此功能关闭流。挂钩函数可以执行一些操作,例如释放为流分配的缓冲区。调用时,它接收一个参数:
int close(无效* cookie);
cookie参数是程序员调用fopencookie()时提供的cookie。
作为函数结果,关闭函数成功应返回0,错误则返回EOF。
如果* close为NULL,则在关闭流时不执行任何特殊操作。

返回值

成功的话,fopencookie()返回一个指向新流的指针。错误时,返回NULL。

属性

有关本节中使用的术语的说明,请参见attribute(7)。

InterfaceAttributeValue
fopencookie()Thread safetyMT-Safe

遵循规范

此函数是非标准的GNU扩展。

示例

下面的程序实现了一个自定义流,其功能与fmemopen(3)相似(但不相同)。它实现了一个流,其数据存储在内存缓冲区中。该程序将其命令行参数写入流,然后在流中搜索,每五个字符中读取两个,并将它们写入标准输出。以下shell会话演示了该程序的用法:

$ ./a.out aqhello worldaq
/he/
/ w/
/d/
Reached end of file

请注意,可以改进以下程序的更通用版本,以更可靠地处理各种错误情况(例如,使用已经具有打开流的cookie打开流;关闭已经关闭的流)。

Program source

#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;        /* 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;
        } 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;

    /* 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, 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];

    /* 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 (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 (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("Reached end of file\n");
            break;
        }

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

    exit(EXIT_SUCCESS);
}

另外参见

fclose(3),fmemopen(3),fopen(3),fseek(3)

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/