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
ntshell.c
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
Generated on Fri Jul 15 2022 00:05:03 by 1.7.2