Provided by: manpages-ja-dev_0.5.0.0.20221215+dfsg-1_all bug

名前

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

              引数 bufsize は、 それぞれ、 入力データを配置できるバッファーとそのバッファーの
              サイズである。 関数の結果として、 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);

              引数 bufsize は、 それぞれ、 ストリームへの出力するデータが入ったバッファーとそ
              のバッファーのサイズである。 関数の結果として、 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/ に書かれている。