FOPENCOOKIE(3) | Linux Programmer's Manual | 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() を使うと、 プログラマーは標準 I/O ストリームの独自の実装を作成することができる。 この実装はストリームのデータを自分が選んだ場所に格納することができる。 例えば、 fopencookie() は fmemopen(3) を実装するのに使用されている。 fmemopen(3) はメモリー上のバッファーに格納されたデータに対するストリームインターフェースを提供している。
独自のストリームを作成するためには、 プログラマーは以下を行う必要がある。
fopencookie() 関数は fopen(3) と同様の機能を持つ。 新しいストリームをオープンし、 そのストリームに対して操作を行うのに使用する FILE オブジェクトへのポインターを返す。
cookie 引数は、 新しいストリームに関連付けられる呼び出し元の cookie 構造体へのポインターである。 このポインターは、 標準 I/O ライブラリが以下で説明するフック関数のいずれかを呼び出す際に第 1 引数として渡される。
mode 引数は fopen(3) と同じ意味を持つ。 指定できるモードは r, w, a, r+, w+, a+ である。 詳細は fopen(3) を参照。
io_funcs 引数は、 このストリームを実装するのに使用されるプログラマーが定義した関数を指す 4 つのフィールドを持つ構造体である。 この構造体は以下のように定義されている。
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;
4 つのフィールドの詳細は以下のとおりである。
成功すると fopencookie() は新しいストリームへのポインターを返す。 エラーの場合、 NULL が返される。
この節で使用されている用語の説明については、 attributes(7) を参照。
インターフェース | 属性 | 値 |
fopencookie() | Thread safety | MT-Safe |
この関数は非標準の GNU 拡張である。
以下のプログラムは、 fmemopen(3) で利用できるのと似た (同じではない) 機能を持つ独自のストリームを実装している。 データがメモリーバッファーに格納されるストリームを実装している。 このプログラムは、 コマンドライン引数をストリームに書き込み、 それからストリームをたどって 5 文字ごとに 2 文字を読み出して、 それを標準出力に書き込む。 以下のシェルセッションはこのプログラムの使用例である。
$ ./a.out 'hello world' /he/ / w/ /d/ Reached end of file
このプログラムを改良して様々なエラー状況に強くすることもできる (例えば、 オープン済みのストリームに対応する 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; /* 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; 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 (int 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); } exit(EXIT_SUCCESS); }
fclose(3), fmemopen(3), fopen(3), fseek(3)
この man ページは Linux man-pages プロジェクトのリリース 5.10 の一部である。プロジェクトの説明とバグ報告に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。
2020-11-01 | Linux |