Natural Tiny Shell (NT-Shell) library is a tiny shell library for a small embedded system. The interface is really simple. You should only know ntshell_execute in ntshell.h. So you can port it to any embedded system easily. Please enjoy your small embedded system with it. :)

Dependents:   NaturalTinyShell_TestProgram

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ntshell.c Source File

ntshell.c

Go to the documentation of this file.
00001 /**
00002  * @file ntshell.c
00003  * @author Shinichiro Nakamura
00004  * @brief 小規模組み込みシステム向けのシェルシステムの実装。
00005  */
00006 
00007 /*
00008  * ===============================================================
00009  *  Natural Tiny Shell (NT-Shell)
00010  *  Version 0.0.6
00011  * ===============================================================
00012  * Copyright (c) 2010-2011 Shinichiro Nakamura
00013  *
00014  * Permission is hereby granted, free of charge, to any person
00015  * obtaining a copy of this software and associated documentation
00016  * files (the "Software"), to deal in the Software without
00017  * restriction, including without limitation the rights to use,
00018  * copy, modify, merge, publish, distribute, sublicense, and/or
00019  * sell copies of the Software, and to permit persons to whom the
00020  * Software is furnished to do so, subject to the following
00021  * conditions:
00022  *
00023  * The above copyright notice and this permission notice shall be
00024  * included in all copies or substantial portions of the Software.
00025  *
00026  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00027  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
00028  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00029  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
00030  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
00031  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00032  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00033  * OTHER DEALINGS IN THE SOFTWARE.
00034  * ===============================================================
00035  */
00036 
00037 #include "ntshell.h"
00038 #include "ntlibc.h"
00039 
00040 #define VERSION_MAJOR 0     /**< メジャー番号。 */
00041 #define VERSION_MINOR 0     /**< マイナー番号。 */
00042 #define VERSION_RELEASE 6   /**< リリース番号。 */
00043 
00044 /**
00045  * @brief 処理で用いるデータ構造体。
00046  *
00047  * @details
00048  * vtparseはユーザデータのポインタを設定することができる。
00049  * Natural Tiny Shellはこれを使って自身の処理で必要な情報を保持する。
00050  */
00051 typedef struct {
00052     text_editor_t *editor;
00053     text_history_t *history;
00054     int suggest_index;
00055     char suggest_source[TEXTEDITOR_MAXLEN];
00056     int (*func_read)(char *buf, int cnt);
00057     int (*func_write)(const char *buf, int cnt);
00058     int (*func_cb)(const char *text);
00059 } ntshell_user_data_t;
00060 
00061 #define SUGGEST_INDEX(vtp) \
00062     ((ntshell_user_data_t *)(vtp)->user_data)->suggest_index
00063 #define SUGGEST_SOURCE(vtp) \
00064     ((ntshell_user_data_t *)(vtp)->user_data)->suggest_source
00065 
00066 /**
00067  * @brief テキストエディタを取得する。
00068  *
00069  * @param vtp vtparse構造体。
00070  */
00071 #define GET_EDITOR(vtp) \
00072     ((ntshell_user_data_t *)(vtp)->user_data)->editor
00073 
00074 /**
00075  * @brief テキストヒストリを取得する。
00076  *
00077  * @param vtp vtparse構造体。
00078  */
00079 #define GET_HISTORY(vtp) \
00080     ((ntshell_user_data_t *)(vtp)->user_data)->history
00081 
00082 /**
00083  * @brief シリアルポートから読み込む。
00084  *
00085  * @param vtp vtparse構造体。
00086  * @param buf 読み込みバッファ。
00087  * @param cnt 読み込み文字数。
00088  */
00089 #define SERIAL_READ(vtp,buf,cnt) \
00090     ((ntshell_user_data_t *)(vtp)->user_data)->func_read(buf, cnt)
00091 
00092 /**
00093  * @brief シリアルポートへ書き込む。
00094  *
00095  * @param vtp vtparse構造体。
00096  * @param buf 書き込みバッファ。
00097  * @param cnt 書き込み文字数。
00098  */
00099 #define SERIAL_WRITE(vtp,buf,cnt) \
00100     ((ntshell_user_data_t *)(vtp)->user_data)->func_write(buf, cnt)
00101 
00102 /**
00103  * @brief コールバックを呼び出す。
00104  *
00105  * @param vtp vtparse構造体。
00106  * @param text コールバック関数へ渡す文字列。
00107  */
00108 #define CALLBACK(vtp, text) \
00109     ((ntshell_user_data_t *)(vtp)->user_data)->func_cb(text)
00110 
00111 /**
00112  * @brief テキストヒストリで1つ後ろを辿る。
00113  *
00114  * @param parser パーサー。
00115  * @param action アクション。
00116  * @param ch 入力文字。
00117  */
00118 static void actfunc_history_prev(
00119         vtparse_t *parser,
00120         vtparse_action_t action,
00121         unsigned char ch) {
00122     if (text_history_read_point_prev(GET_HISTORY(parser))) {
00123         char txt[TEXTHISTORY_MAXLEN];
00124         int n = text_history_read(GET_HISTORY(parser), &txt[0], sizeof(txt));
00125         if (0 < n) {
00126             SERIAL_WRITE(parser, "\x1b[2K", 4);
00127             SERIAL_WRITE(parser, "\x1b[80D", 5);
00128             SERIAL_WRITE(parser, ">", 1);
00129             SERIAL_WRITE(parser, txt, n);
00130             text_editor_set_text(GET_EDITOR(parser), txt);
00131         }
00132     }
00133 }
00134 
00135 /**
00136  * @brief テキストヒストリで1つ前を辿る。
00137  *
00138  * @param parser パーサー。
00139  * @param action アクション。
00140  * @param ch 入力文字。
00141  */
00142 static void actfunc_history_next(
00143         vtparse_t *parser,
00144         vtparse_action_t action,
00145         unsigned char ch) {
00146     if (text_history_read_point_next(GET_HISTORY(parser))) {
00147         char txt[TEXTHISTORY_MAXLEN];
00148         int n = text_history_read(GET_HISTORY(parser), &txt[0], sizeof(txt));
00149         if (0 < n) {
00150             SERIAL_WRITE(parser, "\x1b[2K", 4);
00151             SERIAL_WRITE(parser, "\x1b[80D", 5);
00152             SERIAL_WRITE(parser, ">", 1);
00153             SERIAL_WRITE(parser, txt, n);
00154             text_editor_set_text(GET_EDITOR(parser), txt);
00155         }
00156     }
00157 }
00158 
00159 /**
00160  * @brief カーソルを左へ移動させる。
00161  *
00162  * @param parser パーサー。
00163  * @param action アクション。
00164  * @param ch 入力文字。
00165  */
00166 static void actfunc_cursor_left(
00167         vtparse_t *parser,
00168         vtparse_action_t action,
00169         unsigned char ch) {
00170     if (text_editor_cursor_left(GET_EDITOR(parser))) {
00171         SERIAL_WRITE(parser, "\x1b[1D", 4);
00172     }
00173 }
00174 
00175 /**
00176  * @brief カーソルを右へ移動させる。
00177  *
00178  * @param parser パーサー。
00179  * @param action アクション。
00180  * @param ch 入力文字。
00181  */
00182 static void actfunc_cursor_right(
00183         vtparse_t *parser,
00184         vtparse_action_t action,
00185         unsigned char ch) {
00186     if (text_editor_cursor_right(GET_EDITOR(parser))) {
00187         SERIAL_WRITE(parser, "\x1b[1C", 4);
00188     }
00189 }
00190 
00191 /**
00192  * @brief エンターキーの処理を実行する。
00193  *
00194  * @param parser パーサー。
00195  * @param action アクション。
00196  * @param ch 入力文字。
00197  */
00198 static void actfunc_enter(
00199         vtparse_t *parser,
00200         vtparse_action_t action,
00201         unsigned char ch) {
00202     char txt[TEXTEDITOR_MAXLEN];
00203     text_editor_get_text(GET_EDITOR(parser), &txt[0], sizeof(txt));
00204     text_editor_clear(GET_EDITOR(parser));
00205     text_history_write(GET_HISTORY(parser), txt);
00206     SERIAL_WRITE(parser, "\r\n", 2);
00207     CALLBACK(parser, txt);
00208     SERIAL_WRITE(parser, ">", 1);
00209 }
00210 
00211 /**
00212  * @brief キャンセルキーの処理を実行する。
00213  * @details
00214  * 一般的なOSのCTRL+C処理はシグナルを発行し、受信したプロセスが
00215  * 中断処理を実行する。
00216  * ここでのキャンセルは見た目を再現したもので、
00217  * 入力中の文字列を破棄してカーソルを新しい入力に備えて復帰させるものだ。
00218  *
00219  * @param parser パーサー。
00220  * @param action アクション。
00221  * @param ch 入力文字。
00222  */
00223 static void actfunc_cancel(
00224         vtparse_t *parser,
00225         vtparse_action_t action,
00226         unsigned char ch) {
00227     SERIAL_WRITE(parser, "^C\r\n", 4);
00228     text_editor_clear(GET_EDITOR(parser));
00229     SERIAL_WRITE(parser, ">", 1);
00230 }
00231 
00232 /**
00233  * @brief 挿入処理を実行する。
00234  *
00235  * @param parser パーサー。
00236  * @param action アクション。
00237  * @param ch 入力文字。
00238  */
00239 static void actfunc_insert(
00240         vtparse_t *parser,
00241         vtparse_action_t action,
00242         unsigned char ch) {
00243 
00244     /*
00245      * 入力があった場合、入力補完状態から抜ける。
00246      */
00247     SUGGEST_INDEX(parser) = -1;
00248 
00249     /*
00250      * テキストエディタを使って文字を文字列に挿入する。
00251      */
00252     if (text_editor_insert(GET_EDITOR(parser), ch)) {
00253         char txt[TEXTEDITOR_MAXLEN];
00254         int len = text_editor_get_text(GET_EDITOR(parser), &txt[0], sizeof(txt));
00255         int pos = text_editor_cursor_get_position(GET_EDITOR(parser));
00256         SERIAL_WRITE(parser, (char *)&ch, sizeof(ch));
00257         int n = len - pos;
00258         if (n > 0) {
00259             int i;
00260             SERIAL_WRITE(parser, txt + pos, len - pos);
00261             for (i = 0; i < n; i++) {
00262                 SERIAL_WRITE(parser, "\x1b[1D", 4);
00263             }
00264         }
00265     }
00266 }
00267 
00268 /**
00269  * @brief バックスペース処理を実行する。
00270  *
00271  * @param parser パーサー。
00272  * @param action アクション。
00273  * @param ch 入力文字。
00274  */
00275 static void actfunc_backspace(
00276         vtparse_t *parser,
00277         vtparse_action_t action,
00278         unsigned char ch) {
00279     if (text_editor_backspace(GET_EDITOR(parser))) {
00280         char txt[TEXTEDITOR_MAXLEN];
00281         SERIAL_WRITE(parser, "\x1b[1D", 4);
00282         int len = text_editor_get_text(GET_EDITOR(parser), &txt[0], sizeof(txt));
00283         int pos = text_editor_cursor_get_position(GET_EDITOR(parser));
00284         int n = len - pos;
00285         if (n > 0) {
00286             int i;
00287             SERIAL_WRITE(parser, txt + pos, len - pos);
00288             SERIAL_WRITE(parser, " ", 1);
00289             for (i = 0; i < n + 1; i++) {
00290                 SERIAL_WRITE(parser, "\x1b[1D", 4);
00291             }
00292         } else {
00293             SERIAL_WRITE(parser, " ", 1);
00294             SERIAL_WRITE(parser, "\x1b[1D", 4);
00295         }
00296     }
00297 }
00298 
00299 /**
00300  * @brief 入力補完処理を実行する。
00301  *
00302  * @param parser パーサー。
00303  * @param action アクション。
00304  * @param ch 入力文字。
00305  */
00306 static void actfunc_suggest(
00307         vtparse_t *parser,
00308         vtparse_action_t action,
00309         unsigned char ch) {
00310     char buf[TEXTEDITOR_MAXLEN];
00311     if (SUGGEST_INDEX(parser) < 0) {
00312         /*
00313          * 入力補完モードにこれから入る場合。
00314          * 現在の入力文字列を元に補完候補を取得する。
00315          */
00316         if (text_editor_get_text(
00317                     GET_EDITOR(parser),
00318                     SUGGEST_SOURCE(parser),
00319                     sizeof(SUGGEST_SOURCE(parser))) > 0) {
00320             SUGGEST_INDEX(parser) = 0;
00321             if (text_history_find(
00322                         GET_HISTORY(parser),
00323                         SUGGEST_INDEX(parser),
00324                         SUGGEST_SOURCE(parser),
00325                         buf,
00326                         sizeof(buf)) == 0) {
00327                 // 候補が見つかればテキストを設定して、インデックスをメモする。
00328                 int n = ntlibc_strlen((const char *)buf);
00329                 SERIAL_WRITE(parser, "\x1b[2K", 4);
00330                 SERIAL_WRITE(parser, "\x1b[80D", 5);
00331                 SERIAL_WRITE(parser, ">", 1);
00332                 SERIAL_WRITE(parser, buf, n);
00333                 text_editor_set_text(GET_EDITOR(parser), buf);
00334             } else {
00335                 // 候補がなければ入力補完モードから抜ける。
00336                 SUGGEST_INDEX(parser) = -1;
00337             }
00338         }
00339     } else {
00340         /*
00341          * 既に入力補完モードに入っている場合、
00342          * 次の候補を探して見つかればテキストとして設定する。
00343          */
00344         SUGGEST_INDEX(parser) = SUGGEST_INDEX(parser) + 1;
00345         if (text_history_find(
00346                     GET_HISTORY(parser),
00347                     SUGGEST_INDEX(parser),
00348                     SUGGEST_SOURCE(parser),
00349                     buf,
00350                     sizeof(buf)) == 0) {
00351             // 候補が見つかればテキストを設定する。
00352             int n = ntlibc_strlen((const char *)buf);
00353             SERIAL_WRITE(parser, "\x1b[2K", 4);
00354             SERIAL_WRITE(parser, "\x1b[80D", 5);
00355             SERIAL_WRITE(parser, ">", 1);
00356             SERIAL_WRITE(parser, buf, n);
00357             text_editor_set_text(GET_EDITOR(parser), buf);
00358         } else {
00359             // 候補が見つからなければ元の入力文字列に戻し、入力補完モードから抜ける。
00360             int n = ntlibc_strlen(SUGGEST_SOURCE(parser));
00361             SERIAL_WRITE(parser, "\x1b[2K", 4);
00362             SERIAL_WRITE(parser, "\x1b[80D", 5);
00363             SERIAL_WRITE(parser, ">", 1);
00364             SERIAL_WRITE(parser, SUGGEST_SOURCE(parser), n);
00365             text_editor_set_text(GET_EDITOR(parser), SUGGEST_SOURCE(parser));
00366             SUGGEST_INDEX(parser) = -1;
00367         }
00368     }
00369 }
00370 
00371 static void actfunc_cursor_head(
00372         vtparse_t *parser,
00373         vtparse_action_t action,
00374         unsigned char ch) {
00375     SERIAL_WRITE(parser, "\x1b[80D", 5);
00376     SERIAL_WRITE(parser, ">", 1);
00377     text_editor_cursor_head(GET_EDITOR(parser));
00378 }
00379 
00380 static void actfunc_cursor_tail(
00381         vtparse_t *parser,
00382         vtparse_action_t action,
00383         unsigned char ch) {
00384     char buf[TEXTEDITOR_MAXLEN];
00385     int len;
00386     text_editor_get_text(GET_EDITOR(parser), buf, sizeof(buf));
00387     len = ntlibc_strlen((const char *)buf);
00388     SERIAL_WRITE(parser, "\x1b[80D", 5);
00389     SERIAL_WRITE(parser, ">", 1);
00390     SERIAL_WRITE(parser, buf, len);
00391     text_editor_cursor_tail(GET_EDITOR(parser));
00392 }
00393 
00394 /**
00395  * @brief アクションテーブルのデータ構造体。
00396  * @details
00397  * アクションは状態と入力文字によって与えられる。
00398  * アクションに対する関数もここで定義する。
00399  */
00400 typedef struct {
00401     vtparse_action_t action;
00402     unsigned char ch;
00403     void (*func)(
00404             vtparse_t *parser,
00405             vtparse_action_t action,
00406             unsigned char ch);
00407 } ntshell_action_table_t;
00408 
00409 /**
00410  * @brief アクションに対する処理関数テーブル。
00411  * @details
00412  * やってくるコードは仮想端末側の処理に依存する。
00413  * よって様々なプラットフォームの様々な仮想端末で試すと良い。
00414  *
00415  * <table>
00416  *   <th>
00417  *     <td>Platform</td>
00418  *     <td>Tools</td>
00419  *   </th>
00420  *   <tr>
00421  *     <td>Windows</td>
00422  *     <td>Hyper Terminal, Poderossa, TeraTerm</td>
00423  *   </tr>
00424  *   <tr>
00425  *     <td>Linux</td>
00426  *     <td>minicom, screen, kermit</td>
00427  *   </tr>
00428  * </table>
00429  */
00430 static const ntshell_action_table_t action_table[] = {
00431     {VTPARSE_ACTION_EXECUTE, 0x01, actfunc_cursor_head},
00432     {VTPARSE_ACTION_EXECUTE, 0x02, actfunc_cursor_left},
00433     {VTPARSE_ACTION_EXECUTE, 0x03, actfunc_cancel},
00434     {VTPARSE_ACTION_EXECUTE, 0x05, actfunc_cursor_tail},
00435     {VTPARSE_ACTION_EXECUTE, 0x06, actfunc_cursor_right},
00436     {VTPARSE_ACTION_EXECUTE, 0x08, actfunc_backspace},
00437     {VTPARSE_ACTION_EXECUTE, 0x09, actfunc_suggest},
00438     {VTPARSE_ACTION_EXECUTE, 0x0d, actfunc_enter},
00439     {VTPARSE_ACTION_EXECUTE, 0x0e, actfunc_history_next},
00440     {VTPARSE_ACTION_EXECUTE, 0x10, actfunc_history_prev},
00441     {VTPARSE_ACTION_CSI_DISPATCH, 0x41, actfunc_history_prev},
00442     {VTPARSE_ACTION_CSI_DISPATCH, 0x42, actfunc_history_next},
00443     {VTPARSE_ACTION_CSI_DISPATCH, 0x43, actfunc_cursor_right},
00444     {VTPARSE_ACTION_CSI_DISPATCH, 0x44, actfunc_cursor_left},
00445     {VTPARSE_ACTION_PRINT, 0x7f, actfunc_backspace},
00446 };
00447 
00448 /**
00449  * @brief パーサーに対するコールバック関数。
00450  * @details vtparseモジュールのコールバック関数に従った実装である。
00451  *
00452  * @param parser パーサー。
00453  * @param action アクション。
00454  * @param ch キャラクタ。
00455  */
00456 void parser_callback(
00457         vtparse_t *parser,
00458         vtparse_action_t action,
00459         unsigned char ch) {
00460     ntshell_action_table_t *p;
00461     int i;
00462     const int ACTTBLSIZ = sizeof(action_table) / sizeof(action_table[0]);
00463 
00464     /*
00465      * 制御コードに対する処理はテーブルから探す。
00466      */
00467     p = (ntshell_action_table_t *)action_table;
00468     for (i = 0; i < ACTTBLSIZ; i++) {
00469         if ((p->action == action) && (p->ch == ch)) {
00470             p->func(parser, action, ch);
00471             return;
00472         }
00473         p++;
00474     }
00475 
00476     /*
00477      * 通常の文字列は入力として扱う。
00478      */
00479     if (VTPARSE_ACTION_PRINT == action) {
00480         actfunc_insert(parser, action, ch);
00481     }
00482 }
00483 
00484 /**
00485  * @brief Natural Tiny Shellのバージョンを返す。
00486  * @details 返すバージョンはリリースバージョンである。
00487  *
00488  * @param major メージャーバージョン。
00489  * @param minor マイナーバージョン。
00490  * @param release リリースバージョン。
00491  */
00492 void ntshell_version(int *major, int *minor, int *release)
00493 {
00494     *major = VERSION_MAJOR;
00495     *minor = VERSION_MINOR;
00496     *release = VERSION_RELEASE;
00497 }
00498 
00499 /**
00500  * @brief Natural Tiny Shellを実行する。
00501  * @details この関数は実行を返さない。
00502  *
00503  * @param parser VT100パーサー。
00504  * @param editor テキストエディタ。
00505  * @param history テキストヒストリ。
00506  * @param func_read シリアルリード関数。
00507  * @param func_write シリアルライト関数。
00508  * @param func_cb コールバック関数。
00509  */
00510 void ntshell_execute(
00511         ntshell_t *p,
00512         int (*func_read)(char *buf, int cnt),
00513         int (*func_write)(const char *buf, int cnt),
00514         int (*func_cb)(const char *text))
00515 {
00516     /*
00517      * vtparseはユーザデータへのポインタを設定できるようになっている。
00518      * Natural Tiny Shellはこれを利用してテキストエディタやヒストリ、
00519      * リード関数やライト関数、コールバック関数を処理の中で使用できる
00520      * ようにしてある。
00521      */
00522     ntshell_user_data_t user_data;
00523 
00524     user_data.editor = &(p->editor);
00525     user_data.history = &(p->history);
00526     user_data.func_read = func_read;
00527     user_data.func_write = func_write;
00528     user_data.func_cb = func_cb;
00529 
00530     p->parser.user_data = &user_data;
00531 
00532     /*
00533      * 各モジュールを初期化する。
00534      */
00535     vtparse_init(&(p->parser), parser_callback);
00536     text_editor_init(GET_EDITOR(&(p->parser)));
00537     text_history_init(GET_HISTORY(&(p->parser)));
00538     SUGGEST_INDEX(&(p->parser)) = -1;
00539 
00540     /*
00541      * ユーザ入力ループ。
00542      */
00543     SERIAL_WRITE(&(p->parser), ">", 1);
00544     while(1)
00545     {
00546         unsigned char ch;
00547         SERIAL_READ(&(p->parser), (char *)&ch, sizeof(ch));
00548         vtparse(&(p->parser), &ch, sizeof(ch));
00549     }
00550 }
00551