]> git.sur5r.net Git - openocd/blob - src/openocd.c
added pre/postlude fn's for when OpenOCD is sleeping.
[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          */
156         if (jtag_init(cmd_ctx) == ERROR_OK)
157         {
158                 LOG_DEBUG("jtag init complete");
159                 if (target_examine(cmd_ctx) == ERROR_OK)
160                 {
161                         LOG_DEBUG("jtag examine complete");
162                 }
163         }
164         
165         if (flash_init_drivers(cmd_ctx) != ERROR_OK)
166                 return ERROR_FAIL;
167         LOG_DEBUG("flash init complete");
168
169         if (nand_init(cmd_ctx) != ERROR_OK)
170                 return ERROR_FAIL;
171         LOG_DEBUG("NAND init complete");
172
173         if (pld_init(cmd_ctx) != ERROR_OK)
174                 return ERROR_FAIL;
175         LOG_DEBUG("pld init complete");
176
177         /* initialize tcp server */
178         server_init();
179
180         /* initialize telnet subsystem */
181         telnet_init("Open On-Chip Debugger");
182         gdb_init();
183         tcl_init(); /* allows tcl to just connect without going thru telnet */
184
185         return ERROR_OK;
186 }
187
188
189 Jim_Interp *interp;
190 command_context_t *active_cmd_ctx;
191
192 static int new_int_array_element(Jim_Interp * interp, const char *varname, int idx, u32 val)
193 {
194         char *namebuf;
195         Jim_Obj *nameObjPtr, *valObjPtr;
196         int result;
197
198         namebuf = alloc_printf("%s(%d)", varname, idx );
199         
200     nameObjPtr = Jim_NewStringObj(interp, namebuf, -1);
201     valObjPtr = Jim_NewIntObj(interp, val );
202     Jim_IncrRefCount(nameObjPtr);
203     Jim_IncrRefCount(valObjPtr);
204     result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
205     Jim_DecrRefCount(interp, nameObjPtr);
206     Jim_DecrRefCount(interp, valObjPtr);
207     free(namebuf);
208         // printf( "%s = 0%08x\n", namebuf, val );
209     return result;
210 }
211
212 static int Jim_Command_mem2array(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
213 {
214         target_t *target;
215         long l;
216         u32 width;
217         u32 len;
218         u32 addr;
219         u32 count;
220         u32 v;
221         const char *varname;
222         u8 buffer[4096];
223         int  i,n,e,retval;
224
225         /* argv[1] = name of array to receive the data
226          * argv[2] = desired width
227          * argv[3] = memory address 
228          * argv[4] = length in bytes to read
229          */
230         if( argc != 5 ){
231                 Jim_WrongNumArgs( interp, 1, argv, "varname width addr nelems" );
232                 return JIM_ERR;
233         }
234         varname = Jim_GetString( argv[1], &len );
235         /* given "foo" get space for worse case "foo(%d)" .. add 20 */
236
237         e = Jim_GetLong( interp, argv[2], &l );
238         width = l;
239         if( e != JIM_OK ){
240                 return e;
241         }
242         
243         e = Jim_GetLong( interp, argv[3], &l );
244         addr = l;
245         if( e != JIM_OK ){
246                 return e;
247         }
248         e = Jim_GetLong( interp, argv[4], &l );
249         len = l;
250         if( e != JIM_OK ){
251                 return e;
252         }
253         switch(width){
254         case 8:
255                 width = 1;
256                 break;
257         case 16:
258                 width = 2;
259                 break;
260         case 32:
261                 width = 4;
262                 break;
263         default:
264                 Jim_SetResult(interp, 
265                                           Jim_NewEmptyStringObj(interp));
266                 Jim_AppendStrings( interp, Jim_GetResult(interp),
267                                                    "Invalid width param, must be 8/16/32", NULL );
268                 return JIM_ERR;
269         }
270         if( len == 0 ){
271                 Jim_SetResult(interp, 
272                                           Jim_NewEmptyStringObj(interp));
273                 Jim_AppendStrings( interp, Jim_GetResult(interp),
274                                                    "mem2array: zero width read?", NULL );
275                 return JIM_ERR;
276         }
277         if( (addr + (len * width)) < addr ){
278                 Jim_SetResult(interp, 
279                                           Jim_NewEmptyStringObj(interp));
280                 Jim_AppendStrings( interp, Jim_GetResult(interp),
281                                                    "mem2array: addr + len - wraps to zero?", NULL );
282                 return JIM_ERR;
283         }
284         /* absurd transfer size? */
285         if( len > 65536 ){
286                 Jim_SetResult(interp, 
287                                           Jim_NewEmptyStringObj(interp));
288                 Jim_AppendStrings( interp, Jim_GetResult(interp),
289                                                    "mem2array: absurd > 64K item request", NULL );
290                 return JIM_ERR;
291         }               
292                 
293         if( (width == 1) ||
294                 ((width == 2) && ((addr & 1) == 0)) ||
295                 ((width == 4) && ((addr & 3) == 0)) ){
296                 /* all is well */
297         } else {
298                 char buf[100];
299                 Jim_SetResult(interp, 
300                                           Jim_NewEmptyStringObj(interp));
301                 sprintf( buf, 
302                                  "mem2array address: 0x%08x is not aligned for %d byte reads",
303                                  addr, width );
304                                  
305                 Jim_AppendStrings( interp, Jim_GetResult(interp),
306                                                    buf , NULL );
307                 return JIM_ERR;
308         }
309
310         target = get_current_target( active_cmd_ctx );
311         
312         /* Transfer loop */
313
314         /* index counter */
315         n = 0;
316         /* assume ok */
317         e = JIM_OK;
318         while( len ){
319
320                 /* Slurp... in buffer size chunks */
321                 
322                 count = len; /* in objects.. */
323                 if( count > (sizeof(buffer)/width)){
324                         count = (sizeof(buffer)/width);
325                 }
326                 
327                 retval = target->type->read_memory( target, 
328                                                                                         addr, 
329                                                                                         width, 
330                                                                                         count,
331                                                                                         buffer );
332
333                 if( retval != ERROR_OK ){
334                         /* BOO !*/
335                         LOG_ERROR("mem2array: Read @ 0x%08x, w=%d, cnt=%d, failed",
336                                           addr, width, count );
337                         Jim_SetResult(interp, 
338                                                   Jim_NewEmptyStringObj(interp));
339                         Jim_AppendStrings( interp, Jim_GetResult(interp),
340                                                    "mem2array: cannot read memory", NULL );
341                         e = JIM_ERR;
342                         len = 0;
343                 } else {
344                         v = 0; /* shut up gcc */
345                         for( i = 0 ; i < count ; i++, n++ ){
346                                 switch(width){
347                                 case 4:
348                                         v = target_buffer_get_u32( target, &buffer[i*width] );
349                                         break;
350                                 case 2:
351                                         v = target_buffer_get_u16( target, &buffer[i*width] );
352                                         break;
353                                 case 1:
354                                         v = buffer[i] & 0x0ff;
355                                         break;
356                                 }
357                                 new_int_array_element( interp, varname, n, v );
358                         }
359                         len -= count;
360                 }
361         }
362         Jim_SetResult(interp, 
363                                   Jim_NewEmptyStringObj(interp));
364
365         return JIM_OK;
366 }
367
368 static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
369 {               
370         Jim_Obj *tclOutput=(Jim_Obj *)privData;
371
372         Jim_AppendString(interp, tclOutput, string, strlen(string));
373 }
374
375 /* try to execute as Jim command, otherwise fall back to standard command.
376
377         Note that even if the Jim command caused an error, then we succeeded
378         to execute it, hence this fn pretty much always returns ERROR_OK. 
379
380  */
381 int jim_command(command_context_t *context, char *line)
382 {
383         int retval=ERROR_OK;
384         /* FIX!!!! in reality there is only one cmd_ctx handler, but consider
385         what might happen here if there are multiple handlers w/reentrant callback
386         fn's... shudder!  */
387         active_cmd_ctx=context;
388         int retcode=Jim_Eval(interp, line);
389         
390         const char *result;
391         int reslen;
392     result = Jim_GetString(Jim_GetResult(interp), &reslen);
393     if (retcode == JIM_ERR) {
394             int len, i;
395         
396             LOG_USER_N("Runtime error, file \"%s\", line %d:" JIM_NL,
397                     interp->errorFileName, interp->errorLine);
398             LOG_USER_N("    %s" JIM_NL,
399                     Jim_GetString(interp->result, NULL));
400             Jim_ListLength(interp, interp->stackTrace, &len);
401             for (i = 0; i < len; i+= 3) {
402                 Jim_Obj *objPtr;
403                 const char *proc, *file, *line;
404         
405                 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
406                 proc = Jim_GetString(objPtr, NULL);
407                 Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
408                         JIM_NONE);
409                 file = Jim_GetString(objPtr, NULL);
410                 Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
411                         JIM_NONE);
412                 line = Jim_GetString(objPtr, NULL);
413                 LOG_USER_N("In procedure '%s' called at file \"%s\", line %s" JIM_NL,
414                         proc, file, line);
415             }
416     } else if (retcode == JIM_EXIT) {
417         // ignore.
418         //exit(Jim_GetExitCode(interp));
419     } else {
420         if (reslen) {
421                 int i;
422                 char buff[256+1];
423                 for (i=0; i<reslen; i+=256)
424                 {
425                         int chunk;
426                         chunk=reslen-i;
427                         if (chunk>256)
428                                 chunk=256;
429                         strncpy(buff, result, chunk);
430                         buff[chunk]=0; 
431                 LOG_USER_N("%s", buff);
432                 }
433                 LOG_USER_N("%s", "\n");
434         }
435     }
436         return retval;
437 }
438
439 int startLoop=0;
440
441 static int Jim_Command_openocd_ignore(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int ignore)
442 {
443         int retval;
444     char *cmd = (char*)Jim_GetString(argv[1], NULL);
445
446     Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
447     
448     if (startLoop)
449     {
450         // We don't know whether or not the telnet/gdb server is running...
451         target_call_timer_callbacks_now();
452     }
453         
454         log_add_callback(tcl_output, tclOutput);
455     retval=command_run_line_internal(active_cmd_ctx, cmd);
456     
457     if (startLoop)
458     {
459         target_call_timer_callbacks_now();
460     }
461         log_remove_callback(tcl_output, tclOutput);
462     
463         Jim_SetResult(interp, tclOutput);
464         
465     return (ignore||(retval==ERROR_OK))?JIM_OK:JIM_ERR;
466 }
467
468 static int Jim_Command_openocd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
469 {
470         return Jim_Command_openocd_ignore(interp, argc, argv, 1); 
471 }
472
473 static int Jim_Command_openocd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
474 {
475         return Jim_Command_openocd_ignore(interp, argc, argv, 0); 
476 }
477
478 /* find full path to file */
479 static int Jim_Command_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
480 {
481         if (argc!=2)
482                 return JIM_ERR;
483         char *file = (char*)Jim_GetString(argv[1], NULL);
484         char *full_path=find_file(file);
485         if (full_path==NULL)
486                 return JIM_ERR;
487     Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
488     free(full_path);
489     
490         Jim_SetResult(interp, result);
491         return JIM_OK;
492 }
493
494 static int Jim_Command_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
495 {
496         if (argc!=2)
497                 return JIM_ERR;
498         char *str = (char*)Jim_GetString(argv[1], NULL);
499         LOG_USER("%s", str);
500         return JIM_OK;
501 }
502
503 static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
504 {
505         size_t nbytes;
506         const char *ptr;
507
508         /* make it a char easier to read code */
509         ptr = _ptr;
510
511         nbytes = size * n;
512         if( nbytes == 0 ){
513                 return 0;
514         }
515
516         if( !active_cmd_ctx ){
517                 /* FIXME: Where should this go? */              
518                 return n;
519         }
520
521         /* do we have to chunk it? */
522         if( ptr[ nbytes ] == 0 ){
523                 /* no it is a C style string */
524                 command_output_text( active_cmd_ctx, ptr );
525                 return strlen(ptr);
526         }
527         /* GRR we must chunk - not null terminated */
528         while( nbytes ){
529                 char chunk[128+1];
530                 int x;
531
532                 x = nbytes;
533                 if( x > 128 ){
534                         x = 128;
535                 }
536                 /* copy it */
537                 memcpy( chunk, ptr, x );
538                 /* terminate it */
539                 chunk[n] = 0;
540                 /* output it */
541                 command_output_text( active_cmd_ctx, chunk );
542                 ptr += x;
543                 nbytes -= x;
544         }
545         
546         return n;
547 }
548
549 static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie )
550 {
551         /* TCL wants to read... tell him no */
552         return 0;
553 }
554
555 static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
556 {
557         char *cp;
558         int n;
559         
560         n = -1;
561         if( active_cmd_ctx ){
562                 cp = alloc_vprintf( fmt, ap );
563                 if( cp ){
564                         command_output_text( active_cmd_ctx, cp );
565                         n = strlen(cp);
566                         free(cp);
567                 }
568         }
569         return n;
570 }
571
572 static int openocd_jim_fflush(void *cookie)
573 {
574         /* nothing to flush */
575         return 0;
576 }
577
578 static char* openocd_jim_fgets(char *s, int size, void *cookie)
579 {
580         /* not supported */
581         errno = ENOTSUP;
582         return NULL;
583 }
584
585 void initJim(void)
586 {
587     Jim_CreateCommand(interp, "openocd", Jim_Command_openocd, NULL, NULL);
588     Jim_CreateCommand(interp, "openocd_throw", Jim_Command_openocd_throw, NULL, NULL);
589     Jim_CreateCommand(interp, "find", Jim_Command_find, NULL, NULL);
590     Jim_CreateCommand(interp, "echo", Jim_Command_echo, NULL, NULL);
591     Jim_CreateCommand(interp, "mem2array", Jim_Command_mem2array, NULL, NULL );
592
593         /* Set Jim's STDIO */
594         interp->cookie_stdin  = NULL;
595         interp->cookie_stdout = NULL;
596         interp->cookie_stderr = NULL;
597         interp->cb_fwrite     = openocd_jim_fwrite;
598         interp->cb_fread      = openocd_jim_fread ;
599         interp->cb_vfprintf   = openocd_jim_vfprintf;
600         interp->cb_fflush     = openocd_jim_fflush;
601         interp->cb_fgets      = openocd_jim_fgets;
602 }
603
604 /* after command line parsing */
605 void initJim2(void)
606 {
607         Jim_Eval(interp, "source [find tcl/commands.tcl]");
608 }
609
610 command_context_t *setup_command_handler()
611 {
612         command_context_t *cmd_ctx;
613         
614         cmd_ctx = command_init();
615
616         register_command(cmd_ctx, NULL, "version", handle_version_command,
617                                          COMMAND_EXEC, "show OpenOCD version");
618         register_command(cmd_ctx, NULL, "daemon_startup", handle_daemon_startup_command, COMMAND_CONFIG, 
619                         "deprecated - use \"init\" and \"reset\" at end of startup script instead");
620         
621         /* register subsystem commands */
622         server_register_commands(cmd_ctx);
623         telnet_register_commands(cmd_ctx);
624         gdb_register_commands(cmd_ctx);
625         tcl_register_commands(cmd_ctx); /* tcl server commands */
626         log_register_commands(cmd_ctx);
627         jtag_register_commands(cmd_ctx);
628         interpreter_register_commands(cmd_ctx);
629         xsvf_register_commands(cmd_ctx);
630         target_register_commands(cmd_ctx);
631         flash_register_commands(cmd_ctx);
632         nand_register_commands(cmd_ctx);
633         pld_register_commands(cmd_ctx);
634         
635         if (log_init(cmd_ctx) != ERROR_OK)
636         {
637                 exit(-1);
638         }
639         LOG_DEBUG("log init complete");
640
641         LOG_OUTPUT( OPENOCD_VERSION "\n" );
642         
643         
644         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
645         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
646         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
647         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
648         /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
649         LOG_OUTPUT( "$URL$\n");
650         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
651         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
652         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
653         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
654         /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
655
656         register_command(cmd_ctx, NULL, "init", handle_init_command,
657                                          COMMAND_ANY, "initializes target and servers - nop on subsequent invocations");
658
659         return cmd_ctx;
660 }
661
662 /*
663 normally this is the main() function entry, but if OpenOCD is linked
664 into application, then this fn will not be invoked, but rather that
665 application will have it's own implementation of main().
666 */
667 int openocd_main(int argc, char *argv[])
668 {
669 #ifdef JIM_EMBEDDED
670         Jim_InitEmbedded();
671     /* Create an interpreter */
672     interp = Jim_CreateInterp();
673     /* Add all the Jim core commands */
674     Jim_RegisterCoreCommands(interp);
675 #endif
676     
677         initJim();
678         
679         /* initialize commandline interface */
680         command_context_t *cmd_ctx;
681         cmd_ctx=setup_command_handler();
682         
683         command_context_t *cfg_cmd_ctx;
684         cfg_cmd_ctx = copy_command_context(cmd_ctx);
685         cfg_cmd_ctx->mode = COMMAND_CONFIG;
686         command_set_output_handler(cfg_cmd_ctx, configuration_output_handler, NULL);
687         
688         if (parse_cmdline_args(cfg_cmd_ctx, argc, argv) != ERROR_OK)
689                 return EXIT_FAILURE;
690
691         initJim2();
692
693         if (parse_config_file(cfg_cmd_ctx) != ERROR_OK)
694                 return EXIT_FAILURE;
695         
696         command_done(cfg_cmd_ctx);
697
698         if (command_run_line(cmd_ctx, "init")!=ERROR_OK)
699                 return EXIT_FAILURE;
700         
701         if (daemon_startup)
702                 command_run_line(cmd_ctx, "reset");
703         
704         startLoop=1;
705
706         /* handle network connections */
707         server_loop(cmd_ctx);
708
709         /* shut server down */
710         server_quit();
711
712         unregister_all_commands(cmd_ctx);
713         
714         /* free commandline interface */
715         command_done(cmd_ctx);
716
717         return EXIT_SUCCESS;
718 }