05
1月
2007

GNU Readlineを使ってみる

GNU RreadlineはCUIに劇的な利便性をもたらす偉大なライブラリでありながら,これを使用するプログラムを作成したことがないので,作成してみた.
GNU Readlineって何だ?
準備
readline
ヒストリ
サンプル

GNU Readlineって何だ?

GNU readlineは,GNUプロジェクトにおいて開発された,文字列入力用ライブラリである. Bashの入力プロンプトでタブ補完ができたり,↑や↓で入力ヒストリを参照できたり,emacsライクあるいはviライクなキー操作ができるのは,GNU readlineによる. GNU readlineはCUIに劇的な利便性をもたらし,GNUにおける代表的成果物としての座をしめている(※).
※ LGPLに関するGNUの戦略について記述された頁である「あなたの次のライブラリにはライブラリGPLを適用するべきでない理由」などでも,

準備

Debian GNU/Linux環境では以下をしてライブラリやヘッダファイル等を入手しておく.
apt-get install libreadline5 libreadline5-dev

readline

GNU Readlineを使用しての入力を受け付ける方法はいたって簡単,関数readlineを呼び出すだけでタブ補完やemacsライクあるいはviライクな入力ができる.
char *
readline (const char *prompt);
promptは,入力待ち時のプロンプト文字列である. これがNULLの場合は,プロンプト文字列表示なしとなる. 返り値がユーザの入力文字列であり,readlineその領域の確保までやってくれている. 返り値は呼び出し側で忘れずにfreeしなければならない.

ヒストリ

GNU Radlineのヒストリ機能を使用するには,readlineの後,プログラムにて登録しなければならない.
void
add_history (const char *line);
この関数呼び出しを行うと,引数文字列がヒストリに登録され,次にreadline呼び出しをした際に,↑や↓などを押すだけで,入力候補としてヒストリ情報が表示される. このとき,ヒストリエントリに新しく領域が確保され,lineの内容はコピーされるようだ. したがって,add_historyしたlineは必要なくなれば即freeしてよい. その他いろいろなヒストリ関数が
/usr/include/readline/history.h
に記述されている. これらはreadline関数内でいろいろ使用されるものだが,もちろんユーザプログラムからも呼び出せる. 特にヒストリがたまってきた場合のリソースの開放で使用する. ヒストリのクリアと領域の開放は以下で行う.
void
clear_history (void);
ヒストリからレコードを削除する場合は,以下.
HIST_ENTRY *
remove_history (int record_no);
record_noは0から始まるレコード番号(値が小さいほど昔の入力). ヒストリから削除されてしまうので,返り値のレコードの領域は呼び出し側で管理,領域開放する必要がある.

サンプル

では,readlineと*_historyを使用したサンプルを以下に示す.

#include <stdlib.h>
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>

#define MAX_HISTORY_NO 3

int
main(void)
{
  char *prompt = getenv("PS2");
  char *line = NULL;
  int lineSize = 0;
  int index = 0;
  int history_no = 0;
  HIST_ENTRY *history = NULL;

  // readline呼び出し
  while (line = readline(prompt)) {
    lineSize = strlen(line);
    for (index = lineSize - 1; index >= 0; index--) {
      printf("%c", line[index]);
    }
    printf("\n");

    add_history(line);

    if (++history_no > MAX_HISTORY_NO) {
      history = remove_history(0);
      free(history);
    }

    // 入力文字列領域開放
    free(line);
  }
  printf("\n");

  clear_history();
  line = readline(prompt);
  printf(" last input = [ %s ]\n", line);
  free(line);

  return EXIT_SUCCESS;
}
このサンプルは,ループでreadlineしている. このときヒストリエントリ数が
#define MAX_HISTORY_NO 3
で指定した値以上になると,古いエントリを順に削除していく. これを繰り返し,^Dにてループを抜け,ヒストリを一旦クリアし,最後にもう一度readlineしてクリアの結果を確認できるようにした. リンクの際には,libreadlineを指定する(-lreadline).
gcc -Wall -g -lreadline reverse.c -o reverse
では実行.
$ export PS2='>>> '
$ ./reverse 
>>> 0
0
>>> 1
1
>>> 2
2
>>> 3 (この段階でヒストリに0がでなくなる)
3
>>> 4 (この段階でヒストリに1がでなくなる)
4
>>> 
>>> LAST STRING (この段階でヒストリがなくなる)
 last input = [ LAST STRING ]

You may also like...