]> git.sur5r.net Git - cc65/blob - src/cl65/main.c
a62d461ec7eeb8e267b7bf44a07a44c3701f259b
[cc65] / src / cl65 / main.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                  main.c                                   */
4 /*                                                                           */
5 /*             Main module for the cl65 compile and link utility             */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1999-2000 Ullrich von Bassewitz                                       */
10 /*               Wacholderweg 14                                             */
11 /*               D-70597 Stuttgart                                           */
12 /* EMail:        uz@musoftware.de                                            */
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 <ctype.h>
40 #include <errno.h>
41 #if defined(__WATCOMC__) || defined(_MSC_VER)
42 #  include <process.h>          /* DOS, OS/2 and Windows */
43 #else
44 #  include "spawn.h"            /* All others */
45 #endif
46
47 #include "../common/cmdline.h"
48 #include "../common/fname.h"
49 #include "../common/version.h"
50 #include "../common/xmalloc.h"
51
52 #include "global.h"
53 #include "error.h"
54
55
56
57 /*****************************************************************************/
58 /*                                   Data                                    */
59 /*****************************************************************************/
60
61
62
63 /* Struct that describes a command */
64 typedef struct CmdDesc_ CmdDesc;
65 struct CmdDesc_ {
66     char*       Name;           /* The command name */
67
68     unsigned    ArgCount;       /* Count of arguments */
69     unsigned    ArgMax;         /* Maximum count of arguments */
70     char**      Args;           /* The arguments */
71
72     unsigned    FileCount;      /* Count of files to translate */
73     unsigned    FileMax;        /* Maximum count of files */
74     char**      Files;          /* The files */
75 };
76
77 /* Command descriptors for the different programs */
78 static CmdDesc CC65 = { 0, 0, 0, 0, 0, 0, 0 };
79 static CmdDesc CA65 = { 0, 0, 0, 0, 0, 0, 0 };
80 static CmdDesc LD65 = { 0, 0, 0, 0, 0, 0, 0 };
81 static CmdDesc GRC  = { 0, 0, 0, 0, 0, 0, 0 };
82
83 /* File types */
84 enum {
85     FILETYPE_UNKNOWN,
86     FILETYPE_C,
87     FILETYPE_ASM,
88     FILETYPE_OBJ,
89     FILETYPE_LIB,
90     FILETYPE_GR,                /* GEOS resource file */
91 };
92
93 /* Default file type, used if type unknown */
94 static unsigned DefaultFileType = FILETYPE_UNKNOWN;
95
96 /* Variables controlling the steps we're doing */
97 static int DontLink     = 0;
98 static int DontAssemble = 0;
99
100 /* The name of the output file, NULL if none given */
101 static const char* OutputName = 0;
102
103 /* The name of the linker configuration file if given */
104 static const char* LinkerConfig = 0;
105
106 /* The name of the first input file. This will be used to construct the
107  * executable file name if no explicit name is given.
108  */
109 static const char* FirstInput = 0;
110
111 /* The target system */
112 enum {
113     TGT_UNKNOWN = -1,
114     TGT_NONE,
115     TGT_FIRSTREAL,
116     TGT_ATARI = TGT_FIRSTREAL,
117     TGT_C64,
118     TGT_C128,
119     TGT_ACE,
120     TGT_PLUS4,
121     TGT_CBM610,
122     TGT_PET,
123     TGT_NES,
124     TGT_APPLE2,
125     TGT_GEOS,
126     TGT_COUNT
127 } Target = TGT_UNKNOWN;
128
129 /* Names of the target systems sorted by target name */
130 static const char* TargetNames [] = {
131     "none",
132     "atari",
133     "c64",
134     "c128",
135     "ace",
136     "plus4",
137     "cbm610",
138     "pet",
139     "nes",
140     "apple2",
141     "geos",
142 };
143
144 /* Name of the crt0 object file and the runtime library */
145 static char* TargetCRT0 = 0;
146 static char* TargetLib  = 0;
147
148
149
150 /*****************************************************************************/
151 /*                           Determine a file type                           */
152 /*****************************************************************************/
153
154
155
156 static unsigned GetFileType (const char* File)
157 /* Determine the type of the given file */
158 {
159     /* Table mapping extensions to file types */
160     static const struct {
161         const char*     Ext;
162         unsigned        Type;
163     } FileTypes [] = {
164         {   ".c",       FILETYPE_C      },
165         {   ".s",       FILETYPE_ASM    },
166         {   ".ss",      FILETYPE_ASM    },
167         {   ".asm",     FILETYPE_ASM    },
168         {   ".o",       FILETYPE_OBJ    },
169         {   ".obj",     FILETYPE_OBJ    },
170         {   ".a",       FILETYPE_LIB    },
171         {   ".lib",     FILETYPE_LIB    },
172         {   ".grc",     FILETYPE_GR     },
173     };
174
175     unsigned I;
176
177     /* Determine the file type by the extension */
178     const char* Ext = FindExt (File);
179
180     /* Do we have an extension? */
181     if (Ext == 0) {
182         return DefaultFileType;
183     }
184
185     /* Check for known extensions */
186     for (I = 0; I < sizeof (FileTypes) / sizeof (FileTypes [0]); ++I) {
187         if (strcmp (FileTypes [I].Ext, Ext) == 0) {
188             /* Found */
189             return FileTypes [I].Type;
190         }
191     }
192
193     /* Not found, return the default */
194     return DefaultFileType;
195 }
196
197
198
199 /*****************************************************************************/
200 /*                        Command structure handling                         */
201 /*****************************************************************************/
202
203
204
205 static void CmdAddArg (CmdDesc* Cmd, const char* Arg)
206 /* Add a new argument to the command */
207 {
208     /* Expand the argument vector if needed */
209     if (Cmd->ArgCount == Cmd->ArgMax) {
210         unsigned NewMax  = Cmd->ArgMax + 10;
211         char**   NewArgs = xmalloc (NewMax * sizeof (char*));
212         memcpy (NewArgs, Cmd->Args, Cmd->ArgMax * sizeof (char*));
213         xfree (Cmd->Args);
214         Cmd->Args   = NewArgs;
215         Cmd->ArgMax = NewMax;
216     }
217
218     /* Add a copy of the new argument, allow a NULL pointer */
219     if (Arg) {
220         Cmd->Args [Cmd->ArgCount++] = xstrdup (Arg);
221     } else {
222         Cmd->Args [Cmd->ArgCount++] = 0;
223     }
224 }
225
226
227
228 static void CmdDelArgs (CmdDesc* Cmd, unsigned LastValid)
229 /* Remove all arguments with an index greater than LastValid */
230 {
231     while (Cmd->ArgCount > LastValid) {
232         Cmd->ArgCount--;
233         xfree (Cmd->Args [Cmd->ArgCount]);
234         Cmd->Args [Cmd->ArgCount] = 0;
235     }
236 }
237
238
239
240 static void CmdAddFile (CmdDesc* Cmd, const char* File)
241 /* Add a new file to the command */
242 {
243     /* Expand the file vector if needed */
244     if (Cmd->FileCount == Cmd->FileMax) {
245         unsigned NewMax   = Cmd->FileMax + 10;
246         char**   NewFiles = xmalloc (NewMax * sizeof (char*));
247         memcpy (NewFiles, Cmd->Files, Cmd->FileMax * sizeof (char*));
248         xfree (Cmd->Files);
249         Cmd->Files   = NewFiles;
250         Cmd->FileMax = NewMax;
251     }
252
253     /* If the file name is not NULL (which is legal and is used to terminate
254      * the file list), check if the file name does already exist in the file
255      * list and print a warning if so. Regardless of the search result, add
256      * the file.
257      */
258     if (File) {
259         unsigned I;
260         for (I = 0; I < Cmd->FileCount; ++I) {
261             if (strcmp (Cmd->Files[I], File) == 0) {
262                 /* Duplicate file */
263                 Warning ("Duplicate file in argument list: `%s'", File);
264                 /* No need to search further */
265                 break;
266             }
267         }
268
269         /* Add the file */
270         Cmd->Files [Cmd->FileCount++] = xstrdup (File);
271     } else {
272         /* Add a NULL pointer */
273         Cmd->Files [Cmd->FileCount++] = 0;
274     }
275 }
276
277
278
279 static void CmdInit (CmdDesc* Cmd, const char* Path)
280 /* Initialize the command using the given path to the executable */
281 {
282     /* Remember the command */
283     Cmd->Name = xstrdup (Path);
284
285     /* Use the command name as first argument */
286     CmdAddArg (Cmd, Path);
287 }
288
289
290
291 static void CmdSetOutput (CmdDesc* Cmd, const char* File)
292 /* Set the output file in a command desc */
293 {
294     CmdAddArg (Cmd, "-o");
295     CmdAddArg (Cmd, File);
296 }
297
298
299
300 static void CmdSetTarget (CmdDesc* Cmd, int Target)
301 /* Set the output file in a command desc */
302 {
303     if (Target == TGT_UNKNOWN) {
304         /* Use C64 as default */
305         Target = TGT_C64;
306     }
307
308     if (Target != TGT_NONE) {
309         CmdAddArg (Cmd, "-t");
310         CmdAddArg (Cmd, TargetNames[Target]);
311     }
312 }
313
314
315
316 /*****************************************************************************/
317 /*                              Target handling                              */
318 /*****************************************************************************/
319
320
321
322 static int MapTarget (const char* Name)
323 /* Map a target name to a system code. Abort on errors */
324 {
325     int I;
326
327     /* Check for a numeric target */
328     if (isdigit (*Name)) {
329         int Target = atoi (Name);
330         if (Target >= 0 && Target < TGT_COUNT) {
331             return Target;
332         }
333     }
334
335     /* Check for a target string */
336     for (I = 0; I < TGT_COUNT; ++I) {
337         if (strcmp (TargetNames [I], Name) == 0) {
338             return I;
339         }
340     }
341
342     /* Not found */
343     Error ("No such target system: `%s'", Name);
344     return -1;  /* Not reached */
345 }
346
347
348
349 static void SetTargetFiles (void)
350 /* Set the target system files */
351 {
352     /* Determine the names of the default startup and library file */
353     if (Target >= TGT_FIRSTREAL) {
354
355         /* Get a pointer to the system name and its length */
356         const char* TargetName = TargetNames [Target];
357         unsigned    TargetNameLen = strlen (TargetName);
358
359         /* Set the startup file */
360         TargetCRT0 = xmalloc (TargetNameLen + 2 + 1);
361         strcpy (TargetCRT0, TargetName);
362         strcat (TargetCRT0, ".o");
363
364         /* Set the library file */
365         TargetLib = xmalloc (TargetNameLen + 4 + 1);
366         strcpy (TargetLib, TargetName);
367         strcat (TargetLib, ".lib");
368
369     }
370 }
371
372
373
374 static void SetTargetByName (const char* Name)
375 /* Set the target system by name */
376 {
377     Target = MapTarget (Name);
378     SetTargetFiles ();
379 }
380
381
382
383 /*****************************************************************************/
384 /*                               Subprocesses                                */
385 /*****************************************************************************/
386
387
388
389 static void ExecProgram (CmdDesc* Cmd)
390 /* Execute a subprocess with the given name/parameters. Exit on errors. */
391 {
392     /* Call the program */
393     int Status = spawnvp (P_WAIT, Cmd->Name, Cmd->Args);
394
395     /* Check the result code */
396     if (Status < 0) {
397         /* Error executing the program */
398         Error ("Cannot execute `%s': %s", Cmd->Name, strerror (errno));
399     } else if (Status != 0) {
400         /* Called program had an error */
401         exit (Status);
402     }
403 }
404
405
406
407 static void Link (void)
408 /* Link the resulting executable */
409 {
410     unsigned I;
411
412     /* If we have a linker config file given, set the linker config file.
413      * Otherwise set the target system.
414      */
415     if (LinkerConfig) {
416         CmdAddArg (&LD65, "-C");
417         CmdAddArg (&LD65, LinkerConfig);
418     } else {
419         if (Target == TGT_UNKNOWN) {
420             /* Use c64 instead */
421             Target = TGT_C64;
422         }
423         SetTargetFiles ();
424         CmdSetTarget (&LD65, Target);
425     }
426
427     /* Since linking is always the final step, if we have an output file name
428      * given, set it here. If we don't have an explicit output name given,
429      * try to build one from the name of the first input file.
430      */
431     if (OutputName) {
432
433         CmdAddArg (&LD65, "-o");
434         CmdAddArg (&LD65, OutputName);
435
436     } else if (FirstInput && FindExt (FirstInput)) {  /* Only if ext present! */
437
438         char* Output = MakeFilename (FirstInput, "");
439         CmdAddArg (&LD65, "-o");
440         CmdAddArg (&LD65, Output);
441         xfree (Output);
442
443     }
444
445     /* If we have a startup file, add its name as a parameter */
446     if (TargetCRT0) {
447         CmdAddArg (&LD65, TargetCRT0);
448     }
449
450     /* Add all object files as parameters */
451     for (I = 0; I < LD65.FileCount; ++I) {
452         CmdAddArg (&LD65, LD65.Files [I]);
453     }
454
455     /* Add the system runtime library */
456     if (TargetLib) {
457         CmdAddArg (&LD65, TargetLib);
458     }
459
460     /* Terminate the argument list with a NULL pointer */
461     CmdAddArg (&LD65, 0);
462
463     /* Call the linker */
464     ExecProgram (&LD65);
465 }
466
467
468
469 static void Assemble (const char* File)
470 /* Assemble the given file */
471 {
472     /* Remember the current assembler argument count */
473     unsigned ArgCount = CA65.ArgCount;
474
475     /* If we won't link, this is the final step. In this case, set the
476      * output name.
477      */
478     if (DontLink && OutputName) {
479         CmdSetOutput (&CA65, OutputName);
480     } else {
481         /* The object file name will be the name of the source file
482          * with .s replaced by ".o". Add this file to the list of
483          * linker files.
484          */
485         char* ObjName = MakeFilename (File, ".o");
486         CmdAddFile (&LD65, ObjName);
487         xfree (ObjName);
488     }
489
490     /* Add the file as argument for the assembler */
491     CmdAddArg (&CA65, File);
492
493     /* Add a NULL pointer to terminate the argument list */
494     CmdAddArg (&CA65, 0);
495
496     /* Run the assembler */
497     ExecProgram (&CA65);
498
499     /* Remove the excess arguments */
500     CmdDelArgs (&CA65, ArgCount);
501 }
502
503
504
505 static void Compile (const char* File)
506 /* Compile the given file */
507 {
508     char* AsmName = 0;
509
510     /* Remember the current assembler argument count */
511     unsigned ArgCount = CC65.ArgCount;
512
513     /* Set the target system */
514     CmdSetTarget (&CC65, Target);
515
516     /* If we won't link, this is the final step. In this case, set the
517      * output name.
518      */
519     if (DontAssemble && OutputName) {
520         CmdSetOutput (&CC65, OutputName);
521     } else {
522         /* The assembler file name will be the name of the source file
523          * with .c replaced by ".s".
524          */
525         AsmName = MakeFilename (File, ".s");
526     }
527
528     /* Add the file as argument for the compiler */
529     CmdAddArg (&CC65, File);
530
531     /* Add a NULL pointer to terminate the argument list */
532     CmdAddArg (&CC65, 0);
533
534     /* Run the compiler */
535     ExecProgram (&CC65);
536
537     /* Remove the excess arguments */
538     CmdDelArgs (&CC65, ArgCount);
539
540     /* If this is not the final step, assemble the generated file, then
541      * remove it
542      */
543     if (!DontAssemble) {
544         Assemble (AsmName);
545         if (remove (AsmName) < 0) {
546             Warning ("Cannot remove temporary file `%s': %s",
547                      AsmName, strerror (errno));
548         }
549         xfree (AsmName);
550     }
551 }
552
553
554
555 static void CompileRes (const char* File)
556 /* Compile the given geos resource file */
557 {
558     char* AsmName = 0;
559
560     /* Remember the current assembler argument count */
561     unsigned ArgCount = GRC.ArgCount;
562
563     /* The assembler file name will be the name of the source file
564      * with .grc replaced by ".ss".
565      */
566     AsmName = MakeFilename (File, ".ss");
567
568     /* Add the file as argument for the resource compiler */
569     CmdAddArg (&GRC, File);
570
571     /* Add a NULL pointer to terminate the argument list */
572     CmdAddArg (&GRC, 0);
573
574     /* Run the compiler */
575     ExecProgram (&GRC);
576
577     /* Remove the excess arguments */
578     CmdDelArgs (&GRC, ArgCount);
579
580     /* If this is not the final step, assemble the generated file, then
581      * remove it
582      */
583     if (!DontAssemble) {
584         Assemble (AsmName);
585         if (remove (AsmName) < 0) {
586             Warning ("Cannot remove temporary file `%s': %s",
587                      AsmName, strerror (errno));
588         }
589     }
590
591     /* Free the assembler file name which was allocated from the heap */
592     xfree (AsmName);
593 }
594
595
596
597 /*****************************************************************************/
598 /*                                   Code                                    */
599 /*****************************************************************************/
600
601
602
603 static void Usage (void)
604 /* Print usage information and exit */
605 {
606     fprintf (stderr,
607              "Usage: %s [options] file\n"
608              "Short options:\n"
609              "  -A\t\t\tStrict ANSI mode\n"
610              "  -C name\t\tUse linker config file\n"
611              "  -Cl\t\t\tMake local variables static\n"
612              "  -D sym[=defn]\t\tDefine a preprocessor symbol\n"
613              "  -I dir\t\tSet a compiler include directory path\n"
614              "  -Ln name\t\tCreate a VICE label file\n"
615              "  -O\t\t\tOptimize code\n"
616              "  -Oi\t\t\tOptimize code, inline functions\n"
617              "  -Or\t\t\tOptimize code, honour the register keyword\n"
618              "  -Os\t\t\tOptimize code, inline known C funtions\n"
619              "  -S\t\t\tCompile but don't assemble and link\n"
620              "  -V\t\t\tPrint the version number\n"
621              "  -W\t\t\tSuppress warnings\n"
622              "  -c\t\t\tCompiler and assemble but don't link\n"
623              "  -d\t\t\tDebug mode\n"
624              "  -g\t\t\tAdd debug info\n"
625              "  -h\t\t\tHelp (this text)\n"
626              "  -m name\t\tCreate a map file\n"
627              "  -o name\t\tName the output file\n"
628              "  -t sys\t\tSet the target system\n"
629              "  -v\t\t\tVerbose mode\n"
630              "  -vm\t\t\tVerbose map file\n"
631              "\n"
632              "Long options:\n"
633              "  --ansi\t\tStrict ANSI mode\n"
634              "  --asm-include-dir dir\tSet an assembler include directory\n"
635              "  --debug\t\tDebug mode\n"
636              "  --debug-info\t\tAdd debug info\n"
637              "  --help\t\tHelp (this text)\n"
638              "  --include-dir dir\tSet a compiler include directory path\n"
639              "  --target sys\t\tSet the target system\n"
640              "  --version\t\tPrint the version number\n"
641              "  --verbose\t\tVerbose mode\n",
642              ProgName);
643 }
644
645
646
647 static void OptAnsi (const char* Opt, const char* Arg)
648 /* Strict ANSI mode (compiler) */
649 {
650     CmdAddArg (&CC65, "-A");
651 }
652
653
654
655 static void OptAsmIncludeDir (const char* Opt, const char* Arg)
656 /* Include directory (assembler) */
657 {
658     if (Arg == 0) {
659         NeedArg (Opt);
660     }
661     CmdAddArg (&CA65, "-I");
662     CmdAddArg (&CA65, Arg);
663 }
664
665
666
667 static void OptDebug (const char* Opt, const char* Arg)
668 /* Debug mode (compiler) */
669 {
670     CmdAddArg (&CC65, "-d");
671 }
672
673
674
675 static void OptDebugInfo (const char* Opt, const char* Arg)
676 /* Debug Info - add to compiler and assembler */
677 {
678     CmdAddArg (&CC65, "-g");
679     CmdAddArg (&CA65, "-g");
680 }
681
682
683
684 static void OptHelp (const char* Opt, const char* Arg)
685 /* Print help - cl65 */
686 {
687     Usage ();
688     exit (EXIT_SUCCESS);
689 }
690
691
692
693 static void OptIncludeDir (const char* Opt, const char* Arg)
694 /* Include directory (compiler) */
695 {
696     if (Arg == 0) {
697         NeedArg (Opt);
698     }
699     CmdAddArg (&CC65, "-I");
700     CmdAddArg (&CC65, Arg);
701 }
702
703
704
705 static void OptTarget (const char* Opt, const char* Arg)
706 /* Set the target system */
707 {
708     if (Arg == 0) {
709         NeedArg (Opt);
710     }
711     SetTargetByName (Arg);
712 }
713
714
715
716 static void OptVerbose (const char* Opt, const char* Arg)
717 /* Verbose mode (compiler, assembler, linker) */
718 {
719     CmdAddArg (&CC65, "-v");
720     CmdAddArg (&CA65, "-v");
721     CmdAddArg (&LD65, "-v");
722 }
723
724
725
726 static void OptVersion (const char* Opt, const char* Arg)
727 /* Print version number */
728 {
729     fprintf (stderr,
730              "cl65 V%u.%u.%u - (C) Copyright 1998-2000 Ullrich von Bassewitz\n",
731              VER_MAJOR, VER_MINOR, VER_PATCH);
732 }
733
734
735
736 int main (int argc, char* argv [])
737 /* Utility main program */
738 {
739     /* Program long options */
740     static const LongOpt OptTab[] = {
741         { "--ansi",             0,      OptAnsi                 },
742         { "--asm-include-dir",  1,      OptAsmIncludeDir        },
743         { "--debug",            0,      OptDebug                },
744         { "--debug-info",       0,      OptDebugInfo            },
745         { "--help",             0,      OptHelp                 },
746         { "--include-dir",      1,      OptIncludeDir           },
747         { "--target",           1,      OptTarget               },
748         { "--verbose",          0,      OptVerbose              },
749         { "--version",          0,      OptVersion              },
750     };
751
752     int I;
753
754     /* Initialize the cmdline module */
755     InitCmdLine (argc, argv, "cl65");
756
757     /* Initialize the command descriptors */
758     CmdInit (&CC65, "cc65");
759     CmdInit (&CA65, "ca65");
760     CmdInit (&LD65, "ld65");
761     CmdInit (&GRC,  "grc");
762
763     /* Check the parameters */
764     I = 1;
765     while (I < argc) {
766
767         /* Get the argument */
768         const char* Arg = argv [I];
769
770         /* Check for an option */
771         if (Arg [0] == '-') {
772
773             switch (Arg [1]) {
774
775                 case '-':
776                     LongOption (&I, OptTab, sizeof(OptTab)/sizeof(OptTab[0]));
777                     break;
778
779                 case 'A':
780                     /* Strict ANSI mode (compiler) */
781                     OptAnsi (Arg, 0);
782                     break;
783
784                 case 'C':
785                     if (Arg[2] == 'l' && Arg[3] == '\0') {
786                         /* Make local variables static */
787                         CmdAddArg (&CC65, "-Cl");
788                     } else {
789                         /* Specify linker config file */
790                         LinkerConfig = GetArg (&I, 2);
791                     }
792                     break;
793
794                 case 'D':
795                     /* Define a preprocessor symbol (compiler) */
796                     CmdAddArg (&CC65, "-D");
797                     CmdAddArg (&CC65, GetArg (&I, 2));
798                     break;
799
800                 case 'I':
801                     /* Include directory (compiler) */
802                     OptIncludeDir (Arg, GetArg (&I, 2));
803                     break;
804
805                 case 'L':
806                     if (Arg[2] == 'n') {
807                         /* VICE label file (linker) */
808                         CmdAddArg (&LD65, "-Ln");
809                         CmdAddArg (&LD65, GetArg (&I, 3));
810                     } else {
811                         UnknownOption (Arg);
812                     }
813                     break;
814
815                 case 'O':
816                     /* Optimize code (compiler, also covers -Oi and others) */
817                     CmdAddArg (&CC65, Arg);
818                     break;
819
820                 case 'S':
821                     /* Dont assemble and link the created files */
822                     DontLink = DontAssemble = 1;
823                     break;
824
825                 case 'T':
826                     /* Include source as comment (compiler) */
827                     CmdAddArg (&CC65, "-T");
828                     break;
829
830                 case 'V':
831                     /* Print version number */
832                     OptVersion (Arg, 0);
833                     break;
834
835                 case 'W':
836                     /* Suppress warnings - compiler and assembler */
837                     CmdAddArg (&CC65, "-W");
838                     CmdAddArg (&CA65, "-W");
839                     CmdAddArg (&CA65, "0");
840                     break;
841
842                 case 'c':
843                     /* Don't link the resulting files */
844                     DontLink = 1;
845                     break;
846
847                 case 'd':
848                     /* Debug mode (compiler) */
849                     OptDebug (Arg, 0);
850                     break;
851
852                 case 'g':
853                     /* Debugging - add to compiler and assembler */
854                     OptDebugInfo (Arg, 0);
855                     break;
856
857                 case 'h':
858                 case '?':
859                     /* Print help - cl65 */
860                     OptHelp (Arg, 0);
861                     break;
862
863                 case 'm':
864                     /* Create a map file (linker) */
865                     CmdAddArg (&LD65, "-m");
866                     CmdAddArg (&LD65, GetArg (&I, 2));
867                     break;
868
869                 case 'o':
870                     /* Name the output file */
871                     OutputName = GetArg (&I, 2);
872                     break;
873
874                 case 't':
875                     /* Set target system - compiler and linker */
876                     OptTarget (Arg, GetArg (&I, 2));
877                     break;
878
879                 case 'v':
880                     if (Arg [2] == 'm') {
881                         /* Verbose map file (linker) */
882                         CmdAddArg (&LD65, "-vm");
883                     } else {
884                         /* Verbose mode (compiler, assembler, linker) */
885                         OptVerbose (Arg, 0);
886                     }
887                     break;
888
889                 default:
890                     UnknownOption (Arg);
891             }
892         } else {
893
894             /* Remember the first file name */
895             if (FirstInput == 0) {
896                 FirstInput = Arg;
897             }
898
899             /* Determine the file type by the extension */
900             switch (GetFileType (Arg)) {
901
902                 case FILETYPE_C:
903                     /* Compile the file */
904                     Compile (Arg);
905                     break;
906
907                 case FILETYPE_ASM:
908                     /* Assemble the file */
909                     if (!DontAssemble) {
910                         Assemble (Arg);
911                     }
912                     break;
913
914                 case FILETYPE_OBJ:
915                 case FILETYPE_LIB:
916                     /* Add to the linker files */
917                     CmdAddFile (&LD65, Arg);
918                     break;
919
920                 case FILETYPE_GR:
921                     /* Add to the resource compiler files */
922                     CompileRes (Arg);
923                     break;
924
925                 default:
926                     Error ("Don't know what to do with `%s'", Arg);
927
928             }
929
930         }
931
932         /* Next argument */
933         ++I;
934     }
935
936     /* Check if we had any input files */
937     if (FirstInput == 0) {
938         Warning ("No input files");
939     }
940
941     /* Link the given files if requested and if we have any */
942     if (DontLink == 0 && LD65.FileCount > 0) {
943         Link ();
944     }
945
946     /* Return an apropriate exit code */
947     return EXIT_SUCCESS;
948 }
949
950
951