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 6 /**< リリース番号。 */
45 * @brief 処理で用いるデータ構造体。
48 * vtparseはユーザデータのポインタを設定することができる。
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_cb)(const char *text);
59 } ntshell_user_data_t;
61 #define SUGGEST_INDEX(vtp) \
62 ((ntshell_user_data_t *)(vtp)->user_data)->suggest_index
63 #define SUGGEST_SOURCE(vtp) \
64 ((ntshell_user_data_t *)(vtp)->user_data)->suggest_source
67 * @brief テキストエディタを取得する。
69 * @param vtp vtparse構造体。
71 #define GET_EDITOR(vtp) \
72 ((ntshell_user_data_t *)(vtp)->user_data)->editor
75 * @brief テキストヒストリを取得する。
77 * @param vtp vtparse構造体。
79 #define GET_HISTORY(vtp) \
80 ((ntshell_user_data_t *)(vtp)->user_data)->history
83 * @brief シリアルポートから読み込む。
85 * @param vtp vtparse構造体。
86 * @param buf 読み込みバッファ。
89 #define SERIAL_READ(vtp,buf,cnt) \
90 ((ntshell_user_data_t *)(vtp)->user_data)->func_read(buf, cnt)
93 * @brief シリアルポートへ書き込む。
95 * @param vtp vtparse構造体。
96 * @param buf 書き込みバッファ。
99 #define SERIAL_WRITE(vtp,buf,cnt) \
100 ((ntshell_user_data_t *)(vtp)->user_data)->func_write(buf, cnt)
103 * @brief コールバックを呼び出す。
105 * @param vtp vtparse構造体。
106 * @param text コールバック関数へ渡す文字列。
108 #define CALLBACK(vtp, text) \
109 ((ntshell_user_data_t *)(vtp)->user_data)->func_cb(text)
112 * @brief テキストヒストリで1つ後ろを辿る。
114 * @param parser パーサー。
115 * @param action アクション。
118 static void actfunc_history_prev(
120 vtparse_action_t action,
122 if (text_history_read_point_prev(GET_HISTORY(parser))) {
123 char txt[TEXTHISTORY_MAXLEN];
124 int n = text_history_read(GET_HISTORY(parser), &txt[0], sizeof(txt));
126 SERIAL_WRITE(parser, "\x1b[2K", 4);
127 SERIAL_WRITE(parser, "\x1b[80D", 5);
128 SERIAL_WRITE(parser, ">", 1);
129 SERIAL_WRITE(parser, txt, n);
130 text_editor_set_text(GET_EDITOR(parser), txt);
136 * @brief テキストヒストリで1つ前を辿る。
138 * @param parser パーサー。
139 * @param action アクション。
142 static void actfunc_history_next(
144 vtparse_action_t action,
146 if (text_history_read_point_next(GET_HISTORY(parser))) {
147 char txt[TEXTHISTORY_MAXLEN];
148 int n = text_history_read(GET_HISTORY(parser), &txt[0], sizeof(txt));
150 SERIAL_WRITE(parser, "\x1b[2K", 4);
151 SERIAL_WRITE(parser, "\x1b[80D", 5);
152 SERIAL_WRITE(parser, ">", 1);
153 SERIAL_WRITE(parser, txt, n);
154 text_editor_set_text(GET_EDITOR(parser), txt);
160 * @brief カーソルを左へ移動させる。
162 * @param parser パーサー。
163 * @param action アクション。
166 static void actfunc_cursor_left(
168 vtparse_action_t action,
170 if (text_editor_cursor_left(GET_EDITOR(parser))) {
171 SERIAL_WRITE(parser, "\x1b[1D", 4);
176 * @brief カーソルを右へ移動させる。
178 * @param parser パーサー。
179 * @param action アクション。
182 static void actfunc_cursor_right(
184 vtparse_action_t action,
186 if (text_editor_cursor_right(GET_EDITOR(parser))) {
187 SERIAL_WRITE(parser, "\x1b[1C", 4);
192 * @brief エンターキーの処理を実行する。
194 * @param parser パーサー。
195 * @param action アクション。
198 static void actfunc_enter(
200 vtparse_action_t action,
202 char txt[TEXTEDITOR_MAXLEN];
203 text_editor_get_text(GET_EDITOR(parser), &txt[0], sizeof(txt));
204 text_editor_clear(GET_EDITOR(parser));
205 text_history_write(GET_HISTORY(parser), txt);
206 SERIAL_WRITE(parser, "\r\n", 2);
207 CALLBACK(parser, txt);
208 SERIAL_WRITE(parser, ">", 1);
212 * @brief キャンセルキーの処理を実行する。
214 * 一般的なOSのCTRL+C処理はシグナルを発行し、受信したプロセスが
216 * ここでのキャンセルは見た目を再現したもので、
217 * 入力中の文字列を破棄してカーソルを新しい入力に備えて復帰させるものだ。
219 * @param parser パーサー。
220 * @param action アクション。
223 static void actfunc_cancel(
225 vtparse_action_t action,
227 SERIAL_WRITE(parser, "^C\r\n", 4);
228 text_editor_clear(GET_EDITOR(parser));
229 SERIAL_WRITE(parser, ">", 1);
235 * @param parser パーサー。
236 * @param action アクション。
239 static void actfunc_insert(
241 vtparse_action_t action,
245 * 入力があった場合、入力補完状態から抜ける。
247 SUGGEST_INDEX(parser) = -1;
250 * テキストエディタを使って文字を文字列に挿入する。
252 if (text_editor_insert(GET_EDITOR(parser), ch)) {
253 char txt[TEXTEDITOR_MAXLEN];
254 int len = text_editor_get_text(GET_EDITOR(parser), &txt[0], sizeof(txt));
255 int pos = text_editor_cursor_get_position(GET_EDITOR(parser));
256 SERIAL_WRITE(parser, (char *)&ch, sizeof(ch));
260 SERIAL_WRITE(parser, txt + pos, len - pos);
261 for (i = 0; i < n; i++) {
262 SERIAL_WRITE(parser, "\x1b[1D", 4);
269 * @brief バックスペース処理を実行する。
271 * @param parser パーサー。
272 * @param action アクション。
275 static void actfunc_backspace(
277 vtparse_action_t action,
279 if (text_editor_backspace(GET_EDITOR(parser))) {
280 char txt[TEXTEDITOR_MAXLEN];
281 SERIAL_WRITE(parser, "\x1b[1D", 4);
282 int len = text_editor_get_text(GET_EDITOR(parser), &txt[0], sizeof(txt));
283 int pos = text_editor_cursor_get_position(GET_EDITOR(parser));
287 SERIAL_WRITE(parser, txt + pos, len - pos);
288 SERIAL_WRITE(parser, " ", 1);
289 for (i = 0; i < n + 1; i++) {
290 SERIAL_WRITE(parser, "\x1b[1D", 4);
293 SERIAL_WRITE(parser, " ", 1);
294 SERIAL_WRITE(parser, "\x1b[1D", 4);
300 * @brief 入力補完処理を実行する。
302 * @param parser パーサー。
303 * @param action アクション。
306 static void actfunc_suggest(
308 vtparse_action_t action,
310 char buf[TEXTEDITOR_MAXLEN];
311 if (SUGGEST_INDEX(parser) < 0) {
314 * 現在の入力文字列を元に補完候補を取得する。
316 if (text_editor_get_text(
318 SUGGEST_SOURCE(parser),
319 sizeof(SUGGEST_SOURCE(parser))) > 0) {
320 SUGGEST_INDEX(parser) = 0;
321 if (text_history_find(
323 SUGGEST_INDEX(parser),
324 SUGGEST_SOURCE(parser),
327 // 候補が見つかればテキストを設定して、インデックスをメモする。
328 int n = ntlibc_strlen((const char *)buf);
329 SERIAL_WRITE(parser, "\x1b[2K", 4);
330 SERIAL_WRITE(parser, "\x1b[80D", 5);
331 SERIAL_WRITE(parser, ">", 1);
332 SERIAL_WRITE(parser, buf, n);
333 text_editor_set_text(GET_EDITOR(parser), buf);
335 // 候補がなければ入力補完モードから抜ける。
336 SUGGEST_INDEX(parser) = -1;
342 * 次の候補を探して見つかればテキストとして設定する。
344 SUGGEST_INDEX(parser) = SUGGEST_INDEX(parser) + 1;
345 if (text_history_find(
347 SUGGEST_INDEX(parser),
348 SUGGEST_SOURCE(parser),
351 // 候補が見つかればテキストを設定する。
352 int n = ntlibc_strlen((const char *)buf);
353 SERIAL_WRITE(parser, "\x1b[2K", 4);
354 SERIAL_WRITE(parser, "\x1b[80D", 5);
355 SERIAL_WRITE(parser, ">", 1);
356 SERIAL_WRITE(parser, buf, n);
357 text_editor_set_text(GET_EDITOR(parser), buf);
359 // 候補が見つからなければ元の入力文字列に戻し、入力補完モードから抜ける。
360 int n = ntlibc_strlen(SUGGEST_SOURCE(parser));
361 SERIAL_WRITE(parser, "\x1b[2K", 4);
362 SERIAL_WRITE(parser, "\x1b[80D", 5);
363 SERIAL_WRITE(parser, ">", 1);
364 SERIAL_WRITE(parser, SUGGEST_SOURCE(parser), n);
365 text_editor_set_text(GET_EDITOR(parser), SUGGEST_SOURCE(parser));
366 SUGGEST_INDEX(parser) = -1;
371 static void actfunc_cursor_head(
373 vtparse_action_t action,
375 SERIAL_WRITE(parser, "\x1b[80D", 5);
376 SERIAL_WRITE(parser, ">", 1);
377 text_editor_cursor_head(GET_EDITOR(parser));
380 static void actfunc_cursor_tail(
382 vtparse_action_t action,
384 char buf[TEXTEDITOR_MAXLEN];
386 text_editor_get_text(GET_EDITOR(parser), buf, sizeof(buf));
387 len = ntlibc_strlen((const char *)buf);
388 SERIAL_WRITE(parser, "\x1b[80D", 5);
389 SERIAL_WRITE(parser, ">", 1);
390 SERIAL_WRITE(parser, buf, len);
391 text_editor_cursor_tail(GET_EDITOR(parser));
395 * @brief アクションテーブルのデータ構造体。
397 * アクションは状態と入力文字によって与えられる。
398 * アクションに対する関数もここで定義する。
401 vtparse_action_t action;
405 vtparse_action_t action,
407 } ntshell_action_table_t;
410 * @brief アクションに対する処理関数テーブル。
412 * やってくるコードは仮想端末側の処理に依存する。
413 * よって様々なプラットフォームの様々な仮想端末で試すと良い。
422 * <td>Hyper Terminal, Poderossa, TeraTerm</td>
426 * <td>minicom, screen, kermit</td>
430 static const ntshell_action_table_t action_table[] = {
431 {VTPARSE_ACTION_EXECUTE, 0x01, actfunc_cursor_head},
432 {VTPARSE_ACTION_EXECUTE, 0x02, actfunc_cursor_left},
433 {VTPARSE_ACTION_EXECUTE, 0x03, actfunc_cancel},
434 {VTPARSE_ACTION_EXECUTE, 0x05, actfunc_cursor_tail},
435 {VTPARSE_ACTION_EXECUTE, 0x06, actfunc_cursor_right},
436 {VTPARSE_ACTION_EXECUTE, 0x08, actfunc_backspace},
437 {VTPARSE_ACTION_EXECUTE, 0x09, actfunc_suggest},
438 {VTPARSE_ACTION_EXECUTE, 0x0d, actfunc_enter},
439 {VTPARSE_ACTION_EXECUTE, 0x0e, actfunc_history_next},
440 {VTPARSE_ACTION_EXECUTE, 0x10, actfunc_history_prev},
441 {VTPARSE_ACTION_CSI_DISPATCH, 0x41, actfunc_history_prev},
442 {VTPARSE_ACTION_CSI_DISPATCH, 0x42, actfunc_history_next},
443 {VTPARSE_ACTION_CSI_DISPATCH, 0x43, actfunc_cursor_right},
444 {VTPARSE_ACTION_CSI_DISPATCH, 0x44, actfunc_cursor_left},
445 {VTPARSE_ACTION_PRINT, 0x7f, actfunc_backspace},
449 * @brief パーサーに対するコールバック関数。
450 * @details vtparseモジュールのコールバック関数に従った実装である。
452 * @param parser パーサー。
453 * @param action アクション。
456 void parser_callback(
458 vtparse_action_t action,
460 ntshell_action_table_t *p;
462 const int ACTTBLSIZ = sizeof(action_table) / sizeof(action_table[0]);
465 * 制御コードに対する処理はテーブルから探す。
467 p = (ntshell_action_table_t *)action_table;
468 for (i = 0; i < ACTTBLSIZ; i++) {
469 if ((p->action == action) && (p->ch == ch)) {
470 p->func(parser, action, ch);
479 if (VTPARSE_ACTION_PRINT == action) {
480 actfunc_insert(parser, action, ch);
485 * @brief Natural Tiny Shellのバージョンを返す。
486 * @details 返すバージョンはリリースバージョンである。
488 * @param major メージャーバージョン。
489 * @param minor マイナーバージョン。
490 * @param release リリースバージョン。
492 void ntshell_version(int *major, int *minor, int *release)
494 *major = VERSION_MAJOR;
495 *minor = VERSION_MINOR;
496 *release = VERSION_RELEASE;
500 * @brief Natural Tiny Shellを実行する。
501 * @details この関数は実行を返さない。
503 * @param parser VT100パーサー。
504 * @param editor テキストエディタ。
505 * @param history テキストヒストリ。
506 * @param func_read シリアルリード関数。
507 * @param func_write シリアルライト関数。
508 * @param func_cb コールバック関数。
510 void ntshell_execute(
512 int (*func_read)(char *buf, int cnt),
513 int (*func_write)(const char *buf, int cnt),
514 int (*func_cb)(const char *text))
517 * vtparseはユーザデータへのポインタを設定できるようになっている。
518 * Natural Tiny Shellはこれを利用してテキストエディタやヒストリ、
519 * リード関数やライト関数、コールバック関数を処理の中で使用できる
522 ntshell_user_data_t user_data;
524 user_data.editor = &(p->editor);
525 user_data.history = &(p->history);
526 user_data.func_read = func_read;
527 user_data.func_write = func_write;
528 user_data.func_cb = func_cb;
530 p->parser.user_data = &user_data;
535 vtparse_init(&(p->parser), parser_callback);
536 text_editor_init(GET_EDITOR(&(p->parser)));
537 text_history_init(GET_HISTORY(&(p->parser)));
538 SUGGEST_INDEX(&(p->parser)) = -1;
543 SERIAL_WRITE(&(p->parser), ">", 1);
547 SERIAL_READ(&(p->parser), (char *)&ch, sizeof(ch));
548 vtparse(&(p->parser), &ch, sizeof(ch));