]> git.sur5r.net Git - openocd/blob - src/helper/command.c
f13a86c15da1e2f5928f1f1a589642c981612ff0
[openocd] / src / helper / command.c
1 /***************************************************************************\r
2  *   Copyright (C) 2005 by Dominic Rath                                    *\r
3  *   Dominic.Rath@gmx.de                                                   *\r
4  *                                                                         *\r
5  *   part of this file is taken from libcli (libcli.sourceforge.net)       *\r
6  *   Copyright (C) David Parrish (david@dparrish.com)                      *\r
7  *                                                                         *\r
8  *   This program is free software; you can redistribute it and/or modify  *\r
9  *   it under the terms of the GNU General Public License as published by  *\r
10  *   the Free Software Foundation; either version 2 of the License, or     *\r
11  *   (at your option) any later version.                                   *\r
12  *                                                                         *\r
13  *   This program is distributed in the hope that it will be useful,       *\r
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *\r
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *\r
16  *   GNU General Public License for more details.                          *\r
17  *                                                                         *\r
18  *   You should have received a copy of the GNU General Public License     *\r
19  *   along with this program; if not, write to the                         *\r
20  *   Free Software Foundation, Inc.,                                       *\r
21  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *\r
22  ***************************************************************************/\r
23 #ifdef HAVE_CONFIG_H\r
24 #include "config.h"\r
25 #endif\r
26 \r
27 #include "replacements.h"\r
28 \r
29 #include "command.h"\r
30 \r
31 #include "log.h"\r
32 #include "time_support.h"\r
33 \r
34 #include <stdlib.h>\r
35 #include <string.h>\r
36 #include <ctype.h>\r
37 #include <stdarg.h>\r
38 #include <stdio.h>\r
39 #include <unistd.h>\r
40 \r
41 void command_print_help_line(command_context_t* context, struct command_s *command, int indent);\r
42 \r
43 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
44 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
45 \r
46 int build_unique_lengths(command_context_t *context, command_t *commands)\r
47 {\r
48         command_t *c, *p;\r
49 \r
50         /* iterate through all commands */\r
51         for (c = commands; c; c = c->next)\r
52         {\r
53                 /* find out how many characters are required to uniquely identify a command */\r
54                 for (c->unique_len = 1; c->unique_len <= strlen(c->name); c->unique_len++)\r
55                 {\r
56                         int foundmatch = 0;\r
57                         \r
58                         /* for every command, see if the current length is enough */\r
59                         for (p = commands; p; p = p->next)\r
60                         {\r
61                                 /* ignore the command itself */\r
62                                 if (c == p)\r
63                                         continue;\r
64                                 \r
65                                 /* compare commands up to the current length */\r
66                                 if (strncmp(p->name, c->name, c->unique_len) == 0)\r
67                                         foundmatch++;\r
68                         }\r
69                         \r
70                         /* when none of the commands matched, we've found the minimum length required */\r
71                         if (!foundmatch)\r
72                                 break;\r
73                 }\r
74                 \r
75                 /* if the current command has children, build the unique lengths for them */\r
76                 if (c->children)\r
77                         build_unique_lengths(context, c->children);\r
78         }\r
79         \r
80         return ERROR_OK;\r
81 }\r
82 \r
83 /* Avoid evaluating this each time we add a command. Reduces overhead from O(n^2) to O(n). \r
84  * Makes a difference on ARM7 types machines and is not observable on GHz machines.\r
85  */\r
86 static int unique_length_dirty = 1; \r
87 \r
88 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)\r
89 {\r
90         command_t *c, *p;\r
91         unique_length_dirty = 1;\r
92         \r
93         if (!context || !name)\r
94                 return NULL;\r
95                                 \r
96         c = malloc(sizeof(command_t));\r
97         \r
98         c->name = strdup(name);\r
99         c->parent = parent;\r
100         c->children = NULL;\r
101         c->handler = handler;\r
102         c->mode = mode;\r
103         if (help)\r
104                 c->help = strdup(help);\r
105         else\r
106                 c->help = NULL;\r
107         c->unique_len = 0;\r
108         c->next = NULL;\r
109         \r
110         /* place command in tree */\r
111         if (parent)\r
112         {\r
113                 if (parent->children)\r
114                 {\r
115                         /* find last child */\r
116                         for (p = parent->children; p && p->next; p = p->next);\r
117                         if (p)\r
118                                 p->next = c;\r
119                 }\r
120                 else\r
121                 {\r
122                         parent->children = c;\r
123                 }\r
124         }\r
125         else\r
126         {\r
127                 if (context->commands)\r
128                 {\r
129                         /* find last command */\r
130                         for (p = context->commands; p && p->next; p = p->next);\r
131                         if (p)\r
132                                 p->next = c;\r
133                 }\r
134                 else\r
135                 {\r
136                         context->commands = c;\r
137                 }\r
138         }\r
139         \r
140         return c;\r
141 }\r
142 \r
143 int unregister_command(command_context_t *context, char *name)\r
144 {\r
145         unique_length_dirty = 1;\r
146         \r
147         command_t *c, *p = NULL, *c2;\r
148         \r
149         if ((!context) || (!name))\r
150                 return ERROR_INVALID_ARGUMENTS;\r
151         \r
152         /* find command */\r
153         for (c = context->commands; c; c = c->next)\r
154         {\r
155                 if (strcmp(name, c->name) == 0)\r
156                 {\r
157                         /* unlink command */\r
158                         if (p)\r
159                         {\r
160                                 p->next = c->next;\r
161                         }\r
162                         else\r
163                         {\r
164                                 context->commands = c->next;\r
165                         }\r
166                         \r
167                         /* unregister children */\r
168                         if (c->children)\r
169                         {\r
170                                 for (c2 = c->children; c2; c2 = c2->next)\r
171                                 {\r
172                                         free(c2->name);\r
173                                         if (c2->help)\r
174                                                 free(c2->help);\r
175                                         free(c2);\r
176                                 }\r
177                         }\r
178                         \r
179                         /* delete command */\r
180                         free(c->name);\r
181                         if (c->help)\r
182                                 free(c->help);\r
183                         free(c);\r
184                 }\r
185                 \r
186                 /* remember the last command for unlinking */\r
187                 p = c;\r
188         }\r
189         \r
190         return ERROR_OK;\r
191 }\r
192 \r
193 int parse_line(char *line, char *words[], int max_words)\r
194 {\r
195         int nwords = 0;\r
196         char *p = line;\r
197         char *word_start = line;\r
198         int inquote = 0;\r
199 \r
200         while (nwords < max_words - 1)\r
201         {\r
202                 /* check if we reached\r
203                  * a terminating NUL\r
204                  * a matching closing quote character " or '\r
205                  * we're inside a word but not a quote, and the current character is whitespace\r
206                  */\r
207                 if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))\r
208                 {\r
209                         /* we're inside a word or quote, and reached its end*/\r
210                         if (word_start)\r
211                         {\r
212                                 int len;\r
213                                 char *word_end=p;\r
214                                 \r
215                                 /* This will handle extra whitespace within quotes */\r
216                                 while (isspace(*word_start)&&(word_start<word_end))\r
217                                         word_start++;\r
218                                 while (isspace(*(word_end-1))&&(word_start<word_end))\r
219                                         word_end--;\r
220                                 len = word_end - word_start;\r
221                                 \r
222                                 if (len>0)\r
223                                 {\r
224                                         /* copy the word */\r
225                                         memcpy(words[nwords] = malloc(len + 1), word_start, len);\r
226                                         /* add terminating NUL */\r
227                                         words[nwords++][len] = 0;\r
228                                 }\r
229                         }\r
230                         /* we're done parsing the line */\r
231                         if (!*p)\r
232                                 break;\r
233 \r
234                         /* skip over trailing quote or whitespace*/\r
235                         if (inquote || isspace(*p))\r
236                                 p++;\r
237                         while (isspace(*p))\r
238                                 p++;\r
239                         \r
240                         inquote = 0;\r
241                         word_start = 0;\r
242                 }\r
243                 else if (*p == '"' || *p == '\'')\r
244                 {\r
245                         /* we've reached the beginning of a quote */\r
246                         inquote = *p++;\r
247                         word_start = p;\r
248                 }\r
249                 else\r
250                 {\r
251                         /* we've reached the beginning of a new word */\r
252                         if (!word_start)\r
253                                 word_start = p;\r
254                         \r
255                         /* normal character, skip */\r
256                         p++;\r
257                 }\r
258         }\r
259         \r
260         return nwords;\r
261 }\r
262 \r
263 void command_print(command_context_t *context, char *format, ...)\r
264 {\r
265         char *buffer = NULL;\r
266         int n, size = 0;\r
267         char *p;\r
268 \r
269         /* process format string */\r
270         for (;;)\r
271         {\r
272                 va_list ap;\r
273                 va_start(ap, format);\r
274                 if (!buffer || (n = vsnprintf(buffer, size, format, ap)) >= size)\r
275                 {\r
276                         /* increase buffer until it fits the whole string */\r
277                         if (!(p = realloc(buffer, size += 4096)))\r
278                         {\r
279                                 /* gotta free up */\r
280                                 if (buffer)\r
281                                         free(buffer);\r
282                                 va_end(ap);\r
283                                 return;\r
284                         }\r
285         \r
286                         buffer = p;\r
287                         \r
288                         va_end(ap);\r
289                         continue;\r
290                 }\r
291                 va_end(ap);\r
292                 break;\r
293         }\r
294         \r
295         /* vsnprintf failed */\r
296         if (n < 0)\r
297         {\r
298                 if (buffer)\r
299                         free(buffer);\r
300                 return;\r
301         }\r
302 \r
303         p = buffer;\r
304         \r
305         /* process lines in buffer */\r
306         do {\r
307                 char *next = strchr(p, '\n');\r
308                 \r
309                 if (next)\r
310                         *next++ = 0;\r
311 \r
312                 if (context->output_handler)\r
313                         context->output_handler(context, p);\r
314 \r
315                 p = next;\r
316         } while (p);\r
317         \r
318         if (buffer)\r
319                 free(buffer);\r
320 }\r
321 \r
322 int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word)\r
323 {\r
324         command_t *c;\r
325         \r
326         if (unique_length_dirty)\r
327         {\r
328                 unique_length_dirty = 0;\r
329                 /* update unique lengths */\r
330                 build_unique_lengths(context, context->commands);\r
331         }\r
332         \r
333         for (c = commands; c; c = c->next)\r
334         {\r
335                 if (strncasecmp(c->name, words[start_word], c->unique_len))\r
336                         continue;\r
337 \r
338                 if (strncasecmp(c->name, words[start_word], strlen(words[start_word])))\r
339                         continue;\r
340                 \r
341                 if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )\r
342                 {\r
343                         if (!c->children)\r
344                         {\r
345                                 if (!c->handler)\r
346                                 {\r
347                                         command_print(context, "No handler for command");\r
348                                         break;\r
349                                 }\r
350                                 else\r
351                                 {\r
352                                         int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);\r
353                                         if (retval == ERROR_COMMAND_SYNTAX_ERROR)\r
354                                         {\r
355                                                 command_print(context, "Syntax error:");\r
356                                                 command_print_help_line(context, c, 0);\r
357                                         }\r
358                                         return retval; \r
359                                 }\r
360                         }\r
361                         else\r
362                         {\r
363                                 if (start_word == num_words - 1)\r
364                                 {\r
365                                         command_print(context, "Incomplete command");\r
366                                         break;\r
367                                 }\r
368                                 return find_and_run_command(context, c->children, words, num_words, start_word + 1);\r
369                         }\r
370                 }\r
371         }\r
372         \r
373         command_print(context, "Command %s not found", words[start_word]);\r
374         return ERROR_OK;\r
375 }\r
376 \r
377 int command_run_line(command_context_t *context, char *line)\r
378 {\r
379         int nwords;\r
380         char *words[128] = {0};\r
381         int retval;\r
382         int i;\r
383         \r
384         if ((!context) || (!line))\r
385                 return ERROR_INVALID_ARGUMENTS;\r
386         \r
387         /* skip preceding whitespace */\r
388         while (isspace(*line))\r
389                 line++;\r
390         \r
391         /* empty line, ignore */\r
392         if (!*line)\r
393                 return ERROR_OK;\r
394         \r
395         /* ignore comments */\r
396         if (*line && (line[0] == '#'))\r
397                 return ERROR_OK;\r
398         \r
399         if (context->echo)\r
400         {\r
401                 command_print(context, "%s", line);\r
402         }\r
403 \r
404         nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));\r
405         \r
406         if (nwords > 0)\r
407                 retval = find_and_run_command(context, context->commands, words, nwords, 0);\r
408         else\r
409                 return ERROR_INVALID_ARGUMENTS;\r
410         \r
411         for (i = 0; i < nwords; i++)\r
412                 free(words[i]);\r
413         \r
414         return retval;\r
415 }\r
416 \r
417 int command_run_file(command_context_t *context, FILE *file, enum command_mode mode)\r
418 {\r
419         int retval = ERROR_OK;\r
420         int old_command_mode;\r
421         char *buffer=malloc(4096);\r
422         if (buffer==NULL)\r
423         {\r
424                 return ERROR_INVALID_ARGUMENTS;\r
425         }\r
426         \r
427         old_command_mode = context->mode;\r
428         context->mode = mode;\r
429         \r
430         while (fgets(buffer, 4096, file))\r
431         {\r
432                 char *p;\r
433                 char *cmd, *end;\r
434                 \r
435                 /* stop processing line after a comment (#, !) or a LF, CR were encountered */\r
436                 if ((p = strpbrk(buffer, "#!\r\n")))\r
437                         *p = 0;\r
438 \r
439                 /* skip over leading whitespace */\r
440                 cmd = buffer;\r
441                 while (isspace(*cmd))\r
442                         cmd++;\r
443 \r
444                 /* empty (all whitespace) line? */\r
445                 if (!*cmd)\r
446                         continue;\r
447                 \r
448                 /* search the end of the current line, ignore trailing whitespace */\r
449                 for (p = end = cmd; *p; p++)\r
450                         if (!isspace(*p))\r
451                                 end = p;\r
452                 \r
453                 /* terminate end */\r
454                 *++end = 0;\r
455                 if (strcasecmp(cmd, "quit") == 0)\r
456                         break;\r
457 \r
458                 /* run line */\r
459                 if ((retval = command_run_line(context, cmd)) == ERROR_COMMAND_CLOSE_CONNECTION)\r
460                         break;\r
461         }\r
462         \r
463         context->mode = old_command_mode;\r
464 \r
465         \r
466         free(buffer);\r
467         \r
468         return retval;\r
469 }\r
470 \r
471 void command_print_help_line(command_context_t* context, struct command_s *command, int indent)\r
472 {\r
473         command_t *c;\r
474         char indents[32] = {0};\r
475         char *help = "no help available";\r
476         char name_buf[64];\r
477         int i;\r
478         \r
479         for (i = 0; i < indent; i+=2)\r
480         {\r
481                 indents[i*2] = ' ';\r
482                 indents[i*2+1] = '-';\r
483         }\r
484         indents[i*2] = 0;\r
485         \r
486         if (command->help)\r
487                 help = command->help;\r
488                 \r
489         snprintf(name_buf, 64, command->name);\r
490         strncat(name_buf, indents, 64);\r
491         command_print(context, "%20s\t%s", name_buf, help);\r
492         \r
493         if (command->children)\r
494         {\r
495                 for (c = command->children; c; c = c->next)\r
496                 {\r
497                         command_print_help_line(context, c, indent + 1);\r
498                 }\r
499         }\r
500 }\r
501 \r
502 int command_print_help(command_context_t* context, char* name, char** args, int argc)\r
503 {\r
504         command_t *c;\r
505 \r
506         for (c = context->commands; c; c = c->next)\r
507         {\r
508                 if (argc == 1)\r
509                 {\r
510                          if (strncasecmp(c->name, args[0], c->unique_len))\r
511                                  continue;\r
512 \r
513                          if (strncasecmp(c->name, args[0], strlen(args[0])))\r
514                                  continue;\r
515                 } \r
516 \r
517                 command_print_help_line(context, c, 0);\r
518         }\r
519         \r
520         return ERROR_OK;\r
521 }\r
522 \r
523 void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv)\r
524 {\r
525         context->output_handler = output_handler;\r
526         context->output_handler_priv = priv;\r
527 }\r
528 \r
529 command_context_t* copy_command_context(command_context_t* context)\r
530 {\r
531         command_context_t* copy_context = malloc(sizeof(command_context_t));\r
532 \r
533         *copy_context = *context;\r
534         \r
535         return copy_context;\r
536 }\r
537 \r
538 int command_done(command_context_t *context)\r
539 {\r
540         free(context);\r
541         context = NULL;\r
542         \r
543         return ERROR_OK;\r
544 }\r
545 \r
546 command_context_t* command_init()\r
547 {\r
548         command_context_t* context = malloc(sizeof(command_context_t));\r
549         \r
550         context->mode = COMMAND_EXEC;\r
551         context->commands = NULL;\r
552         context->current_target = 0;\r
553         context->echo = 0;\r
554         context->output_handler = NULL;\r
555         context->output_handler_priv = NULL;\r
556         \r
557         register_command(context, NULL, "help", command_print_help,\r
558                                          COMMAND_EXEC, "display this help");\r
559         \r
560         register_command(context, NULL, "sleep", handle_sleep_command,\r
561                                          COMMAND_ANY, "sleep for <n> milliseconds");\r
562         \r
563         register_command(context, NULL, "time", handle_time_command,\r
564                                          COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");\r
565         \r
566         return context;\r
567 }\r
568 \r
569 /* sleep command sleeps for <n> miliseconds\r
570  * this is useful in target startup scripts\r
571  */\r
572 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
573 {\r
574         unsigned long duration = 0;\r
575         \r
576         if (argc == 1)\r
577         {\r
578                 duration = strtoul(args[0], NULL, 0);\r
579                 usleep(duration * 1000);\r
580         }\r
581 \r
582         return ERROR_OK;\r
583 }\r
584 \r
585 int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)\r
586 {\r
587         if (argc<1)\r
588                 return ERROR_COMMAND_SYNTAX_ERROR;\r
589         \r
590         duration_t duration;\r
591         char *duration_text;\r
592         int retval;\r
593         \r
594         duration_start_measure(&duration);\r
595         \r
596         retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc, 0);\r
597         \r
598         duration_stop_measure(&duration, &duration_text);\r
599         \r
600         float t=duration.duration.tv_sec;\r
601         t+=((float)duration.duration.tv_usec / 1000000.0);\r
602         command_print(cmd_ctx, "%s took %fs", args[0], t);\r
603         \r
604         free(duration_text);\r
605 \r
606         return retval;\r
607 }\r