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;
ssize_t read(void *cookie, char *buf, size_t size);
引き数 buf と size は、 それぞれ、 入力データを配置できるバッファーとそのバッファーのサイズである。 関数の結果として、 read 関数は buf にコピーされたバイト数を、 ファイル末尾の場合は 0 を、 エラーの場合は -1 を返す。 read 関数はストリームのオフセットを適切に更新すべきである。
*read がヌルポインターの場合、 独自のストリームからの読み出しは常にファイル末尾 (end of file) を返す。
ssize_t write(void *cookie, const char *buf, size_t size);
引き数 buf と size は、 それぞれ、 ストリームへの出力するデータが入ったバッファーとそのバッファーのサイズである。 関数の結果として、 write 関数は buf からコピーされたバイト数を返し、 エラーの場合は -1 を返す。 (この関数は負の値を返してはならない。) write 関数はストリームのオフセットを適切に更新すべきである。
*write がヌルポインターの場合、 このストリームへの出力は破棄される。
int seek(void *cookie, off64_t *offset, int whence);
*offset 引き数は新しいファイルオフセットを指定する。 新しいオフセットは whence に以下の値のどれが指定されたかに応じて決まる。
関数の結果として、 seek 関数は成功すると 0 を、 エラーの場合 -1 を返す。
*seek がヌルポインターの場合、 このストリームに対して seek 操作を行うことができない。
int close(void *cookie);
cookie 引き数は fopencookie() の呼び出し時にプログラマーが渡した cookie である。
関数の結果として、 close 関数は成功すると 0 を、 エラーの場合 EOF を返す。
*close が NULL の場合、 ストリームがクローズされる際に特別な操作は何も行われない。
成功すると fopencookie() は新しいストリームへのポインターを返す。 エラーの場合、 NULL が返される。
この関数は非標準の 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;
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); }
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2015-01-22 | Linux |