入力待ち【編集中】

Tip: 入力待ち【編集中】

以下のコードはY/N選択型の入力待ちのパターンで、入力待ち処理の基本形になります。
入力を促すメッセージプロンプトを表示して入力待機処理に入り、 利用者が入力することで各々の処理に分岐するまでの動作を3つのステップで行います。

このコードでは入力制御モジュール(Input)を使用しています。

モジュールについてArgのドキュメントもあわせてご覧ください。

11111111111111111111111
package MyApp;

my $subs = {
  '1' =>  # 入力制御設定
  sub {
    my $arg = $self->from_param('_Arg');
    $arg->mode( 'echo'=>1, 'line'=>1 );
    $arg->param( 'line_size'=>10 );
    $self->sethandler( sub { $self->MyApp::MyUtil::call(2) } );
  },

ステップ1では入力制御オブジェクトに動作モードとパラメータを設定し、次のステップを呼び出すようハンドラを設定します。

  '2' =>  # プロンプト表示
  sub {
    $self->send('cp932', "よろしいですか (Y/N) : ");
    $self->sethandler( sub { $self->MyApp::MyUtil::call(3) } );
  },

ステップ2ではメッセージプロンプトをクライアントに送信し、次のステップを呼び出すようハンドラが設定されます。

ステップ3では入力待ち処理になります。

この処理では'Y'または'N'が入力されたら各々のルーチンを呼び出すようハンドラが設定されます。
キャンセルキーとしてESCやETX(Ctrl+C)が入力待ちのあいだに押されると、キャンセル処理ルーチンを呼び出すようハンドラが設定されます。
'Y'または'N'以外の文字が入力された場合はプロンプトを再び表示して入力待ち処理に戻ります。

3つのセクションに分かれていますので、順を追って説明いたします。

  '2' =>  # 入力待ち
  sub {
    my $arg = $self->from_param('_Arg');
    my $inkey = $arg->arg(); $inkey = defined($inkey) ? $inkey : '';

最初のセクションでは、蓄積されている受信データを取り出します。
このとき取り出した文字は制御文字なども含め、inkey に渡しています。
ステップ1でライン入力モードを有効にしていますので、取り出したデータはライン入力バッファにも取り込まれます。

ライン入力バッファは status('line') を呼び出すことで文字列として取得することができますが、 ライン入力バッファには制御文字が取り込まれませんので、 押されたキーコードとライン入力バッファに取り込まれた文字列のどちらを取得するのかによってアプローチが変わります。

    # ●[ETX][ESC]	【終了】
    if ( $inkey eq "\x03" || $inkey eq "\x1b" ) {
      $self->send("\r\n");
      $arg->flush();
      $self->MyApp::MyUtil::cancel();
    }

2つ目のセクションでは、押されたキーコードに関する判定をしています。

ESCやETX(Ctrl+C)は制御文字ですので、ライン入力の文字列として取得することができませんが、 制御文字は inkey に入りますので、取り込んだ inkey を評価するだけで判定可能です。

注意すべきこととして、ライン入力バッファが有効で、入力途中でキャンセルキーが押され入力が中断された場合、 それまでに入力した文字はライン入力バッファに残っているため、 flush() を呼び出してライン入力バッファをクリアする必要があります。

flush() を行わず、次の入力待ち処理が行われた場合、 ライン入力バッファに残っている文字列に取り込まれた文字が追加されるため、誤った文字列を取り出すことになります。

    # ●(改行)
    elsif ( $arg->status('line_cr') == 1 ) {
      $self->send("\r\n");
      my $input = $arg->status('line');

      # ●(ヌル)	  【戻る】
      if ( $input eq '' ) {
        $self->BIGModel::Mail::finalize;
      }

      # ●[Y]      【 はい 】
      elsif ($input =~ /^Y$/i) {
        $self->sethandler( sub { $self->MyApp::MyUtil::yes() } );
      }

      # ●[N]      【 いいえ 】
      elsif ($input =~ /^N$/i) {
        $self->sethandler( sub { $self->MyApp::MyUtil::no() } );
      }

      # ●(その他)  【プロンプト再表示】
      else {
        $self->sethandler( sub { $self->MyApp::MyUtil::call(2) } );
      }
   },

最後のセクションではライン入力バッファに取り込まれた文字列に関する判定をしています。

1行目ではライン入力バッファ確定フラグ(line_cr)を確認しています。

ライン入力バッファ確定フラグはライン入力モードが有効のとき、 改行が入力するとライン入力バッファの内容が確定したことを示すために、このフラグが1に変わります。

上記の処理では、このフラグが1でない場合は何もせずステップを抜けます。

ステップを抜けた場合はプロンプト表示でハンドラに設定した call(3) が保持されていますので、 onIdleイベントが発生すると、再び call(3) が呼び出され、入力が確定するまでループして呼び出されます。

※ call() についてはこのページでは説明していませんが、ユーティリティの呼び出しルーチンとして、全てのユーティリティにcall()が定義され、 各ステップの番号を引数に指定してcall()を呼び出すと、そのステップのコードが呼び出される関数です。
詳細はアプリケーションの作成方法(ユーティリティ)を参照ください。

もしフラグが1だった場合は2行目で改行を送信し、3行目でライン入力バッファの文字列を input に取り込みます。

その後、 input の内容に応じて各々のステップをハンドラに登録します。

ライン入力バッファ確定フラグは改行が押されると1がセットされるので、改行がエコーバックされるのが理想的ですが、 ライン入力バッファには制御文字が取り込まれませんので、エコーバックモードが有効であってもエコーバックされません。
そのため、改行(確定)した場合は2行目のように単独で送信する必要があります。

ライン入力バッファ確定フラグが1でなくても、status('line')でライン入力バッファの文字列はいつでも取得することができます。
ただし、ライン入力バッファ確定フラグが1になり、その後、入力バッファの文字列を取得することなく蓄積した受信データを取り出す(arg())と、 それまでの内容が消去されますので、適切なタイミングで取得するようにします。

Task Runner