help is now implemented in startup.tcl/help
[openocd.git] / src / helper / command.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * part of this file is taken from libcli (libcli.sourceforge.net) *
6 * Copyright (C) David Parrish (david@dparrish.com) *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22 ***************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "replacements.h"
28
29 #include "command.h"
30
31 #include "log.h"
32 #include "time_support.h"
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <unistd.h>
40
41 #include <openocd_tcl.h>
42
43 int fast_and_dangerous = 0;
44
45 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
46 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
47 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
48
49 /* forward declaration of jim_command */
50 extern int jim_command(command_context_t *context, char *line);
51
52
53
54 command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help)
55 {
56 command_t *c, *p;
57
58 if (!context || !name)
59 return NULL;
60
61 c = malloc(sizeof(command_t));
62
63 c->name = strdup(name);
64 c->parent = parent;
65 c->children = NULL;
66 c->handler = handler;
67 c->mode = mode;
68 if (!help)
69 help="";
70 c->next = NULL;
71
72 /* place command in tree */
73 if (parent)
74 {
75 if (parent->children)
76 {
77 /* find last child */
78 for (p = parent->children; p && p->next; p = p->next);
79 if (p)
80 p->next = c;
81 }
82 else
83 {
84 parent->children = c;
85 }
86 }
87 else
88 {
89 if (context->commands)
90 {
91 /* find last command */
92 for (p = context->commands; p && p->next; p = p->next);
93 if (p)
94 p->next = c;
95 }
96 else
97 {
98 context->commands = c;
99 }
100 }
101 /* accumulate help text in Tcl helptext list. */
102 Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
103 Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
104
105 Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
106
107 /* maximum of two levels :-) */
108 if (c->parent!=NULL)
109 {
110 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
111 }
112 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
113
114 Jim_ListAppendElement(interp, cmd_entry, cmd_list);
115 Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
116 Jim_ListAppendElement(interp, helptext, cmd_entry);
117 return c;
118 }
119
120 int unregister_all_commands(command_context_t *context)
121 {
122 command_t *c, *c2;
123
124 if (context == NULL)
125 return ERROR_OK;
126
127
128 while(NULL != context->commands)
129 {
130 c = context->commands;
131
132 while(NULL != c->children)
133 {
134 c2 = c->children;
135 c->children = c->children->next;
136 free(c2->name);
137 c2->name = NULL;
138 free(c2);
139 c2 = NULL;
140 }
141
142 context->commands = context->commands->next;
143
144 free(c->name);
145 c->name = NULL;
146 free(c);
147 c = NULL;
148 }
149
150 return ERROR_OK;
151 }
152
153 int unregister_command(command_context_t *context, char *name)
154 {
155 command_t *c, *p = NULL, *c2;
156
157 if ((!context) || (!name))
158 return ERROR_INVALID_ARGUMENTS;
159
160 /* find command */
161 for (c = context->commands; c; c = c->next)
162 {
163 if (strcmp(name, c->name) == 0)
164 {
165 /* unlink command */
166 if (p)
167 {
168 p->next = c->next;
169 }
170 else
171 {
172 context->commands = c->next;
173 }
174
175 /* unregister children */
176 if (c->children)
177 {
178 for (c2 = c->children; c2; c2 = c2->next)
179 {
180 free(c2->name);
181 free(c2);
182 }
183 }
184
185 /* delete command */
186 free(c->name);
187 free(c);
188 }
189
190 /* remember the last command for unlinking */
191 p = c;
192 }
193
194 return ERROR_OK;
195 }
196
197 int parse_line(char *line, char *words[], int max_words)
198 {
199 int nwords = 0;
200 char *p = line;
201 char *word_start = line;
202 int inquote = 0;
203
204 while (nwords < max_words - 1)
205 {
206 /* check if we reached
207 * a terminating NUL
208 * a matching closing quote character " or '
209 * we're inside a word but not a quote, and the current character is whitespace
210 */
211 if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))
212 {
213 /* we're inside a word or quote, and reached its end*/
214 if (word_start)
215 {
216 int len;
217 char *word_end=p;
218
219 /* This will handle extra whitespace within quotes */
220 while (isspace(*word_start)&&(word_start<word_end))
221 word_start++;
222 while (isspace(*(word_end-1))&&(word_start<word_end))
223 word_end--;
224 len = word_end - word_start;
225
226 if (len>0)
227 {
228 /* copy the word */
229 memcpy(words[nwords] = malloc(len + 1), word_start, len);
230 /* add terminating NUL */
231 words[nwords++][len] = 0;
232 }
233 }
234 /* we're done parsing the line */
235 if (!*p)
236 break;
237
238 /* skip over trailing quote or whitespace*/
239 if (inquote || isspace(*p))
240 p++;
241 while (isspace(*p))
242 p++;
243
244 inquote = 0;
245 word_start = 0;
246 }
247 else if (*p == '"' || *p == '\'')
248 {
249 /* we've reached the beginning of a quote */
250 inquote = *p++;
251 word_start = p;
252 }
253 else
254 {
255 /* we've reached the beginning of a new word */
256 if (!word_start)
257 word_start = p;
258
259 /* normal character, skip */
260 p++;
261 }
262 }
263
264 return nwords;
265 }
266
267 void command_output_text(command_context_t *context, const char *data)
268 {
269 if( context && context->output_handler && data ){
270 context->output_handler( context, data );
271 }
272 }
273
274 void command_print_n(command_context_t *context, char *format, ...)
275 {
276 char *string;
277
278 va_list ap;
279 va_start(ap, format);
280
281 string = alloc_vprintf(format, ap);
282 if (string != NULL)
283 {
284 context->output_handler(context, string);
285 free(string);
286 }
287
288 va_end(ap);
289 }
290
291 void command_print(command_context_t *context, char *format, ...)
292 {
293 char *string;
294
295 va_list ap;
296 va_start(ap, format);
297
298 string = alloc_vprintf(format, ap);
299 if (string != NULL)
300 {
301 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
302 context->output_handler(context, string);
303 free(string);
304 }
305
306 va_end(ap);
307 }
308
309 command_t *find_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word, int *new_start_word)
310 {
311 command_t *c;
312
313 for (c = commands; c; c = c->next)
314 {
315 if (strcasecmp(c->name, words[start_word]))
316 continue;
317
318 if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )
319 {
320 if (!c->children)
321 {
322 if (!c->handler)
323 {
324 return NULL;
325 }
326 else
327 {
328 *new_start_word=start_word;
329 return c;
330 }
331 }
332 else
333 {
334 if (start_word == num_words - 1)
335 {
336 return NULL;
337 }
338 return find_command(context, c->children, words, num_words, start_word + 1, new_start_word);
339 }
340 }
341 }
342 return NULL;
343 }
344
345 int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words)
346 {
347 int start_word=0;
348 command_t *c;
349 c = find_command(context, commands, words, num_words, start_word, &start_word);
350 if (c == NULL)
351 {
352 /* just return command not found */
353 return ERROR_COMMAND_NOTFOUND;
354 }
355
356 int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
357 if (retval == ERROR_COMMAND_SYNTAX_ERROR)
358 {
359
360 }
361 else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
362 {
363 /* just fall through for a shutdown request */
364 }
365 else if (retval != ERROR_OK)
366 {
367 /* we do not print out an error message because the command *should*
368 * have printed out an error
369 */
370 LOG_DEBUG("Command failed with error code %d", retval);
371 }
372
373 return retval;
374 }
375
376 int command_run_line_internal(command_context_t *context, char *line)
377 {
378 LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
379
380 int nwords;
381 char *words[128] = {0};
382 int retval;
383 int i;
384
385 /* skip preceding whitespace */
386 while (isspace(*line))
387 line++;
388
389 /* empty line, ignore */
390 if (!*line)
391 return ERROR_OK;
392
393 /* ignore comments */
394 if (*line && (line[0] == '#'))
395 return ERROR_OK;
396
397 LOG_DEBUG("%s", line);
398
399 nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
400
401 if (nwords > 0)
402 {
403 retval = find_and_run_command(context, context->commands, words, nwords);
404 }
405 else
406 return ERROR_INVALID_ARGUMENTS;
407
408 for (i = 0; i < nwords; i++)
409 free(words[i]);
410
411 return retval;
412 }
413
414 int command_run_line(command_context_t *context, char *line)
415 {
416 /* if a command is unknown to the "unknown" proc in tcl/commands.tcl will
417 * redirect it to OpenOCD.
418 *
419 * This avoids having to type the "openocd" prefix and makes OpenOCD
420 * commands "native" to Tcl.
421 */
422 return jim_command(context, line);
423 }
424
425
426 int command_run_linef(command_context_t *context, char *format, ...)
427 {
428 int retval=ERROR_FAIL;
429 char *string;
430 va_list ap;
431 va_start(ap, format);
432 string = alloc_vprintf(format, ap);
433 if (string!=NULL)
434 {
435 retval=command_run_line(context, string);
436 }
437 va_end(ap);
438 return retval;
439 }
440
441
442
443 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
444 {
445 context->output_handler = output_handler;
446 context->output_handler_priv = priv;
447 }
448
449 command_context_t* copy_command_context(command_context_t* context)
450 {
451 command_context_t* copy_context = malloc(sizeof(command_context_t));
452
453 *copy_context = *context;
454
455 return copy_context;
456 }
457
458 int command_done(command_context_t *context)
459 {
460 free(context);
461 context = NULL;
462
463 return ERROR_OK;
464 }
465
466 command_context_t* command_init()
467 {
468 command_context_t* context = malloc(sizeof(command_context_t));
469
470 context->mode = COMMAND_EXEC;
471 context->commands = NULL;
472 context->current_target = 0;
473 context->output_handler = NULL;
474 context->output_handler_priv = NULL;
475
476 register_command(context, NULL, "sleep", handle_sleep_command,
477 COMMAND_ANY, "sleep for <n> milliseconds");
478
479 register_command(context, NULL, "time", handle_time_command,
480 COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
481
482 register_command(context, NULL, "fast", handle_fast_command,
483 COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
484
485 return context;
486 }
487
488 /* sleep command sleeps for <n> miliseconds
489 * this is useful in target startup scripts
490 */
491 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
492 {
493 unsigned long duration = 0;
494
495 if (argc == 1)
496 {
497 duration = strtoul(args[0], NULL, 0);
498 usleep(duration * 1000);
499 }
500
501 return ERROR_OK;
502 }
503
504 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
505 {
506 if (argc!=1)
507 return ERROR_COMMAND_SYNTAX_ERROR;
508
509 fast_and_dangerous = strcmp("enable", args[0])==0;
510
511 return ERROR_OK;
512 }
513
514
515 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
516 {
517 duration_t duration;
518 char *duration_text;
519 int retval;
520 float t;
521
522 if (argc<1)
523 return ERROR_COMMAND_SYNTAX_ERROR;
524
525 duration_start_measure(&duration);
526
527 retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc);
528 if (retval == ERROR_COMMAND_NOTFOUND)
529 {
530 command_print(cmd_ctx, "Command %s not found", args[0]);
531 }
532
533 duration_stop_measure(&duration, &duration_text);
534
535 t=duration.duration.tv_sec;
536 t+=((float)duration.duration.tv_usec / 1000000.0);
537 command_print(cmd_ctx, "%s took %fs", args[0], t);
538
539 free(duration_text);
540
541 return retval;
542 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)