Collect output from openocd commands into openocd_output local variable
[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 extern command_context_t *active_cmd_ctx;
45
46 int handle_sleep_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 static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
49 {
50 Jim_Obj *tclOutput=(Jim_Obj *)privData;
51
52 Jim_AppendString(interp, tclOutput, string, strlen(string));
53 }
54
55 static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
56 {
57 /* the private data is stashed in the interp structure */
58 command_t *c;
59 command_context_t *context;
60 int *retval;
61 int i;
62 int nwords;
63 char **words;
64
65 target_call_timer_callbacks_now();
66 LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
67
68 c = interp->cmdPrivData;
69 LOG_DEBUG("script_command - %s", c->name);
70
71 nwords = argc;
72 words = malloc(sizeof(char *) * nwords);
73 for (i = 0; i < nwords; i++)
74 {
75 int len;
76
77 words[i] = strdup(Jim_GetString(argv[i], &len));
78 if (words[i] == NULL)
79 {
80 return JIM_ERR;
81 }
82 LOG_DEBUG("script_command - %s, argv[%u]=%s", c->name, i, words[i]);
83 }
84
85 /* grab the command context from the associated data */
86 context = Jim_GetAssocData(interp, "context");
87 retval = Jim_GetAssocData(interp, "retval");
88 if (context != NULL && retval != NULL)
89 {
90 /* capture log output and return it */
91 Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
92 log_add_callback(tcl_output, tclOutput);
93
94 *retval = run_command(context, c, words, nwords);
95
96 log_remove_callback(tcl_output, tclOutput);
97
98 /* We dump output into this local variable */
99 Jim_SetVariableStr(interp, "openocd_output", tclOutput);
100 }
101
102 for (i = 0; i < nwords; i++)
103 free(words[i]);
104 free(words);
105
106 return (*retval==ERROR_OK)?JIM_OK:JIM_ERR;
107 }
108
109 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)
110 {
111 command_t *c, *p;
112
113 if (!context || !name)
114 return NULL;
115
116 c = malloc(sizeof(command_t));
117
118 c->name = strdup(name);
119 c->parent = parent;
120 c->children = NULL;
121 c->handler = handler;
122 c->mode = mode;
123 if (!help)
124 help="";
125 c->next = NULL;
126
127 /* place command in tree */
128 if (parent)
129 {
130 if (parent->children)
131 {
132 /* find last child */
133 for (p = parent->children; p && p->next; p = p->next);
134 if (p)
135 p->next = c;
136 }
137 else
138 {
139 parent->children = c;
140 }
141 }
142 else
143 {
144 if (context->commands)
145 {
146 /* find last command */
147 for (p = context->commands; p && p->next; p = p->next);
148 if (p)
149 p->next = c;
150 }
151 else
152 {
153 context->commands = c;
154 }
155 }
156
157 /* just a placeholder, no handler */
158 if (c->handler==NULL)
159 return c;
160
161 /* If this is a two level command, e.g. "flash banks", then the
162 * "unknown" proc in startup.tcl must redirect to this command.
163 *
164 * "flash banks" is translated by "unknown" to "flash_banks"
165 * if such a proc exists
166 */
167 /* Print help for command */
168 const char *t1="";
169 const char *t2="";
170 const char *t3="";
171 /* maximum of two levels :-) */
172 if (c->parent!=NULL)
173 {
174 t1=c->parent->name;
175 t2="_";
176 }
177 t3=c->name;
178 const char *full_name=alloc_printf("%s%s%s", t1, t2, t3);
179 Jim_CreateCommand(interp, full_name, script_command, c, NULL);
180 free((void *)full_name);
181
182
183 /* accumulate help text in Tcl helptext list. */
184 Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
185 if (Jim_IsShared(helptext))
186 helptext = Jim_DuplicateObj(interp, helptext);
187 Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
188
189 Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
190
191 /* maximum of two levels :-) */
192 if (c->parent!=NULL)
193 {
194 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
195 }
196 Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
197
198 Jim_ListAppendElement(interp, cmd_entry, cmd_list);
199 Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
200 Jim_ListAppendElement(interp, helptext, cmd_entry);
201 return c;
202 }
203
204 int unregister_all_commands(command_context_t *context)
205 {
206 command_t *c, *c2;
207
208 if (context == NULL)
209 return ERROR_OK;
210
211
212 while(NULL != context->commands)
213 {
214 c = context->commands;
215
216 while(NULL != c->children)
217 {
218 c2 = c->children;
219 c->children = c->children->next;
220 free(c2->name);
221 c2->name = NULL;
222 free(c2);
223 c2 = NULL;
224 }
225
226 context->commands = context->commands->next;
227
228 free(c->name);
229 c->name = NULL;
230 free(c);
231 c = NULL;
232 }
233
234 return ERROR_OK;
235 }
236
237 int unregister_command(command_context_t *context, char *name)
238 {
239 command_t *c, *p = NULL, *c2;
240
241 if ((!context) || (!name))
242 return ERROR_INVALID_ARGUMENTS;
243
244 /* find command */
245 for (c = context->commands; c; c = c->next)
246 {
247 if (strcmp(name, c->name) == 0)
248 {
249 /* unlink command */
250 if (p)
251 {
252 p->next = c->next;
253 }
254 else
255 {
256 context->commands = c->next;
257 }
258
259 /* unregister children */
260 if (c->children)
261 {
262 for (c2 = c->children; c2; c2 = c2->next)
263 {
264 free(c2->name);
265 free(c2);
266 }
267 }
268
269 /* delete command */
270 free(c->name);
271 free(c);
272 }
273
274 /* remember the last command for unlinking */
275 p = c;
276 }
277
278 return ERROR_OK;
279 }
280
281
282 void command_output_text(command_context_t *context, const char *data)
283 {
284 if( context && context->output_handler && data ){
285 context->output_handler( context, data );
286 }
287 }
288
289 void command_print_n(command_context_t *context, char *format, ...)
290 {
291 char *string;
292
293 va_list ap;
294 va_start(ap, format);
295
296 string = alloc_vprintf(format, ap);
297 if (string != NULL)
298 {
299 /* we want this collected in the log + we also want to pick it up as a tcl return
300 * value.
301 *
302 * The latter bit isn't precisely neat, but will do for now.
303 */
304 LOG_USER_N("%s", string);
305 // We already printed it above
306 //command_output_text(context, string);
307 free(string);
308 }
309
310 va_end(ap);
311 }
312
313 void command_print(command_context_t *context, char *format, ...)
314 {
315 char *string;
316
317 va_list ap;
318 va_start(ap, format);
319
320 string = alloc_vprintf(format, ap);
321 if (string != NULL)
322 {
323 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
324 /* we want this collected in the log + we also want to pick it up as a tcl return
325 * value.
326 *
327 * The latter bit isn't precisely neat, but will do for now.
328 */
329 LOG_USER_N("%s", string);
330 // We already printed it above
331 //command_output_text(context, string);
332 free(string);
333 }
334
335 va_end(ap);
336 }
337
338 int run_command(command_context_t *context, command_t *c, char *words[], int num_words)
339 {
340 int start_word=0;
341 if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) ))
342 {
343 /* Config commands can not run after the config stage */
344 return ERROR_FAIL;
345 }
346
347 int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
348 if (retval == ERROR_COMMAND_SYNTAX_ERROR)
349 {
350 /* Print help for command */
351 const char *t1="";
352 const char *t2="";
353 const char *t3="";
354 /* maximum of two levels :-) */
355 if (c->parent!=NULL)
356 {
357 t1=c->parent->name;
358 t2=" ";
359 }
360 t3=c->name;
361 command_run_linef(context, "help {%s%s%s}", t1, t2, t3);
362 }
363 else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
364 {
365 /* just fall through for a shutdown request */
366 }
367 else if (retval != ERROR_OK)
368 {
369 /* we do not print out an error message because the command *should*
370 * have printed out an error
371 */
372 LOG_DEBUG("Command failed with error code %d", retval);
373 }
374
375 return retval;
376 }
377
378 int command_run_line(command_context_t *context, char *line)
379 {
380 /* all the parent commands have been registered with the interpreter
381 * so, can just evaluate the line as a script and check for
382 * results
383 */
384 /* run the line thru a script engine */
385 int retval;
386 int retcode;
387 Jim_DeleteAssocData(interp, "context"); /* remove existing */
388 retcode = Jim_SetAssocData(interp, "context", NULL, context);
389 if (retcode != JIM_OK)
390 return ERROR_FAIL;
391
392 /* associated the return value */
393 retval = ERROR_OK;
394 Jim_DeleteAssocData(interp, "retval"); /* remove existing */
395 retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
396 if (retcode != JIM_OK)
397 return ERROR_FAIL;
398
399 active_cmd_ctx = context;
400 retcode = Jim_Eval(interp, line);
401 if (retcode == JIM_ERR) {
402 if (retval!=ERROR_COMMAND_CLOSE_CONNECTION)
403 {
404 /* We do not print the connection closed error message */
405 Jim_PrintErrorMessage(interp);
406 }
407 if (retval==ERROR_OK)
408 {
409 /* It wasn't a low level OpenOCD command that failed */
410 return ERROR_FAIL;
411 }
412 return retval;
413 } else if (retcode == JIM_EXIT) {
414 /* ignore. */
415 /* exit(Jim_GetExitCode(interp)); */
416 } else {
417 const char *result;
418 int reslen;
419
420 result = Jim_GetString(Jim_GetResult(interp), &reslen);
421 if (reslen) {
422 int i;
423 char buff[256+1];
424 for (i = 0; i < reslen; i += 256)
425 {
426 int chunk;
427 chunk = reslen - i;
428 if (chunk > 256)
429 chunk = 256;
430 strncpy(buff, result+i, chunk);
431 buff[chunk] = 0;
432 LOG_USER_N("%s", buff);
433 }
434 LOG_USER_N("%s", "\n");
435 }
436 }
437 return retval;
438 }
439
440
441 int command_run_linef(command_context_t *context, char *format, ...)
442 {
443 int retval=ERROR_FAIL;
444 char *string;
445 va_list ap;
446 va_start(ap, format);
447 string = alloc_vprintf(format, ap);
448 if (string!=NULL)
449 {
450 retval=command_run_line(context, string);
451 }
452 va_end(ap);
453 return retval;
454 }
455
456
457
458 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, const char* line), void *priv)
459 {
460 context->output_handler = output_handler;
461 context->output_handler_priv = priv;
462 }
463
464 command_context_t* copy_command_context(command_context_t* context)
465 {
466 command_context_t* copy_context = malloc(sizeof(command_context_t));
467
468 *copy_context = *context;
469
470 return copy_context;
471 }
472
473 int command_done(command_context_t *context)
474 {
475 free(context);
476 context = NULL;
477
478 return ERROR_OK;
479 }
480
481 command_context_t* command_init()
482 {
483 command_context_t* context = malloc(sizeof(command_context_t));
484
485 context->mode = COMMAND_EXEC;
486 context->commands = NULL;
487 context->current_target = 0;
488 context->output_handler = NULL;
489 context->output_handler_priv = NULL;
490
491 register_command(context, NULL, "sleep", handle_sleep_command,
492 COMMAND_ANY, "sleep for <n> milliseconds");
493
494 register_command(context, NULL, "fast", handle_fast_command,
495 COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
496
497 return context;
498 }
499
500 int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
501 {
502 if (!cmd_ctx)
503 return ERROR_INVALID_ARGUMENTS;
504
505 cmd_ctx->mode = mode;
506 return ERROR_OK;
507 }
508
509 /* sleep command sleeps for <n> miliseconds
510 * this is useful in target startup scripts
511 */
512 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
513 {
514 unsigned long duration = 0;
515
516 if (argc == 1)
517 {
518 duration = strtoul(args[0], NULL, 0);
519 usleep(duration * 1000);
520 }
521
522 return ERROR_OK;
523 }
524
525 int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
526 {
527 if (argc!=1)
528 return ERROR_COMMAND_SYNTAX_ERROR;
529
530 fast_and_dangerous = strcmp("enable", args[0])==0;
531
532 return ERROR_OK;
533 }

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)