"flash banks" is now implemented in Tcl on top of "flash_banks". openocd_throw prefix...
[fw/openocd] / src / openocd.c
1 /***************************************************************************
2  *   Copyright (C) 2005 by Dominic Rath                                    *
3  *   Dominic.Rath@gmx.de                                                   *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #define OPENOCD_VERSION "Open On-Chip Debugger " VERSION " (" PKGBLDDATE ") svn:" PKGBLDREV
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "log.h"
28 #include "types.h"
29 #include "jtag.h"
30 #include "configuration.h"
31 #include "interpreter.h"
32 #include "xsvf.h"
33 #include "target.h"
34 #include "flash.h"
35 #include "nand.h"
36 #include "pld.h"
37
38 #include "command.h"
39 #include "server.h"
40 #include "telnet_server.h"
41 #include "gdb_server.h"
42 #include "tcl_server.h"
43
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <strings.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <errno.h>
52
53 #ifdef _WIN32
54 #include <malloc.h>
55 #else
56 #include <alloca.h>
57 #endif
58
59 #ifdef __ECOS
60 /* Jim is provied by eCos */
61 #include <cyg/jimtcl/jim.h>
62 #else
63 #define JIM_EMBEDDED
64 #include "jim.h"
65 #endif
66
67 #include "replacements.h"
68
69 int launchTarget(struct command_context_s *cmd_ctx)
70 {
71         int retval;
72         /* Try to examine & validate jtag chain, though this may require a reset first
73          * in which case we continue setup */
74         jtag_init(cmd_ctx);
75
76         /* try to examine target at this point. If it fails, perhaps a reset will
77          * bring it up later on via a telnet/gdb session */
78         target_examine(cmd_ctx);
79
80         retval=flash_init_drivers(cmd_ctx);
81         if (retval!=ERROR_OK)
82                 return retval;
83         LOG_DEBUG("flash init complete");
84
85         retval=nand_init(cmd_ctx);
86         if (retval!=ERROR_OK)
87                 return retval;
88         LOG_DEBUG("NAND init complete");
89
90         retval=pld_init(cmd_ctx);
91         if (retval!=ERROR_OK)
92                 return retval;
93         LOG_DEBUG("pld init complete");
94         return retval;
95 }
96
97 /* Give TELNET a way to find out what version this is */
98 int handle_version_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
99 {
100         command_print(cmd_ctx, OPENOCD_VERSION);
101
102         return ERROR_OK;
103 }
104
105 static int daemon_startup = 0;
106
107 int handle_daemon_startup_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
108 {
109         if (argc==0)
110                 return ERROR_OK;
111         if (argc > 1 )
112                 return ERROR_COMMAND_SYNTAX_ERROR;
113         
114         daemon_startup = strcmp("reset", args[0])==0;
115         
116         command_print(cmd_ctx, OPENOCD_VERSION);
117
118         return ERROR_OK;
119 }
120
121 void exit_handler(void)
122 {
123         /* close JTAG interface */
124         if (jtag && jtag->quit)
125                 jtag->quit();
126 }
127
128 /* OpenOCD can't really handle failure of this command. Patches welcome! :-) */
129 int handle_init_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
130 {
131         int retval;
132         static int initialized=0;
133         if (initialized)
134                 return ERROR_OK;
135         
136         initialized=1;
137         
138         command_set_output_handler(cmd_ctx, configuration_output_handler, NULL);
139
140         atexit(exit_handler);
141         
142         if (target_init(cmd_ctx) != ERROR_OK)
143                 return ERROR_FAIL;
144         LOG_DEBUG("target init complete");
145
146         if ((retval=jtag_interface_init(cmd_ctx)) != ERROR_OK)
147         {
148                 /* we must be able to set up the jtag interface */
149                 return retval;
150         }
151         LOG_DEBUG("jtag interface init complete");
152
153         /* Try to initialize & examine the JTAG chain at this point, but
154          * continue startup regardless */
155         if (jtag_init(cmd_ctx) == ERROR_OK)
156         {
157                 LOG_DEBUG("jtag init complete");
158                 if (target_examine(cmd_ctx) == ERROR_OK)
159                 {
160                         LOG_DEBUG("jtag examine complete");
161                 }
162         }
163         
164         if (flash_init_drivers(cmd_ctx) != ERROR_OK)
165                 return ERROR_FAIL;
166         LOG_DEBUG("flash init complete");
167
168         if (nand_init(cmd_ctx) != ERROR_OK)
169                 return ERROR_FAIL;
170         LOG_DEBUG("NAND init complete");
171
172         if (pld_init(cmd_ctx) != ERROR_OK)
173                 return ERROR_FAIL;
174         LOG_DEBUG("pld init complete");
175
176         /* initialize tcp server */
177         server_init();
178
179         /* initialize telnet subsystem */
180         telnet_init("Open On-Chip Debugger");
181         gdb_init();
182         tcl_init(); /* allows tcl to just connect without going thru telnet */
183
184         return ERROR_OK;
185 }
186
187 Jim_Interp *interp;
188 command_context_t *active_cmd_ctx;
189
190 static int new_int_array_element(Jim_Interp * interp, const char *varname, int idx, u32 val)
191 {
192         char *namebuf;
193         Jim_Obj *nameObjPtr, *valObjPtr;
194         int result;
195
196         namebuf = alloc_printf("%s(%d)", varname, idx);
197         
198         nameObjPtr = Jim_NewStringObj(interp, namebuf, -1);
199         valObjPtr = Jim_NewIntObj(interp, val);
200         Jim_IncrRefCount(nameObjPtr);
201         Jim_IncrRefCount(valObjPtr);
202         result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
203         Jim_DecrRefCount(interp, nameObjPtr);
204         Jim_DecrRefCount(interp, valObjPtr);
205         free(namebuf);
206         /* printf( "%s = 0%08x\n", namebuf, val ); */
207         return result;
208 }
209
210 static int Jim_Command_mem2array(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
211 {
212         target_t *target;
213         long l;
214         u32 width;
215         u32 len;
216         u32 addr;
217         u32 count;
218         u32 v;
219         const char *varname;
220         u8 buffer[4096];
221         int  i, n, e, retval;
222
223         /* argv[1] = name of array to receive the data
224          * argv[2] = desired width
225          * argv[3] = memory address 
226          * argv[4] = length in bytes to read
227          */
228         if (argc != 5) {
229                 Jim_WrongNumArgs(interp, 1, argv, "varname width addr nelems");
230                 return JIM_ERR;
231         }
232         varname = Jim_GetString(argv[1], &len);
233         /* given "foo" get space for worse case "foo(%d)" .. add 20 */
234
235         e = Jim_GetLong(interp, argv[2], &l);
236         width = l;
237         if (e != JIM_OK) {
238                 return e;
239         }
240         
241         e = Jim_GetLong(interp, argv[3], &l);
242         addr = l;
243         if (e != JIM_OK) {
244                 return e;
245         }
246         e = Jim_GetLong(interp, argv[4], &l);
247         len = l;
248         if (e != JIM_OK) {
249                 return e;
250         }
251         switch (width) {
252                 case 8:
253                         width = 1;
254                         break;
255                 case 16:
256                         width = 2;
257                         break;
258                 case 32:
259                         width = 4;
260                         break;
261                 default:
262                         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
263                         Jim_AppendStrings( interp, Jim_GetResult(interp), "Invalid width param, must be 8/16/32", NULL );
264                         return JIM_ERR;
265         }
266         if (len == 0) {
267                 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
268                 Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: zero width read?", NULL);
269                 return JIM_ERR;
270         }
271         if ((addr + (len * width)) < addr) {
272                 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
273                 Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: addr + len - wraps to zero?", NULL);
274                 return JIM_ERR;
275         }
276         /* absurd transfer size? */
277         if (len > 65536) {
278                 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
279                 Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: absurd > 64K item request", NULL);
280                 return JIM_ERR;
281         }               
282                 
283         if ((width == 1) ||
284                 ((width == 2) && ((addr & 1) == 0)) ||
285                 ((width == 4) && ((addr & 3) == 0))) {
286                 /* all is well */
287         } else {
288                 char buf[100];
289                 Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
290                 sprintf(buf, "mem2array address: 0x%08x is not aligned for %d byte reads", addr, width); 
291                 Jim_AppendStrings(interp, Jim_GetResult(interp), buf , NULL);
292                 return JIM_ERR;
293         }
294
295         target = get_current_target(active_cmd_ctx);
296         
297         /* Transfer loop */
298
299         /* index counter */
300         n = 0;
301         /* assume ok */
302         e = JIM_OK;
303         while (len) {
304                 /* Slurp... in buffer size chunks */
305                 
306                 count = len; /* in objects.. */
307                 if (count > (sizeof(buffer)/width)) {
308                         count = (sizeof(buffer)/width);
309                 }
310                 
311                 retval = target->type->read_memory( target, addr, width, count, buffer );
312
313                 if (retval != ERROR_OK) {
314                         /* BOO !*/
315                         LOG_ERROR("mem2array: Read @ 0x%08x, w=%d, cnt=%d, failed", addr, width, count);
316                         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
317                         Jim_AppendStrings(interp, Jim_GetResult(interp), "mem2array: cannot read memory", NULL);
318                         e = JIM_ERR;
319                         len = 0;
320                 } else {
321                         v = 0; /* shut up gcc */
322                         for (i = 0 ;i < count ;i++, n++) {
323                                 switch (width) {
324                                         case 4:
325                                                 v = target_buffer_get_u32(target, &buffer[i*width]);
326                                                 break;
327                                         case 2:
328                                                 v = target_buffer_get_u16(target, &buffer[i*width]);
329                                                 break;
330                                         case 1:
331                                                 v = buffer[i] & 0x0ff;
332                                                 break;
333                                 }
334                                 new_int_array_element(interp, varname, n, v);
335                         }
336                         len -= count;
337                 }
338         }
339         
340         Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
341
342         return JIM_OK;
343 }
344
345 static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
346 {               
347         Jim_Obj *tclOutput=(Jim_Obj *)privData;
348
349         Jim_AppendString(interp, tclOutput, string, strlen(string));
350 }
351
352 /* try to execute as Jim command, otherwise fall back to standard command.
353  * Note that even if the Jim command caused an error, then we succeeded
354  * to execute it, hence this fn pretty much always returns ERROR_OK. */
355 int jim_command(command_context_t *context, char *line)
356 {
357         int retval=ERROR_OK;
358         int retcode=Jim_Eval(interp, line);
359         
360         const char *result;
361         int reslen;
362         result = Jim_GetString(Jim_GetResult(interp), &reslen);
363         if (retcode == JIM_ERR) {
364                 int len, i;
365                 
366                 LOG_USER_N("Runtime error, file \"%s\", line %d:" JIM_NL, interp->errorFileName, interp->errorLine);
367                 LOG_USER_N("    %s" JIM_NL,
368                 Jim_GetString(interp->result, NULL));
369                 Jim_ListLength(interp, interp->stackTrace, &len);
370                 for (i = 0; i < len; i += 3) {
371                         Jim_Obj *objPtr;
372                         const char *proc, *file, *line;
373                         
374                         Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
375                         proc = Jim_GetString(objPtr, NULL);
376                         Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr, JIM_NONE);
377                         file = Jim_GetString(objPtr, NULL);
378                         Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr, JIM_NONE);
379                         line = Jim_GetString(objPtr, NULL);
380                         LOG_USER_N("In procedure '%s' called at file \"%s\", line %s" JIM_NL, proc, file, line);
381             }
382             long t;
383             if (Jim_GetLong(interp, Jim_GetVariableStr(interp, "openocd_result", JIM_ERRMSG), &t)==JIM_OK)
384             {
385                 return t;
386             }
387             return ERROR_FAIL;
388         } else if (retcode == JIM_EXIT) {
389                 /* ignore. */
390         /* exit(Jim_GetExitCode(interp)); */
391         } else {
392                 if (reslen) {
393                         int i;
394                         char buff[256+1];
395                         for (i = 0; i < reslen; i += 256)
396                         {
397                                 int chunk;
398                                 chunk = reslen - i;
399                                 if (chunk > 256)
400                                         chunk = 256;
401                         strncpy(buff, result+i, chunk);
402                                 buff[chunk] = 0; 
403                                 LOG_USER_N("%s", buff);
404                         }
405                         LOG_USER_N("%s", "\n");
406                 }
407         }
408         return retval;
409 }
410
411 int startLoop = 0;
412
413 static int Jim_Command_openocd_ignore(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int ignore)
414 {
415         int retval;
416         char *cmd = (char*)Jim_GetString(argv[1], NULL);
417         
418         Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
419         
420         if (startLoop)
421         {
422                 /* We don't know whether or not the telnet/gdb server is running... */
423                 target_call_timer_callbacks_now();
424         }
425         
426         log_add_callback(tcl_output, tclOutput);
427         retval=command_run_line_internal(active_cmd_ctx, cmd);
428
429         /* we need to be able to get at the retval, so we store in a variable
430          */
431         Jim_Obj *resultvar=Jim_NewIntObj(interp, retval);
432         Jim_IncrRefCount(resultvar);
433         Jim_SetGlobalVariableStr(interp, "openocd_result", resultvar);
434         Jim_DecrRefCount(interp, resultvar);
435         
436         if (startLoop)
437         {
438                 target_call_timer_callbacks_now();
439         }
440         log_remove_callback(tcl_output, tclOutput);
441         
442         Jim_SetResult(interp, tclOutput);
443         
444         return (ignore||(retval==ERROR_OK))?JIM_OK:JIM_ERR;
445 }
446
447 static int Jim_Command_openocd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
448 {
449         return Jim_Command_openocd_ignore(interp, argc, argv, 1); 
450 }
451
452 static int Jim_Command_openocd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
453 {
454         return Jim_Command_openocd_ignore(interp, argc, argv, 0); 
455 }
456
457 /* find full path to file */
458 static int Jim_Command_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
459 {
460         if (argc != 2)
461                 return JIM_ERR;
462         char *file = (char*)Jim_GetString(argv[1], NULL);
463         char *full_path = find_file(file);
464         if (full_path == NULL)
465                 return JIM_ERR;
466         Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
467         free(full_path);
468         
469         Jim_SetResult(interp, result);
470         return JIM_OK;
471 }
472
473 static int Jim_Command_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
474 {
475         if (argc != 2)
476                 return JIM_ERR;
477         char *str = (char*)Jim_GetString(argv[1], NULL);
478         LOG_USER("%s", str);
479         return JIM_OK;
480 }
481
482 static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
483 {
484         size_t nbytes;
485         const char *ptr;
486
487         /* make it a char easier to read code */
488         ptr = _ptr;
489
490         nbytes = size * n;
491         if (nbytes == 0) {
492                 return 0;
493         }
494
495         if (!active_cmd_ctx) {
496                 /* TODO: Where should this go? */               
497                 return n;
498         }
499
500         /* do we have to chunk it? */
501         if (ptr[nbytes] == 0) {
502                 /* no it is a C style string */
503                 command_output_text(active_cmd_ctx, ptr);
504                 return strlen(ptr);
505         }
506         /* GRR we must chunk - not null terminated */
507         while (nbytes) {
508                 char chunk[128+1];
509                 int x;
510
511                 x = nbytes;
512                 if (x > 128) {
513                         x = 128;
514                 }
515                 /* copy it */
516                 memcpy(chunk, ptr, x);
517                 /* terminate it */
518                 chunk[n] = 0;
519                 /* output it */
520                 command_output_text(active_cmd_ctx, chunk);
521                 ptr += x;
522                 nbytes -= x;
523         }
524         
525         return n;
526 }
527
528 static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie )
529 {
530         /* TCL wants to read... tell him no */
531         return 0;
532 }
533
534 static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
535 {
536         char *cp;
537         int n;
538         
539         n = -1;
540         if (active_cmd_ctx) {
541                 cp = alloc_vprintf(fmt, ap);
542                 if (cp) {
543                         command_output_text(active_cmd_ctx, cp);
544                         n = strlen(cp);
545                         free(cp);
546                 }
547         }
548         return n;
549 }
550
551 static int openocd_jim_fflush(void *cookie)
552 {
553         /* nothing to flush */
554         return 0;
555 }
556
557 static char* openocd_jim_fgets(char *s, int size, void *cookie)
558 {
559         /* not supported */
560         errno = ENOTSUP;
561         return NULL;
562 }
563
564 void initJim(void)
565 {
566         Jim_CreateCommand(interp, "openocd", Jim_Command_openocd, NULL, NULL);
567         Jim_CreateCommand(interp, "openocd_throw", Jim_Command_openocd_throw, NULL, NULL);
568         Jim_CreateCommand(interp, "find", Jim_Command_find, NULL, NULL);
569         Jim_CreateCommand(interp, "echo", Jim_Command_echo, NULL, NULL);
570         Jim_CreateCommand(interp, "mem2array", Jim_Command_mem2array, NULL, NULL );
571         
572         /* Set Jim's STDIO */
573         interp->cookie_stdin = NULL;
574         interp->cookie_stdout = NULL;
575         interp->cookie_stderr = NULL;
576         interp->cb_fwrite = openocd_jim_fwrite;
577         interp->cb_fread = openocd_jim_fread ;
578         interp->cb_vfprintf = openocd_jim_vfprintf;
579         interp->cb_fflush = openocd_jim_fflush;
580         interp->cb_fgets = openocd_jim_fgets;
581 }
582
583 /* after command line parsing */
584 void initJim2(void)
585 {
586         Jim_Eval(interp, "source [find tcl/commands.tcl]");
587 }
588
589 command_context_t *setup_command_handler(void)
590 {
591         command_context_t *cmd_ctx;
592         
593         cmd_ctx = command_init();
594
595         register_command(cmd_ctx, NULL, "version", handle_version_command,
596                                          COMMAND_EXEC, "show OpenOCD version");
597         register_command(cmd_ctx, NULL, "daemon_startup", handle_daemon_startup_command, COMMAND_CONFIG, 
598                         "deprecated - use \"init\" and \"reset\" at end of startup script instead");
599         
600         /* register subsystem commands */
601         server_register_commands(cmd_ctx);
602         telnet_register_commands(cmd_ctx);
603         gdb_register_commands(cmd_ctx);
604         tcl_register_commands(cmd_ctx); /* tcl server commands */
605         log_register_commands(cmd_ctx);
606         jtag_register_commands(cmd_ctx);
607         interpreter_register_commands(cmd_ctx);
608         xsvf_register_commands(cmd_ctx);
609         target_register_commands(cmd_ctx);
610         flash_register_commands(cmd_ctx);
611         nand_register_commands(cmd_ctx);
612         pld_register_commands(cmd_ctx);
613         
614         if (log_init(cmd_ctx) != ERROR_OK)
615         {
616                 exit(-1);
617         }
618         LOG_DEBUG("log init complete");
619
620         LOG_OUTPUT( OPENOCD_VERSION "\n" );
621         
622         
623         register_command(cmd_ctx, NULL, "init", handle_init_command,
624                                          COMMAND_ANY, "initializes target and servers - nop on subsequent invocations");
625
626         return cmd_ctx;
627 }
628
629 /* normally this is the main() function entry, but if OpenOCD is linked
630  * into application, then this fn will not be invoked, but rather that
631  * application will have it's own implementation of main(). */
632 int openocd_main(int argc, char *argv[])
633 {
634 #ifdef JIM_EMBEDDED
635         Jim_InitEmbedded();
636         /* Create an interpreter */
637         interp = Jim_CreateInterp();
638         /* Add all the Jim core commands */
639         Jim_RegisterCoreCommands(interp);
640 #endif
641         
642         initJim();
643         
644         /* initialize commandline interface */
645         command_context_t *cmd_ctx;
646         cmd_ctx=setup_command_handler();
647         
648         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
649         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
650         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
651         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
652         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
653         LOG_OUTPUT( "$URL$\n");
654         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
655         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
656         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
657         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
658         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
659
660         command_context_t *cfg_cmd_ctx;
661         cfg_cmd_ctx = copy_command_context(cmd_ctx);
662         cfg_cmd_ctx->mode = COMMAND_CONFIG;
663         command_set_output_handler(cfg_cmd_ctx, configuration_output_handler, NULL);
664         
665         active_cmd_ctx=cfg_cmd_ctx;
666         
667         if (parse_cmdline_args(cfg_cmd_ctx, argc, argv) != ERROR_OK)
668                 return EXIT_FAILURE;
669
670         initJim2();
671
672         if (parse_config_file(cfg_cmd_ctx) != ERROR_OK)
673                 return EXIT_FAILURE;
674         
675         active_cmd_ctx=cmd_ctx;
676         
677         command_done(cfg_cmd_ctx);
678
679         if (command_run_line(cmd_ctx, "init")!=ERROR_OK)
680                 return EXIT_FAILURE;
681         
682         if (daemon_startup)
683                 command_run_line(cmd_ctx, "reset");
684         
685         startLoop=1;
686
687         /* handle network connections */
688         server_loop(cmd_ctx);
689
690         /* shut server down */
691         server_quit();
692
693         unregister_all_commands(cmd_ctx);
694         
695         /* free commandline interface */
696         command_done(cmd_ctx);
697
698         return EXIT_SUCCESS;
699 }