1 /*
2 * Copyright (c) 2015, Freescale Semiconductor, Inc.
3 * Copyright 2016-2017 NXP
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 *
8 * POSIX getopt for Windows
9 * Code given out at the 1985 UNIFORUM conference in Dallas.
10 *
11 * From std-unix@ut-sally.UUCP (Moderator, John Quarterman) Sun Nov 3 14:34:15 1985
12 * Relay-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site gatech.CSNET
13 * Posting-Version: version B 2.10.2 9/18/84; site ut-sally.UUCP
14 * Path: gatech!akgua!mhuxv!mhuxt!mhuxr!ulysses!allegra!mit-eddie!genrad!panda!talcott!harvard!seismo!ut-sally!std-unix
15 * From: std-unix@ut-sally.UUCP (Moderator, John Quarterman)
16 * Newsgroups: mod.std.unix
17 * Subject: public domain AT&T getopt source
18 * Message-ID: <3352@ut-sally.UUCP>
19 * Date: 3 Nov 85 19:34:15 GMT
20 * Date-Received: 4 Nov 85 12:25:09 GMT
21 * Organization: IEEE/P1003 Portable Operating System Environment Committee
22 * Lines: 91
23 * Approved: jsq@ut-sally.UUC
24 * Here's something you've all been waiting for: the AT&T public domain
25 * source for getopt(3). It is the code which was given out at the 1985
26 * UNIFORUM conference in Dallas. I obtained it by electronic mail
27 * directly from AT&T. The people there assure me that it is indeed
28 * in the public domain
29 * There is no manual page. That is because the one they gave out at
30 * UNIFORUM was slightly different from the current System V Release 2
31 * manual page. The difference apparently involved a note about the
32 * famous rules 5 and 6, recommending using white space between an option
33 * and its first argument, and not grouping options that have arguments.
34 * Getopt itself is currently lenient about both of these things White
35 * space is allowed, but not mandatory, and the last option in a group can
36 * have an argument. That particular version of the man page evidently
37 * has no official existence, and my source at AT&T did not send a copy.
38 * The current SVR2 man page reflects the actual behavor of this getopt.
39 * However, I am not about to post a copy of anything licensed by AT&T.
40 */
41
42 #include <assert.h>
43 #include "fsl_shell.h"
44
45 /*******************************************************************************
46 * Definitions
47 ******************************************************************************/
48 #define KEY_ESC (0x1BU)
49 #define KET_DEL (0x7FU)
50
51 /*******************************************************************************
52 * Prototypes
53 ******************************************************************************/
54 static int32_t HelpCommand(p_shell_context_t context, int32_t argc, char **argv); /*!< help command */
55
56 static int32_t ExitCommand(p_shell_context_t context, int32_t argc, char **argv); /*!< exit command */
57
58 static int32_t ParseLine(const char *cmd, uint32_t len, char *argv[SHELL_MAX_ARGS]); /*!< parse line command */
59
60 static int32_t StrCompare(const char *str1, const char *str2, int32_t count); /*!< compare string command */
61
62 static void ProcessCommand(p_shell_context_t context, const char *cmd); /*!< process a command */
63
64 static void GetHistoryCommand(p_shell_context_t context, uint8_t hist_pos); /*!< get commands history */
65
66 static void AutoComplete(p_shell_context_t context); /*!< auto complete command */
67
68 static uint8_t GetChar(p_shell_context_t context); /*!< get a char from communication interface */
69
70 static int32_t StrLen(const char *str); /*!< get string length */
71
72 static char *StrCopy(char *dest, const char *src, int32_t count); /*!< string copy */
73
74 /*******************************************************************************
75 * Variables
76 ******************************************************************************/
77 static const shell_command_context_t xHelpCommand = {"help", "\r\n\"help\": Lists all the registered commands\r\n",
78 HelpCommand, 0};
79
80 static const shell_command_context_t xExitCommand = {"exit", "\r\n\"exit\": Exit program\r\n", ExitCommand, 0};
81
82 static shell_command_context_list_t g_RegisteredCommands;
83
84 static char g_paramBuffer[SHELL_BUFFER_SIZE];
85
86 /*******************************************************************************
87 * Code
88 ******************************************************************************/
SHELL_Init(p_shell_context_t context,send_data_cb_t send_cb,recv_data_cb_t recv_cb,printf_data_t shell_printf,char * prompt)89 void SHELL_Init(
90 p_shell_context_t context, send_data_cb_t send_cb, recv_data_cb_t recv_cb, printf_data_t shell_printf, char *prompt)
91 {
92 assert(send_cb != NULL);
93 assert(recv_cb != NULL);
94 assert(prompt != NULL);
95 assert(shell_printf != NULL);
96
97 /* Memset for context */
98 memset(context, 0, sizeof(shell_context_struct));
99 context->send_data_func = send_cb;
100 context->recv_data_func = recv_cb;
101 context->printf_data_func = shell_printf;
102 context->prompt = prompt;
103
104 SHELL_RegisterCommand(&xHelpCommand);
105 SHELL_RegisterCommand(&xExitCommand);
106 }
107
SHELL_Main(p_shell_context_t context)108 int32_t SHELL_Main(p_shell_context_t context)
109 {
110 uint8_t ch;
111 int32_t i;
112
113 if (!context)
114 {
115 return -1;
116 }
117
118 context->exit = false;
119 context->printf_data_func("\r\nSHELL (build: %s)\r\n", __DATE__);
120 context->printf_data_func("Copyright (c) 2017 NXP Semiconductor\r\n");
121 context->printf_data_func(context->prompt);
122
123 while (1)
124 {
125 if (context->exit)
126 {
127 break;
128 }
129 ch = GetChar(context);
130 /* If error occured when getting a char, continue to receive a new char. */
131 if ((uint8_t)(-1) == ch)
132 {
133 continue;
134 }
135 /* Special key */
136 if (ch == KEY_ESC)
137 {
138 context->stat = kSHELL_Special;
139 continue;
140 }
141 else if (context->stat == kSHELL_Special)
142 {
143 /* Function key */
144 if (ch == '[')
145 {
146 context->stat = kSHELL_Function;
147 continue;
148 }
149 context->stat = kSHELL_Normal;
150 }
151 else if (context->stat == kSHELL_Function)
152 {
153 context->stat = kSHELL_Normal;
154
155 switch ((uint8_t)ch)
156 {
157 /* History operation here */
158 case 'A': /* Up key */
159 GetHistoryCommand(context, context->hist_current);
160 if (context->hist_current < (context->hist_count - 1))
161 {
162 context->hist_current++;
163 }
164 break;
165 case 'B': /* Down key */
166 GetHistoryCommand(context, context->hist_current);
167 if (context->hist_current > 0)
168 {
169 context->hist_current--;
170 }
171 break;
172 case 'D': /* Left key */
173 if (context->c_pos)
174 {
175 context->printf_data_func("\b");
176 context->c_pos--;
177 }
178 break;
179 case 'C': /* Right key */
180 if (context->c_pos < context->l_pos)
181 {
182 context->printf_data_func("%c", context->line[context->c_pos]);
183 context->c_pos++;
184 }
185 break;
186 default:
187 break;
188 }
189 continue;
190 }
191 /* Handle tab key */
192 else if (ch == '\t')
193 {
194 #if SHELL_AUTO_COMPLETE
195 /* Move the cursor to the beginning of line */
196 for (i = 0; i < context->c_pos; i++)
197 {
198 context->printf_data_func("\b");
199 }
200 /* Do auto complete */
201 AutoComplete(context);
202 /* Move position to end */
203 context->c_pos = context->l_pos = StrLen(context->line);
204 #endif
205 continue;
206 }
207 #if SHELL_SEARCH_IN_HIST
208 /* Search command in history */
209 else if ((ch == '`') && (context->l_pos == 0) && (context->line[0] == 0x00))
210 {
211 }
212 #endif
213 /* Handle backspace key */
214 else if ((ch == KET_DEL) || (ch == '\b'))
215 {
216 /* There must be at last one char */
217 if (context->c_pos == 0)
218 {
219 continue;
220 }
221
222 context->l_pos--;
223 context->c_pos--;
224
225 if (context->l_pos > context->c_pos)
226 {
227 memmove(&context->line[context->c_pos], &context->line[context->c_pos + 1],
228 context->l_pos - context->c_pos);
229 context->line[context->l_pos] = 0;
230 context->printf_data_func("\b%s \b", &context->line[context->c_pos]);
231
232 /* Reset position */
233 for (i = context->c_pos; i <= context->l_pos; i++)
234 {
235 context->printf_data_func("\b");
236 }
237 }
238 else /* Normal backspace operation */
239 {
240 context->printf_data_func("\b \b");
241 context->line[context->l_pos] = 0;
242 }
243 continue;
244 }
245 else
246 {
247 }
248
249 /* Input too long */
250 if (context->l_pos >= (SHELL_BUFFER_SIZE - 1))
251 {
252 context->l_pos = 0;
253 }
254
255 /* Handle end of line, break */
256 if ((ch == '\r') || (ch == '\n'))
257 {
258 static char endoflinechar = 0U;
259
260 if ((endoflinechar != 0U) && (endoflinechar != ch))
261 {
262 continue;
263 }
264 else
265 {
266 endoflinechar = ch;
267 context->printf_data_func("\r\n");
268 /* If command line is NULL, will start a new transfer */
269 if (0U == StrLen(context->line))
270 {
271 context->printf_data_func(context->prompt);
272 continue;
273 }
274 ProcessCommand(context, context->line);
275 /* Reset all params */
276 context->c_pos = context->l_pos = 0;
277 context->hist_current = 0;
278 context->printf_data_func(context->prompt);
279 memset(context->line, 0, sizeof(context->line));
280 continue;
281 }
282 }
283
284 /* Normal character */
285 if (context->c_pos < context->l_pos)
286 {
287 memmove(&context->line[context->c_pos + 1], &context->line[context->c_pos],
288 context->l_pos - context->c_pos);
289 context->line[context->c_pos] = ch;
290 context->printf_data_func("%s", &context->line[context->c_pos]);
291 /* Move the cursor to new position */
292 for (i = context->c_pos; i < context->l_pos; i++)
293 {
294 context->printf_data_func("\b");
295 }
296 }
297 else
298 {
299 context->line[context->l_pos] = ch;
300 context->printf_data_func("%c", ch);
301 }
302
303 ch = 0;
304 context->l_pos++;
305 context->c_pos++;
306 }
307 return 0;
308 }
309
HelpCommand(p_shell_context_t context,int32_t argc,char ** argv)310 static int32_t HelpCommand(p_shell_context_t context, int32_t argc, char **argv)
311 {
312 uint8_t i = 0;
313
314 for (i = 0; i < g_RegisteredCommands.numberOfCommandInList; i++)
315 {
316 context->printf_data_func(g_RegisteredCommands.CommandList[i]->pcHelpString);
317 }
318 return 0;
319 }
320
ExitCommand(p_shell_context_t context,int32_t argc,char ** argv)321 static int32_t ExitCommand(p_shell_context_t context, int32_t argc, char **argv)
322 {
323 /* Skip warning */
324 context->printf_data_func("\r\nSHELL exited\r\n");
325 context->exit = true;
326 return 0;
327 }
328
ProcessCommand(p_shell_context_t context,const char * cmd)329 static void ProcessCommand(p_shell_context_t context, const char *cmd)
330 {
331 static const shell_command_context_t *tmpCommand = NULL;
332 static const char *tmpCommandString;
333 int32_t argc;
334 char *argv[SHELL_BUFFER_SIZE];
335 uint8_t flag = 1;
336 uint8_t tmpCommandLen;
337 uint8_t tmpLen;
338 uint8_t i = 0;
339
340 tmpLen = StrLen(cmd);
341 argc = ParseLine(cmd, tmpLen, argv);
342
343 if ((tmpCommand == NULL) && (argc > 0))
344 {
345 for (i = 0; i < g_RegisteredCommands.numberOfCommandInList; i++)
346 {
347 tmpCommand = g_RegisteredCommands.CommandList[i];
348 tmpCommandString = tmpCommand->pcCommand;
349 tmpCommandLen = StrLen(tmpCommandString);
350 /* Compare with space or end of string */
351 if ((cmd[tmpCommandLen] == ' ') || (cmd[tmpCommandLen] == 0x00))
352 {
353 if (StrCompare(tmpCommandString, argv[0], tmpCommandLen) == 0)
354 {
355 /* support commands with optional number of parameters */
356 if (tmpCommand->cExpectedNumberOfParameters == SHELL_OPTIONAL_PARAMS)
357 {
358 flag = 0;
359 }
360 else if ((tmpCommand->cExpectedNumberOfParameters == 0) && (argc == 1))
361 {
362 flag = 0;
363 }
364 else if (tmpCommand->cExpectedNumberOfParameters > 0)
365 {
366 if ((argc - 1) == tmpCommand->cExpectedNumberOfParameters)
367 {
368 flag = 0;
369 }
370 }
371 else
372 {
373 flag = 1;
374 }
375 break;
376 }
377 }
378 }
379 }
380
381 if ((tmpCommand != NULL) && (flag == 1U))
382 {
383 context->printf_data_func(
384 "\r\nIncorrect command parameter(s). Enter \"help\" to view a list of available commands.\r\n\r\n");
385 tmpCommand = NULL;
386 }
387 else if (tmpCommand != NULL)
388 {
389 tmpLen = StrLen(cmd);
390 /* Compare with last command. Push back to history buffer if different */
391 if (tmpLen != StrCompare(cmd, context->hist_buf[0], StrLen(cmd)))
392 {
393 for (i = SHELL_HIST_MAX - 1; i > 0; i--)
394 {
395 memset(context->hist_buf[i], '\0', SHELL_BUFFER_SIZE);
396 tmpLen = StrLen(context->hist_buf[i - 1]);
397 StrCopy(context->hist_buf[i], context->hist_buf[i - 1], tmpLen);
398 }
399 memset(context->hist_buf[0], '\0', SHELL_BUFFER_SIZE);
400 tmpLen = StrLen(cmd);
401 StrCopy(context->hist_buf[0], cmd, tmpLen);
402 if (context->hist_count < SHELL_HIST_MAX)
403 {
404 context->hist_count++;
405 }
406 }
407 tmpCommand->pFuncCallBack(context, argc, argv);
408 tmpCommand = NULL;
409 }
410 else
411 {
412 context->printf_data_func(
413 "\r\nCommand not recognised. Enter 'help' to view a list of available commands.\r\n\r\n");
414 tmpCommand = NULL;
415 }
416 }
417
GetHistoryCommand(p_shell_context_t context,uint8_t hist_pos)418 static void GetHistoryCommand(p_shell_context_t context, uint8_t hist_pos)
419 {
420 uint8_t i;
421 uint32_t tmp;
422
423 if (context->hist_buf[0][0] == '\0')
424 {
425 context->hist_current = 0;
426 return;
427 }
428 if (hist_pos >= SHELL_HIST_MAX)
429 {
430 hist_pos = SHELL_HIST_MAX - 1;
431 }
432 tmp = StrLen(context->line);
433 /* Clear current if have */
434 if (tmp > 0)
435 {
436 memset(context->line, '\0', tmp);
437 for (i = 0; i < tmp; i++)
438 {
439 context->printf_data_func("\b \b");
440 }
441 }
442
443 context->l_pos = StrLen(context->hist_buf[hist_pos]);
444 context->c_pos = context->l_pos;
445 StrCopy(context->line, context->hist_buf[hist_pos], context->l_pos);
446 context->printf_data_func(context->hist_buf[hist_pos]);
447 }
448
AutoComplete(p_shell_context_t context)449 static void AutoComplete(p_shell_context_t context)
450 {
451 int32_t len;
452 int32_t minLen;
453 uint8_t i = 0;
454 const shell_command_context_t *tmpCommand = NULL;
455 const char *namePtr;
456 const char *cmdName;
457
458 minLen = 0;
459 namePtr = NULL;
460
461 if (!StrLen(context->line))
462 {
463 return;
464 }
465 context->printf_data_func("\r\n");
466 /* Empty tab, list all commands */
467 if (context->line[0] == '\0')
468 {
469 HelpCommand(context, 0, NULL);
470 return;
471 }
472 /* Do auto complete */
473 for (i = 0; i < g_RegisteredCommands.numberOfCommandInList; i++)
474 {
475 tmpCommand = g_RegisteredCommands.CommandList[i];
476 cmdName = tmpCommand->pcCommand;
477 if (StrCompare(context->line, cmdName, StrLen(context->line)) == 0)
478 {
479 if (minLen == 0)
480 {
481 namePtr = cmdName;
482 minLen = StrLen(namePtr);
483 /* Show possible matches */
484 context->printf_data_func("%s\r\n", cmdName);
485 continue;
486 }
487 len = StrCompare(namePtr, cmdName, StrLen(namePtr));
488 if (len < 0)
489 {
490 len = len * (-1);
491 }
492 if (len < minLen)
493 {
494 minLen = len;
495 }
496 }
497 }
498 /* Auto complete string */
499 if (namePtr)
500 {
501 StrCopy(context->line, namePtr, minLen);
502 }
503 context->printf_data_func("%s%s", context->prompt, context->line);
504 return;
505 }
506
StrCopy(char * dest,const char * src,int32_t count)507 static char *StrCopy(char *dest, const char *src, int32_t count)
508 {
509 char *ret = dest;
510 int32_t i = 0;
511
512 for (i = 0; i < count; i++)
513 {
514 dest[i] = src[i];
515 }
516
517 return ret;
518 }
519
StrLen(const char * str)520 static int32_t StrLen(const char *str)
521 {
522 int32_t i = 0;
523
524 while (*str)
525 {
526 str++;
527 i++;
528 }
529 return i;
530 }
531
StrCompare(const char * str1,const char * str2,int32_t count)532 static int32_t StrCompare(const char *str1, const char *str2, int32_t count)
533 {
534 while (count--)
535 {
536 if (*str1++ != *str2++)
537 {
538 return *(unsigned char *)(str1 - 1) - *(unsigned char *)(str2 - 1);
539 }
540 }
541 return 0;
542 }
543
ParseLine(const char * cmd,uint32_t len,char * argv[SHELL_MAX_ARGS])544 static int32_t ParseLine(const char *cmd, uint32_t len, char *argv[SHELL_MAX_ARGS])
545 {
546 uint32_t argc;
547 char *p;
548 uint32_t position;
549
550 /* Init params */
551 memset(g_paramBuffer, '\0', len + 1);
552 StrCopy(g_paramBuffer, cmd, len);
553
554 p = g_paramBuffer;
555 position = 0;
556 argc = 0;
557
558 while (position < len)
559 {
560 /* Skip all blanks */
561 while (((char)(*p) == ' ') && (position < len))
562 {
563 *p = '\0';
564 p++;
565 position++;
566 }
567 /* Process begin of a string */
568 if (*p == '"')
569 {
570 p++;
571 position++;
572 argv[argc] = p;
573 argc++;
574 /* Skip this string */
575 while ((*p != '"') && (position < len))
576 {
577 p++;
578 position++;
579 }
580 /* Skip '"' */
581 *p = '\0';
582 p++;
583 position++;
584 }
585 else /* Normal char */
586 {
587 argv[argc] = p;
588 argc++;
589 while (((char)*p != ' ') && ((char)*p != '\t') && (position < len))
590 {
591 p++;
592 position++;
593 }
594 }
595 }
596 return argc;
597 }
598
SHELL_RegisterCommand(const shell_command_context_t * command_context)599 int32_t SHELL_RegisterCommand(const shell_command_context_t *command_context)
600 {
601 int32_t result = 0;
602
603 /* If have room in command list */
604 if (g_RegisteredCommands.numberOfCommandInList < SHELL_MAX_CMD)
605 {
606 g_RegisteredCommands.CommandList[g_RegisteredCommands.numberOfCommandInList++] = command_context;
607 }
608 else
609 {
610 result = -1;
611 }
612 return result;
613 }
614
GetChar(p_shell_context_t context)615 static uint8_t GetChar(p_shell_context_t context)
616 {
617 uint8_t ch;
618
619 #if SHELL_USE_FILE_STREAM
620 ch = fgetc(context->STDIN);
621 #else
622 context->recv_data_func(&ch, 1U);
623 #endif
624 return ch;
625 }
626