Add missing includes
[contiki-2.x.git] / apps / shell / shell.c
blob5a9c01ad3efa6a2b0833aa5c54e5fe20f46a496c
1 /**
2 * \addtogroup shell
3 * @{
4 */
6 /*
7 * Copyright (c) 2008, Swedish Institute of Computer Science.
8 * All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the Institute nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
34 * This file is part of the Contiki operating system.
36 * $Id: shell.c,v 1.13 2009/03/02 20:44:15 adamdunkels Exp $
39 /**
40 * \file
41 * The shell application
42 * \author
43 * Adam Dunkels <adam@sics.se>
46 #include "contiki.h"
47 #include "contiki-lib.h"
49 #include "net/rime.h"
51 #include "shell.h"
53 #include <ctype.h>
54 #include <string.h>
55 #include <stdio.h>
57 LIST(commands);
59 int shell_event_input;
61 static struct process *front_process;
63 static unsigned long time_offset;
65 PROCESS(shell_process, "Shell");
66 PROCESS(shell_server_process, "Shell server");
67 /*---------------------------------------------------------------------------*/
68 PROCESS(help_command_process, "help");
69 SHELL_COMMAND(help_command, "help", "help: shows this help",
70 &help_command_process);
71 SHELL_COMMAND(question_command, "?", "?: shows this help",
72 &help_command_process);
73 PROCESS(shell_killall_process, "killall");
74 SHELL_COMMAND(killall_command, "killall", "killall: stop all running commands",
75 &shell_killall_process);
76 PROCESS(shell_kill_process, "kill");
77 SHELL_COMMAND(kill_command, "kill", "kill <command>: stop a specific command",
78 &shell_kill_process);
79 PROCESS(shell_null_process, "null");
80 SHELL_COMMAND(null_command,
81 "null",
82 "null: discard input",
83 &shell_null_process);
84 /*---------------------------------------------------------------------------*/
85 PROCESS_THREAD(shell_null_process, ev, data)
87 struct shell_input *input;
88 PROCESS_BEGIN();
89 while(1) {
90 PROCESS_WAIT_EVENT_UNTIL(ev == shell_event_input);
91 input = data;
93 if(input->len1 + input->len2 == 0) {
94 PROCESS_EXIT();
97 PROCESS_END();
99 /*---------------------------------------------------------------------------*/
100 static void
101 command_kill(struct shell_command *c)
103 if(c != NULL) {
104 shell_output_str(&killall_command, "Stopping command ", c->command);
105 process_exit(c->process);
108 /*---------------------------------------------------------------------------*/
109 static void
110 killall(void)
112 struct shell_command *c;
113 for(c = list_head(commands);
114 c != NULL;
115 c = c->next) {
116 if(c != &killall_command && process_is_running(c->process)) {
117 command_kill(c);
121 /*---------------------------------------------------------------------------*/
122 PROCESS_THREAD(shell_killall_process, ev, data)
125 PROCESS_BEGIN();
127 killall();
129 PROCESS_END();
131 /*---------------------------------------------------------------------------*/
132 PROCESS_THREAD(shell_kill_process, ev, data)
134 struct shell_command *c;
135 char *name;
136 PROCESS_BEGIN();
138 name = data;
139 if(name == NULL || strlen(name) == 0) {
140 shell_output_str(&kill_command,
141 "kill <command>: command name must be given", "");
144 for(c = list_head(commands);
145 c != NULL;
146 c = c->next) {
147 if(strcmp(name, c->command) == 0 &&
148 c != &kill_command &&
149 process_is_running(c->process)) {
150 command_kill(c);
151 PROCESS_EXIT();
155 shell_output_str(&kill_command, "Command not found: ", name);
157 PROCESS_END();
159 /*---------------------------------------------------------------------------*/
160 PROCESS_THREAD(help_command_process, ev, data)
162 struct shell_command *c;
163 PROCESS_BEGIN();
165 shell_output_str(&help_command, "Available commands:", "");
166 for(c = list_head(commands);
167 c != NULL;
168 c = c->next) {
169 shell_output_str(&help_command, c->description, "");
172 PROCESS_END();
174 /*---------------------------------------------------------------------------*/
175 static void
176 replace_braces(char *commandline)
178 char *ptr;
179 int level = 0;
181 for(ptr = commandline; *ptr != 0; ++ptr) {
182 if(*ptr == '{') {
183 if(level == 0) {
184 *ptr = ' ';
186 ++level;
187 } else if(*ptr == '}') {
188 --level;
189 if(level == 0) {
190 *ptr = ' ';
195 /*---------------------------------------------------------------------------*/
196 static char *
197 find_pipe(char *commandline)
199 char *ptr;
200 int level = 0;
202 for(ptr = commandline; *ptr != 0; ++ptr) {
203 if(*ptr == '{') {
204 ++level;
205 } else if(*ptr == '}') {
206 --level;
207 } else if(*ptr == '|') {
208 if(level == 0) {
209 return ptr;
213 return NULL;
215 /*---------------------------------------------------------------------------*/
216 static struct shell_command *
217 start_command(char *commandline, struct shell_command *child)
219 char *next, *args;
220 int command_len;
221 struct shell_command *c;
223 /* Shave off any leading spaces. */
224 while(*commandline == ' ') {
225 commandline++;
228 /* Find the next command in a pipeline and start it. */
229 next = find_pipe(commandline);
230 if(next != NULL) {
231 *next = 0;
232 child = start_command(next + 1, child);
235 /* Separate the command arguments, and remove braces. */
236 replace_braces(commandline);
237 args = strchr(commandline, ' ');
238 if(args != NULL) {
239 args++;
242 /* Shave off any trailing spaces. */
243 command_len = (int)strlen(commandline);
244 while(command_len > 0 && commandline[command_len - 1] == ' ') {
245 commandline[command_len - 1] = 0;
246 command_len--;
249 if(args == NULL) {
250 command_len = (int)strlen(commandline);
251 args = &commandline[command_len];
252 } else {
253 command_len = (int)(args - commandline - 1);
258 /* Go through list of commands to find a match for the first word in
259 the command line. */
260 for(c = list_head(commands);
261 c != NULL &&
262 !(strncmp(c->command, commandline, command_len) == 0 &&
263 c->command[command_len] == 0);
264 c = c->next);
266 if(c == NULL) {
267 shell_output_str(NULL, commandline, ": command not found (try 'help')");
268 command_kill(child);
269 c = NULL;
270 } else if(process_is_running(c->process) || child == c) {
271 shell_output_str(NULL, commandline, ": command already running");
272 c->child = NULL;
273 c = NULL;
274 } else {
275 c->child = child;
276 /* printf("shell: start_command starting '%s'\n", c->process->name);*/
277 /* Start a new process for the command. */
278 process_start(c->process, args);
281 return c;
283 /*---------------------------------------------------------------------------*/
285 shell_start_command(char *commandline, int commandline_len,
286 struct shell_command *child,
287 struct process **started_process)
289 struct shell_command *c;
290 int background = 0;
292 if(commandline_len == 0) {
293 if(started_process != NULL) {
294 *started_process = NULL;
296 return SHELL_NOTHING;
299 if(commandline[commandline_len - 1] == '&') {
300 commandline[commandline_len - 1] = 0;
301 background = 1;
302 commandline_len--;
305 c = start_command(commandline, child);
307 /* Return a pointer to the started process, so that the caller can
308 wait for the process to complete. */
309 if(c != NULL && started_process != NULL) {
310 *started_process = c->process;
311 if(background) {
312 return SHELL_BACKGROUND;
313 } else {
314 return SHELL_FOREGROUND;
317 return SHELL_NOTHING;
319 /*---------------------------------------------------------------------------*/
320 static void
321 input_to_child_command(struct shell_command *c,
322 char *data1, int len1,
323 const char *data2, int len2)
325 struct shell_input input;
326 if(process_is_running(c->process)) {
327 input.data1 = data1;
328 input.len1 = len1;
329 input.data2 = data2;
330 input.len2 = len2;
331 process_post_synch(c->process, shell_event_input, &input);
334 /*---------------------------------------------------------------------------*/
335 void
336 shell_input(char *commandline, int commandline_len)
338 struct shell_input input;
340 /* printf("shell_input front_process '%s'\n", front_process->name);*/
342 if(commandline[0] == '~' &&
343 commandline[1] == 'K') {
344 /* process_start(&shell_killall_process, commandline);*/
345 if(front_process != &shell_process) {
346 process_exit(front_process);
348 } else {
349 if(process_is_running(front_process)) {
350 input.data1 = commandline;
351 input.len1 = commandline_len;
352 input.data2 = "";
353 input.len2 = 0;
354 process_post_synch(front_process, shell_event_input, &input);
358 /*---------------------------------------------------------------------------*/
359 void
360 shell_output_str(struct shell_command *c, char *text1, const char *text2)
362 if(c != NULL && c->child != NULL) {
363 input_to_child_command(c->child, text1, (int)strlen(text1),
364 text2, (int)strlen(text2));
365 } else {
366 shell_default_output(text1, (int)strlen(text1),
367 text2, (int)strlen(text2));
370 /*---------------------------------------------------------------------------*/
371 void
372 shell_output(struct shell_command *c,
373 void *data1, int len1,
374 const void *data2, int len2)
376 if(c != NULL && c->child != NULL) {
377 input_to_child_command(c->child, data1, len1, data2, len2);
378 } else {
379 shell_default_output(data1, len1, data2, len2);
382 /*---------------------------------------------------------------------------*/
383 void
384 shell_unregister_command(struct shell_command *c)
386 list_remove(commands, c);
388 /*---------------------------------------------------------------------------*/
389 void
390 shell_register_command(struct shell_command *c)
392 struct shell_command *i, *p;
394 p = NULL;
395 for(i = list_head(commands);
396 i != NULL &&
397 strcmp(i->command, c->command) < 0;
398 i = i->next) {
399 p = i;
401 if(p == NULL) {
402 list_push(commands, c);
403 } else if(i == NULL) {
404 list_add(commands, c);
405 } else {
406 list_insert(commands, p, c);
409 /*---------------------------------------------------------------------------*/
410 PROCESS_THREAD(shell_process, ev, data)
412 static struct process *started_process;
413 PROCESS_BEGIN();
415 /* Let the system start up before showing the prompt. */
416 PROCESS_PAUSE();
418 while(1) {
419 shell_prompt("Contiki> ");
421 PROCESS_WAIT_EVENT_UNTIL(ev == shell_event_input);
423 struct shell_input *input = data;
424 int ret;
426 ret = shell_start_command(input->data1, input->len1, NULL,
427 &started_process);
429 if(started_process != NULL &&
430 ret == SHELL_FOREGROUND &&
431 process_is_running(started_process)) {
432 front_process = started_process;
433 PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_EXITED &&
434 data == started_process);
436 front_process = &shell_process;
440 PROCESS_END();
442 /*---------------------------------------------------------------------------*/
443 PROCESS_THREAD(shell_server_process, ev, data)
445 struct process *p;
446 struct shell_command *c;
447 static struct etimer etimer;
448 PROCESS_BEGIN();
450 etimer_set(&etimer, CLOCK_SECOND * 10);
451 while(1) {
452 PROCESS_WAIT_EVENT();
453 if(ev == PROCESS_EVENT_EXITED) {
454 p = data;
455 /* printf("process exited '%s' (front '%s')\n", p->name,
456 front_process->name);*/
457 for(c = list_head(commands);
458 c != NULL && c->process != p;
459 c = c->next);
460 while(c != NULL) {
461 if(c->child != NULL && c->child->process != NULL) {
462 /* printf("Killing '%s'\n", c->process->name);*/
463 input_to_child_command(c->child, "", 0, "", 0);
464 /* process_exit(c->process);*/
466 c = c->child;
468 } else if(ev == PROCESS_EVENT_TIMER) {
469 etimer_reset(&etimer);
470 shell_set_time(shell_time());
474 PROCESS_END();
476 /*---------------------------------------------------------------------------*/
477 void
478 shell_init(void)
480 list_init(commands);
481 shell_register_command(&help_command);
482 shell_register_command(&question_command);
483 shell_register_command(&killall_command);
484 shell_register_command(&kill_command);
485 shell_register_command(&null_command);
487 shell_event_input = process_alloc_event();
489 process_start(&shell_process, NULL);
490 process_start(&shell_server_process, NULL);
492 front_process = &shell_process;
494 /*---------------------------------------------------------------------------*/
495 unsigned long
496 shell_strtolong(const char *str, const char **retstr)
498 int i;
499 unsigned long num = 0;
500 const char *strptr = str;
502 if(str == NULL) {
503 return 0;
506 while(*strptr == ' ') {
507 ++strptr;
510 for(i = 0; i < 10 && isdigit(strptr[i]); ++i) {
511 num = num * 10 + strptr[i] - '0';
513 if(retstr != NULL) {
514 if(i == 0) {
515 *retstr = str;
516 } else {
517 *retstr = strptr + i;
521 return num;
523 /*---------------------------------------------------------------------------*/
524 unsigned long
525 shell_time(void)
527 return clock_seconds() + time_offset;
529 /*---------------------------------------------------------------------------*/
530 void
531 shell_set_time(unsigned long seconds)
533 time_offset = seconds - clock_seconds();
535 /*---------------------------------------------------------------------------*/
536 void
537 shell_start(void)
539 shell_output_str(NULL, "Contiki command shell", "");
540 shell_output_str(NULL, "Type '?' and return for help", "");
541 shell_prompt("Contiki> ");
543 /*---------------------------------------------------------------------------*/
544 void
545 shell_quit(void)
547 killall();
548 process_exit(&shell_process);
549 process_exit(&shell_server_process);
551 /*---------------------------------------------------------------------------*/
553 /** @} */