Provided by: manpages-ja-dev_0.5.0.0.20221215+dfsg-1_all
名前
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) はメモリー上のバッ ファーに格納されたデータに対するストリームインターフェースを提供している。 独自のストリームを作成するためには、 プログラマーは以下を行う必要がある。 * ストリームに対する I/O を実行する際に標準 I/O ライブラリが内部で使用する 4 つの "フッ ク" 関数を実装する。 * "cookie" データ型を定義する。 "cookie" データ型は、上記のフック関数が使用する管理情報 (例えば、データを格納する場所など) を提供する構造体である。 標準の I/O パッケージにはこ の cookie の内容に関する情報を持たないが (したがって fopencookie() に渡される際の型は void * である)、 フック関数が呼び出される際に第一引数として cookie が渡される。 * fopencookie() を呼び出して、新しいストリームをオープンし、 そのストリームに cookie と フック関数を関連付ける。 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 つのフィールドの詳細は以下のとおりである。 cookie_read_function_t *read この関数はストリームに対する read 操作を実装する。 呼び出される際、 3 つの引数を受 け取る。 ssize_t read(void *cookie, char *buf, size_t size); 引数 buf と size は、 それぞれ、 入力データを配置できるバッファーとそのバッファーの サイズである。 関数の結果として、 read 関数は buf にコピーされたバイト数を、 ファイ ル末尾の場合は 0 を、 エラーの場合は -1 を返す。 read 関数はストリームのオフセット を適切に更新すべきである。 *read がヌルポインターの場合、 独自のストリームからの読み出しは常にファイル末尾 (end of file) を返す。 cookie_write_function_t *write この関数はストリームに対する write 操作を実装する。 呼び出される際、 3 つの引数を受 け取る。 ssize_t write(void *cookie, const char *buf, size_t size); 引数 buf と size は、 それぞれ、 ストリームへの出力するデータが入ったバッファーとそ のバッファーのサイズである。 関数の結果として、 write 関数は buf からコピーされたバ イト数を返し、 エラーの場合は -1 を返す。 (この関数は負の値を返してはならない。) write 関数はストリームのオフセットを適切に更新すべきである。 *write がヌルポインターの場合、 このストリームへの出力は破棄される。 cookie_seek_function_t *seek この関数はストリームに対する seek 操作を実装する。 呼び出される際、 3 つの引数を受 け取る。 int seek(void *cookie, off64_t *offset, int whence); *offset 引数は新しいファイルオフセットを指定する。 新しいオフセットは whence に以下 の値のどれが指定されたかに応じて決まる。 SEEK_SET ストリームオフセットを、ストリームの先頭から *offset バイトの位置に設定す る。 SEEK_CUR ストリームの現在のオフセットに *offset を加算する。 SEEK_END ストリームのオフセットを、ストリームのサイズに *offset を足した場所に設定す る。 関数が返る前に、 seek 関数はストリームの新しいオフセットを示すように *offset を更新 すべきである。 関数の結果として、 seek 関数は成功すると 0 を、 エラーの場合 -1 を返す。 *seek がヌルポインターの場合、 このストリームに対して seek 操作を行うことができな い。 cookie_close_function_t *close この関数はストリームをクローズする。 このフック関数では、 このストリームに割り当て られたバッファーを解放するといったことができる。 呼び出される際、 1 つの引数を受け 取る。 int close(void *cookie); cookie 引数は fopencookie() の呼び出し時にプログラマーが渡した cookie である。 関数の結果として、 close 関数は成功すると 0 を、 エラーの場合 EOF を返す。 *close が NULL の場合、 ストリームがクローズされる際に特別な操作は何も行われない。
返り値
成功すると 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/ に書かれている。