GadgetSeed  0.9.6
shell.c
[詳解]
1 /** @file
2  @brief コマンドシェル
3 
4  @date 2007.03.16
5  @data 2002.03.24
6  @author Takashi SHUDO
7 */
8 
9 #include "shell.h"
10 #include "lineedit.h"
11 #include "str.h"
12 #include "tprintf.h"
13 #include "history.h"
14 
15 static void init_commands(struct st_shell_command * const * coms)
16 {
17  struct st_shell_command * const *cp = coms;
18 
19  while(*cp != 0) {
20  if((*cp)->init != 0) {
21  (*cp)->init();
22  }
23 
24  if((*cp)->sublist != 0) {
25  init_commands((struct st_shell_command * const *)(*cp)->sublist);
26  }
27 
28  cp ++;
29  }
30 }
31 
32 /**
33  @brief シェルを初期化する
34 
35  @param[in] shell shellデータ構造体ポインタ
36  @param[in] coms 初期化するshell_commandのポインタ
37  @param[in] prompt プロンプト文字列ポインタ
38 */
39 void init_shell(struct st_shell *shell, struct st_shell_command * const *coms,
40  const uchar *prompt)
41 {
42  shell->shell_coms = coms;
43  shell->lastCom[0] = 0;
44  shell->prompt = prompt;
45 
46  init_commands(coms);
47 
48  init_history(&shell->his);
49  init_lineedit(&shell->comLine);
50 }
51 
52 /**
53  @brief プロンプトを表示する
54 
55  @param[in] shell shellデータ構造体ポインタ
56 */
57 void print_prompt(struct st_shell *shell)
58 {
59  tprintf("%s", shell->prompt);
60 }
61 
62 /**
63  @brief 編集中のコマンドラインを破棄する
64 
65  @param[in] shell shellデータ構造体ポインタ
66 */
67 void dispose_shell_line(struct st_shell *shell)
68 {
69  new_lineedit(&shell->comLine);
70 }
71 
72 
73 /*
74  トークンを区切る
75 */
76 static int token_str(struct st_shell *shell, uchar *str, int nullterm)
77 {
78  uchar *tk = str;
79  int i;
80  static const char nstr[] = "";
81  int flg_esc = 0;
82 
83  shell->argc = 0;
84 
85  //xdump(str, SHELL_MAX_LINE_COLUMS);
86 
87  for(i=0; i<GSC_SHELL_MAX_COM_ARGV; i++) {
88  shell->argv[i] = (uchar *)nstr;
89  }
90 
91  for(i=0; i<GSC_SHELL_MAX_COM_ARGV; i++) {
92  /*
93  ' '以外の文字が先頭
94  */
95  while(tk < &str[GSC_SHELL_MAX_LINE_COLUMS]) {
96  if((*tk == 0) || (*tk == ASCII_CR)) {
97  if(nullterm) {
98  *tk = 0;
99  }
100  goto next;
101  } else if(*tk == '\\') { // "\"バックスラッシュはエスケープシーケンス
102  // '/'を削除して詰める
103  uchar *p = tk;
104  while((*(p+1)) != 0) {
105  *p =*(p+1);
106  p++;
107  }
108  *p = 0;
109  flg_esc = 1;
110  shell->argv[i] = tk;
111  shell->argc ++;
112  break;
113  } else if(*tk != ' ') {
114  shell->argv[i] = tk;
115  shell->argc ++;
116  tk ++;
117  break;
118  }
119 
120  tk ++;
121  }
122 
123  /*
124  ' 'まで読み捨て
125  */
126  while(tk < &str[GSC_SHELL_MAX_LINE_COLUMS]) {
127  if((*tk == 0) || (*tk == ASCII_CR) || (*tk == ASCII_LF)) {
128  if(nullterm) {
129  *tk = 0;
130  }
131  goto next;
132  } else if(*tk == '\\') { // "\"バックスラッシュはエスケープシーケンス
133  if(flg_esc == 0) {
134  // '/'を削除して詰める
135  uchar *p = tk;
136  while((*(p+1)) != 0) {
137  *p =*(p+1);
138  p++;
139  }
140  *p = 0;
141  flg_esc = 1;
142  continue;
143  }
144  } else if(*tk == ' ') {
145  if(flg_esc != 0) {
146  flg_esc = 0;
147  } else {
148  flg_esc = 0;
149  if(nullterm) {
150  *tk = 0;
151  }
152  tk ++;
153  break;
154  }
155  } else {
156  if(flg_esc != 0) {
157  flg_esc = 0;
158  }
159  }
160 
161  tk ++;
162  }
163  }
164 next:
165 #if 0 // DEBUG
166  eprintf("argc = %d\n", shell->argc);
167  for(i=0; i<shell->argc; i++) {
168  eprintf("[%d] %s\n", i, shell->argv[i]);
169  }
170 #endif
171  return shell->argc;
172 }
173 
174 /*
175  一致するコマンドを探す
176 */
177 struct st_shell_command * search_command(
178  struct st_shell_command * const * coms,
179  uchar *argv[], int *arg_top)
180 {
181  struct st_shell_command * const *cp = coms;
182  struct st_shell_command *rt = 0;
183 
184  while(*cp != 0) {
185  if(strcomp((*cp)->name, argv[*arg_top]) == 0) {
186  if((*cp)->sublist == 0) {
187  if((*cp)->command != 0) {
188  rt = (*cp);
189  break;
190  }
191  } else {
192  (*arg_top) ++;
193  rt = search_command((struct st_shell_command * const *)(*cp)->sublist,
194  argv, arg_top);
195  if(rt == 0) {
196  if((*cp)->command != 0) {
197  rt = (*cp);
198  }
199  }
200  break;
201  }
202  }
203  cp ++;
204  }
205 
206  return rt;
207 }
208 
209 
210 /**
211  @brief str文字列のコマンドを実行する
212 
213  @param[in] shell shellデータ構造体ポインタ
214  @param[in] str コマンド文字列ポインタ
215 
216  @return コマンド実行結果
217 */
218 int exec_shell_command(struct st_shell *shell, uchar *str)
219 {
220  struct st_shell_command * const *cp = shell->shell_coms;
221  int rt = 0;
222  int flgc = 0;
223  int arg_top = 0;
224  int argc;
225  struct st_shell_command * com;
226 
227  if(cp == 0) {
228  return 0;
229  }
230 
231  if(*str == 0) {
232  flgc = 1;
233  if(shell->lastCom[0] != 0) {
234  (void)strncopy(str, shell->lastCom, GSC_SHELL_MAX_LINE_COLUMS);
235  } else {
236  return 0;
237  }
238  } else {
239  (void)strncopy(shell->lastCom, str, GSC_SHELL_MAX_LINE_COLUMS);
240  }
241 
242  if(token_str(shell, str, 1) == 0) {
243  return 0;
244  }
245 
246  com = search_command(shell->shell_coms, shell->argv, &arg_top);
247  if(com != 0) {
248  argc = shell->argc - arg_top;
249  if(flgc) {
250  if(com->attr & CMDATTR_CONT) {
251  if(com->attr & CMDATTR_ARGLESS) {
252  argc = 1;
253  }
254  } else {
255  goto end;
256  }
257  }
258  rt = com->command(argc, &shell->argv[arg_top]);
259  } else {
260  int i;
261  shell->lastCom[0] = 0;
262  tprintf("\"");
263  for(i=0; i<arg_top; i++) {
264  tprintf("%s ", shell->argv[i]);
265  }
266  tprintf("%s\" command not found.\n", shell->argv[i]);
267  }
268 
269 end:
270  return rt;
271 }
272 
273 void print_command_usage(const struct st_shell_command *command)
274 {
275  if(command->usage_str == 0) {
276  tprintf("Usage: %s\n", command->name);
277  } else {
278  tprintf("Usage: %s %s\n", command->name, command->usage_str);
279  }
280 }
281 
282 static int strcomps(const uchar *s1, const uchar *s2)
283 {
284  uchar s1t, s2t;
285 
286  do {
287  s1t = *s1;
288  if(s1t == ' ') {
289  s1t = 0;
290  }
291  s2t = *s2;
292  if(s2t == ' ') {
293  s2t = 0;
294  }
295 
296  if(s1t > s2t) {
297  return 1;
298  } else if(s1t < s2t) {
299  return -1;
300  }
301  s1 ++;
302  s2 ++;
303  } while((s1t != 0) || (s2t != 0));
304 
305  return 0;
306 }
307 
308 #if 0
309 static long strlengs(const char *str)
310 {
311  long len = 0;
312 
313  while((*str != 0) && (*str != ' ')) {
314  len ++;
315  str ++;
316  }
317 
318  return len;
319 }
320 #endif
321 
322 static unsigned int str_match_len(uchar *s1, uchar *s2)
323 {
324  unsigned int len = 0;
325 
326  do {
327  if(*s1 == *s2) {
328  len ++;
329  } else {
330  return len;
331  }
332  s1 ++;
333  s2 ++;
334  } while((*s1 != 0) || (*s2 != 0));
335 
336  return len;
337 }
338 
339 /*
340  補完候補コマンドを探す
341 */
342 #define MATCH_NO 0 // 補完不要、不可
343 #define MATCH_PART 1 // 部分補完
344 #define MATCH_COMP 2 // 完全一致
345 
346 static int match_command(struct st_shell_command * const * coms,
347  struct st_shell *shell, int arg,
348  uchar **comstr,// 補完で挿入出来る文字列
349  unsigned int *len, // 補完文字数
350  int disp)
351 {
352  struct st_shell_command * const *cp = coms;
353  int match = MATCH_NO;
354  static const uchar *null = (const uchar *)"";
355 
356  while(*cp != 0) {
357  if(strcomps(shell->argv[arg], (*cp)->name) == 0) {
358  // 完全にマッチした場合はサブコマンドを探す
359  if(strcomp(shell->argv[arg], (*cp)->name) == 0) {
360  *len = 0;
361  *comstr = (uchar *)null;
362  return MATCH_COMP;
363  } else if((*cp)->sublist) {
364  arg ++;
365  if(disp) {
366  tprintf("\n %s ->", (*cp)->name);
367  }
368  return match_command((struct st_shell_command * const *)(*cp)->sublist, shell, arg,
369  comstr, len, disp);
370  } else {
371  *len = 0;
372  *comstr = (uchar *)null;
373  return MATCH_NO;
374  }
375  } else {
376  if(strncomp(shell->argv[arg], (*cp)->name,
377  strleng(shell->argv[arg])) == 0) {
378  // 現在のカーソル入力までマッチしてい
379  // るコマンドを探す
380  uchar* strp = (uchar *)(*cp)->name + strleng(shell->argv[arg]);
381  if(match == MATCH_NO) {
382  unsigned int comlen, arglen;
383  match = MATCH_COMP;
384  *comstr = strp;
385  comlen = strleng((*cp)->name);
386  arglen = strleng(shell->argv[arg]);
387  if(comlen > arglen) {
388  *len = comlen - arglen;
389  } else {
390  *len = 0;
391  }
392  } else {
393  unsigned int tmp = str_match_len((uchar *)(*comstr), strp);
394  if(*len > tmp) {
395  *len = tmp;
396  match = MATCH_PART;
397  }
398  }
399 // tprintf("\n### %s:%s", (*comstr), strp);
400 // tprintf("\n### len = %d", *len);
401  if(disp) {
402  tprintf("\n %s", (*cp)->name);
403  }
404  }
405  }
406 
407  cp ++;
408  }
409 
410 // tprintf("\n### arg = %d, \"%s\"\n", arg, shell->argv[arg]);
411 
412  return match;
413 }
414 
415 static void complement_command(struct st_shell *shell)
416 {
418  static const uchar *space = (const uchar *)" ";
419  int match = 0;
420  uchar *comstr;
421  unsigned int len = 0;
422 
423  (void)strncopy(str, shell->comLine.buf, GSC_SHELL_MAX_LINE_COLUMS);
424  str[shell->comLine.cur_pos] = 0;
425 
426  token_str(shell, str, 0);
427 
428  match = match_command(shell->shell_coms, shell, 0,
429  &comstr, &len, 0);
430 
431  switch(match) {
432  case MATCH_PART:
433  match_command(shell->shell_coms, shell, 0,
434  &comstr, &len, 1);
435  tprintf("\n");
436  print_prompt(shell);
437  draw_lineedit(&shell->comLine);
438  insert_str_lineedit(&shell->comLine, comstr, len);
439  break;
440 
441  case MATCH_COMP:
442  insert_str_lineedit(&shell->comLine, comstr, len);
443  insert_str_lineedit(&shell->comLine, (uchar *)space, 1);
444  break;
445  }
446 }
447 
448 
449 /**
450  @brief 文字列編集タスク
451 
452  @param[in] shell shellデータ構造体ポインタ
453  @param ch 編集文字データ
454 
455  @return コマンド実行結果
456 */
457 int task_shell(struct st_shell *shell, uchar ch)
458 {
459  int rt;
460 
461  if(ch == ASCII_CTRL_C) { // CTRL-C
462  init_lineedit(&shell->comLine);
463  tprintf("\n");
464  print_prompt(shell);
465  } else if(ch == ASCII_HT) { // TAB
466  // コマンド補完
467  complement_command(shell);
468  } else {
469  switch(do_lineedit(&shell->comLine, ch)) {
470  case LER_NOP:
471  break;
472 
473  case LER_RETURN: // '\r'受信でコマンド実行
474  save_history(&shell->his, shell->comLine.buf);
475 // tprintf("\023"); // X Start
476  rt = exec_shell_command(shell, shell->comLine.buf);
477 // tprintf("\021"); // X Stop
478  new_lineedit(&shell->comLine);
479 // if(rt) {
480 // return rt;
481 // }
482  print_prompt(shell);
483  return rt;
484 
485  case LER_BACKLINE:
486  back_history(&shell->his, &shell->comLine);
487  break;
488 
489  case LER_NEXTLINE:
490  foward_history(&shell->his, &shell->comLine);
491  break;
492  }
493  }
494 
495  return 0;
496 }
497 
498 /**
499  @brief 文字列をshellで処理可能なようにエスケープシーケンスを追加する
500 
501  @param dsrt 変換後文字列
502  @param ssrt 変換前文字列
503 
504  @return 変換後文字数
505 */
506 int escaped_str(uchar *dstr, uchar *sstr)
507 {
508  int len = 0;
509  //char *dp = dstr;
510  //char *sp = sstr;
511 
512  while((*sstr) != 0) {
513  if((*sstr == ' ') || (*sstr == '\\')) {
514  *dstr = (uchar)'\\';
515  dstr ++;
516  len ++;
517  }
518  *dstr = *sstr;
519  sstr ++;
520  dstr ++;
521  len ++;
522  }
523  *dstr = 0;
524 
525  //xdump(sp, len);
526  //xdump(dp, len);
527 
528  return len;
529 }
unsigned char uchar
GadgetSeedの文字(列)は unsigned char 型となる
Definition: str.h:13
シェルコマンド構造体
Definition: shell.h:33
void(* init)(void)
コマンド初期化関数
Definition: shell.h:35
コマンドシェル
int argc
コマンド引数数
Definition: shell.h:48
int exec_shell_command(struct st_shell *shell, uchar *str)
str文字列のコマンドを実行する
Definition: shell.c:218
struct st_shell_command *const * shell_coms
各シェルコマンド配列
Definition: shell.h:50
unsigned int cur_pos
カーソル位置
Definition: lineedit.h:32
uchar lastCom[GSC_SHELL_MAX_LINE_COLUMS+1]
最後に実行したコマンド
Definition: shell.h:45
シェルデータ構造体
Definition: shell.h:43
uchar buf[GSC_SHELL_MAX_LINE_COLUMS+1]
編集中文字列
Definition: lineedit.h:31
ラインエディタ
const uchar * prompt
コマンドプロンプト文字列
Definition: shell.h:47
int strncomp(const uchar *s1, const uchar *s2, unsigned int n)
文字列比較(長さ指定あり)
Definition: str.c:605
int escaped_str(uchar *dstr, uchar *sstr)
文字列をshellで処理可能なようにエスケープシーケンスを追加する
Definition: shell.c:506
文字列処理
struct st_lineedit comLine
コマンドライン
Definition: shell.h:44
int do_lineedit(struct st_lineedit *le, uchar ch)
リターンを受けるまでの1行編集を行う 1文字づつデータを入れる
Definition: lineedit.c:71
int(* command)(int argc, uchar *argv[])
コマンド実行関数
Definition: shell.h:36
void draw_lineedit(struct st_lineedit *le)
表示する
Definition: lineedit.c:337
struct st_history his
コマンドヒストリ
Definition: shell.h:46
void new_lineedit(struct st_lineedit *le)
編集中のエディタを初期化(コマンド実行後にバッファ破棄)
Definition: lineedit.c:290
int strcomp(const uchar *s1, const uchar *s2)
文字列比較
Definition: str.c:583
int task_shell(struct st_shell *shell, uchar ch)
文字列編集タスク
Definition: shell.c:457
unsigned int strleng(const uchar *str)
文字列長
Definition: str.c:657
uchar name[12]
コマンド名文字列
Definition: shell.h:34
unsigned short attr
コマンド属性
Definition: shell.h:37
int eprintf(const char *fmt,...)
エラー出力用簡易printf
Definition: tprintf.c:104
コマンドヒストリ
int tprintf(const char *fmt,...)
簡易printf
Definition: tprintf.c:85
void print_prompt(struct st_shell *shell)
プロンプトを表示する
Definition: shell.c:57
void init_lineedit(struct st_lineedit *le)
lineeditを初期化する
Definition: lineedit.c:51
uchar * strncopy(uchar *dest, const uchar *src, unsigned int n)
文字列コピー
Definition: str.c:632
uchar * argv[GSC_SHELL_MAX_COM_ARGV]
コマンド引数文字列
Definition: shell.h:49
char * usage_str
使用法文字列
Definition: shell.h:38
#define GSC_SHELL_MAX_LINE_COLUMS
$gsc shellコマンドラインの最大文字数
Definition: lineedit.h:16
void init_shell(struct st_shell *shell, struct st_shell_command *const *coms, const uchar *prompt)
シェルを初期化する
Definition: shell.c:39
void dispose_shell_line(struct st_shell *shell)
編集中のコマンドラインを破棄する
Definition: shell.c:67
機能限定printf