続・gets
高木です。こんばんは。
私は基本的には朝7:00にブログを公開する方針なのですが、いろいろ事情があって、変な時間になってしまうこともあります。
今日も変な時間に投稿します。
さて、以前の記事で、Cの関数の中では結構なじみ深いものでありながら、非推奨を経て廃止されてしまったgets関数をC++で蘇らせることをやりました。
それはそれで悪くないとは思うのですが、本来Cの関数であるgetsを、わざわざC++を導入しなければ使えないというのも辛い話です。
また、シグニチャが異なるので、完全互換というわけにはいきませんでした。
今回はその反省を踏まえ、C++ではなく純然たるCで、完全互換なgetsを実装してみたいと思います。
とはいえ、もともとある欠陥をそのまま残したのでは面白くありません。
バッファオーバーランを検知できない従来の欠陥を克服し、完全互換のgetsを実現したいと思います。
使用する言語は、C11です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | #include <stdio.h> #ifdef _WIN32 static inline void flockfile(FILE* stream) { _lock_file(stream); } static inline void funlockfile(FILE* stream) { _unlock_file(stream); } static inline int getc_unlocked(FILE* stream) { return _getc_nolock(stream); } static inline int putc_unlocked(int c, FILE* stream) { return _putc_nolock(c, stream); } #endif static _Thread_local int gets_buffer_size = 256; int get_gets_buffer_size(void) { return gets_buffer_size; } int set_gets_buffer_size(int size) { if (size < 1) return -1; gets_buffer_size = size; return 0; } char *gets(char *s) { char* r = 0; flockfile(stdin); for (char* first = &s[0], * last = &s[gets_buffer_size - 1]; first < last; ++first) { int c = getc_unlocked(stdin); if (c == EOF) goto end; if (c == '\n') { *first = '\0'; break; } *first = c; } s[gets_buffer_size - 1] = '\0'; r = &s[0]; end: funlockfile(stdin); return r; } // Copyright © 2017 by TAKAGI Nobuhisa // takagi@cloverfield.jp // どなたでも、自己の責任においてこのプログラムを使用していただいてかまいません。 |
set_gets_buffer_size関数を用いて事前にバッファサイズを指定しておくことで、バッファオーバーランを回避できるようにしました。
また、バッファサイズを格納する変数gets_buffer_sizeは、_Thread_localとしているのでスレッドセーフです。
_Thread_localはC11からしか使えませんが、C99までならgets関数は使えるので、これで問題ないと判断しました。
そもそも、標準ライブラリと同名の識別子を外部結合にすると未定義の動作になりますので、(getsが廃止された)C11でなければ使えませんから。