1/*****************************************************************************/
2/* */
3/* main.c */
4/* */
5/* Main program for the ld65 linker */
6/* */
7/* */
8/* */
9/* (C) 1998-2013, Ullrich von Bassewitz */
10/* Roemerstrasse 52 */
11/* D-70794 Filderstadt */
12/* EMail: uz@cc65.org */
13/* */
14/* */
15/* This software is provided 'as-is', without any expressed or implied */
16/* warranty. In no event will the authors be held liable for any damages */
17/* arising from the use of this software. */
18/* */
19/* Permission is granted to anyone to use this software for any purpose, */
20/* including commercial applications, and to alter it and redistribute it */
21/* freely, subject to the following restrictions: */
22/* */
23/* 1. The origin of this software must not be misrepresented; you must not */
24/* claim that you wrote the original software. If you use this software */
25/* in a product, an acknowledgment in the product documentation would be */
26/* appreciated but is not required. */
27/* 2. Altered source versions must be plainly marked as such, and must not */
28/* be misrepresented as being the original software. */
29/* 3. This notice may not be removed or altered from any source */
30/* distribution. */
31/* */
32/*****************************************************************************/
33
34
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <errno.h>
40
41/* common */
42#include "addrsize.h"
43#include "chartype.h"
44#include "cmdline.h"
45#include "filetype.h"
46#include "libdefs.h"
47#include "objdefs.h"
48#include "print.h"
49#include "target.h"
50#include "version.h"
51#include "xmalloc.h"
52
53/* ld65 */
54#include "asserts.h"
55#include "binfmt.h"
56#include "condes.h"
57#include "config.h"
58#include "dbgfile.h"
59#include "error.h"
60#include "exports.h"
61#include "fileio.h"
62#include "filepath.h"
63#include "global.h"
64#include "library.h"
65#include "mapfile.h"
66#include "objfile.h"
67#include "scanner.h"
68#include "segments.h"
69#include "spool.h"
70#include "tpool.h"
71
72
73
74/*****************************************************************************/
75/* Data */
76/*****************************************************************************/
77
78
79
80static unsigned ObjFiles = 0; /* Count of object files linked */
81static unsigned LibFiles = 0; /* Count of library files linked */
82
83/* struct InputFile.Type definitions */
84#define INPUT_FILES_FILE 0 /* Entry is a file (unknown type) */
85#define INPUT_FILES_FILE_OBJ 1 /* Entry is a object file */
86#define INPUT_FILES_FILE_LIB 2 /* Entry is a library file */
87#define INPUT_FILES_SGROUP 3 /* Entry is 'StartGroup' */
88#define INPUT_FILES_EGROUP 4 /* Entry is 'EndGroup' */
89
90#define MAX_INPUTFILES 256
91
92/* Array of inputs (libraries and object files) */
93static struct InputFile {
94 const char *FileName;
95 unsigned Type;
96} *InputFiles;
97static unsigned InputFilesCount = 0;
98static const char *CmdlineCfgFile = NULL,
99 *CmdlineTarget = NULL;
100
101
102
103/*****************************************************************************/
104/* Code */
105/*****************************************************************************/
106
107
108
109static void Usage (void)
110/* Print usage information and exit */
111{
112 printf ("Usage: %s [options] module ...\n"
113 "Short options:\n"
114 " -(\t\t\tStart a library group\n"
115 " -)\t\t\tEnd a library group\n"
116 " -C name\t\tUse linker config file\n"
117 " -D sym=val\t\tDefine a symbol\n"
118 " -L path\t\tSpecify a library search path\n"
119 " -Ln name\t\tCreate a VICE label file\n"
120 " -S addr\t\tSet the default start address\n"
121 " -V\t\t\tPrint the linker version\n"
122 " -h\t\t\tHelp (this text)\n"
123 " -m name\t\tCreate a map file\n"
124 " -o name\t\tName the default output file\n"
125 " -t sys\t\tSet the target system\n"
126 " -u sym\t\tForce an import of symbol 'sym'\n"
127 " -v\t\t\tVerbose mode\n"
128 " -vm\t\t\tVerbose map file\n"
129 "\n"
130 "Long options:\n"
131 " --allow-multiple-definition\tAllow multiple definitions\n"
132 " --cfg-path path\t\tSpecify a config file search path\n"
133 " --config name\t\t\tUse linker config file\n"
134 " --dbgfile name\t\tGenerate debug information\n"
135 " --define sym=val\t\tDefine a symbol\n"
136 " --end-group\t\t\tEnd a library group\n"
137 " --force-import sym\t\tForce an import of symbol 'sym'\n"
138 " --help\t\t\tHelp (this text)\n"
139 " --lib file\t\t\tLink this library\n"
140 " --lib-path path\t\tSpecify a library search path\n"
141 " --mapfile name\t\tCreate a map file\n"
142 " --module-id id\t\tSpecify a module id\n"
143 " --obj file\t\t\tLink this object file\n"
144 " --obj-path path\t\tSpecify an object file search path\n"
145 " --start-addr addr\t\tSet the default start address\n"
146 " --start-group\t\t\tStart a library group\n"
147 " --target sys\t\t\tSet the target system\n"
148 " --version\t\t\tPrint the linker version\n",
149 ProgName);
150}
151
152
153
154static unsigned long CvtNumber (const char* Arg, const char* Number)
155/* Convert a number from a string. Allow '$' and '0x' prefixes for hex
156** numbers.
157*/
158{
159 unsigned long Val;
160 int Converted;
161
162 /* Convert */
163 if (*Number == '$') {
164 ++Number;
165 Converted = sscanf (Number, "%lx", &Val);
166 } else {
167 Converted = sscanf (Number, "%li", (long*)&Val);
168 }
169
170 /* Check if we do really have a number */
171 if (Converted != 1) {
172 Error ("Invalid number given in argument: %s\n", Arg);
173 }
174
175 /* Return the result */
176 return Val;
177}
178
179
180
181static void LinkFile (const char* Name, FILETYPE Type)
182/* Handle one file */
183{
184 char* PathName;
185 FILE* F;
186 unsigned long Magic;
187
188
189 /* If we don't know the file type, determine it from the extension */
190 if (Type == FILETYPE_UNKNOWN) {
191 Type = GetFileType (Name);
192 }
193
194 /* For known file types, search the file in the directory list */
195 switch (Type) {
196
197 case FILETYPE_LIB:
198 PathName = SearchFile (LibSearchPath, Name);
199 if (PathName == 0) {
200 PathName = SearchFile (LibDefaultPath, Name);
201 }
202 break;
203
204 case FILETYPE_OBJ:
205 PathName = SearchFile (ObjSearchPath, Name);
206 if (PathName == 0) {
207 PathName = SearchFile (ObjDefaultPath, Name);
208 }
209 break;
210
211 default:
212 PathName = xstrdup (Name); /* Use the name as is */
213 break;
214 }
215
216 /* We must have a valid name now */
217 if (PathName == 0) {
218 Error ("Input file '%s' not found", Name);
219 }
220
221 /* Try to open the file */
222 F = fopen (PathName, "rb");
223 if (F == 0) {
224 Error ("Cannot open '%s': %s", PathName, strerror (errno));
225 }
226
227 /* Read the magic word */
228 Magic = Read32 (F);
229
230 /* Check the magic for known file types. The handling is somewhat weird
231 ** since we may have given a file with a ".lib" extension, which was
232 ** searched and found in a directory for library files, but we now find
233 ** out (by looking at the magic) that it's indeed an object file. We just
234 ** ignore the problem and hope no one will notice...
235 */
236 switch (Magic) {
237
238 case OBJ_MAGIC:
239 ObjAdd (F, PathName);
240 ++ObjFiles;
241 break;
242
243 case LIB_MAGIC:
244 LibAdd (F, PathName);
245 ++LibFiles;
246 break;
247
248 default:
249 fclose (F);
250 Error ("File '%s' has unknown type", PathName);
251
252 }
253
254 /* Free allocated memory. */
255 xfree (PathName);
256}
257
258
259
260static void DefineSymbol (const char* Def)
261/* Define a symbol from the command line */
262{
263 const char* P;
264 long Val;
265 StrBuf SymName = AUTO_STRBUF_INITIALIZER;
266
267
268 /* The symbol must start with a character or underline */
269 if (Def [0] != '_' && !IsAlpha (Def [0])) {
270 InvDef (Def);
271 }
272 P = Def;
273
274 /* Copy the symbol, checking the remainder */
275 while (IsAlNum (*P) || *P == '_') {
276 SB_AppendChar (&SymName, *P++);
277 }
278 SB_Terminate (&SymName);
279
280 /* Do we have a value given? */
281 if (*P != '=') {
282 InvDef (Def);
283 } else {
284 /* We have a value */
285 ++P;
286 if (*P == '$') {
287 ++P;
288 if (sscanf (P, "%lx", &Val) != 1) {
289 InvDef (Def);
290 }
291 } else {
292 if (sscanf (P, "%li", &Val) != 1) {
293 InvDef (Def);
294 }
295 }
296 }
297
298 /* Define the new symbol */
299 CreateConstExport (GetStringId (SB_GetConstBuf (&SymName)), Val);
300}
301
302
303
304static void OptCfgPath (const char* Opt attribute ((unused)), const char* Arg)
305/* Specify a config file search path */
306{
307 AddSearchPath (CfgSearchPath, Arg);
308}
309
310
311
312static void OptConfig (const char* Opt attribute ((unused)), const char* Arg)
313/* Define the config file */
314{
315 char* PathName;
316
317 if (CfgAvail ()) {
318 Error ("Cannot use -C/-t twice");
319 }
320 /* Search for the file */
321 PathName = SearchFile (CfgSearchPath, Arg);
322 if (PathName == 0) {
323 PathName = SearchFile (CfgDefaultPath, Arg);
324 }
325 if (PathName == 0) {
326 Error ("Cannot find config file '%s'", Arg);
327 }
328
329 /* Read the config */
330 CfgSetName (PathName);
331 CfgRead ();
332}
333
334
335
336static void OptDbgFile (const char* Opt attribute ((unused)), const char* Arg)
337/* Give the name of the debug file */
338{
339 DbgFileName = Arg;
340}
341
342
343
344static void OptDefine (const char* Opt attribute ((unused)), const char* Arg)
345/* Define a symbol on the command line */
346{
347 DefineSymbol (Arg);
348}
349
350
351
352static void OptEndGroup (const char* Opt attribute ((unused)),
353 const char* Arg attribute ((unused)))
354/* End a library group */
355{
356 LibEndGroup ();
357}
358
359
360
361static void OptForceImport (const char* Opt attribute ((unused)), const char* Arg)
362/* Force an import of a symbol */
363{
364 /* An optional address size may be specified */
365 const char* ColPos = strchr (Arg, ':');
366 if (ColPos == 0) {
367
368 /* Use default address size (which for now is always absolute
369 ** addressing)
370 */
371 InsertImport (GenImport (GetStringId (Arg), ADDR_SIZE_ABS));
372
373 } else {
374
375 char* A;
376
377 /* Get the address size and check it */
378 unsigned char AddrSize = AddrSizeFromStr (ColPos+1);
379 if (AddrSize == ADDR_SIZE_INVALID) {
380 Error ("Invalid address size '%s'", ColPos+1);
381 }
382
383 /* Create a copy of the argument */
384 A = xstrdup (Arg);
385
386 /* We need just the symbol */
387 A[ColPos - Arg] = '\0';
388
389 /* Generate the import */
390 InsertImport (GenImport (GetStringId (A), AddrSize));
391
392 /* Delete the copy of the argument */
393 xfree (A);
394 }
395}
396
397
398
399static void OptHelp (const char* Opt attribute ((unused)),
400 const char* Arg attribute ((unused)))
401/* Print usage information and exit */
402{
403 Usage ();
404 exit (EXIT_SUCCESS);
405}
406
407
408
409static void OptLib (const char* Opt attribute ((unused)), const char* Arg)
410/* Link a library */
411{
412 InputFiles[InputFilesCount].Type = INPUT_FILES_FILE_LIB;
413 InputFiles[InputFilesCount].FileName = Arg;
414 if (++InputFilesCount >= MAX_INPUTFILES)
415 Error ("Too many input files");
416}
417
418
419
420static void OptLibPath (const char* Opt attribute ((unused)), const char* Arg)
421/* Specify a library file search path */
422{
423 AddSearchPath (LibSearchPath, Arg);
424}
425
426
427
428static void OptMapFile (const char* Opt attribute ((unused)), const char* Arg)
429/* Give the name of the map file */
430{
431 if (MapFileName) {
432 Error ("Cannot use -m twice");
433 }
434 MapFileName = Arg;
435}
436
437
438
439static void OptModuleId (const char* Opt, const char* Arg)
440/* Specify a module id */
441{
442 unsigned long Id = CvtNumber (Opt, Arg);
443 if (Id > 0xFFFFUL) {
444 Error ("Range error in module id");
445 }
446 ModuleId = (unsigned) Id;
447}
448
449
450
451static void OptObj (const char* Opt attribute ((unused)), const char* Arg)
452/* Link an object file */
453{
454 InputFiles[InputFilesCount].Type = INPUT_FILES_FILE_OBJ;
455 InputFiles[InputFilesCount].FileName = Arg;
456 if (++InputFilesCount >= MAX_INPUTFILES)
457 Error ("Too many input files");
458}
459
460
461
462static void OptObjPath (const char* Opt attribute ((unused)), const char* Arg)
463/* Specify an object file search path */
464{
465 AddSearchPath (ObjSearchPath, Arg);
466}
467
468
469
470static void OptOutputName (const char* Opt attribute ((unused)), const char* Arg)
471/* Give the name of the output file */
472{
473 static int OutputNameSeen = 0;
474 if (OutputNameSeen) {
475 Error ("Cannot use -o twice");
476 }
477 OutputNameSeen = 1;
478 OutputName = Arg;
479}
480
481
482
483static void OptStartAddr (const char* Opt, const char* Arg)
484/* Set the default start address */
485{
486 if (HaveStartAddr) {
487 Error ("Cannot use -S twice");
488 }
489 StartAddr = CvtNumber (Opt, Arg);
490 HaveStartAddr = 1;
491}
492
493
494
495static void OptStartGroup (const char* Opt attribute ((unused)),
496 const char* Arg attribute ((unused)))
497/* Start a library group */
498{
499 LibStartGroup ();
500}
501
502
503
504static void OptTarget (const char* Opt attribute ((unused)), const char* Arg)
505/* Set the target system */
506{
507 StrBuf FileName = STATIC_STRBUF_INITIALIZER;
508 char* PathName;
509
510 /* Map the target name to a target id */
511 Target = FindTarget (Arg);
512 if (Target == TGT_UNKNOWN) {
513 Error ("Invalid target name: '%s'", Arg);
514 }
515
516 /* Set the target binary format */
517 DefaultBinFmt = GetTargetProperties (Target)->BinFmt;
518
519 /* Build config file name from target name */
520 SB_CopyStr (&FileName, GetTargetName (Target));
521 SB_AppendStr (&FileName, ".cfg");
522 SB_Terminate (&FileName);
523
524 /* Search for the file */
525 PathName = SearchFile (CfgSearchPath, SB_GetBuf (&FileName));
526 if (PathName == 0) {
527 PathName = SearchFile (CfgDefaultPath, SB_GetBuf (&FileName));
528 }
529 if (PathName == 0) {
530 Error ("Cannot find config file '%s'", SB_GetBuf (&FileName));
531 }
532
533 /* Free file name memory */
534 SB_Done (&FileName);
535
536 /* Read the file */
537 CfgSetName (PathName);
538 CfgRead ();
539}
540
541
542
543static void OptVersion (const char* Opt attribute ((unused)),
544 const char* Arg attribute ((unused)))
545/* Print the assembler version */
546{
547 fprintf (stderr, "%s V%s\n", ProgName, GetVersionAsString ());
548 exit(EXIT_SUCCESS);
549}
550
551
552
553static void OptMultDef (const char* Opt attribute ((unused)),
554 const char* Arg attribute ((unused)))
555/* Set flag to allow multiple definitions of a global symbol */
556{
557 AllowMultDef = 1;
558}
559
560
561
562static void CmdlOptStartGroup (const char* Opt attribute ((unused)),
563 const char* Arg attribute ((unused)))
564/* Remember 'start group' occurrence in input files array */
565{
566 InputFiles[InputFilesCount].Type = INPUT_FILES_SGROUP;
567 InputFiles[InputFilesCount].FileName = Arg; /* Unused */
568 if (++InputFilesCount >= MAX_INPUTFILES)
569 Error ("Too many input files");
570}
571
572
573
574static void CmdlOptEndGroup (const char* Opt attribute ((unused)),
575 const char* Arg attribute ((unused)))
576/* Remember 'end group' occurrence in input files array */
577{
578 InputFiles[InputFilesCount].Type = INPUT_FILES_EGROUP;
579 InputFiles[InputFilesCount].FileName = Arg; /* Unused */
580 if (++InputFilesCount >= MAX_INPUTFILES)
581 Error ("Too many input files");
582}
583
584
585
586static void CmdlOptConfig (const char* Opt attribute ((unused)), const char* Arg)
587/* Set 'config file' command line parameter */
588{
589 if (CmdlineCfgFile || CmdlineTarget) {
590 Error ("Cannot use -C/-t twice");
591 }
592 CmdlineCfgFile = Arg;
593}
594
595
596
597static void CmdlOptTarget (const char* Opt attribute ((unused)), const char* Arg)
598/* Set 'target' command line parameter */
599{
600 if (CmdlineCfgFile || CmdlineTarget) {
601 Error ("Cannot use -C/-t twice");
602 }
603 CmdlineTarget = Arg;
604}
605
606
607
608static void ParseCommandLine(void)
609{
610 /* Program long options */
611 static const LongOpt OptTab[] = {
612 { "--allow-multiple-definition", 0, OptMultDef },
613 { "--cfg-path", 1, OptCfgPath },
614 { "--config", 1, CmdlOptConfig },
615 { "--dbgfile", 1, OptDbgFile },
616 { "--define", 1, OptDefine },
617 { "--end-group", 0, CmdlOptEndGroup },
618 { "--force-import", 1, OptForceImport },
619 { "--help", 0, OptHelp },
620 { "--lib", 1, OptLib },
621 { "--lib-path", 1, OptLibPath },
622 { "--mapfile", 1, OptMapFile },
623 { "--module-id", 1, OptModuleId },
624 { "--obj", 1, OptObj },
625 { "--obj-path", 1, OptObjPath },
626 { "--start-addr", 1, OptStartAddr },
627 { "--start-group", 0, CmdlOptStartGroup },
628 { "--target", 1, CmdlOptTarget },
629 { "--version", 0, OptVersion },
630 };
631
632 unsigned I;
633 unsigned LabelFileGiven = 0;
634
635 /* Allocate memory for input file array */
636 InputFiles = xmalloc (MAX_INPUTFILES * sizeof (struct InputFile));
637
638 /* Defer setting of config/target and input files until all options are parsed */
639 I = 1;
640 while (I < ArgCount) {
641
642 /* Get the argument */
643 const char* Arg = ArgVec[I];
644
645 /* Check for an option */
646 if (Arg [0] == '-') {
647
648 /* An option */
649 switch (Arg [1]) {
650
651 case '-':
652 LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
653 break;
654
655 case '(':
656 CmdlOptStartGroup (Arg, 0);
657 break;
658
659 case ')':
660 CmdlOptEndGroup (Arg, 0);
661 break;
662
663 case 'h':
664 case '?':
665 OptHelp (Arg, 0);
666 break;
667
668 case 'm':
669 OptMapFile (Arg, GetArg (&I, 2));
670 break;
671
672 case 'o':
673 OptOutputName (NULL, GetArg (&I, 2));
674 break;
675
676 case 't':
677 CmdlOptTarget (Arg, GetArg (&I, 2));
678 break;
679
680 case 'u':
681 OptForceImport (Arg, GetArg (&I, 2));
682 break;
683
684 case 'v':
685 switch (Arg [2]) {
686 case 'm': VerboseMap = 1; break;
687 case '\0': ++Verbosity; break;
688 default: UnknownOption (Arg);
689 }
690 break;
691
692 case 'C':
693 CmdlOptConfig (Arg, GetArg (&I, 2));
694 break;
695
696 case 'D':
697 OptDefine (Arg, GetArg (&I, 2));
698 break;
699
700 case 'L':
701 switch (Arg [2]) {
702 case 'n':
703 /* ## This one is obsolete and will go */
704 if (LabelFileGiven) {
705 Error ("Cannot use -Ln twice");
706 }
707 LabelFileGiven = 1;
708 LabelFileName = GetArg (&I, 3);
709 break;
710 default:
711 OptLibPath (Arg, GetArg (&I, 2));
712 break;
713 }
714 break;
715
716 case 'S':
717 OptStartAddr (Arg, GetArg (&I, 2));
718 break;
719
720 case 'V':
721 OptVersion (Arg, 0);
722 break;
723
724 default:
725 UnknownOption (Arg);
726 break;
727 }
728
729 } else {
730
731 /* A filename */
732 InputFiles[InputFilesCount].Type = INPUT_FILES_FILE;
733 InputFiles[InputFilesCount].FileName = Arg;
734 if (++InputFilesCount >= MAX_INPUTFILES)
735 Error ("Too many input files");
736
737 }
738
739 /* Next argument */
740 ++I;
741 }
742
743 if (CmdlineTarget) {
744 OptTarget (NULL, CmdlineTarget);
745 } else if (CmdlineCfgFile) {
746 OptConfig (NULL, CmdlineCfgFile);
747 }
748
749 /* Process input files */
750 for (I = 0; I < InputFilesCount; ++I) {
751 switch (InputFiles[I].Type) {
752 case INPUT_FILES_FILE:
753 LinkFile (InputFiles[I].FileName, FILETYPE_UNKNOWN);
754 break;
755 case INPUT_FILES_FILE_LIB:
756 LinkFile (InputFiles[I].FileName, FILETYPE_LIB);
757 break;
758 case INPUT_FILES_FILE_OBJ:
759 LinkFile (InputFiles[I].FileName, FILETYPE_OBJ);
760 break;
761 case INPUT_FILES_SGROUP:
762 OptStartGroup (NULL, 0);
763 break;
764 case INPUT_FILES_EGROUP:
765 OptEndGroup (NULL, 0);
766 break;
767 default:
768 abort ();
769 }
770 }
771
772 /* Free memory used for input file array */
773 xfree (InputFiles);
774}
775
776
777
778int main (int argc, char* argv [])
779/* Linker main program */
780{
781 unsigned MemoryAreaOverflows;
782
783 /* Initialize the cmdline module */
784 InitCmdLine (&argc, &argv, "ld65");
785
786 /* Initialize the input file search paths */
787 InitSearchPaths ();
788
789 /* Initialize the string pool */
790 InitStrPool ();
791
792 /* Initialize the type pool */
793 InitTypePool ();
794
795 /* Parse the command line */
796 ParseCommandLine ();
797
798 /* Check if we had any object files */
799 if (ObjFiles == 0) {
800 Error ("No object files to link");
801 }
802
803 /* Check if we have a valid configuration */
804 if (!CfgAvail ()) {
805 Error ("Memory configuration missing");
806 }
807
808 /* Check if we have open library groups */
809 LibCheckGroup ();
810
811 /* Create the condes tables if requested */
812 ConDesCreate ();
813
814 /* Process data from the config file. Assign start addresses for the
815 ** segments, define linker symbols. The function will return the number
816 ** of memory area overflows (zero on success).
817 */
818 MemoryAreaOverflows = CfgProcess ();
819
820 /* Check module assertions */
821 CheckAssertions ();
822
823 /* Check for import/export mismatches */
824 CheckExports ();
825
826 /* If we had a memory area overflow before, we cannot generate the output
827 ** file. However, we will generate a short map file if requested, since
828 ** this will help the user to rearrange segments and fix the overflow.
829 */
830 if (MemoryAreaOverflows) {
831 if (MapFileName) {
832 CreateMapFile (SHORT_MAPFILE);
833 }
834 Error ("Cannot generate most of the files due to memory area overflow%c",
835 (MemoryAreaOverflows > 1) ? 's' : ' ');
836 }
837
838 /* Create the output file */
839 CfgWriteTarget ();
840
841 /* Check for segments not written to the output file */
842 CheckSegments ();
843
844 /* If requested, create a map file and a label file for VICE */
845 if (MapFileName) {
846 CreateMapFile (LONG_MAPFILE);
847 }
848 if (LabelFileName) {
849 CreateLabelFile ();
850 }
851 if (DbgFileName) {
852 CreateDbgFile ();
853 }
854
855 /* Dump the data for debugging */
856 if (Verbosity > 1) {
857 SegDump ();
858 ConDesDump ();
859 }
860
861 /* Return an apropriate exit code */
862 return EXIT_SUCCESS;
863}
864