Provided by: manpages-ja-dev_0.5.0.0.20210215+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 が返
       される。

準拠

       この関数は非標準の 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);
       }

関連項目

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

この文書について

       この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部  である。プロジェクト
       の説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。