3 * @author Shinichiro Nakamura
4 * @brief 小規模組み込みシステム向けのシェルシステムの実装。
8 * ===============================================================
9 * Natural Tiny Shell (NT-Shell)
11 * ===============================================================
12 * Copyright (c) 2010-2011 Shinichiro Nakamura
14 * Permission is hereby granted, free of charge, to any person
15 * obtaining a copy of this software and associated documentation
16 * files (the "Software"), to deal in the Software without
17 * restriction, including without limitation the rights to use,
18 * copy, modify, merge, publish, distribute, sublicense, and/or
19 * sell copies of the Software, and to permit persons to whom the
20 * Software is furnished to do so, subject to the following
23 * The above copyright notice and this permission notice shall be
24 * included in all copies or substantial portions of the Software.
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
28 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
30 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
31 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33 * OTHER DEALINGS IN THE SOFTWARE.
34 * ===============================================================
40 #define VERSION_MAJOR 0 /**< メジャー番号。 */
41 #define VERSION_MINOR 0 /**< マイナー番号。 */
42 #define VERSION_RELEASE 8 /**< リリース番号。 */
45 * @brief 処理で用いるデータ構造体。
48 * vtrecvはユーザデータのポインタを設定することができる。
49 * Natural Tiny Shellはこれを使って自身の処理で必要な情報を保持する。
52 text_editor_t *editor;
53 text_history_t *history;
55 char suggest_source[TEXTEDITOR_MAXLEN];
56 int (*func_read)(char *buf, int cnt);
57 int (*func_write)(const char *buf, int cnt);
58 int (*func_callback)(const char *text, void *extobj);
59 void *func_callback_extobj;
60 } ntshell_user_data_t;
62 #define SUGGEST_INDEX(vtp) \
63 ((ntshell_user_data_t *)(vtp)->user_data)->suggest_index
64 #define SUGGEST_SOURCE(vtp) \
65 ((ntshell_user_data_t *)(vtp)->user_data)->suggest_source
68 * @brief テキストエディタを取得する。
70 * @param vtp vtrecv構造体。
72 #define GET_EDITOR(vtp) \
73 ((ntshell_user_data_t *)(vtp)->user_data)->editor
76 * @brief テキストヒストリを取得する。
78 * @param vtp vtrecv構造体。
80 #define GET_HISTORY(vtp) \
81 ((ntshell_user_data_t *)(vtp)->user_data)->history
84 * @brief シリアルポートから読み込む。
86 * @param vtp vtrecv構造体。
87 * @param buf 読み込みバッファ。
90 #define SERIAL_READ(vtp,buf,cnt) \
91 ((ntshell_user_data_t *)(vtp)->user_data)->func_read(buf, cnt)
94 * @brief シリアルポートへ書き込む。
96 * @param vtp vtrecv構造体。
97 * @param buf 書き込みバッファ。
100 #define SERIAL_WRITE(vtp,buf,cnt) \
101 ((ntshell_user_data_t *)(vtp)->user_data)->func_write(buf, cnt)
104 * @brief コールバックを呼び出す。
106 * @param vtp vtrecv構造体。
107 * @param text コールバック関数へ渡す文字列。
109 #define CALLBACK(vtp, text) \
110 ((ntshell_user_data_t *)(vtp)->user_data)->func_callback(text, ((ntshell_user_data_t *)(vtp)->user_data)->func_callback_extobj)
113 * @brief テキストヒストリで1つ後ろを辿る。
115 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
116 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
118 * @param vtrecv パーサー。
119 * @param action アクション。
122 static void actfunc_history_prev(
124 vtrecv_action_t action,
126 if (text_history_read_point_prev(GET_HISTORY(vtrecv))) {
127 char txt[TEXTHISTORY_MAXLEN];
128 int n = text_history_read(GET_HISTORY(vtrecv), &txt[0], sizeof(txt));
130 SERIAL_WRITE(vtrecv, "\x1b[2K", 4);
131 SERIAL_WRITE(vtrecv, "\x1b[80D", 5);
132 SERIAL_WRITE(vtrecv, ">", 1);
133 SERIAL_WRITE(vtrecv, txt, n);
134 text_editor_set_text(GET_EDITOR(vtrecv), txt);
140 * @brief テキストヒストリで1つ前を辿る。
142 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
143 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
145 * @param vtrecv パーサー。
146 * @param action アクション。
149 static void actfunc_history_next(
151 vtrecv_action_t action,
153 if (text_history_read_point_next(GET_HISTORY(vtrecv))) {
154 char txt[TEXTHISTORY_MAXLEN];
155 int n = text_history_read(GET_HISTORY(vtrecv), &txt[0], sizeof(txt));
157 SERIAL_WRITE(vtrecv, "\x1b[2K", 4);
158 SERIAL_WRITE(vtrecv, "\x1b[80D", 5);
159 SERIAL_WRITE(vtrecv, ">", 1);
160 SERIAL_WRITE(vtrecv, txt, n);
161 text_editor_set_text(GET_EDITOR(vtrecv), txt);
167 * @brief カーソルを左へ移動させる。
169 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
170 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
172 * @param vtrecv パーサー。
173 * @param action アクション。
176 static void actfunc_cursor_left(
178 vtrecv_action_t action,
180 if (text_editor_cursor_left(GET_EDITOR(vtrecv))) {
181 SERIAL_WRITE(vtrecv, "\x1b[1D", 4);
186 * @brief カーソルを右へ移動させる。
188 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
189 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
191 * @param vtrecv パーサー。
192 * @param action アクション。
195 static void actfunc_cursor_right(
197 vtrecv_action_t action,
199 if (text_editor_cursor_right(GET_EDITOR(vtrecv))) {
200 SERIAL_WRITE(vtrecv, "\x1b[1C", 4);
205 * @brief エンターキーの処理を実行する。
207 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
208 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
210 * @param vtrecv パーサー。
211 * @param action アクション。
214 static void actfunc_enter(
216 vtrecv_action_t action,
218 char txt[TEXTEDITOR_MAXLEN];
219 text_editor_get_text(GET_EDITOR(vtrecv), &txt[0], sizeof(txt));
220 text_editor_clear(GET_EDITOR(vtrecv));
221 text_history_write(GET_HISTORY(vtrecv), txt);
222 SERIAL_WRITE(vtrecv, "\r\n", 2);
223 CALLBACK(vtrecv, txt);
224 SERIAL_WRITE(vtrecv, ">", 1);
228 * @brief キャンセルキーの処理を実行する。
230 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
231 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
233 * 一般的なOSのCTRL+C処理はシグナルを発行し、受信したプロセスが
235 * ここでのキャンセルは見た目を再現したもので、
236 * 入力中の文字列を破棄してカーソルを新しい入力に備えて復帰させるものだ。
238 * @param vtrecv パーサー。
239 * @param action アクション。
242 static void actfunc_cancel(
244 vtrecv_action_t action,
246 SERIAL_WRITE(vtrecv, "^C\r\n", 4);
247 text_editor_clear(GET_EDITOR(vtrecv));
248 SERIAL_WRITE(vtrecv, ">", 1);
254 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
255 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
257 * @param vtrecv パーサー。
258 * @param action アクション。
261 static void actfunc_insert(
263 vtrecv_action_t action,
267 * 入力があった場合、入力補完状態から抜ける。
269 SUGGEST_INDEX(vtrecv) = -1;
272 * テキストエディタを使って文字を文字列に挿入する。
274 if (text_editor_insert(GET_EDITOR(vtrecv), ch)) {
275 char txt[TEXTEDITOR_MAXLEN];
276 int len = text_editor_get_text(GET_EDITOR(vtrecv), &txt[0], sizeof(txt));
277 int pos = text_editor_cursor_get_position(GET_EDITOR(vtrecv));
279 SERIAL_WRITE(vtrecv, (char *)&ch, sizeof(ch));
282 SERIAL_WRITE(vtrecv, txt + pos, len - pos);
283 for (i = 0; i < n; i++) {
284 SERIAL_WRITE(vtrecv, "\x1b[1D", 4);
291 * @brief バックスペース処理を実行する。
293 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
294 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
296 * @param vtrecv パーサー。
297 * @param action アクション。
300 static void actfunc_backspace(
302 vtrecv_action_t action,
304 if (text_editor_backspace(GET_EDITOR(vtrecv))) {
305 char txt[TEXTEDITOR_MAXLEN];
306 int len = text_editor_get_text(GET_EDITOR(vtrecv), &txt[0], sizeof(txt));
307 int pos = text_editor_cursor_get_position(GET_EDITOR(vtrecv));
309 SERIAL_WRITE(vtrecv, "\x1b[1D", 4);
312 SERIAL_WRITE(vtrecv, txt + pos, n);
313 SERIAL_WRITE(vtrecv, " ", 1);
314 for (i = 0; i < n + 1; i++) {
315 SERIAL_WRITE(vtrecv, "\x1b[1D", 4);
318 SERIAL_WRITE(vtrecv, " ", 1);
319 SERIAL_WRITE(vtrecv, "\x1b[1D", 4);
325 * @brief デリート処理を実行する。
327 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
328 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
330 * @param vtrecv パーサー。
331 * @param action アクション。
334 static void actfunc_delete(
336 vtrecv_action_t action,
338 if (text_editor_delete(GET_EDITOR(vtrecv))) {
339 char txt[TEXTEDITOR_MAXLEN];
340 int len = text_editor_get_text(GET_EDITOR(vtrecv), &txt[0], sizeof(txt));
341 int pos = text_editor_cursor_get_position(GET_EDITOR(vtrecv));
345 SERIAL_WRITE(vtrecv, txt + pos, n);
346 SERIAL_WRITE(vtrecv, " ", 1);
347 for (i = 0; i < n + 1; i++) {
348 SERIAL_WRITE(vtrecv, "\x1b[1D", 4);
351 SERIAL_WRITE(vtrecv, " ", 1);
352 SERIAL_WRITE(vtrecv, "\x1b[1D", 4);
358 * @brief 入力補完処理を実行する。
360 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
361 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
363 * @param vtrecv パーサー。
364 * @param action アクション。
367 static void actfunc_suggest(
369 vtrecv_action_t action,
371 char buf[TEXTEDITOR_MAXLEN];
372 if (SUGGEST_INDEX(vtrecv) < 0) {
375 * 現在の入力文字列を元に補完候補を取得する。
377 if (text_editor_get_text(
379 SUGGEST_SOURCE(vtrecv),
380 sizeof(SUGGEST_SOURCE(vtrecv))) > 0) {
381 SUGGEST_INDEX(vtrecv) = 0;
382 if (text_history_find(
384 SUGGEST_INDEX(vtrecv),
385 SUGGEST_SOURCE(vtrecv),
388 // 候補が見つかればテキストを設定して、インデックスをメモする。
389 int n = ntlibc_strlen((const char *)buf);
390 SERIAL_WRITE(vtrecv, "\x1b[2K", 4);
391 SERIAL_WRITE(vtrecv, "\x1b[80D", 5);
392 SERIAL_WRITE(vtrecv, ">", 1);
393 SERIAL_WRITE(vtrecv, buf, n);
394 text_editor_set_text(GET_EDITOR(vtrecv), buf);
396 // 候補がなければ入力補完モードから抜ける。
397 SUGGEST_INDEX(vtrecv) = -1;
403 * 次の候補を探して見つかればテキストとして設定する。
405 SUGGEST_INDEX(vtrecv) = SUGGEST_INDEX(vtrecv) + 1;
406 if (text_history_find(
408 SUGGEST_INDEX(vtrecv),
409 SUGGEST_SOURCE(vtrecv),
412 // 候補が見つかればテキストを設定する。
413 int n = ntlibc_strlen((const char *)buf);
414 SERIAL_WRITE(vtrecv, "\x1b[2K", 4);
415 SERIAL_WRITE(vtrecv, "\x1b[80D", 5);
416 SERIAL_WRITE(vtrecv, ">", 1);
417 SERIAL_WRITE(vtrecv, buf, n);
418 text_editor_set_text(GET_EDITOR(vtrecv), buf);
420 // 候補が見つからなければ元の入力文字列に戻し、入力補完モードから抜ける。
421 int n = ntlibc_strlen(SUGGEST_SOURCE(vtrecv));
422 SERIAL_WRITE(vtrecv, "\x1b[2K", 4);
423 SERIAL_WRITE(vtrecv, "\x1b[80D", 5);
424 SERIAL_WRITE(vtrecv, ">", 1);
425 SERIAL_WRITE(vtrecv, SUGGEST_SOURCE(vtrecv), n);
426 text_editor_set_text(GET_EDITOR(vtrecv), SUGGEST_SOURCE(vtrecv));
427 SUGGEST_INDEX(vtrecv) = -1;
433 * @brief カーソルを行頭へ移動させる。
435 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
436 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
438 * @param vtrecv パーサー。
439 * @param action アクション。
442 static void actfunc_cursor_head(
444 vtrecv_action_t action,
446 SERIAL_WRITE(vtrecv, "\x1b[80D", 5);
447 SERIAL_WRITE(vtrecv, ">", 1);
448 text_editor_cursor_head(GET_EDITOR(vtrecv));
452 * @brief カーソルを行末へ移動させる。
454 * 論理上のテキスト編集装置内でのテキスト編集と編集結果をビューの更新を行なう。
455 * text_editorは論理上のテキスト編集装置であり、ビューに関して一切感知しない。
457 * @param vtrecv パーサー。
458 * @param action アクション。
461 static void actfunc_cursor_tail(
463 vtrecv_action_t action,
465 char buf[TEXTEDITOR_MAXLEN];
467 text_editor_get_text(GET_EDITOR(vtrecv), buf, sizeof(buf));
468 len = ntlibc_strlen((const char *)buf);
469 SERIAL_WRITE(vtrecv, "\x1b[80D", 5);
470 SERIAL_WRITE(vtrecv, ">", 1);
471 SERIAL_WRITE(vtrecv, buf, len);
472 text_editor_cursor_tail(GET_EDITOR(vtrecv));
476 * @brief アクションテーブルのデータ構造体。
478 * アクションは状態と入力文字によって与えられる。
479 * アクションに対する関数もここで定義する。
482 vtrecv_action_t action;
486 vtrecv_action_t action,
488 } ntshell_action_table_t;
491 * @brief アクションに対する処理関数テーブル。
493 * やってくるコードは仮想端末側の処理に依存する。
494 * よって様々なプラットフォームの様々な仮想端末で試すと良い。
503 * <td>Hyper Terminal, Poderossa, TeraTerm</td>
507 * <td>minicom, screen, kermit</td>
511 static const ntshell_action_table_t action_table[] = {
512 {VTRECV_ACTION_EXECUTE, 0x01, actfunc_cursor_head},
513 {VTRECV_ACTION_EXECUTE, 0x02, actfunc_cursor_left},
514 {VTRECV_ACTION_EXECUTE, 0x03, actfunc_cancel},
515 {VTRECV_ACTION_EXECUTE, 0x04, actfunc_delete},
516 {VTRECV_ACTION_EXECUTE, 0x05, actfunc_cursor_tail},
517 {VTRECV_ACTION_EXECUTE, 0x06, actfunc_cursor_right},
518 {VTRECV_ACTION_EXECUTE, 0x08, actfunc_backspace},
519 {VTRECV_ACTION_EXECUTE, 0x09, actfunc_suggest},
520 {VTRECV_ACTION_EXECUTE, 0x0d, actfunc_enter},
521 {VTRECV_ACTION_EXECUTE, 0x0e, actfunc_history_next},
522 {VTRECV_ACTION_EXECUTE, 0x10, actfunc_history_prev},
523 {VTRECV_ACTION_CSI_DISPATCH, 0x41, actfunc_history_prev},
524 {VTRECV_ACTION_CSI_DISPATCH, 0x42, actfunc_history_next},
525 {VTRECV_ACTION_CSI_DISPATCH, 0x43, actfunc_cursor_right},
526 {VTRECV_ACTION_CSI_DISPATCH, 0x44, actfunc_cursor_left},
527 {VTRECV_ACTION_PRINT, 0x7f, actfunc_backspace},
531 * @brief パーサーに対するコールバック関数。
532 * @details vtrecvモジュールのコールバック関数に従った実装である。
534 * @param vtrecv パーサー。
535 * @param action アクション。
538 void vtrecv_callback(
540 vtrecv_action_t action,
542 ntshell_action_table_t *p;
544 const int ACTTBLSIZ = sizeof(action_table) / sizeof(action_table[0]);
547 * 制御コードに対する処理はテーブルから探す。
549 p = (ntshell_action_table_t *)action_table;
550 for (i = 0; i < ACTTBLSIZ; i++) {
551 if ((p->action == action) && (p->ch == ch)) {
552 p->func(vtrecv, action, ch);
561 if (VTRECV_ACTION_PRINT == action) {
562 actfunc_insert(vtrecv, action, ch);
571 * 必要なキー入力に対する動作を加えたい場合、
572 * vtrecvによって得られるコードを調べるために
573 * ここにテストコードを加える事ができる。
578 * @brief Natural Tiny Shellのバージョンを返す。
579 * @details 返すバージョンはリリースバージョンである。
581 * @param major メージャーバージョン。
582 * @param minor マイナーバージョン。
583 * @param release リリースバージョン。
585 void ntshell_version(int *major, int *minor, int *release)
587 *major = VERSION_MAJOR;
588 *minor = VERSION_MINOR;
589 *release = VERSION_RELEASE;
593 * @brief Natural Tiny Shellを実行する。
594 * @details この関数は実行を返さない。
596 * @param p NT-Shellハンドラ。
597 * @param func_read シリアルリード関数。
598 * @param func_write シリアルライト関数。
599 * @param func_callback コールバック関数。
600 * @param func_callback_extobj コールバック関数呼び出し時に渡す拡張オブジェクト。
602 void ntshell_execute(
604 int (*func_read)(char *buf, int cnt),
605 int (*func_write)(const char *buf, int cnt),
606 int (*func_callback)(const char *text, void *extobj),
607 void *func_callback_extobj)
610 * vtrecvはユーザデータへのポインタを設定できるようになっている。
611 * Natural Tiny Shellはこれを利用してテキストエディタやヒストリ、
612 * リード関数やライト関数、コールバック関数を処理の中で使用できる
615 ntshell_user_data_t user_data;
617 user_data.editor = &(p->editor);
618 user_data.history = &(p->history);
619 user_data.func_read = func_read;
620 user_data.func_write = func_write;
621 user_data.func_callback = func_callback;
622 user_data.func_callback_extobj = func_callback_extobj;
624 p->vtrecv.user_data = &user_data;
629 vtrecv_init(&(p->vtrecv), vtrecv_callback);
630 text_editor_init(GET_EDITOR(&(p->vtrecv)));
631 text_history_init(GET_HISTORY(&(p->vtrecv)));
632 SUGGEST_INDEX(&(p->vtrecv)) = -1;
637 SERIAL_WRITE(&(p->vtrecv), ">", 1);
641 SERIAL_READ(&(p->vtrecv), (char *)&ch, sizeof(ch));
642 vtrecv_execute(&(p->vtrecv), &ch, sizeof(ch));