1 /*
2 *      Front End for The Small C+ Compiler
3 *
4 *      Based on the frontend from zcc096 but substantially
5 *      reworked
6 *
7 *      Each file is now processed in turn (all the way through)
8 *      And then everything is linked at the end, this makes it
9 *      quite a bit nicer, and a bit more standard - saves having
10 *      to preprocess all files and then find out there's an error
11 *      at the start of the first one!
12 *
13 *      $Id: zcc.c,v 1.195 2017-01-09 17:53:07 aralbrec Exp $
14 */
15 
16 
17 #include        <stdio.h>
18 #include        <string.h>
19 #include        <stdlib.h>
20 #include        <stdarg.h>
21 #include        <ctype.h>
22 #include        <stddef.h>
23 #include        <stdint.h>
24 #include        <inttypes.h>
25 #include        <time.h>
26 #include        <sys/stat.h>
27 #include        "zcc.h"
28 #include        "regex/regex.h"
29 #include        "dirname.h"
30 #include        "option.h"
31 
32 #ifdef WIN32
33 #include        <direct.h>
34 #include        <process.h>
35 #else
36 #include        <unistd.h>
37 #endif
38 
39 
40 #ifdef WIN32
41 #ifndef strcasecmp
42 #define strcasecmp(a,b) stricmp(a,b)
43 #endif
44 #endif
45 
46 #if (_BSD_SOURCE || _SVID_SOURCE || _XOPEN_SOURCE >= 500)
47 #define mktempfile(a) mkstemp(a)
48 #else
49 #define mktempfile(a) mktemp(a)
50 #endif
51 
52 enum {
53     CPU_MAP_TOOL_Z80ASM = 0,
54     CPU_MAP_TOOL_SCCZ80,
55     CPU_MAP_TOOL_ZSDCC,
56     CPU_MAP_TOOL_COPT,
57     CPU_MAP_TOOL_SIZE
58 };
59 
60 enum {
61     CPU_TYPE_Z80 = 0,
62     CPU_TYPE_Z80N,
63     CPU_TYPE_Z180,
64     CPU_TYPE_R2K,
65     CPU_TYPE_R3K,
66     CPU_TYPE_8080,
67     CPU_TYPE_8085,
68     CPU_TYPE_GBZ80,
69     CPU_TYPE_SIZE
70 };
71 
72 
73 typedef struct arg_s arg_t;
74 
75 struct arg_s {
76     char  *name;
77     int    flags;
78     void(*setfunc)(arg_t *arg, char *);
79     void  *data;
80     int   *num_ptr;
81     char  *help;
82     char  *defvalue;
83 };
84 
85 
86 typedef struct cpu_map_s cpu_map_t;
87 
88 struct cpu_map_s {
89     char *tool[CPU_MAP_TOOL_SIZE];
90 };
91 
92 
93 typedef struct pragma_m4_s pragma_m4_t;
94 
95 struct pragma_m4_s {
96     int         seen;
97     const char *pragma;
98     const char *m4_name;
99 };
100 
101 
102 /* All our function prototypes */
103 
104 static void            add_option_to_compiler(char *arg);
105 static void            gather_from_list_file(char *filename);
106 static void            add_file_to_process(char *filename);
107 
108 static void            SetNumber(arg_t *argument, char *arg);
109 static void            SetStringConfig(arg_t *argument, char *arg);
110 static void            LoadConfigFile(arg_t *argument, char *arg);
111 static void            parse_cmdline_arg(char *arg);
112 static void            AddPreProc(option *arg, char *);
113 static void            AddPreProcIncPath(option *arg, char *);
114 static void            AddToArgs(option *arg, char *);
115 static void            AddToArgsQuoted(option *arg, char *);
116 static void            AddLinkLibrary(option *arg, char *);
117 static void            AddLinkSearchPath(option *arg, char *);
118 static void            usage(const char *program);
119 static void            print_help_text(const char *program);
120 static void            GlobalDefc(option *argument, char *);
121 static void            Alias(option *arg, char *);
122 static void            PragmaDefine(option *arg, char *);
123 static void            PragmaExport(option *arg, char *);
124 static void            PragmaRedirect(option *arg, char *);
125 static void            PragmaNeed(option *arg, char *);
126 static void            PragmaBytes(option *arg, char *);
127 static void            PragmaInclude(option *arg, char *);
128 static void            AddArray(arg_t *arg, char *);
129 static void            conf_opt_code_speed(option *arg, char *);
130 static void            write_zcc_defined(char *name, int value, int export);
131 
132 static void           *mustmalloc(size_t);
133 static char           *muststrdup(const char *s);
134 static char           *zcc_strstrip(char *s);
135 static char           *zcc_strrstrip(char *s);
136 static char           *zcc_ascii_only(char *s);
137 static int             hassuffix(char *file, char *suffix_to_check);
138 static char           *stripsuffix(char *, char *);
139 static char           *changesuffix(char *, char *);
140 static char           *find_file_ext(char *filename);
141 static int             is_path_absolute(char *filename);
142 static int             process(char *, char *, char *, char *, enum iostyle, int, int, int);
143 static int             linkthem(char *);
144 static int             get_filetype_by_suffix(char *);
145 static void            BuildAsmLine(char *, size_t, char *);
146 static void            parse_cmdline_arg(char *option);
147 static void            BuildOptions(char **, char *);
148 static void            BuildOptions_start(char **, char *);
149 static void            BuildOptionsQuoted(char **, char *);
150 static void            copy_output_files_to_destdir(char *suffix, int die_on_fail);
151 static void            parse_configfile(const char *config_line);
152 static void            parse_configfile_line(char *config_line);
153 static void            KillEOL(char *line);
154 static int             add_variant_args(char *wanted, int num_choices, char **choices);
155 
156 static void            configure_assembler();
157 static void            configure_compiler();
158 static void            configure_misc_options();
159 static void            configure_maths_library(char **libstring);
160 
161 static void            apply_copt_rules(int filenumber, int num, char **rules, char *ext1, char *ext2, char *ext);
162 static void            zsdcc_asm_filter_comments(int filenumber, char *ext);
163 static void            remove_temporary_files(void);
164 static void            remove_file_with_extension(char *file, char *suffix);
165 static void            ShowErrors(char *, char *);
166 static int             copyprepend_file(char *src, char *src_extension, char *dest, char *dest_extension, char *prepend);
167 static int             copy_file(char *src, char *src_extension, char *dest, char *dest_extension);
168 static int             prepend_file(char *src, char *src_extension, char *dest, char *dest_extension, char *prepend);
169 static int             copy_defc_file(char *name1, char *ext1, char *name2, char *ext2);
170 static void            tempname(char *);
171 static int             find_zcc_config_fileFile(const char *program, char *arg, int argc, char *buf, size_t buflen);
172 static void            parse_option(char *option);
173 static void            add_zccopt(char *fmt, ...);
174 static char           *replace_str(const char *str, const char *old, const char *new);
175 static void            setup_default_configuration();
176 static void            print_specs();
177 static int             isquote(unsigned char c);
178 static char           *qstrtok(char *s, const char *delim);
179 static char           *strip_inner_quotes(char *p);
180 static char           *strip_outer_quotes(char *p);
181 static int             zcc_asprintf(char **s, const char *fmt, ...);
182 static int             zcc_getdelim(char **lineptr, unsigned int *n, int delimiter, FILE *stream);
183 
184 static int             createapp = 0;    /* Go the next stage and create the app */
185 static int             z80verbose = 0;
186 static int             cleanup = 1;
187 static int             assembleonly = 0;
188 static int             lstcwd = 0;
189 static int             compileonly = 0;
190 static int             m4only = 0;
191 static int             clangonly = 0;
192 static int             llvmonly = 0;
193 static int             makelib = 0;
194 static int             build_bin = 0;
195 static int             c_code_in_asm = 0;
196 static int             opt_code_size = 0;
197 static int             zopt = 0;
198 static int             verbose = 0;
199 static int             peepholeopt = 0;
200 static int             sdccpeepopt = 0;
201 static int             symbolson = 0;
202 static int             lston = 0;
203 static int             mapon = 0;
204 static int             globaldefon = 0;
205 static char           *globaldefrefile = NULL;
206 static int             preprocessonly = 0;
207 static int             relocate = 0;
208 static int             relocinfo = 0;
209 static int             sdcc_signed_char = 0;
210 static int             swallow_M = 0;
211 static int             c_print_specs = 0;
212 static int             c_zorg = -1;
213 static int             c_sccz80_inline_ints = 0;
214 static int             max_argc;
215 static int             gargc;
216 static char          **gargv;
217 /* filelist has to stay as ** because we change suffix all the time */
218 static int             nfiles = 0;
219 static char          **filelist = NULL;              /* Working filenames   */
220 static char          **original_filenames = NULL;    /* Original filenames  */
221 static char          **temporary_filenames = NULL;   /* Temporary filenames */
222 static char           *outputfile = NULL;
223 static char           *c_linker_output_file = NULL;
224 static char           *cpparg;
225 static char           *cpp_incpath_first;
226 static char           *cpp_incpath_last;
227 static char           *comparg;
228 static char           *clangarg;
229 static char           *clangcpparg = "-D\"__attribute__(x)= \" -D\"__builtin_unreachable(x)= \" ";
230 static char           *llvmopt;
231 static char           *llvmarg;
232 static char           *linkargs;
233 static char           *linker_libpath_first;
234 static char           *linker_libpath_last;
235 static char           *linklibs;
236 static char           *linker_linklib_first;
237 static char           *linker_linklib_last;
238 static char           *asmargs;
239 static char           *appmakeargs;
240 static char           *sccz80arg = NULL;
241 static char           *sdccarg = NULL;
242 static char           *m4arg = NULL;
243 static char           *pragincname = NULL;  /* File containing pragmas to append to zcc_opt.def */
244 static char           *zccopt = NULL;       /* Text to append to zcc_opt.def */
245 static char           *c_subtype = NULL;
246 static char           *c_clib = NULL;
247 static int             c_startup = -2;
248 static int             c_startupoffset = -1;
249 static int             c_nostdlib = 0;
250 static int             c_cpu = 0;
251 static int             c_nocrt = 0;
252 static char           *c_crt_incpath = NULL;
253 static int             processing_user_command_line_arg = 0;
254 static char            c_sccz80_r2l_calling;
255 
256 static char            filenamebuf[FILENAME_MAX + 1];
257 #ifdef WIN32
258 static char            tmpnambuf[FILENAME_MAX+1];
259 #endif
260 
261 #define ASM_Z80ASM     0
262 #define IS_ASM(x)  ( assembler_type == (x) )
263 static int             assembler_type = ASM_Z80ASM;
264 static enum iostyle    assembler_style = outimplied;
265 static int             linker_output_separate_arg = 0;
266 
267 static enum iostyle    compiler_style = outimplied;
268 
269 #define CC_SCCZ80 0
270 #define CC_SDCC   1
271 static char           *c_compiler_type = "sccz80";
272 static int             compiler_type = CC_SCCZ80;
273 
274 static char           *zcc_opt_dir = ".";
275 static char           *zcc_opt_def = "zcc_opt.def";
276 
277 static char           *defaultout = "a.bin";
278 
279 #define AF_BOOL_TRUE      1
280 #define AF_BOOL_FALSE     2
281 #define AF_MORE           4
282 #define AF_DEPRECATED     8
283 
284 static char  *c_install_dir = PREFIX "/";
285 static char  *c_options = NULL;
286 
287 static char  *c_z80asm_exe = "z88dk-z80asm";
288 
289 static char  *c_clang_exe = "zclang";
290 static char  *c_llvm_exe = "zllvm-cbe";
291 static char  *c_sdcc_exe = "zsdcc";
292 static char  *c_sccz80_exe = "sccz80";
293 static char  *c_cpp_exe = "z88dk-ucpp";
294 static char  *c_sdcc_preproc_exe = "zsdcpp";
295 static char  *c_zpragma_exe = "z88dk-zpragma";
296 static char  *c_copt_exe = "z88dk-copt";
297 static char  *c_appmake_exe = "z88dk-appmake";
298 #ifndef WIN32
299 static char  *c_copycmd = "cat";
300 #else
301 static char  *c_copycmd = "type";
302 #endif
303 static char  *c_extension_config = "o";
304 static char  *c_incpath = NULL;
305 static char  *c_clangincpath = NULL;
306 static char  *c_m4opts = NULL;
307 static char  *c_coptrules1 = NULL;
308 static char  *c_coptrules2 = NULL;
309 static char  *c_coptrules3 = NULL;
310 static char  *c_coptrules9 = NULL;
311 static char  *c_coptrules_cpu = NULL;
312 static char  *c_coptrules_user = NULL;
313 static char  *c_coptrules_sccz80 = NULL;
314 static char  *c_coptrules_target = NULL;
315 static char  *c_sdccopt1 = NULL;
316 static char  *c_sdccopt2 = NULL;
317 static char  *c_sdccopt3 = NULL;
318 static char  *c_sdccopt9 = NULL;
319 static char  *c_sdccpeeph0 = NULL;
320 static char  *c_sdccpeeph1 = NULL;
321 static char  *c_sdccpeeph2 = NULL;
322 static char  *c_sdccpeeph3 = NULL;
323 static char  *c_sdccpeeph0cs = NULL;
324 static char  *c_sdccpeeph1cs = NULL;
325 static char  *c_sdccpeeph2cs = NULL;
326 static char  *c_sdccpeeph3cs = NULL;
327 static char  *c_crt0 = NULL;
328 static char  *c_linkopts = NULL;
329 static char  *c_asmopts = NULL;
330 static char  *c_altmathlib = NULL;
331 static char  *c_altmathflags = NULL;        /* "-math-z88 -D__NATIVE_MATH__"; */
332 static char  *c_startuplib = "z80_crt0";
333 static char  *c_genmathlib = "genmath";
334 static int    c_stylecpp = outspecified;
335 
336 static char  *c_extension = NULL;
337 static char  *c_assembler = NULL;
338 static char  *c_linker = NULL;
339 static char  *c_compiler = NULL;
340 static char **c_subtype_array = NULL;
341 static int    c_subtype_array_num = 0;
342 static char **c_clib_array = NULL;
343 static int    c_clib_array_num = 0;
344 static char **c_aliases_array = NULL;
345 static int    c_aliases_array_num = 0;
346 
347 static char **aliases = NULL;
348 static int    aliases_num = 0;
349 
350 static char   c_generate_debug_info = 0;
351 static char   c_help = 0;
352 
353 static arg_t  config[] = {
354     { "OPTIONS", 0, SetStringConfig, &c_options, NULL, "Extra options for port" },
355     { "CPP", 0, SetStringConfig, &c_cpp_exe, NULL, "Name of the cpp binary" },
356     { "SDCPP", 0, SetStringConfig, &c_sdcc_preproc_exe, NULL, "Name of the SDCC cpp binary" },
357     { "STYLECPP", 0, SetNumber, &c_stylecpp, NULL, "" },
358     { "ZPRAGMAEXE", 0, SetStringConfig, &c_zpragma_exe, NULL, "Name of the zpragma binary" },
359 
360     { "Z80EXE", 0, SetStringConfig, &c_z80asm_exe, NULL, "Name of the z80asm binary" },
361     { "LINKOPTS", 0, SetStringConfig, &c_linkopts, NULL, "Options for z80asm as linker", " -L\"DESTDIR/lib/clibs\" -I\"DESTDIR/lib\" " },
362     { "ASMOPTS", 0, SetStringConfig, &c_asmopts, NULL, "Options for z80asm as assembler", "-I\"DESTDIR/lib\"" },
363 
364     { "COMPILER", AF_DEPRECATED, SetStringConfig, &c_compiler, NULL, "Name of sccz80 binary (use SCCZ80EXE)" },
365     { "SCCZ80EXE", 0, SetStringConfig, &c_sccz80_exe, NULL, "Name of sccz80 binary" },
366     { "ZSDCCEXE", 0, SetStringConfig, &c_sdcc_exe, NULL, "Name of the sdcc binary" },
367     { "ZCLANGEXE", 0, SetStringConfig, &c_clang_exe, NULL, "Name of the clang binary" },
368     { "ZLLVMEXE", 0, SetStringConfig, &c_llvm_exe, NULL, "Name of the llvm-cbe binary" },
369 
370     { "APPMAKEEXE", 0, SetStringConfig, &c_appmake_exe, NULL, "" },
371     { "APPMAKER", AF_DEPRECATED, SetStringConfig, &c_appmake_exe, NULL, "Name of the applink binary (use APPMAKEEXE)" },
372 
373     { "COPTEXE", 0, SetStringConfig, &c_copt_exe, NULL, "" },
374     { "COPYCMD", 0, SetStringConfig, &c_copycmd, NULL, "" },
375 
376     { "INCPATH", 0, SetStringConfig, &c_incpath, NULL, "", "-isystem\"DESTDIR/include\" " },
377     { "CLANGINCPATH", 0, SetStringConfig, &c_clangincpath, NULL, "", "-isystem \"DESTDIR/include/_DEVELOPMENT/clang\" " },
378     { "M4OPTS", 0, SetStringConfig, &c_m4opts, NULL, "", " -I \"DESTDIR/src/m4\" " },
379     { "COPTRULES1", 0, SetStringConfig, &c_coptrules1, NULL, "", "\"DESTDIR/lib/z80rules.1\"" },
380     { "COPTRULES2", 0, SetStringConfig, &c_coptrules2, NULL, "", "\"DESTDIR/lib/z80rules.2\"" },
381     { "COPTRULES3", 0, SetStringConfig, &c_coptrules3, NULL, "", "\"DESTDIR/lib/z80rules.0\"" },
382     { "COPTRULES9", 0, SetStringConfig, &c_coptrules9, NULL, "", "\"DESTDIR/lib/z80rules.9\"" },
383     { "COPTRULESCPU", 0, SetStringConfig, &c_coptrules_cpu, NULL, "An extra copt file for CPU optimisation", NULL },
384     { "COPTRULESINLINE", 0, SetStringConfig, &c_coptrules_sccz80, NULL, "Optimisation file for inlining sccz80 ops", "\"DESTDIR/lib/z80rules.8\"" },
385     { "COPTRULESTARGET", 0, SetStringConfig, &c_coptrules_target, NULL, "Optimisation file for target specific operations",NULL },
386     { "SDCCOPT1", 0, SetStringConfig, &c_sdccopt1, NULL, "", "\"DESTDIR/libsrc/_DEVELOPMENT/sdcc_opt.1\"" },
387     { "SDCCOPT2", 0, SetStringConfig, &c_sdccopt2, NULL, "", "\"DESTDIR/libsrc/_DEVELOPMENT/sdcc_opt.2\"" },
388     { "SDCCOPT3", 0, SetStringConfig, &c_sdccopt3, NULL, "", "\"DESTDIR/libsrc/_DEVELOPMENT/sdcc_opt.3\"" },
389     { "SDCCOPT9", 0, SetStringConfig, &c_sdccopt9, NULL, "", "\"DESTDIR/libsrc/_DEVELOPMENT/sdcc_opt.9\"" },
390     { "SDCCPEEP0", 0, SetStringConfig, &c_sdccpeeph0, NULL, "", " --no-peep --peep-file \"DESTDIR/libsrc/_DEVELOPMENT/sdcc_peeph.0\"" },
391     { "SDCCPEEP1", 0, SetStringConfig, &c_sdccpeeph1, NULL, "", " --no-peep --peep-file \"DESTDIR/libsrc/_DEVELOPMENT/sdcc_peeph.1\"" },
392     { "SDCCPEEP2", 0, SetStringConfig, &c_sdccpeeph2, NULL, "", " --no-peep --peep-file \"DESTDIR/libsrc/_DEVELOPMENT/sdcc_peeph.2\"" },
393     { "SDCCPEEP3", 0, SetStringConfig, &c_sdccpeeph3, NULL, "", " --no-peep --peep-file \"DESTDIR/libsrc/_DEVELOPMENT/sdcc_peeph.3\"" },
394     { "SDCCOPTSZ0", 0, SetStringConfig, &c_sdccpeeph0cs, NULL, "", " --no-peep --peep-file \"DESTDIR/libsrc/_DEVELOPMENT/sdcc_peeph_cs.0\"" },
395     { "SDCCOPTSZ1", 0, SetStringConfig, &c_sdccpeeph1cs, NULL, "", " --no-peep --peep-file \"DESTDIR/libsrc/_DEVELOPMENT/sdcc_peeph_cs.1\"" },
396     { "SDCCOPTSZ2", 0, SetStringConfig, &c_sdccpeeph2cs, NULL, "", " --no-peep --peep-file \"DESTDIR/libsrc/_DEVELOPMENT/sdcc_peeph_cs.2\"" },
397     { "SDCCOPTSZ3", 0, SetStringConfig, &c_sdccpeeph3cs, NULL, "", " --no-peep --peep-file \"DESTDIR/libsrc/_DEVELOPMENT/sdcc_peeph_cs.3\"" },
398     { "CRT0", 0, SetStringConfig, &c_crt0, NULL, "" },
399 
400     { "ALTMATHLIB", 0, SetStringConfig, &c_altmathlib, NULL, "Name of the alt maths library" },
401     { "ALTMATHFLG", 0, SetStringConfig, &c_altmathflags, NULL, "Additional options for non-generic maths" },
402     { "Z88MATHLIB", AF_DEPRECATED, SetStringConfig, &c_altmathlib, NULL, "Name of the alt maths library (use ALTMATHLIB)" },
403     { "Z88MATHFLG", AF_DEPRECATED, SetStringConfig, &c_altmathflags, NULL, "Additional options for non-generic maths (use ALTMATHFLG)" },
404     { "STARTUPLIB", 0, SetStringConfig, &c_startuplib, NULL, "" },
405     { "GENMATHLIB", 0, SetStringConfig, &c_genmathlib, NULL, "" },
406     { "SUBTYPE",  0, AddArray, &c_subtype_array, &c_subtype_array_num, "Add a sub-type alias and config" },
407     { "CLIB",  0, AddArray, &c_clib_array, &c_clib_array_num, "Add a clib variant config" },
408     { "ALIAS",  0, AddArray, &c_aliases_array, &c_aliases_array_num, "Add an alias and options" },
409     { "INCLUDE", 0, LoadConfigFile, NULL, NULL, "Load a configuration file"},
410     { "", 0, NULL, NULL }
411 };
412 
413 static option options[] = {
414     { 'v', "verbose", OPT_BOOL,  "Output all commands that are run (-vn suppresses)" , &verbose, NULL, 0},
415     { 'h', "help", OPT_BOOL,  "Display this text" , &c_help, NULL, 0},
416     { 0, "o", OPT_STRING,  "Set the basename for linker output files" , &outputfile, NULL, 0},
417     { 0, "specs", OPT_BOOL,  "Print out compiler specs" , &c_print_specs, NULL, 0},
418 
419     { 0, "", OPT_HEADER, "CPU Targetting:", NULL, NULL, 0 },
420     { 0, "m8080", OPT_ASSIGN|OPT_INT, "Generate output for the i8080", &c_cpu, NULL, CPU_TYPE_8080 },
421     { 0, "m8085", OPT_ASSIGN|OPT_INT, "Generate output for the i8085", &c_cpu, NULL, CPU_TYPE_8085 },
422     { 0, "mz80", OPT_ASSIGN|OPT_INT, "Generate output for the z80", &c_cpu, NULL, CPU_TYPE_Z80 },
423     { 0, "mz80n", OPT_ASSIGN|OPT_INT, "Generate output for the z80n", &c_cpu, NULL, CPU_TYPE_Z80N },
424     { 0, "mz180", OPT_ASSIGN|OPT_INT, "Generate output for the z180", &c_cpu, NULL, CPU_TYPE_Z180 },
425     { 0, "mr2k", OPT_ASSIGN|OPT_INT, "Generate output for the Rabbit 2000", &c_cpu, NULL, CPU_TYPE_R2K },
426     { 0, "mr3k", OPT_ASSIGN|OPT_INT, "Generate output for the Rabbit 3000", &c_cpu, NULL, CPU_TYPE_R3K },
427     { 0, "mgbz80", OPT_ASSIGN|OPT_INT, "Generate output for the Gameboy Z80", &c_cpu, NULL, CPU_TYPE_GBZ80 },
428 
429     { 0, "", OPT_HEADER, "Target options:", NULL, NULL, 0 },
430     { 0, "subtype", OPT_STRING,  "Set the target subtype" , &c_subtype, NULL, 0},
431     { 0, "clib", OPT_STRING,  "Set the target clib type" , &c_clib, NULL, 0},
432     { 0, "crt0", OPT_STRING,  "Override the crt0 assembler file to use" , &c_crt0, NULL, 0},
433     { 0, "startuplib", OPT_STRING,  "Override STARTUPLIB - compiler base support routines" , &c_startuplib, NULL, 0},
434     { 0, "no-crt", OPT_BOOL|OPT_DOUBLE_DASH,  "Link without crt0 file" , &c_nocrt, NULL, 0},
435     { 0, "startup", OPT_INT,  "Set the startup type" , &c_startup, NULL, 0},
436     { 0, "startupoffset", OPT_INT|OPT_PRIVATE,  "Startup offset value (internal)" , &c_startupoffset, NULL, 0},
437     { 0, "zorg", OPT_INT,  "Set the origin (only certain targets)" , &c_zorg, NULL, 0},
438     { 0, "nostdlib", OPT_BOOL,  "If set ignore INCPATH, STARTUPLIB", &c_nostdlib, NULL, 0},
439     { 0, "pragma-redirect", OPT_FUNCTION,  "Redirect a function" , NULL, PragmaRedirect, 0},
440     { 0, "pragma-define", OPT_FUNCTION,  "Define the option in zcc_opt.def" , NULL, PragmaDefine, 0},
441     { 0, "pragma-output", OPT_FUNCTION,  "Define the option in zcc_opt.def (same as above)" , NULL, PragmaDefine, 0},
442     { 0, "pragma-export", OPT_FUNCTION,  "Define the option in zcc_opt.def and export as public" , NULL, PragmaExport, 0},
443     { 0, "pragma-need", OPT_FUNCTION,  "NEED the option in zcc_opt.def" , NULL, PragmaNeed, 0},
444     { 0, "pragma-bytes", OPT_FUNCTION,  "Dump a string of bytes zcc_opt.def" , NULL, PragmaBytes, 0},
445     { 0, "pragma-include", OPT_FUNCTION,  "Process include file containing pragmas" , NULL, PragmaInclude, 0},
446 
447     { 0, "", OPT_HEADER, "Lifecycle options:", NULL, NULL, 0 },
448     { 0, "m4", OPT_BOOL,  "Stop after processing m4 files" , &m4only, NULL, 0},
449     { 'E', "preprocess-only", OPT_BOOL|OPT_DOUBLE_DASH,  "Stop after preprocessing files" , &preprocessonly, NULL, 0},
450     { 'c', "compile-only", OPT_BOOL|OPT_DOUBLE_DASH,  "Stop after compiling .c .s .asm files to .o files" , &compileonly, NULL, 0},
451     { 'a', "assemble-only", OPT_BOOL|OPT_DOUBLE_DASH,  "Stop after compiling .c .s files to .asm files" , &assembleonly, NULL, 0},
452     { 'S', "assemble-only", OPT_BOOL|OPT_DOUBLE_DASH,  "Stop after compiling .c .s files to .asm files" , &assembleonly, NULL, 0},
453     { 'x', NULL, OPT_BOOL,  "Make a library out of source files" , &makelib, NULL, 0},
454     { 0, "create-app", OPT_BOOL,  "Run appmake on the resulting binary to create emulator usable file" , &createapp, NULL, 0},
455 
456 
457     { 0, "", OPT_HEADER, "M4 options:", NULL, NULL, 0 },
458     { 0, "Cm", OPT_FUNCTION,  "Add an option to m4" , &m4arg, AddToArgs, 0},
459 
460     { 0, "", OPT_HEADER, "Preprocessor options:", NULL, NULL, 0 },
461     { 0, "Cp", OPT_FUNCTION,  "Add an option to the preprocessor" , &cpparg, AddToArgs, 0},
462     { 0, "D", OPT_FUNCTION|OPT_INCLUDE_OPT,  "Define a preprocessor option" , NULL, AddPreProc, 0},
463     { 0, "U", OPT_FUNCTION|OPT_INCLUDE_OPT,  "Undefine a preprocessor option" , NULL, AddPreProc, 0},
464     { 0, "I", OPT_FUNCTION|OPT_INCLUDE_OPT,  "Add an include directory for the preprocessor" , NULL, AddPreProcIncPath, 0},
465     { 0, "iquote", OPT_FUNCTION|OPT_INCLUDE_OPT,  "Add a quoted include path for the preprocessor" , &cpparg, AddToArgsQuoted, 0},
466     { 0, "isystem", OPT_FUNCTION|OPT_INCLUDE_OPT,  "Add a system include path for the preprocessor" , &cpparg, AddToArgsQuoted, 0},
467 
468     { 0, "", OPT_HEADER, "Compiler (all) options:", NULL, NULL, 0 },
469     { 0, "compiler", OPT_STRING,  "Set the compiler type from the command line (sccz80,sdcc)" , &c_compiler_type, NULL, 0},
470     { 0, "c-code-in-asm", OPT_BOOL|OPT_DOUBLE_DASH,  "Add C code to .asm files" , &c_code_in_asm, NULL, 0},
471     { 0, "debug", OPT_BOOL, "Enable debugging support", &c_generate_debug_info, NULL, 0 },
472     { 0, "", OPT_HEADER, "Compiler (sccz80) options:", NULL, NULL, 0 },
473     { 0, "Cc", OPT_FUNCTION,  "Add an option to sccz80" , &sccz80arg, AddToArgs, 0},
474     { 0, "set-r2l-by-default", OPT_BOOL,  "(sccz80) Use r2l calling convention by default", &c_sccz80_r2l_calling, NULL, 0},
475     { 0, "O", OPT_INT,  "Set the peephole optimiser setting for copt" , &peepholeopt, NULL, 0},
476     { 0, "opt-code-speed", OPT_FUNCTION|OPT_DOUBLE_DASH,  "Optimize for code speed (sccz80 only)" , NULL, conf_opt_code_speed, 0},
477     { 0, "", OPT_HEADER, "Compiler (sdcc) options:", NULL, NULL, 0 },
478     { 0, "Cs", OPT_FUNCTION,  "Add an option to sdcc" , &sdccarg, AddToArgs, 0},
479     { 0, "opt-code-size", OPT_BOOL|OPT_DOUBLE_DASH,  "Optimize for code size (sdcc only)" , &opt_code_size, NULL, 0},
480     { 0, "SO", OPT_INT,  "Set the peephole optimiser setting for sdcc-peephole" , &sdccpeepopt, NULL, 0},
481     { 0, "fsigned-char", OPT_BOOL|OPT_DOUBLE_DASH,  "Use signed chars by default" , &sdcc_signed_char, NULL, 0},
482     { 0, "", OPT_HEADER, "Compiler (clang/llvm) options:", NULL, NULL, 0 },
483     { 0, "Cg", OPT_FUNCTION,  "Add an option to clang" , &clangarg, AddToArgs, 0},
484     { 0, "clang", OPT_BOOL,  "Stop after translating .c files to llvm ir" , &clangonly, NULL, 0},
485     { 0, "llvm", OPT_BOOL,  "Stop after llvm-cbe generates new .cbe.c files" , &llvmonly, NULL, 0},
486     { 0, "Co", OPT_FUNCTION,  "Add an option to llvm-opt" , &llvmopt, AddToArgs, 0},
487     { 0, "Cv", OPT_FUNCTION,  "Add an option to llvm-cbe" , &llvmarg, AddToArgs, 0},
488     { 0, "zopt", OPT_BOOL,  "Enable llvm-optimizer (clang only)" , &zopt, NULL, 0},
489     { 0, "", OPT_HEADER, "Assembler options:", NULL, NULL, 0 },
490     { 0, "Ca", OPT_FUNCTION,  "Add an option to the assembler" , &asmargs, AddToArgsQuoted, 0},
491     { 0, "z80-verb", OPT_BOOL,  "Make the assembler more verbose" , &z80verbose, NULL, 0},
492     { 0, "", OPT_HEADER, "Linker options:", NULL, NULL, 0 },
493     { 0, "Cl", OPT_FUNCTION,  "Add an option to the linker" , &linkargs, AddToArgsQuoted, 0},
494     { 0, "bn", OPT_STRING,  "Set the output file for the linker stage" , &c_linker_output_file, NULL, 0},
495     { 0, "reloc-info", OPT_BOOL,  "Generate binary file relocation information" , &relocinfo, NULL, 0},
496     { 'm', "gen-map-file", OPT_BOOL,  "Generate an output map of the final executable" , &mapon, NULL, 0},
497     { 's', "gen-symbol-file", OPT_BOOL,  "Generate a symbol map of the final executable" , &symbolson, NULL, 0},
498     { 0, "list", OPT_BOOL|OPT_DOUBLE_DASH,  "Generate list files" , &lston, NULL, 0},
499     { 'R', NULL, OPT_BOOL|OPT_DEPRECATED,  "Generate relocatable code (deprecated)" , &relocate, NULL, 0},
500     { 0, NULL, OPT_HEADER, "Appmake options:", NULL, NULL, 0 },
501     { 0, "L", OPT_FUNCTION|OPT_INCLUDE_OPT,  "Add a library search path" , NULL, AddLinkSearchPath, 0},
502     { 0, "l", OPT_FUNCTION|OPT_INCLUDE_OPT,  "Add a library" , NULL, AddLinkLibrary, 0},
503     { 0, "Cz", OPT_FUNCTION,  "Add an option to appmake" , &appmakeargs, AddToArgs, 0},
504 
505     { 0, "", OPT_HEADER, "Misc options:", NULL, NULL, 0 },
506     { 0, "g", OPT_FUNCTION|OPT_INCLUDE_OPT,  "Generate a global defc file of the final executable (-g -gp -gpf:filename)" , &globaldefrefile, GlobalDefc, 0},
507     { 0, "alias", OPT_FUNCTION,  "Define a command line alias" , NULL, Alias, 0},
508     { 0, "lstcwd", OPT_BOOL|OPT_DOUBLE_DASH,  "Paths in .lst files are relative to the current working dir" , &lstcwd, NULL, 0},
509     { 0, "custom-copt-rules", OPT_STRING,  "Custom user copt rules" , &c_coptrules_user, NULL, 0},
510     { 'M', NULL, OPT_BOOL|OPT_PRIVATE,  "Swallow -M option in configs" , &swallow_M, NULL, 0},
511     { 0, "vn", OPT_BOOL_FALSE|OPT_PRIVATE,  "Turn off command tracing" , &verbose, NULL, 0},
512     { 0, "", 0, NULL },
513 
514 };
515 
516 
517 cpu_map_t cpu_map[CPU_TYPE_SIZE] = {
518     {{ "-mz80"   , "-mz80"   , "-mz80"   , ""        }},          /* CPU_TYPE_Z80     : CPU_MAP_TOOL_Z80ASM, CPU_MAP_TOOL_SCCZ80, CPU_MAP_TOOL_ZSDCC, CPU_TOOL_COPT */
519     {{ "-mz80n"  , "-mz80n"  , "-mz80"   , ""        }},          /* CPU_TYPE_Z80N    : CPU_MAP_TOOL_Z80ASM, CPU_MAP_TOOL_SCCZ80, CPU_MAP_TOOL_ZSDCC                */
520     {{ "-mz180"  , "-mz180"  , "-mz180 -portmode=z180", "" }},    /* CPU_TYPE_Z180    : CPU_MAP_TOOL_Z80ASM, CPU_MAP_TOOL_SCCZ80, CPU_MAP_TOOL_ZSDCC, CPU_TOOL_COPT */
521     {{ "-mr2k"   , "-mr2k"   , "-mr2k"   , ""        }},          /* CPU_TYPE_R2K     : CPU_MAP_TOOL_Z80ASM, CPU_MAP_TOOL_SCCZ80, CPU_MAP_TOOL_ZSDCC, CPU_TOOL_COPT */
522     {{ "-mr3k"   , "-mr3k"   , "-mr3ka"  , ""        }},          /* CPU_TYPE_R3K     : CPU_MAP_TOOL_Z80ASM, CPU_MAP_TOOL_SCCZ80, CPU_MAP_TOOL_ZSDCC, CPU_TOOL_COPT */
523     {{ "-m8080"  , "-m8080"  , "-mz80"   , "-m8080"  }},          /* CPU_TYPE_8080    : CPU_MAP_TOOL_Z80ASM, CPU_MAP_TOOL_SCCZ80, CPU_MAP_TOOL_ZSDCC, CPU_TOOL_COPT */
524     {{ "-m8085"  , "-m8085"  , "-mz80"   , "-m8080"  }},          /* CPU_TYPE_8085    : CPU_MAP_TOOL_Z80ASM, CPU_MAP_TOOL_SCCZ80, CPU_MAP_TOOL_ZSDCC, CPU_TOOL_COPT */
525     {{ "-mgbz80" , "-mgbz80" , "-mgbz80" , "-mgbz80" }}           /* CPU_TYPE_GBZ80   : CPU_MAP_TOOL_Z80ASM, CPU_MAP_TOOL_SCCZ80, CPU_MAP_TOOL_ZSDCC, CPU_TOOL_COPT */
526 };
527 
528 
select_cpu(int n)529 char *select_cpu(int n)
530 {
531     return cpu_map[c_cpu].tool[n];
532 }
533 
534 
535 pragma_m4_t important_pragmas[] = {
536     { 0, "startup", "__STARTUP" },
537     { 0, "startupoffset", "__STARTUP_OFFSET" },
538     { 0, "CRT_INCLUDE_DRIVER_INSTANTIATION", "M4__CRT_INCLUDE_DRIVER_INSTANTIATION" },
539     { 0, "CRT_ITERM_EDIT_BUFFER_SIZE", "M4__CRT_ITERM_EDIT_BUFFER_SIZE" },
540     { 0, "CRT_OTERM_FZX_DRAW_MODE", "M4__CRT_OTERM_FZX_DRAW_MODE" },
541     { 0, "CRT_APPEND_MMAP", "M4__CRT_APPEND_MMAP" },
542     { 0, "__MMAP", "M4__MMAP" },
543 };
544 
545 
mustmalloc(size_t n)546 static void *mustmalloc(size_t n)
547 {
548     void           *p;
549 
550     if ((p = malloc(n)) == 0) {
551         fprintf(stderr, "malloc failed\n");
552         exit(1);
553     }
554     return (p);
555 }
556 
muststrdup(const char * s)557 static char *muststrdup(const char *s)
558 {
559     char *r;
560 
561     if ((r = strdup(s)) == NULL) {
562         fprintf(stderr, "strdup failed\n");
563         exit(1);
564     }
565     return r;
566 }
567 
zcc_strstrip(char * s)568 static char *zcc_strstrip(char *s)
569 {
570     while (isspace(*s)) ++s;
571     return zcc_strrstrip(s);
572 }
573 
zcc_strrstrip(char * s)574 static char *zcc_strrstrip(char *s)
575 {
576     char *p;
577 
578     for (p = s + strlen(s); p != s; *p = 0)
579         if (!isspace(*--p)) break;
580 
581     return s;
582 }
583 
zcc_ascii_only(char * s)584 static char *zcc_ascii_only(char *s)
585 {
586     char *p;
587 
588     for (p = s; *p; ++p)
589         *p &= 0x7f;
590 
591     return s;
592 }
593 
hassuffix(char * name,char * suffix)594 static int hassuffix(char *name, char *suffix)
595 {
596     int             nlen, slen;
597 
598     nlen = strlen(name);
599     slen = strlen(suffix);
600 
601     if (slen > nlen)
602         return (0);
603     return (strcmp(&name[nlen - slen], suffix) == 0);
604 }
605 
stripsuffix(char * name,char * suffix)606 static char *stripsuffix(char *name, char *suffix)
607 {
608     char *p, *r;
609 
610     /* Recursively strip suffix */
611     r = muststrdup(name);
612     while (((p = find_file_ext(r)) != NULL) && (strcmp(p, suffix) == 0))
613         *p = '\0';
614     return r;
615 }
616 
changesuffix(char * name,char * suffix)617 static char *changesuffix(char *name, char *suffix)
618 {
619     char *p, *r;
620 
621     if ((p = find_file_ext(name)) == NULL) {
622         r = mustmalloc(strlen(name) + strlen(suffix) + 1);
623         sprintf(r, "%s%s", name, suffix);
624     }
625     else {
626         r = mustmalloc(p - name + strlen(suffix) + 1);
627         r[0] = '\0';
628         strncat(r, name, p - name);
629         strcat(r, suffix);
630     }
631 
632     return (r);
633 }
634 
process(char * suffix,char * nextsuffix,char * processor,char * extraargs,enum iostyle ios,int number,int needsuffix,int src_is_original)635 int process(char *suffix, char *nextsuffix, char *processor, char *extraargs, enum iostyle ios, int number, int needsuffix, int src_is_original)
636 {
637     int             status, errs;
638     int             tstore;
639     char            buffer[8192], *outname;
640 
641     errs = 0;
642 
643     if (!hassuffix(filelist[number], suffix))
644         return (0);
645 
646     outname = changesuffix(temporary_filenames[number], nextsuffix);
647 
648     switch (ios) {
649     case outimplied:
650         /* Dropping the suffix for Z80..cheating! */
651         tstore = strlen(filelist[number]) - strlen(suffix);
652         if (!needsuffix)
653             filelist[number][tstore] = 0;
654         snprintf(buffer, sizeof(buffer), "%s %s \"%s\"", processor, extraargs, filelist[number]);
655         filelist[number][tstore] = '.';
656         break;
657     case outspecified:
658         snprintf(buffer, sizeof(buffer), "%s %s \"%s\" \"%s\"", processor, extraargs, filelist[number], outname);
659         break;
660     case outspecified_flag:
661         snprintf(buffer, sizeof(buffer), "%s %s \"%s\" -o \"%s\"", processor, extraargs, filelist[number], outname);
662         break;
663     case filter:
664         snprintf(buffer, sizeof(buffer), "%s %s < \"%s\" > \"%s\"", processor, extraargs, filelist[number], outname);
665         break;
666     case filter_outspecified_flag:
667         snprintf(buffer, sizeof(buffer), "%s %s < \"%s\" -o \"%s\"", processor, extraargs, filelist[number], outname);
668         break;
669     }
670 
671     if (verbose) {
672         printf("%s\n", buffer);
673         fflush(stdout);
674     }
675 
676     status = system(buffer);
677 
678     if (status != 0) {
679         errs = 1;
680         free(outname);
681     }
682     else {
683         /* Free up the allocated memory */
684         free(filelist[number]);
685         filelist[number] = outname;
686     }
687 
688     return (errs);
689 }
690 
691 
linkthem(char * linker)692 int linkthem(char *linker)
693 {
694     int             i, len, offs, status;
695     char           *temp, *cmdline;
696     char            tname[FILENAME_MAX + 1];
697     FILE           *out, *prj;
698 
699     if (compileonly)
700     {
701         len = offs = zcc_asprintf(&temp, "%s %s -o\"%s\" %s",
702             linker,
703             select_cpu(CPU_MAP_TOOL_Z80ASM),
704             outputfile,
705             linkargs);
706     }
707     else if (makelib)
708     {
709         len = offs = zcc_asprintf(&temp, "%s %s %s -d %s %s -x\"%s\"",
710             linker,
711             (z80verbose && IS_ASM(ASM_Z80ASM)) ? "-v" : "",
712             select_cpu(CPU_MAP_TOOL_Z80ASM),
713             IS_ASM(ASM_Z80ASM) ? "" : "-Mo ",
714             linkargs,
715             outputfile);
716     }
717     else
718     {
719         /* late assembly for first file which acts as crt */
720         if (verbose) printf("\nPROCESSING CRT\n");
721         if (process(".asm", c_extension, c_assembler, c_crt_incpath ? c_crt_incpath : "", assembler_style, 0, YES, NO))
722             exit(1);
723         if (verbose) puts("");
724         free(c_crt_incpath);
725 
726         len = offs = zcc_asprintf(&temp, "%s %s -b -d %s -o%s\"%s\" %s%s%s%s%s%s%s%s%s",
727             linker,
728             select_cpu(CPU_MAP_TOOL_Z80ASM),
729             IS_ASM(ASM_Z80ASM) ? "" : "-Mo ",
730             linker_output_separate_arg ? " " : "",
731             outputfile,
732             (z80verbose && IS_ASM(ASM_Z80ASM)) ? "-v " : "",
733             (relocate && IS_ASM(ASM_Z80ASM)) ? "-R " : "",
734             globaldefon ? "-g " : "",
735             (createapp || mapon) ? "-m " : "",
736             (createapp || symbolson) ? "-s " : "",
737             relocinfo ? "--reloc-info " : "",
738             linkargs,
739             (c_nostdlib == 0) ? c_linkopts : "",
740             linklibs);
741     }
742 
743     tname[0] = '\0';
744     prj = z80verbose ? fopen(PROJFILE, "w") : NULL;
745 
746     if ((nfiles > 2) && IS_ASM(ASM_Z80ASM))
747     {
748         /* place source files into a list file for z80asm */
749 
750         tempname(tname);
751         strcat(tname, ".lst");
752         if ((out = fopen(tname, "w")) == NULL) goto USE_COMMANDLINE;
753 
754         for (i = 0; i < nfiles; ++i)
755         {
756             if (hassuffix(filelist[i], c_extension))
757             {
758                 fprintf(out, "%s\n", filelist[i]);
759                 if (prj) fprintf(prj, "%s\n", original_filenames[i]);
760             }
761         }
762 
763         fclose(out);
764 
765         len += strlen(tname) + 5;
766         cmdline = calloc(len, sizeof(char));
767 
768         snprintf(cmdline, len, "%s \"@%s\"", temp, tname);
769     }
770     else
771     {
772     USE_COMMANDLINE:
773 
774         /* place source files on the command line */
775 
776         for (i = 0; i < nfiles; i++)
777             len += strlen(filelist[i]) + 7;
778         len++;
779 
780         /* So the total length we need is now in len, let's malloc and do it */
781 
782         cmdline = calloc(len, sizeof(char));
783         strcpy(cmdline, temp);
784 
785         for (i = 0; i < nfiles; ++i)
786         {
787             if (hassuffix(filelist[i], c_extension))
788             {
789                 offs += snprintf(cmdline + offs, len - offs, " \"%s\"", filelist[i]);
790                 if (prj) fprintf(prj, "%s\n", original_filenames[i]);
791             }
792         }
793     }
794 
795     if (prj) fclose(prj);
796 
797     if (verbose) {
798         printf("%s\n", cmdline);
799         fflush(stdout);
800     }
801     status = system(cmdline);
802 
803     if (cleanup && strlen(tname))
804         remove_file_with_extension(tname, ".lst");
805 
806     free(cmdline);
807     free(temp);
808 
809     return (status);
810 }
811 
main(int argc,char ** argv)812 int main(int argc, char **argv)
813 {
814     int             i, ft, gc;
815     char           *ptr;
816     char            config_filename[FILENAME_MAX + 1];
817     char            asmarg[4096];    /* Hell, that should be long enough! */
818     char            buffer[LINEMAX + 1];    /* For reading in option file */
819     FILE           *fp;
820 
821 #ifdef WIN32
822     /* Randomize temporary filenames for windows (it may end up in cwd)  */
823     snprintf(tmpnambuf, sizeof(tmpnambuf), "zcc%08X%04X",_getpid(),  ((unsigned int)time(NULL)) & 0xffff);
824 #endif
825 
826     processing_user_command_line_arg = 0;
827 
828     asmargs = linkargs = cpparg = clangarg = llvmopt = llvmarg = NULL;
829     linklibs = muststrdup("");
830 
831     cpp_incpath_first = cpp_incpath_last = NULL;
832     linker_libpath_first = linker_libpath_last = NULL;
833     linker_linklib_first = linker_linklib_last = NULL;
834 
835     atexit(remove_temporary_files);
836     add_option_to_compiler("");
837 
838     gc = 1;            /* Set for the first argument to scan for */
839     if (argc == 1) {
840         print_help_text(argv[0]);
841         exit(1);
842     }
843 
844     /* Setup the install prefix based on ZCCCFG */
845     if ((ptr = getenv("ZCCCFG")) != NULL) {
846 #ifdef WIN32
847         snprintf(config_filename, sizeof(config_filename), "%s\\..\\..\\", ptr);
848 #else
849         snprintf(config_filename, sizeof(config_filename), "%s/../../", ptr);
850 #endif
851         c_install_dir = muststrdup(config_filename);
852     }
853 
854     setup_default_configuration();
855 
856     gc = find_zcc_config_fileFile(argv[0], argv[gc], gc, config_filename, sizeof(config_filename));
857     parse_configfile(config_filename);
858 
859 
860     /* Now, parse the default options list */
861     if (c_options != NULL) {
862         parse_option(muststrdup(c_options));
863     }
864 
865     /* Add in any aliases defined by the config file */
866     for ( i = 0; i < c_aliases_array_num; i++ ) {
867         char buf[LINEMAX+1];
868         char *ptr = c_aliases_array[i];
869         char *dest = buf;
870         while ( *ptr && !isspace(*ptr)) {
871             *dest++ = *ptr++;
872         }
873         *dest = 0;
874         while ( isspace(*ptr) )
875            ptr++;
876         aliases = realloc(aliases, (aliases_num + 2) * sizeof(aliases[0]));
877         aliases[aliases_num++] = strdup(buf);
878         aliases[aliases_num++] = strdup(ptr);
879     }
880 
881     /* Now, let's parse the command line arguments */
882     max_argc = argc;
883     gargv = argv;        /* Point argv to start of command line */
884 
885     processing_user_command_line_arg = 1;
886     argc = option_parse(&options[0], argc - 1, &argv[1]);
887     for (gargc = 1; gargc < argc+1; gargc++) {
888         // We have some options left over, it may well be an alias
889         if (argv[gargc][0] == '-') {
890             parse_cmdline_arg(argv[gargc]);
891         } else {
892             add_file_to_process(argv[gargc]);
893         }
894     }
895     processing_user_command_line_arg = 0;
896 
897     if (c_print_specs) {
898         print_specs();
899         exit(0);
900     }
901 
902 
903     if (add_variant_args(c_subtype, c_subtype_array_num, c_subtype_array) == -1) {
904         fprintf(stderr, "Cannot find definition for target -subtype=%s\n", c_subtype);
905         exit(1);
906     }
907     if (add_variant_args(c_clib, c_clib_array_num, c_clib_array) == -1) {
908         fprintf(stderr, "Cannot find definition for -clib=%s\n", c_clib);
909         exit(1);
910     }
911 
912     /* We must have at least a crt file - we can rely on defaults for everything else */
913     if (c_crt0 == NULL) {
914         fprintf(stderr, "No CRT0 defined in configuration file <%s>\n", config_filename);
915         exit(1);
916     }
917 
918     /* Setup zcc_opt.def */
919     {
920         char tempdir[FILENAME_MAX+1];
921 
922         unlink("zcc_opt.def");
923 #ifndef WIN32
924         char* ret = NULL;
925 
926         while ( ret == NULL ) {
927             snprintf(tempdir, sizeof(tempdir),"/tmp/tmpzccXXXXXXXX");
928             ret = mkdtemp(tempdir);
929         }
930 #else
931         int ret = -1;
932 
933         while ( ret != 0 ) {
934             tempname(tempdir);
935             ret = mkdir(tempdir);
936         }
937 #endif
938         zcc_opt_dir = strdup(tempdir);
939         strcat(tempdir,"/zcc_opt.def");
940         zcc_opt_def = strdup(tempdir);
941     }
942 
943 
944 
945     if (c_sccz80_inline_ints == 0 ) {
946         c_coptrules_sccz80 = NULL;
947     }
948 
949     configure_assembler();
950     configure_compiler();
951     configure_misc_options();
952 
953     if (c_nostdlib == 0) {
954         /* Add the startup library to the linker arguments */
955         if (c_startuplib && strlen(c_startuplib)) {
956             snprintf(buffer, sizeof(buffer), "-l\"%s\" ", c_startuplib);
957             AddLinkLibrary(NULL, buffer);
958             /* Add the default cpp path */
959             AddPreProcIncPath(NULL, c_incpath);
960         }
961     }
962 
963     if (lston) {
964         /* list on so add list options to assembler and linker */
965         BuildOptions(&asmargs, "-l ");
966         BuildOptions(&linkargs, "-l ");
967     }
968 
969     if (c_zorg != -1) {
970         write_zcc_defined("CRT_ORG_CODE", c_zorg, 0);
971     }
972 
973     if ((fp = fopen(zcc_opt_def, "a")) != NULL) {
974         fprintf(fp, "%s", zccopt ? zccopt : "");
975         fclose(fp);
976     }
977     else {
978         fprintf(stderr, "Could not create %s: File in use?\n", zcc_opt_def);
979         exit(1);
980     }
981 
982     /* process pragma-include */
983     if (pragincname)
984     {
985         char cmdline[FILENAME_MAX + 128];
986 
987 #ifdef _WIN32
988         snprintf(cmdline, FILENAME_MAX + 127, "%s -zcc-opt=%s < \"%s\" > nul",c_zpragma_exe, zcc_opt_def, pragincname);
989 #else
990         snprintf(cmdline, FILENAME_MAX + 127, "%s -zcc-opt=%s < \"%s\" > /dev/null",c_zpragma_exe, zcc_opt_def, pragincname);
991 #endif
992         if (verbose) { printf("%s\n", cmdline); fflush(stdout); }
993         if (-1 == system(cmdline)) {
994             fprintf(stderr, "Could not execute %s\n", cmdline);
995             exit(1);
996         }
997     }
998 
999 
1000     if (nfiles <= 0 || c_help) {
1001         print_help_text(argv[0]);
1002         exit(0);
1003     }
1004 
1005     /* Mangle math lib name but only for classic compiles */
1006     if ((c_clib == 0) || (!strstr(c_clib, "new") && !strstr(c_clib, "sdcc") && !strstr(c_clib, "clang")))
1007         if (linker_linklib_first) configure_maths_library(&linker_linklib_first);   /* -lm appears here */
1008 
1009     /* Options that must be sequenced in specific order */
1010     if (compiler_type == CC_SDCC)
1011         BuildOptions_start(&comparg, "--constseg rodata_compiler ");
1012 
1013     BuildOptions(&clangarg, c_clangincpath);
1014     if (cpp_incpath_last) {
1015         BuildOptions(&cpparg, cpp_incpath_last);
1016         BuildOptions(&clangarg, cpp_incpath_last);
1017     }
1018     if (cpp_incpath_first) {
1019         BuildOptions_start(&cpparg, cpp_incpath_first);
1020         BuildOptions_start(&clangarg, cpp_incpath_first);
1021     }
1022     clangarg = replace_str(ptr = clangarg, "-I", "-idirafter ");
1023     free(ptr);
1024     clangarg = replace_str(ptr = clangarg, "-D__SDCC", "-D__CLANG");
1025     free(ptr);
1026 
1027     BuildOptions(&linker_libpath_first, "-L. ");
1028     if (linker_libpath_last)
1029         BuildOptions(&linker_libpath_first, linker_libpath_last);
1030     BuildOptions_start(&linkargs, linker_libpath_first);
1031 
1032     if (linker_linklib_last)
1033         BuildOptions(&linker_linklib_first, linker_linklib_last);
1034     if (linker_linklib_first)
1035         BuildOptions_start(&linklibs, linker_linklib_first);
1036 
1037     /* CLANG & LLVM options */
1038     BuildOptions_start(&clangarg, "--target=sdcc-z80 -S -emit-llvm ");
1039     if (!sdcc_signed_char) BuildOptions_start(&clangarg, "-fno-signed-char ");
1040     BuildOptions(&llvmarg, llvmarg ? "-disable-partial-libcall-inlining " : "-O2 -disable-partial-libcall-inlining ");
1041     BuildOptions(&llvmopt, llvmopt ? "-disable-simplify-libcalls -disable-loop-vectorization -disable-slp-vectorization -S " : "-O2 -disable-simplify-libcalls -disable-loop-vectorization -disable-slp-vectorization -S ");
1042 
1043 
1044     /* Peephole optimization level for sdcc */
1045     if (compiler_type == CC_SDCC && c_cpu != CPU_TYPE_GBZ80)
1046     {
1047         switch (sdccpeepopt)
1048         {
1049         case 0:
1050             add_option_to_compiler(opt_code_size ? c_sdccpeeph0cs : c_sdccpeeph0);
1051             break;
1052         case 1:
1053             add_option_to_compiler(opt_code_size ? c_sdccpeeph1cs : c_sdccpeeph1);
1054             break;
1055         case 2:
1056             add_option_to_compiler(opt_code_size ? c_sdccpeeph2cs : c_sdccpeeph2);
1057             break;
1058         default:
1059             add_option_to_compiler(opt_code_size ? c_sdccpeeph3cs : c_sdccpeeph3);
1060             break;
1061         }
1062     }
1063 
1064     /* m4 include path points to target's home directory */
1065     if ((ptr = last_path_char(c_crt0)) != NULL) {
1066         char *p;
1067         p = mustmalloc((ptr - c_crt0 + 7) * sizeof(char));
1068         sprintf(p, "-I \"%.*s\"",(int)( ptr - c_crt0), c_crt0);
1069         BuildOptions(&m4arg, p);
1070         free(p);
1071     }
1072 
1073     /* m4 must include zcc_opt_dir */
1074     {
1075         char  optdir[FILENAME_MAX+1];
1076 
1077         snprintf(optdir,sizeof(optdir),"-I \"%s\"",zcc_opt_dir);
1078         BuildOptions(&m4arg, optdir);
1079     }
1080 
1081     /* m4 include path finds z88dk macro definition file "z88dk.m4" */
1082     BuildOptions(&m4arg, c_m4opts);
1083 
1084     build_bin = !m4only && !clangonly && !llvmonly && !preprocessonly && !assembleonly && !compileonly && !makelib;
1085 
1086     /* Create M4 defines out of some pragmas for the CRT */
1087     /* Some pragma values need to be known at m4 time in the new c lib CRTs */
1088 
1089     if (build_bin) {
1090         if ((fp = fopen(zcc_opt_def, "r")) != NULL) {
1091             char buffer[LINEMAX + 1];
1092             int32_t val;
1093 
1094             while (fgets(buffer, LINEMAX, fp) != NULL) {
1095                 for (i = 0; i < sizeof(important_pragmas) / sizeof(*important_pragmas); ++i) {
1096                     if (important_pragmas[i].seen == 0) {
1097                         char match[LINEMAX + 1];
1098 
1099                         snprintf(match, sizeof(match), " defc %s = %%" SCNi32, important_pragmas[i].pragma);
1100                         if (sscanf(buffer, match, &val) == 1) {
1101                             important_pragmas[i].seen = 1;
1102                             snprintf(buffer, sizeof(buffer), "--define=%s=%" PRId32, important_pragmas[i].m4_name, val);
1103                             buffer[sizeof(buffer) - 1] = 0;
1104                             BuildOptions(&m4arg, buffer);
1105                         }
1106                     }
1107                 }
1108             }
1109         } else {
1110             fprintf(stderr, "Could not open %s: File in use?\n", zcc_opt_def);
1111             exit(1);
1112         }
1113 
1114         fclose(fp);
1115     }
1116     /* Activate target's crt file */
1117     if ((c_nocrt == 0) && build_bin) {
1118         /* append target crt to end of filelist */
1119         add_file_to_process(c_crt0);
1120         /* move crt to front of filelist */
1121         ptr = original_filenames[nfiles - 1];
1122         memmove(&original_filenames[1], &original_filenames[0], (nfiles - 1) * sizeof(*original_filenames));
1123         original_filenames[0] = ptr;
1124         ptr = filelist[nfiles - 1];
1125         memmove(&filelist[1], &filelist[0], (nfiles - 1) * sizeof(*filelist));
1126         filelist[0] = ptr;
1127         ptr = temporary_filenames[nfiles - 1];
1128         memmove(&temporary_filenames[1], &temporary_filenames[0], (nfiles - 1) * sizeof(*temporary_filenames));
1129         temporary_filenames[0] = ptr;
1130     }
1131 
1132     /* crt file is now the first file in filelist */
1133     c_crt0 = temporary_filenames[0];
1134 
1135     /* PLOT TWIST - See #190 on Github                                                                           */
1136     /* The crt must be processed last because the new c library now processes zcc_opt.def with m4.               */
1137     /* With the crt first, the other .c files have not been processed yet and zcc_opt.def may not be complete.   */
1138     /*                                                                                                           */
1139     /* To solve let's do something awkward.  Process the files starting at index one but go one larger than      */
1140     /* the number of files.  When the one larger number is hit, set the index to zero to do the crt.             */
1141     /*                                                                                                           */
1142     /* This nastiness is marked "HACK" in the loop below.  Maybe something better will come along later.         */
1143 
1144     /* Parse through the files, handling each one in turn */
1145     for (i = 1; (i <= nfiles) && (i != 0); i += (i != 0)) { /* HACK 1 OF 2 */
1146         char   temp_filename[FILENAME_MAX+1];
1147         char   *ext;
1148 
1149         if (i == nfiles) i = 0;                            /* HACK 2 OF 2 */
1150         if (verbose) printf("\nPROCESSING %s\n", original_filenames[i]);
1151     SWITCH_REPEAT:
1152         switch (get_filetype_by_suffix(filelist[i]))
1153         {
1154         case M4FILE:
1155             // Strip off the .m4 suffix and find the underlying extension
1156             snprintf(temp_filename,sizeof(temp_filename),"%s", filelist[i]);
1157             ext = find_file_ext(temp_filename);
1158             if ( ext != NULL ) {
1159                 *ext = 0;
1160                 ext = find_file_ext(temp_filename);
1161             }
1162             if ( ext == NULL) ext = "";
1163 
1164             if (process(".m4", ext, "m4", (m4arg == NULL) ? "" : m4arg, filter, i, YES, YES))
1165                 exit(1);
1166             /* Disqualify recursive .m4 extensions */
1167             ft = get_filetype_by_suffix(filelist[i]);
1168             if (ft == M4FILE) {
1169                 fprintf(stderr, "Cannot process recursive .m4 file %s\n", original_filenames[i]);
1170                 exit(1);
1171             }
1172             ft = get_filetype_by_suffix(filelist[i]);
1173             if ((ft == HDRFILE) || (ft == INCFILE)) continue;
1174             /* Continue processing macro expanded source file */
1175             goto SWITCH_REPEAT;
1176             break;
1177         CASE_LLFILE:
1178         case LLFILE:
1179             if (m4only || clangonly) continue;
1180             /* llvm-cbe translates llvm-ir to c */
1181             if (zopt && process(".ll", ".opt.ll", "zopt", llvmopt, outspecified_flag, i, YES, NO))
1182                 exit(1);
1183             if (process(".ll", ".cbe.c", c_llvm_exe, llvmarg, outspecified_flag, i, YES, NO))
1184                 exit(1);
1185             /* Write .cbe.c to original directory immediately */
1186             ptr = changesuffix(original_filenames[i], ".cbe.c");
1187             if (copy_file(filelist[i], "", ptr, "")) {
1188                 fprintf(stderr, "Couldn't write output file %s\n", ptr);
1189                 exit(1);
1190             }
1191             /* Copied file becomes the new original file */
1192             free(original_filenames[i]);
1193             free(filelist[i]);
1194             original_filenames[i] = ptr;
1195             filelist[i] = muststrdup(ptr);
1196         case CFILE:
1197             if (m4only) continue;
1198             /* special treatment for clang+llvm */
1199             if ((strcmp(c_compiler_type, "clang") == 0) && !hassuffix(filelist[i], ".cbe.c")) {
1200                 if (process(".c", ".ll", c_clang_exe, clangarg, outspecified_flag, i, YES, NO))
1201                     exit(1);
1202                 goto CASE_LLFILE;
1203             }
1204             if (clangonly || llvmonly) continue;
1205             if (hassuffix(filelist[i], ".cbe.c"))
1206                 BuildOptions(&cpparg, clangcpparg);
1207             /* past clang+llvm related pre-processing */
1208             if (compiler_type == CC_SDCC) {
1209                 char zpragma_args[1024];
1210                 snprintf(zpragma_args, sizeof(zpragma_args),"-zcc-opt=%s", zcc_opt_def);
1211                 if (process(".c", ".i2", c_cpp_exe, cpparg, c_stylecpp, i, YES, YES))
1212                     exit(1);
1213                 if (process(".i2", ".i", c_zpragma_exe, zpragma_args, filter, i, YES, NO))
1214                     exit(1);
1215             }
1216             else {
1217                 char zpragma_args[1024];
1218                 snprintf(zpragma_args, sizeof(zpragma_args),"-sccz80 -zcc-opt=%s", zcc_opt_def);
1219 
1220                 if (process(".c", ".i2", c_cpp_exe, cpparg, c_stylecpp, i, YES, YES))
1221                     exit(1);
1222                 if (process(".i2", ".i", c_zpragma_exe, zpragma_args, filter, i, YES, NO))
1223                     exit(1);
1224             }
1225         case CPPFILE:
1226             if (m4only || clangonly || llvmonly || preprocessonly) continue;
1227             if (process(".i", ".opt", c_compiler, comparg, compiler_style, i, YES, NO))
1228                 exit(1);
1229         case OPTFILE:
1230             if (m4only || clangonly || llvmonly || preprocessonly) continue;
1231             if (compiler_type == CC_SDCC) {
1232                 char  *rules[MAX_COPT_RULE_FILES];
1233                 int    num_rules = 0;
1234 
1235                 /* filter comments out of asz80 asm file see issue #801 on github */
1236                 if (peepholeopt) zsdcc_asm_filter_comments(i, ".op1");
1237 
1238                 /* sdcc_opt.9 bugfixes critical sections and implements RST substitution */
1239                 /* rules[num_rules++] = c_sdccopt9;                                      */
1240 
1241                 switch (peepholeopt)
1242                 {
1243                 case 0:
1244                     rules[num_rules++] = c_sdccopt9;
1245                     break;
1246                 case 1:
1247                     rules[num_rules++] = c_sdccopt1;
1248                     rules[num_rules++] = c_sdccopt9;
1249                     break;
1250                 default:
1251                     rules[num_rules++] = c_sdccopt1;
1252                     rules[num_rules++] = c_sdccopt9;
1253                     rules[num_rules++] = c_sdccopt2;
1254                     break;
1255                 }
1256 
1257                 if ( c_coptrules_target ) {
1258                     rules[num_rules++] = c_coptrules_target;
1259                 }
1260                 if ( c_coptrules_cpu ) {
1261                     rules[num_rules++] = c_coptrules_cpu;
1262                 }
1263                 if ( c_coptrules_user ) {
1264                     rules[num_rules++] = c_coptrules_user;
1265                 }
1266 
1267                 if (peepholeopt == 0)
1268                     apply_copt_rules(i, num_rules, rules, ".opt", ".op1", ".s");
1269                 else
1270                     apply_copt_rules(i, num_rules, rules, ".op1", ".opt", ".asm");
1271             } else {
1272                 char  *rules[MAX_COPT_RULE_FILES];
1273                 int    num_rules = 0;
1274 
1275                 /* z80rules.9 implements intrinsics and RST substitution */
1276                 rules[num_rules++] = c_coptrules9;
1277 
1278                 switch (peepholeopt) {
1279                 case 0:
1280                     break;
1281                 case 1:
1282                     rules[num_rules++] = c_coptrules1;
1283                     break;
1284                 case 2:
1285                     rules[num_rules++] = c_coptrules2;
1286                     rules[num_rules++] = c_coptrules1;
1287                     break;
1288                 default:
1289                     rules[num_rules++] = c_coptrules2;
1290                     rules[num_rules++] = c_coptrules1;
1291                     rules[num_rules++] = c_coptrules3;
1292                     break;
1293                 }
1294 
1295                 if ( c_coptrules_target ) {
1296                     rules[num_rules++] = c_coptrules_target;
1297                 }
1298                 if ( c_coptrules_cpu ) {
1299                     rules[num_rules++] = c_coptrules_cpu;
1300                 }
1301                 if ( c_coptrules_sccz80 ) {
1302                     rules[num_rules++] = c_coptrules_sccz80;
1303                 }
1304                 if ( c_coptrules_user ) {
1305                     rules[num_rules++] = c_coptrules_user;
1306                 }
1307 
1308                 apply_copt_rules(i, num_rules, rules, ".opt", ".op1", ".asm");
1309             }
1310             /* continue processing if this is not a .s file */
1311             if ((compiler_type != CC_SDCC) || (peepholeopt != 0))
1312                 goto CASE_ASMFILE;
1313             /* user wants to stop at the .s file if stopping at assembly translation */
1314             if (assembleonly) continue;
1315         case SFILE:
1316             if (m4only || clangonly || llvmonly || preprocessonly) continue;
1317             /* filter comments out of asz80 asm file see issue #801 on github */
1318             zsdcc_asm_filter_comments(i, ".s2");
1319             if (process(".s2", ".asm", c_copt_exe, c_sdccopt1, filter, i, YES, NO))
1320                 exit(1);
1321         CASE_ASMFILE:
1322         case ASMFILE:
1323             if (m4only || clangonly || llvmonly || preprocessonly || assembleonly)
1324                 continue;
1325 
1326             /* See #16 on github.                                                                    */
1327             /* z80asm is unable to output object files to an arbitrary destination directory.        */
1328             /* We don't want to assemble files in their original source directory because that would */
1329             /* create a temporary object file there which may accidentally overwrite user files.     */
1330 
1331             /* Instead the plan is to copy the asm file to the temp directory and add the original   */
1332             /* source directory to the include search path                                           */
1333 
1334             BuildAsmLine(asmarg, sizeof(asmarg), " -s ");
1335 
1336             /* Check if source .asm file is in the temp directory already (indicates this is an intermediate file) */
1337             ptr = changesuffix(temporary_filenames[i], ".asm");
1338             if (strcmp(ptr, filelist[i]) == 0) {
1339                 free(ptr);
1340                 ptr = muststrdup(asmarg);
1341             } else {
1342                 char *p, tmp[FILENAME_MAX*2 + 2];
1343 
1344                 /* copy .asm file to temp directory */
1345                 if (copy_file(filelist[i], "", ptr, "")) {
1346                     fprintf(stderr, "Couldn't write output file %s\n", ptr);
1347                     exit(1);
1348                 }
1349 
1350                 /* determine path to original source directory */
1351                 p = last_path_char(filelist[i]);
1352 
1353                 if (!is_path_absolute(filelist[i])) {
1354                     int len;
1355 #ifdef WIN32
1356                     if (_getcwd(tmp, sizeof(tmp) - 1) == NULL)
1357                         *tmp = '\0';
1358 #else
1359                     if (getcwd(tmp, sizeof(tmp) - 1) == NULL)
1360                         *tmp = '\0';
1361 #endif
1362                     if (p) {
1363                         len = strlen(tmp);
1364                         snprintf(tmp + len, sizeof(tmp) - len - 1, "/%.*s", (int)(p - filelist[i]), filelist[i]);
1365                     }
1366 
1367                     if (*tmp == '\0')
1368                         strcpy(tmp, ".");
1369                 }
1370                 else if (p) {
1371                     snprintf(tmp, sizeof(tmp) - 1, "%.*s", (int)(p - filelist[i]), filelist[i]);
1372                 } else {
1373                     strcpy(tmp, ".");
1374                 }
1375 
1376                 /* working file is now the .asm file in the temp directory */
1377                 free(filelist[i]);
1378                 filelist[i] = ptr;
1379 
1380                 /* add original source directory to the include path */
1381                 ptr = mustmalloc((strlen(asmarg) + strlen(tmp) + 7) * sizeof(char));
1382                 sprintf(ptr, "%s -I\"%s\" ", asmarg, tmp);
1383             }
1384 
1385             /* insert module directive at front of .asm file see issue #46 on github                                                 */
1386             /* this is a bit of a hack - foo.asm is copied to foo.tmp and then foo.tmp is written back to foo.asm with module header */
1387 
1388             {
1389                 char *p, *q, tmp[FILENAME_MAX*2 + 100];
1390 
1391                 p = changesuffix(temporary_filenames[i], ".tmp");
1392 
1393                 if (copy_file(filelist[i], "", p, "")) {
1394                     fprintf(stderr, "Couldn't write output file %s\n", p);
1395                     exit(1);
1396                 }
1397 
1398                 if ((q = last_path_char(original_filenames[i])) != NULL )
1399                     q++;
1400                 else
1401                     q = original_filenames[i];
1402 
1403                 snprintf(tmp, sizeof(tmp) - 3, "MODULE %s\n"
1404                          "LINE 0, \"%s\"\n\n", q, original_filenames[i]);
1405 
1406                 /* change non-alnum chars in module name to underscore */
1407 
1408                 for (q = tmp+7; *q != '\n'; ++q)
1409                     if (!isalnum(*q)) *q = '_';
1410 
1411                 if (prepend_file(p, "", filelist[i], "", tmp)) {
1412                     fprintf(stderr, "Couldn't append output file %s\n", p);
1413                     exit(1);
1414                 }
1415 
1416                 free(p);
1417             }
1418 
1419             /* must be late assembly for the first file when making a binary */
1420             if ((i == 0) && build_bin)
1421             {
1422                 c_crt_incpath = ptr;
1423                 if (verbose) printf("WILL ACT AS CRT\n");
1424                 continue;
1425             }
1426 
1427             if (process(".asm", c_extension, c_assembler, ptr, assembler_style, i, YES, NO))
1428                 exit(1);
1429             free(ptr);
1430             break;
1431         case OBJFILE:
1432             break;
1433         default:
1434             if (strcmp(filelist[i], original_filenames[i]) == 0)
1435                 fprintf(stderr, "Filetype of %s unrecognized\n", filelist[i]);
1436             else
1437                 fprintf(stderr, "Filetype of %s (%s) unrecognized\n", filelist[i], original_filenames[i]);
1438             exit(1);
1439         }
1440     }
1441 
1442     if (verbose) printf("\nGENERATING OUTPUT\n");
1443 
1444     if (m4only) exit(0);
1445 
1446     if (clangonly) {
1447         if (nfiles > 1) outputfile = NULL;
1448         copy_output_files_to_destdir(".ll", 1);
1449         exit(0);
1450     }
1451 
1452     if (llvmonly) exit(0);
1453 
1454     if (preprocessonly) {
1455         if (nfiles > 1) outputfile = NULL;
1456         copy_output_files_to_destdir(".i", 1);
1457         exit(0);
1458     }
1459 
1460     if (assembleonly) {
1461         if (nfiles > 1) outputfile = NULL;
1462         copy_output_files_to_destdir(".asm", 1);
1463         copy_output_files_to_destdir(".s", 1);
1464         exit(0);
1465     }
1466 
1467     {
1468         char *tempofile = outputfile;
1469         outputfile = NULL;
1470         if (lston) copy_output_files_to_destdir(".lis", 1);
1471         if (symbolson) copy_output_files_to_destdir(".sym", 1);
1472         outputfile = tempofile;
1473     }
1474 
1475     if (compileonly) {
1476         if ((nfiles > 1) && (outputfile != NULL)) {
1477             /* consolidated object file */
1478             outputfile = changesuffix(outputfile, ".o");
1479             if (linkthem(c_linker))
1480                 exit(1);
1481         }
1482         else
1483         {
1484             /* independent object files */
1485             copy_output_files_to_destdir(c_extension, 1);
1486         }
1487         exit(0);
1488     }
1489 
1490     /* Set the default name as necessary */
1491 
1492     if (outputfile == NULL)
1493         outputfile = c_linker_output_file ? c_linker_output_file : defaultout;
1494 
1495     strcpy(filenamebuf, outputfile);
1496 
1497     if ((ptr = find_file_ext(filenamebuf)) != NULL)
1498         *ptr = 0;
1499 
1500     /* Link */
1501 
1502     if (linkthem(c_linker))
1503     {
1504         if (build_bin && lston && copy_file(c_crt0, ".lis", filenamebuf, ".lis"))
1505             fprintf(stderr, "Cannot copy crt0 list file\n");
1506 
1507         exit(1);
1508     }
1509 
1510     /* Build binary */
1511 
1512     if (build_bin) {
1513 
1514         int status = 0;
1515 
1516         if (mapon && copy_file(c_crt0, ".map", filenamebuf, ".map")) {
1517             fprintf(stderr, "Cannot copy map file\n");
1518             status = 1;
1519         }
1520 
1521         if (symbolson && copy_file(c_crt0, ".sym", filenamebuf, ".sym")) {
1522             fprintf(stderr, "Cannot copy symbols file\n");
1523             status = 1;
1524         }
1525 
1526         if (globaldefon && copy_defc_file(c_crt0, ".def", filenamebuf, ".def")) {
1527             fprintf(stderr, "Cannot create global defc file\n");
1528             status = 1;
1529         }
1530 
1531         if (lston && copy_file(c_crt0, ".lis", filenamebuf, ".lis")) {
1532             fprintf(stderr, "Cannot copy crt0 list file\n");
1533             status = 1;
1534         }
1535 
1536         if (c_generate_debug_info && compiler_type == CC_SDCC && copy_file(c_crt0, ".adb", filenamebuf, ".adb")) {
1537             // Ignore error
1538         }
1539 
1540         if (createapp) {
1541             /* Building an application - run the appmake command on it */
1542             snprintf(buffer, sizeof(buffer), "%s %s -b \"%s\" -c \"%s\"", c_appmake_exe, appmakeargs ? appmakeargs : "", outputfile, c_crt0);
1543             if (verbose) {
1544                 printf("%s\n", buffer);
1545                 fflush(stdout);
1546             }
1547             if (system(buffer)) {
1548                 fprintf(stderr, "Building application code failed\n");
1549                 status = 1;
1550             }
1551         }
1552 
1553         exit(status);
1554     }
1555 
1556     exit(0);    /* If this point is reached, all went well */
1557 }
1558 
1559 
apply_copt_rules(int filenumber,int num,char ** rules,char * ext1,char * ext2,char * ext)1560 static void apply_copt_rules(int filenumber, int num, char **rules, char *ext1, char *ext2, char *ext)
1561 {
1562     char   argbuf[FILENAME_MAX+1];
1563     int    i;
1564     char  *input_ext;
1565     char  *output_ext;
1566 
1567     for ( i = 0; i < num ; i++ ) {
1568         if (i % 2 == 0) {
1569             input_ext = ext1;
1570             output_ext = ext2;
1571         } else {
1572             input_ext = ext2;
1573             output_ext = ext1;
1574         }
1575 
1576         if ( i == (num-1) ) {
1577             output_ext = ext;
1578         }
1579         snprintf(argbuf,sizeof(argbuf),"%s %s", select_cpu(CPU_MAP_TOOL_COPT), rules[i]);
1580         if (process(input_ext, output_ext, c_copt_exe, argbuf, filter, filenumber, YES, NO))
1581             exit(1);
1582     }
1583 }
1584 
1585 
1586 /* filter comments out of asz80 asm file see issue #801 on github */
zsdcc_asm_filter_comments(int filenumber,char * ext)1587 void zsdcc_asm_filter_comments(int filenumber, char *ext)
1588 {
1589     FILE *fin;
1590     FILE *fout;
1591     char *outname;
1592 
1593     char *line = NULL;
1594     unsigned int len = 0;
1595 
1596     outname = changesuffix(temporary_filenames[filenumber], ext);
1597 
1598     if ((fin = fopen(filelist[filenumber], "r")) == NULL)
1599     {
1600         fprintf(stderr, "Error: Cannot read %s\n", filelist[filenumber]);
1601         exit(1);
1602     }
1603 
1604     if ((fout = fopen(outname, "w")) == NULL)
1605     {
1606         fprintf(stderr, "Error: Cannot write %s\n", outname);
1607         fclose(fin);
1608         exit(1);
1609     }
1610 
1611     /* read lines from asm file */
1612 
1613     while (zcc_getdelim(&line, &len, '\n', fin) > 0)
1614     {
1615         unsigned int i;
1616 
1617         int seen_semicolon = 0;
1618         int seen_nonws = 0;
1619         int accept_line = 0;
1620 
1621         unsigned int quote_type = 0;
1622         unsigned int quote_count = 0;
1623 
1624         for (i = 0; i <= strlen(line); ++i)
1625         {
1626             if (accept_line == 0)
1627             {
1628                 if (seen_semicolon && (line[i] != '@'))
1629                     break;
1630 
1631                 seen_semicolon = 0;
1632 
1633                 switch (line[i])
1634                 {
1635                 case '\'':
1636 #ifdef WIN32
1637                     if ((i >= 2) && (strnicmp(&line[i - 2], "af'", 3) == 0))
1638 #else
1639                     if ((i >= 2) && (strncasecmp(&line[i - 2], "af'", 3) == 0))
1640 #endif
1641                         break;
1642                     if (quote_count && ((quote_type & 0x1) == 0))
1643                     {
1644                         quote_count--;
1645                         quote_type >>= 1;
1646                     }
1647                     else
1648                     {
1649                         quote_count++;
1650                         quote_type <<= 1;
1651                     }
1652                     break;
1653 
1654                 case '"':
1655                     if (quote_count && ((quote_type & 0x1) == 1))
1656                     {
1657                         quote_count--;
1658                         quote_type >>= 1;
1659                     }
1660                     else
1661                     {
1662                         quote_count++;
1663                         quote_type = (quote_type << 1) + 1;
1664                     }
1665                     break;
1666 
1667                 case ';':
1668                     if (quote_count)
1669                         break;
1670                     if (seen_nonws == 0)
1671                     {
1672                         accept_line = 1;
1673                         break;
1674                     }
1675                     seen_semicolon = 1;
1676                     break;
1677 
1678                 default:
1679                     break;
1680                 }
1681 
1682                 if (!isspace(line[i]))
1683                     seen_nonws = 1;
1684             }
1685         }
1686 
1687         if (seen_semicolon) line[i-1] = '\0';                         /* terminate at semicolon */
1688         fprintf(fout, "%s\n", zcc_ascii_only(zcc_strrstrip(line)));   /* remove trailing whitespace (copt asz80 translator) and ascii-ify source (copt) */
1689     }
1690 
1691     free(line);
1692 
1693     fclose(fin);
1694     fclose(fout);
1695 
1696     free(filelist[filenumber]);
1697     filelist[filenumber] = outname;
1698 }
1699 
1700 
1701 
1702 /* Filter global defc file as it is written to the destination directory.
1703  *
1704  * (globaldefon     &   0x2) = make symbols PUBLIC
1705  * (globaldefrefile != NULL) = file holding one regular expression per line with
1706  *                             leading +- indicating acceptance or rejection for matches
1707 */
1708 
1709 typedef struct gfilter_s {
1710     int      accept;
1711     regex_t  preg;
1712 } GFILTER;
1713 
gdf_cleanup(GFILTER * filter,int nfilter)1714 void gdf_cleanup(GFILTER *filter, int nfilter)
1715 {
1716     while (nfilter > 1)
1717         regfree(&filter[--nfilter].preg);
1718     free(filter);
1719 }
1720 
copy_defc_file(char * name1,char * ext1,char * name2,char * ext2)1721 int copy_defc_file(char *name1, char *ext1, char *name2, char *ext2)
1722 {
1723     FILE *in, *out, *rules;
1724     char buffer[LINEMAX + 1];
1725     char *line, *ptr;
1726     GFILTER *filter;
1727     regmatch_t pmatch[3];
1728     int nfilter, errcode, lineno;
1729     unsigned int len;
1730 
1731     /* the first regular expression is used to parse a z80asm generated defc line */
1732     filter  = mustmalloc(sizeof(*filter));
1733     nfilter = 1;
1734     if (regcomp(&filter->preg, "(defc|DEFC)[\t ]+([^\t =]+)", REG_EXTENDED))
1735     {
1736         fprintf(stderr, "Cannot create regular expressions\n");
1737         return 1;
1738     }
1739 
1740     /* read the filters from the regular expression file */
1741     if (globaldefrefile)
1742     {
1743         if ((rules = fopen(globaldefrefile, "r")) == NULL)
1744         {
1745             fprintf(stderr, "Cannot open rules file %s\n", globaldefrefile);
1746             gdf_cleanup(filter, nfilter);
1747             return 1;
1748         }
1749 
1750         line = NULL;
1751         for (lineno = 1; zcc_getdelim(&line, &len, '\n', rules) > 0; ++lineno)
1752         {
1753             ptr = zcc_strstrip(line);
1754             if ((*ptr == 0) || (*ptr == ';')) continue;
1755             if ((filter = realloc(filter, (nfilter + 1) * sizeof(*filter))) == NULL)
1756             {
1757                 fprintf(stderr, "Cannot realloc global defc filter\n");
1758                 gdf_cleanup(filter, nfilter);
1759                 free(line);
1760                 fclose(rules);
1761                 return 1;
1762             }
1763             filter[nfilter].accept = !(*ptr == '-');
1764             if ((*ptr == '+') || (*ptr == '-')) ++ptr;
1765             while (isspace(*ptr)) ++ptr;
1766             if ( (errcode = regcomp(&filter[nfilter].preg, ptr, REG_EXTENDED)) )
1767             {
1768                 regerror(errcode, &filter[nfilter].preg, buffer, sizeof(buffer));
1769                 fprintf(stderr, "Ignoring %s line %u: %s", globaldefrefile, lineno, buffer);
1770                 regfree(&filter[nfilter].preg);
1771                 continue;
1772             }
1773             ++nfilter;
1774         }
1775         free(line);
1776         fclose(rules);
1777     }
1778 
1779     /* open the defc file generated by z80asm */
1780     buffer[sizeof(LINEMAX)] = 0;
1781     snprintf(buffer, sizeof(buffer) - 1, "%s%s", name1, ext1);
1782     if ((in = fopen(buffer, "r")) == NULL)
1783     {
1784         gdf_cleanup(filter, nfilter);
1785         return 1;
1786     }
1787 
1788     /* create the output defc file */
1789     snprintf(buffer, sizeof(buffer) - 1, "%s%s", name2, ext2);
1790     if ((out = fopen(buffer, "w")) == NULL)
1791     {
1792         gdf_cleanup(filter, nfilter);
1793         fclose(in);
1794         return 1;
1795     }
1796 
1797     line = NULL;
1798     while(zcc_getdelim(&line, &len, '\n', in) > 0)
1799     {
1800         /* determine symbol name */
1801         if (regexec(&filter->preg, line, sizeof(pmatch)/sizeof(pmatch[0]), pmatch, 0) != 0)
1802         {
1803             fprintf(stderr, "Cannot find symbol name in:\n%s", line);
1804             gdf_cleanup(filter, nfilter);
1805             free(line);
1806             fclose(in);
1807             fclose(out);
1808             return 1;
1809         }
1810         snprintf(buffer, sizeof(buffer) - 1, "%.*s",(int)(pmatch[2].rm_eo - pmatch[2].rm_so), &line[pmatch[2].rm_so]);
1811 
1812         /* accept or reject */
1813         if (globaldefrefile)
1814         {
1815             for (lineno = 1; lineno < nfilter; ++lineno)
1816             {
1817                 if (regexec(&filter[lineno].preg, buffer, 0, NULL, 0) == 0)
1818                 {
1819                     /* symbol name matches rule */
1820                     if (filter[lineno].accept)
1821                     {
1822                         if (globaldefon & 0x2)
1823                             fprintf(out, "\nPUBLIC %s\n", buffer);
1824                         fprintf(out, "%s", line);
1825                     }
1826                     break;
1827                 }
1828             }
1829         }
1830         else
1831         {
1832             if (globaldefon & 0x2)
1833                 fprintf(out, "\nPUBLIC %s\n", buffer);
1834             fprintf(out, "%s", line);
1835         }
1836     }
1837 
1838     gdf_cleanup(filter, nfilter);
1839     free(line);
1840 
1841     fclose(in);
1842     fclose(out);
1843 
1844     return 0;
1845 }
1846 
1847 
copyprepend_file(char * name1,char * ext1,char * name2,char * ext2,char * prepend)1848 int copyprepend_file(char *name1, char *ext1, char *name2, char *ext2, char *prepend)
1849 {
1850     FILE           *out;
1851     char            buffer[LINEMAX + 1];
1852     char           *cmd;
1853     int             ret;
1854 
1855     if (prepend == NULL) prepend = "";
1856 
1857     buffer[sizeof(LINEMAX)] = 0;
1858     snprintf(buffer, sizeof(buffer) - 1, "%s%s", name2, ext2);
1859 
1860     if ((out = fopen(buffer, "w")) == NULL)
1861         return 1;
1862 
1863     fprintf(out, "%s", prepend);
1864     fclose(out);
1865 
1866 #ifdef WIN32
1867     snprintf(buffer, sizeof(buffer), "%s \"%s%s\" >> \"%s%s\"", c_copycmd, name1, ext1, name2, ext2);
1868 #else
1869     snprintf(buffer, sizeof(buffer), "%s \"%s%s\" >> \"%s%s\"", c_copycmd, name1, ext1, name2, ext2);
1870 #endif
1871 #ifdef WIN32
1872     /* Argh....annoying */
1873     if ((strcmp(c_copycmd, "type") == 0) || (strcmp(c_copycmd, "copy") == 0)){
1874         cmd = replace_str(buffer, "/", "\\");
1875     }
1876     else {
1877         cmd = muststrdup(buffer);
1878     }
1879 #else
1880     cmd = muststrdup(buffer);
1881 #endif
1882     if (verbose) {
1883         printf("%s\n", buffer);
1884         fflush(stdout);
1885     }
1886     ret = (system(cmd));
1887     free(cmd);
1888     return ret;
1889 }
1890 
copy_file(char * name1,char * ext1,char * name2,char * ext2)1891 int copy_file(char *name1, char *ext1, char *name2, char *ext2)
1892 {
1893     return copyprepend_file(name1, ext1, name2, ext2, NULL);
1894 }
1895 
prepend_file(char * name1,char * ext1,char * name2,char * ext2,char * prepend)1896 int prepend_file(char *name1, char *ext1, char *name2, char *ext2, char *prepend)
1897 {
1898     return copyprepend_file(name1, ext1, name2, ext2, prepend);
1899 }
1900 
1901 
get_filetype_by_suffix(char * name)1902 int get_filetype_by_suffix(char *name)
1903 {
1904     char      *ext = find_file_ext(name);
1905 
1906     if (ext == NULL) {
1907         return 0;
1908     }
1909     if (strcmp(ext, ".c") == 0)
1910         return CFILE;
1911     if (strcmp(ext, ".i") == 0)
1912         return CPPFILE;
1913     if (strcmp(ext, ".opt") == 0)
1914         return OPTFILE;
1915     if (strcmp(ext, ".s") == 0)
1916         return SFILE;
1917     if (strcmp(ext, ".asm") == 0)
1918         return ASMFILE;
1919     if (strcmp(ext, ".o") == 0)
1920         return OBJFILE;
1921     if (strcmp(ext, ".m4") == 0)
1922         return M4FILE;
1923     if (strcmp(ext, ".h") == 0)
1924         return HDRFILE;
1925     if (strcmp(ext, ".inc") == 0)
1926         return INCFILE;
1927     if (strcmp(ext, ".ll") == 0)
1928         return LLFILE;
1929     return 0;
1930 }
1931 
1932 
add_variant_args(char * wanted,int num_choices,char ** choices)1933 int add_variant_args(char *wanted, int num_choices, char **choices)
1934 {
1935     int  i;
1936 
1937     if (wanted != NULL) {
1938         size_t len = strlen(wanted);
1939         for (i = 0; i < num_choices; i++) {
1940             if (strncmp(wanted, choices[i], len) == 0 && isspace(choices[i][len])) {
1941                 parse_option(muststrdup(choices[i] + len));
1942                 break;
1943             }
1944         }
1945         if (i == num_choices) {
1946             return -1;
1947         }
1948     }
1949     return 0;
1950 }
1951 
1952 
BuildAsmLine(char * dest,size_t destlen,char * prefix)1953 void BuildAsmLine(char *dest, size_t destlen, char *prefix)
1954 {
1955     size_t offs;
1956     offs = snprintf(dest, destlen, "%s", asmargs ? asmargs : " ");
1957 
1958     if (IS_ASM(ASM_Z80ASM)) {
1959         offs += snprintf(dest + offs, destlen - offs, "%s%s%s%s",
1960             prefix,
1961             z80verbose ? " -v " : " ",
1962             select_cpu(CPU_MAP_TOOL_Z80ASM),
1963             symbolson ? " -s " : " ");
1964     }
1965 
1966     snprintf(dest + offs, destlen - offs, "%s", c_asmopts);
1967 }
1968 
1969 
1970 
GlobalDefc(option * argument,char * arg)1971 void GlobalDefc(option *argument, char *arg)
1972 {
1973     char *ptr = arg + 1;
1974 
1975     if (*ptr++ == 'g') {
1976         /* global defc is on */
1977         globaldefon = 0x1;
1978 
1979         if (*ptr == 'p') {
1980             /* make defc symbols public */
1981             globaldefon |= 0x2;
1982             ++ptr;
1983         }
1984 
1985         if (*ptr == 'f') {
1986             /* filename containing regular expressions */
1987             ++ptr;
1988             while (isspace(*ptr) || (*ptr == '=') || (*ptr == ':')) ++ptr;
1989 
1990             if (*ptr != 0) {
1991                 globaldefrefile = muststrdup(ptr);
1992             }
1993         }
1994     }
1995 }
1996 
1997 
expand_macros(char * arg)1998 static char *expand_macros(char *arg)
1999 {
2000     char  *ptr, *nval;
2001     char  *rep, *start;
2002     char  *value = muststrdup(arg);
2003     char   varname[300];
2004 
2005     start = value;
2006     while ((ptr = strchr(start, '$')) != NULL) {
2007         if (*(ptr + 1) == '{') {
2008             char  *end = strchr(ptr + 1, '}');
2009 
2010             if (end != NULL) {
2011                 snprintf(varname, sizeof(varname), "%.*s", (int)(end - ptr - 2), ptr + 2);
2012                 rep = getenv(varname);
2013                 if (rep == NULL) {
2014                     rep = "";
2015                 }
2016 
2017                 snprintf(varname, sizeof(varname), "%.*s", (int)(end - ptr + 1), ptr);
2018                 nval = replace_str(value, varname, rep);
2019                 free(value);
2020                 value = nval;
2021                 start = value + (ptr - start);
2022             }
2023         }
2024         else {
2025             start++;
2026         }
2027     }
2028 
2029     nval = replace_str(value, "DESTDIR", c_install_dir);
2030     free(value);
2031 
2032     return nval;
2033 }
2034 
SetStringConfig(arg_t * argument,char * arg)2035 void SetStringConfig(arg_t *argument, char *arg)
2036 {
2037     *(char **)argument->data = expand_macros(arg);
2038 }
2039 
SetNumber(arg_t * argument,char * arg)2040 void SetNumber(arg_t *argument, char *arg)
2041 {
2042     char *ptr = arg + 1;
2043     char *end;
2044     int   val;
2045 
2046     if (strncmp(ptr, argument->name, strlen(argument->name)) == 0) {
2047         ptr += strlen(argument->name);
2048     }
2049 
2050     while (ispunct(*ptr)) ++ptr;
2051     val = (int)strtol(ptr, &end, 0);
2052 
2053     if (end != ptr) {
2054         *(int *)argument->data = val;
2055     }
2056 }
2057 
AddArray(arg_t * argument,char * arg)2058 void AddArray(arg_t *argument, char *arg)
2059 {
2060     int   i = *argument->num_ptr;
2061     char **arr = *(char ***)argument->data;
2062     *argument->num_ptr = *argument->num_ptr + 1;
2063     arr = realloc(arr, *argument->num_ptr * sizeof(char *));
2064     arr[i] = expand_macros(arg);
2065     *(char ***)argument->data = arr;
2066 }
2067 
2068 
conf_opt_code_speed(option * argument,char * arg)2069 void conf_opt_code_speed(option *argument, char *arg)
2070 {
2071     char *sccz80_arg = NULL;
2072     if ( strstr(arg,"inlineints") != NULL || strstr(arg,"all") != NULL) {
2073         c_sccz80_inline_ints = 1;
2074     }
2075     zcc_asprintf(&sccz80_arg,"-%s%s=%s", argument->type & OPT_DOUBLE_DASH ? "-" : "",argument->long_name, arg);
2076     BuildOptions(&sccz80arg, sccz80_arg);
2077     free(sccz80_arg);
2078 }
2079 
2080 
AddToArgs(option * argument,char * arg)2081 void AddToArgs(option *argument, char *arg)
2082 {
2083     BuildOptions(argument->value, arg);
2084 }
2085 
AddToArgsQuoted(option * argument,char * arg)2086 void AddToArgsQuoted(option *argument, char *arg)
2087 {
2088     BuildOptionsQuoted(argument->value, arg);
2089 }
2090 
AddPreProcIncPath(option * argument,char * arg)2091 void AddPreProcIncPath(option *argument, char *arg)
2092 {
2093     /* user-supplied inc path takes precedence over system-supplied inc path */
2094     if (processing_user_command_line_arg)
2095         BuildOptionsQuoted(&cpp_incpath_first, arg);
2096     else
2097         BuildOptionsQuoted(&cpp_incpath_last, arg);
2098 }
2099 
AddPreProc(option * argument,char * arg)2100 void AddPreProc(option *argument, char *arg)
2101 {
2102     BuildOptions(&cpparg, arg);
2103     BuildOptions(&clangarg, arg);
2104 }
2105 
2106 
AddLinkLibrary(option * argument,char * arg)2107 void AddLinkLibrary(option *argument, char *arg)
2108 {
2109     /* user-supplied lib takes precedence over system-supplied lib */
2110     if (processing_user_command_line_arg)
2111         BuildOptionsQuoted(&linker_linklib_first, arg);
2112     else
2113         BuildOptionsQuoted(&linker_linklib_last, arg);
2114 }
2115 
AddLinkSearchPath(option * argument,char * arg)2116 void AddLinkSearchPath(option *argument, char *arg)
2117 {
2118     /* user-supplied lib path takes precedence over system-supplied lib path */
2119     if (processing_user_command_line_arg)
2120         BuildOptionsQuoted(&linker_libpath_first, arg);
2121     else
2122         BuildOptionsQuoted(&linker_libpath_last, arg);
2123 }
2124 
2125 
2126 /** \brief Append arg to *list
2127 */
BuildOptions(char ** list,char * arg)2128 void BuildOptions(char **list, char *arg)
2129 {
2130     char           *val;
2131     char           *orig = *list;
2132 
2133     zcc_asprintf(&val, "%s%s ", orig ? orig : "", arg);
2134 
2135     free(orig);
2136     *list = val;
2137 }
2138 
BuildOptions_start(char ** list,char * arg)2139 void BuildOptions_start(char **list, char *arg)
2140 {
2141     char           *val;
2142     char           *orig = *list;
2143 
2144     zcc_asprintf(&val, "%s %s", arg, orig ? orig : "");
2145 
2146     free(orig);
2147     *list = val;
2148 }
2149 
BuildOptionsQuoted(char ** list,char * arg)2150 void BuildOptionsQuoted(char **list, char *arg)
2151 {
2152     char           *val;
2153     char           *orig = *list;
2154     int             len = -1;
2155 
2156     if ((strchr(arg, '"') == NULL) && (strchr(arg, '\'') == NULL)) {
2157         if ((strncmp(arg, "-I", 2) == 0) || (strncmp(arg, "-L", 2) == 0))
2158             len = 2;
2159         else if (strncmp(arg, "-iquote", 7) == 0)
2160             len = 7;
2161         else if (strncmp(arg, "-isystem", 8) == 0)
2162             len = 8;
2163     }
2164 
2165     if (len > 0) {
2166         zcc_asprintf(&val, "%s%.*s\"%s\" ", orig ? orig : "", len, arg, arg+len);
2167         free(orig);
2168         *list = val;
2169     } else {
2170         BuildOptions(list, arg);
2171     }
2172 }
2173 
2174 
add_option_to_compiler(char * arg)2175 void add_option_to_compiler(char *arg)
2176 {
2177     BuildOptions(&comparg, arg);
2178 }
2179 
2180 
find_file_ext(char * filename)2181 char *find_file_ext(char *filename)
2182 {
2183     char *p;
2184 
2185     if ((p = last_path_char(filename)) == NULL)
2186         p = filename;
2187     return strrchr(p, '.');
2188 }
2189 
2190 
is_path_absolute(char * p)2191 int is_path_absolute(char *p)
2192 {
2193     while (*p && isspace(*p))
2194         ++p;
2195 
2196 #ifdef WIN32
2197     return ((*p == '/') || (*p == '\\') || (isalpha(*p) && (*(p + 1) == ':')));
2198 #else
2199     return (*p == '/') || (*p == '\\');
2200 #endif
2201 }
2202 
gather_from_list_file(char * filename)2203 void gather_from_list_file(char *filename)
2204 {
2205     FILE *in;
2206     char *line, *p;
2207     unsigned int len;
2208     char pathname[FILENAME_MAX + 1];
2209     char outname[FILENAME_MAX * 2 + 2];
2210 
2211     /* reject non-filenames */
2212     if (((filename = strtok(filename, " \r\n\t")) == NULL) || !(*filename))
2213         return;
2214 
2215     /* open list file for reading */
2216     if ((in = fopen(filename, "r")) == NULL) {
2217         fprintf(stderr, "Unable to open list file \"%s\"\n", filename);
2218         exit(1);
2219     }
2220 
2221     /* extract path from filename */
2222     p = last_path_char(filename);
2223     memset(pathname, 0, sizeof(pathname));
2224     if (p != NULL)
2225         strncpy(pathname, filename, p - filename + 1);
2226 
2227     /* read filenames from list file */
2228     line = NULL;
2229     while (zcc_getdelim(&line, &len, '\n', in) > 0) {
2230         if (((p = strtok(line, " \r\n\t")) != NULL) && *p) {
2231             /* check for comment line */
2232             if ((*p == ';') || (*p == '#'))
2233                 continue;
2234 
2235             /* clear output filename */
2236             *outname = '\0';
2237 
2238             /* prepend list file indicator if the filename is a list file */
2239             if (*p == '@') {
2240                 strcpy(outname, "@");
2241                 if (((p = strtok(p + 1, " \r\n\t")) == NULL) || !(*p))
2242                     continue;
2243             }
2244 
2245             /* sanity check */
2246             if (strlen(p) > FILENAME_MAX) {
2247                 fprintf(stderr, "Filename is too long \"%s\"\n", p);
2248                 exit(1);
2249             }
2250 
2251             /* prepend path if filename is not absolute */
2252             if (!lstcwd && !is_path_absolute(p))
2253                 strcat(outname, pathname);
2254 
2255             /* append rest of filename */
2256             strcat(outname, p);
2257 
2258             /* add file to process */
2259 
2260             if (strlen(outname) >= FILENAME_MAX) {
2261                 fprintf(stderr, "Filename is too long \"%s\"\n", outname);
2262                 exit(1);
2263             }
2264 
2265             add_file_to_process(outname);
2266         }
2267     }
2268 
2269     if (!feof(in)) {
2270         fprintf(stderr, "Malformed line in list file \"%s\"\n", filename);
2271         exit(1);
2272     }
2273 
2274     free(line);
2275     fclose(in);
2276 }
2277 
add_file_to_process(char * filename)2278 void add_file_to_process(char *filename)
2279 {
2280     FILE *fclaim;
2281     char tname[FILENAME_MAX + 1];
2282     char *p;
2283     struct stat tmp;
2284 
2285     if (((p = strtok(filename, " \r\n\t")) != NULL) && *p) {
2286         p = strip_outer_quotes(p);
2287 
2288         if (*p == '@') {
2289             gather_from_list_file(p + 1);
2290     } else if ((*p != ';') && (*p != '#')) { /* ignore filename leading with semicolon or hash */
2291             /* Expand memory for filenames */
2292             if ((original_filenames = realloc(original_filenames, (nfiles + 1) * sizeof(char *))) == NULL) {
2293                 fprintf(stderr, "Unable to realloc memory for input filenames\n");
2294                 exit(1);
2295             }
2296             if ((filelist = realloc(filelist, (nfiles + 1) * sizeof(char *))) == NULL) {
2297                 fprintf(stderr, "Unable to realloc memory for input filenames\n");
2298                 exit(1);
2299             }
2300             if ((temporary_filenames = realloc(temporary_filenames, (nfiles + 1) * sizeof(char *))) == NULL) {
2301                 fprintf(stderr, "Unable to realloc memory for input filenames\n");
2302                 exit(1);
2303             }
2304 
2305             /* Add this file to the list of original files */
2306             if (find_file_ext(p) == NULL) {
2307                 /* file without extension - see if it exists, exclude directories */
2308                 if ((stat(p, &tmp) == 0) && (!(tmp.st_mode & S_IFDIR))) {
2309                     fprintf(stderr, "Unrecognized file type %s\n", p);
2310                     exit(1);
2311                 }
2312                 /* input file has no extension and does not exist so assume .asm then .o then .asm.m4 */
2313                 original_filenames[nfiles] = mustmalloc((strlen(p) + 8) * sizeof(char));
2314                 strcpy(original_filenames[nfiles], p);
2315                 strcat(original_filenames[nfiles], ".asm");
2316                 if (stat(original_filenames[nfiles], &tmp) != 0) {
2317                     strcpy(strrchr(original_filenames[nfiles], '.'), ".o");
2318                     if (stat(original_filenames[nfiles], &tmp) != 0)
2319                         strcpy(strrchr(original_filenames[nfiles], '.'), ".asm.m4");
2320                 }
2321             } else {
2322                 original_filenames[nfiles] = muststrdup(p);
2323             }
2324 
2325             /* Working file is the original file */
2326             filelist[nfiles] = muststrdup(original_filenames[nfiles]);
2327 
2328             /* Now work out temporary filename */
2329             tempname(tname);
2330             temporary_filenames[nfiles] = muststrdup(tname);
2331 
2332             /* Claim the temporary filename */
2333             if ((fclaim = fopen(temporary_filenames[nfiles], "w")) == NULL) {
2334                 fprintf(stderr, "Unable to claim temporary filename %s\n", temporary_filenames[nfiles]);
2335                 exit(1);
2336             }
2337             fclose(fclaim);
2338 
2339             nfiles++;
2340         }
2341     }
2342 }
2343 
2344 
2345 
2346 
2347 
usage(const char * program)2348 void usage(const char *program)
2349 {
2350     fprintf(stderr,"zcc - Frontend for the z88dk Cross-C Compiler - %s\n",version);
2351     fprintf(stderr,"Usage: %s +[target] {options} {files}\n",program);
2352 }
2353 
print_help_text(const char * program)2354 void print_help_text(const char *program)
2355 {
2356     int         i;
2357 
2358     usage(program);
2359 
2360     option_list(&options[0]);
2361 
2362     fprintf(stderr,"\nArgument Aliases:\n\n");
2363     for ( i = 0; i < aliases_num; i+=2 ) {
2364         fprintf(stderr,"%-20s %s\n", aliases[i],aliases[i+1]);
2365     }
2366 
2367     if ( c_clib_array_num ) {
2368         fprintf(stderr,"\n-clib options:\n\n");
2369         for ( i = 0; i < c_clib_array_num; i++ ) {
2370             char buf[LINEMAX+1];
2371             char *ptr = c_clib_array[i];
2372             char *dest = buf;
2373             while ( *ptr && !isspace(*ptr)) {
2374                 *dest++ = *ptr++;
2375             }
2376             *dest = 0;
2377             fprintf(stderr,"-clib=%-14s\n", buf);
2378         }
2379     }
2380     if ( c_subtype_array_num ) {
2381         fprintf(stderr,"\n-subtype options:\n\n");
2382         for ( i = 0; i < c_subtype_array_num; i++ ) {
2383             char buf[LINEMAX+1];
2384             char *ptr = c_subtype_array[i];
2385             char *dest = buf;
2386             while ( *ptr && !isspace(*ptr)) {
2387                 *dest++ = *ptr++;
2388             }
2389             *dest = 0;
2390             fprintf(stderr,"-subtype=%-11s\n", buf);
2391         }
2392     }
2393 
2394     exit(0);
2395 }
2396 
2397 
2398 
parse_cmdline_arg(char * arg)2399 void parse_cmdline_arg(char *arg)
2400 {
2401     int             i;
2402     char           *tempargv[2];
2403 
2404     tempargv[1] = arg;
2405 
2406     if ( option_parse(&options[0], 2, &tempargv[0]) == 0 ) {
2407         return;
2408     }
2409 
2410     for ( i = 0; i < aliases_num; i+=2 ) {
2411         if ( strcmp(arg, aliases[i]) == 0 ) {
2412             parse_option(muststrdup(aliases[i+1]));
2413             return;
2414         }
2415     }
2416     add_option_to_compiler(arg);
2417 }
2418 
2419 
LoadConfigFile(arg_t * argument,char * arg)2420 void LoadConfigFile(arg_t *argument, char *arg)
2421 {
2422     char   buf[FILENAME_MAX+1];
2423     char  *cfgfile;
2424 
2425     cfgfile = getenv("ZCCCFG");
2426     if (cfgfile != NULL) {
2427         if (strlen(cfgfile) > ( FILENAME_MAX - strlen(arg) - strlen("/") )) {
2428             fprintf(stderr, "Possibly corrupt env variable ZCCCFG\n");
2429             exit(1);
2430         }
2431         /* Config file in config directory */
2432         snprintf(buf, sizeof(buf), "%s/%s", cfgfile, arg);
2433     } else {
2434         snprintf(buf, sizeof(buf), "%s/lib/config/%s", c_install_dir, arg);
2435     }
2436     parse_configfile(buf);
2437 }
2438 
parse_configfile(const char * filename)2439 void parse_configfile(const char *filename)
2440 {
2441     FILE *fp;
2442     char  buffer[LINEMAX+1];
2443 
2444       /* Okay, so now we read in the options file and get some info for us */
2445     if ((fp = fopen(filename, "r")) == NULL) {
2446         fprintf(stderr, "Can't open config file %s\n", filename);
2447         exit(1);
2448     }
2449     while (fgets(buffer, LINEMAX, fp) != NULL) {
2450         if (!isupper(buffer[0]))
2451             continue;
2452         parse_configfile_line(buffer);
2453     }
2454     fclose(fp);
2455 }
2456 
parse_configfile_line(char * arg)2457 void parse_configfile_line(char *arg)
2458 {
2459     arg_t          *pargs = config;
2460 
2461     while (pargs->setfunc) {
2462         if (strncmp(arg, pargs->name, strlen(pargs->name)) == 0) {
2463             char *val = arg + strlen(pargs->name);
2464             while (isspace(*val)) {
2465                 val++;
2466             }
2467             KillEOL(val);
2468             (*pargs->setfunc) (pargs, val);
2469             return;
2470         }
2471         pargs++;
2472     }
2473     printf("Unrecognised config option: %s\n", arg);
2474     return;
2475 }
2476 
configure_misc_options()2477 static void configure_misc_options()
2478 {
2479     char     buf[256];
2480 
2481     /* Setup the extension - config files just seem to specify -M, so fudge it */
2482     snprintf(buf, sizeof(buf), ".%s", c_extension_config && strlen(c_extension_config) ? c_extension_config : "o");
2483     c_extension = muststrdup(buf);
2484 
2485     /*
2486     the new c lib uses startup=-1 to mean user supplies the crt
2487     current working dir will be different than when using -crt0
2488     */
2489 
2490     if (c_startup >= -1) {
2491         write_zcc_defined("startup", c_startup, 0);
2492     }
2493 
2494     if (c_startupoffset >= 0) {
2495         write_zcc_defined("startupoffset", c_startupoffset, 0);
2496     }
2497 
2498     if (linkargs == NULL) {
2499         linkargs = muststrdup("");
2500     }
2501 
2502     if (linklibs == NULL) {
2503         linklibs = muststrdup("");
2504     }
2505 }
2506 
configure_maths_library(char ** libstring)2507 static void configure_maths_library(char **libstring)
2508 {
2509     char   buf[1024];
2510 
2511     /* By convention, -lm refers to GENMATH, -lmz to Z88MATHLIB/ALTMATHLIB */
2512 
2513     if (c_altmathlib) {
2514         if (strstr(*libstring, "-lmz ") != NULL) {
2515             snprintf(buf, sizeof(buf), "-l\"%s\" ", c_altmathlib);
2516             if ((*libstring = replace_str(*libstring, "-lmz ", buf)) == NULL) {
2517                 fprintf(stderr, "Malloc failed\n");
2518                 exit(1);
2519             }
2520             parse_option(c_altmathflags);
2521         }
2522     }
2523 
2524     if (c_genmathlib) {
2525         if (strstr(*libstring, "-lm ") != NULL) {
2526             snprintf(buf, sizeof(buf), "-l\"%s\" ", c_genmathlib);
2527             if ((*libstring = replace_str(*libstring, "-lm ", buf)) == NULL) {
2528                 fprintf(stderr, "Malloc failed\n");
2529                 exit(1);
2530             }
2531         }
2532     }
2533 }
2534 
configure_assembler()2535 static void configure_assembler()
2536 {
2537     char            buf[FILENAME_MAX+1];
2538     char           *assembler = NULL;
2539     char           *linker = NULL;
2540     int             type = ASM_Z80ASM;
2541     enum iostyle    style = outimplied;
2542 
2543     type = ASM_Z80ASM;
2544     linker = c_z80asm_exe;
2545     assembler = c_z80asm_exe;
2546 
2547     assembler_type = type;
2548     assembler_style = style;
2549     if (assembler) {
2550         c_assembler = assembler;
2551     }
2552     if (linker) {
2553         c_linker = linker;
2554     }
2555     if ( c_generate_debug_info) {
2556         mapon = 1;
2557         BuildOptions(&asmargs, "-m");
2558         BuildOptions(&asmargs, "-debug");
2559         BuildOptions(&linkargs, "-debug");
2560     }
2561     snprintf(buf,sizeof(buf),"-I\"%s\"",zcc_opt_dir);
2562     BuildOptions(&asmargs, buf);
2563     BuildOptions(&linkargs, buf);
2564 }
2565 
2566 
2567 
2568 
configure_compiler()2569 static void configure_compiler()
2570 {
2571     char *preprocarg;
2572     char  buf[256];
2573 
2574     compiler_type = CC_SCCZ80;
2575 
2576     /* compiler= */
2577     if ((strcmp(c_compiler_type, "clang") == 0) || (strcmp(c_compiler_type, "sdcc") == 0)) {
2578         compiler_type = CC_SDCC;
2579         snprintf(buf, sizeof(buf), "%s --no-optsdcc-in-asm --c1mode --emit-externs %s %s %s ", \
2580             select_cpu(CPU_MAP_TOOL_ZSDCC), \
2581             (sdcc_signed_char ? "--fsigned-char" : ""), \
2582             (c_code_in_asm ? "" : "--no-c-code-in-asm"), \
2583             (opt_code_size ? "--opt-code-size" : ""));
2584         add_option_to_compiler(buf);
2585         if (sdccarg) {
2586             add_option_to_compiler(sdccarg);
2587         }
2588         if ( c_generate_debug_info) {
2589             add_option_to_compiler("--debug");
2590         }
2591         preprocarg = " -D__SDCC";
2592         BuildOptions(&cpparg, preprocarg);
2593         c_compiler = c_sdcc_exe;
2594         c_cpp_exe = c_sdcc_preproc_exe;
2595         compiler_style = filter_outspecified_flag;
2596         BuildOptions(&asmargs, "-D__SDCC");
2597         BuildOptions(&linkargs, "-D__SDCC");
2598     }
2599     else {
2600         preprocarg = " -DSCCZ80 -DSMALL_C -D__SCCZ80";
2601         BuildOptions(&cpparg, preprocarg);
2602                 BuildOptions(&asmargs, "-D__SCCZ80");
2603                 BuildOptions(&linkargs, "-D__SCCZ80");
2604         /* Indicate to sccz80 what assembler we want */
2605         snprintf(buf, sizeof(buf), "-ext=opt %s -zcc-opt=%s", select_cpu(CPU_MAP_TOOL_SCCZ80),zcc_opt_def);
2606         add_option_to_compiler(buf);
2607 
2608         if (sccz80arg) {
2609             add_option_to_compiler(sccz80arg);
2610         }
2611         if (c_code_in_asm) {
2612             add_option_to_compiler("-cc");
2613         }
2614         if (c_sccz80_r2l_calling) {
2615             add_option_to_compiler("-set-r2l-by-default");
2616             preprocarg = " -DZ88DK_R2L_CALLING_CONVENTION";
2617             BuildOptions(&cpparg, preprocarg);
2618         }
2619         if ( c_generate_debug_info) {
2620             add_option_to_compiler("-debug-defc");
2621         }
2622         c_compiler = c_sccz80_exe;
2623         compiler_style = outspecified_flag;
2624     }
2625 }
2626 
2627 
PragmaInclude(option * arg,char * val)2628 void PragmaInclude(option *arg, char *val)
2629 {
2630     char *ptr = strip_outer_quotes(val);
2631 
2632     while ((*ptr == '=') || (*ptr == ':')) ++ptr;
2633 
2634     if (*ptr != '\0') {
2635         free(pragincname);
2636         pragincname = muststrdup(ptr);
2637     }
2638 }
2639 
PragmaRedirect(option * arg,char * val)2640 void PragmaRedirect(option *arg, char *val)
2641 {
2642     char *eql;
2643     char *ptr = val;
2644     char *value = "";
2645 
2646     if ((eql = strchr(ptr, '=')) != NULL) {
2647         *eql = 0;
2648         value = eql + 1;
2649     }
2650     if (strlen(value)) {
2651         add_zccopt("\nIF !DEFINED_%s\n", ptr);
2652         add_zccopt("\tPUBLIC %s\n", ptr);
2653         add_zccopt("\tEXTERN %s\n", value);
2654         add_zccopt("\tdefc\tDEFINED_%s = 1\n", ptr);
2655         add_zccopt("\tdefc %s = %s\n", ptr, value);
2656         add_zccopt("ENDIF\n\n");
2657     }
2658 }
2659 
Alias(option * arg,char * val)2660 void Alias(option *arg, char *val)
2661 {
2662     char *ptr = val;
2663     char *eql;
2664 
2665     if ((eql = strchr(ptr, '=')) != NULL) {
2666         *eql = 0;
2667         aliases = realloc(aliases, (aliases_num + 2) * sizeof(aliases[0]));
2668         aliases[aliases_num++] = strdup(ptr);
2669         aliases[aliases_num++] = strdup(eql+1);
2670     }
2671 }
2672 
2673 
PragmaDefine(option * arg,char * val)2674 void PragmaDefine(option *arg, char *val)
2675 {
2676     char *ptr = val;
2677     int   value = 0;
2678     char *eql;
2679 
2680     if ((eql = strchr(ptr, '=')) != NULL) {
2681         *eql = 0;
2682         value = (int)strtol(eql + 1, NULL, 0);
2683     }
2684     write_zcc_defined(ptr, value, 0);
2685 }
2686 
PragmaExport(option * arg,char * val)2687 void PragmaExport(option *arg, char *val)
2688 {
2689     char *ptr = val;
2690     int   value = 0;
2691     char *eql;
2692 
2693     if ((eql = strchr(ptr, '=')) != NULL) {
2694         *eql = 0;
2695         value = (int)strtol(eql + 1, NULL, 0);
2696     }
2697     write_zcc_defined(ptr, value, 1);
2698 }
2699 
write_zcc_defined(char * name,int value,int export)2700 void write_zcc_defined(char *name, int value, int export)
2701 {
2702     add_zccopt("\nIF !DEFINED_%s\n", name);
2703     add_zccopt("\tdefc\tDEFINED_%s = 1\n", name);
2704     if (export) add_zccopt("\tPUBLIC\t%s\n", name);
2705     add_zccopt("\tdefc %s = %d\n", name, value);
2706     add_zccopt("\tIFNDEF %s\n\tENDIF\n", name);
2707     add_zccopt("ENDIF\n\n");
2708 }
2709 
PragmaNeed(option * arg,char * val)2710 void PragmaNeed(option *arg, char *val)
2711 {
2712     char *ptr = val;
2713 
2714     add_zccopt("\nIF !NEED_%s\n", ptr);
2715     add_zccopt("\tDEFINE\tNEED_%s\n", ptr);
2716     add_zccopt("ENDIF\n\n");
2717 }
2718 
2719 
PragmaBytes(option * arg,char * val)2720 void PragmaBytes(option *arg, char *val)
2721 {
2722     char *ptr = val;
2723     char *value;
2724 
2725     if ((value = strchr(ptr, '=')) != NULL) {
2726         *value++ = 0;
2727     }
2728     else {
2729         return;
2730     }
2731 
2732     add_zccopt("\nIF NEED_%s\n", ptr);
2733     add_zccopt("\tDEFINED\tDEFINED_NEED_%s\n", ptr);
2734     add_zccopt("\tdefb\t%s\n", value);
2735     add_zccopt("ENDIF\n\n");
2736 }
2737 
2738 /** \brief Remove line feeds at the end of a line
2739 */
KillEOL(char * str)2740 void KillEOL(char *str)
2741 {
2742     char           *ptr;
2743     if ((ptr = strrchr(str, '\n')))
2744         *ptr = 0;
2745     if ((ptr = strrchr(str, '\r')))
2746         *ptr = 0;
2747 }
2748 
2749 
2750 /** \brief Copy any generated output files back to the directory they came from
2751 */
copy_output_files_to_destdir(char * suffix,int die_on_fail)2752 void copy_output_files_to_destdir(char *suffix, int die_on_fail)
2753 {
2754     int             j;
2755     char           *ptr, *p, *name;
2756     struct stat     tmp;
2757     char            fname[FILENAME_MAX + 32];
2758 
2759     /* copy each output file having indicated extension */
2760     for (j = build_bin ? 1 : 0; j < nfiles; ++j) {
2761 
2762         /* check that original file was actually processed */
2763         if (((ptr = find_file_ext(original_filenames[j])) == NULL) || (strcmp(ptr, suffix) != 0)) {
2764 
2765             ptr = changesuffix(filelist[j], suffix);
2766 
2767             /* check that this file was generated */
2768             if (stat(ptr, &tmp) == 0) {
2769 
2770                 /* generate output filename */
2771                 if (outputfile != NULL)
2772                     strcpy(fname, outputfile);                                  /* use supplied output filename */
2773                 else {
2774                     /* using original filename to create output filename */
2775                     name = stripsuffix(original_filenames[j], ".m4");
2776                     if (strcmp(suffix, c_extension) == 0) {
2777                         p = changesuffix(name, suffix);                         /* for .o, use original filename with extension changed to .o */
2778                         strcpy(fname, p);
2779                         free(p);
2780                     }
2781                     else {
2782                         p = stripsuffix(name, suffix);
2783                         snprintf(fname, sizeof(fname), "%s%s", p, suffix);      /* use original filename with extension appended */
2784                         free(p);
2785                     }
2786                     free(name);
2787                 }
2788 
2789                 /* copy to output directory */
2790                 if (copy_file(ptr, "", fname, "")) {
2791                     fprintf(stderr, "Couldn't copy output file %s\n", fname);
2792                     if (die_on_fail) {
2793                         free(ptr);
2794                         exit(1);
2795                     }
2796                 }
2797             }
2798 
2799             free(ptr);
2800         }
2801     }
2802 }
2803 
2804 
remove_temporary_files(void)2805 void remove_temporary_files(void)
2806 {
2807     int             j;
2808 
2809     /* Show all error files */
2810 
2811     for (j = 0; j < nfiles; j++) {
2812         ShowErrors(filelist[j], original_filenames[j]);
2813     }
2814 
2815     if (cleanup) {    /* Default is yes */
2816         for (j = 0; j < nfiles; j++) {
2817             remove_file_with_extension(temporary_filenames[j], "");
2818             remove_file_with_extension(temporary_filenames[j], ".ll");
2819             remove_file_with_extension(temporary_filenames[j], ".i");
2820             remove_file_with_extension(temporary_filenames[j], ".i2");
2821             remove_file_with_extension(temporary_filenames[j], ".asm");
2822             remove_file_with_extension(temporary_filenames[j], ".s2");
2823             remove_file_with_extension(temporary_filenames[j], ".err");
2824             remove_file_with_extension(temporary_filenames[j], ".op2");
2825             remove_file_with_extension(temporary_filenames[j], ".op1");
2826             remove_file_with_extension(temporary_filenames[j], ".opt");
2827             remove_file_with_extension(temporary_filenames[j], ".o");
2828             remove_file_with_extension(temporary_filenames[j], ".map");
2829             remove_file_with_extension(temporary_filenames[j], ".adb");
2830             remove_file_with_extension(temporary_filenames[j], ".sym");
2831             remove_file_with_extension(temporary_filenames[j], ".def");
2832             remove_file_with_extension(temporary_filenames[j], ".tmp");
2833             remove_file_with_extension(temporary_filenames[j], ".lis");
2834         }
2835     }
2836     /* Cleanup zcc_opt files */
2837     remove(zcc_opt_def);
2838     rmdir(zcc_opt_dir);
2839 }
2840 
2841 
remove_file_with_extension(char * file,char * ext)2842 void remove_file_with_extension(char *file, char *ext)
2843 {
2844     char           *temp;
2845     temp = changesuffix(file, ext);
2846     remove(temp);
2847     free(temp);
2848 }
2849 
2850 
2851 void
ShowErrors(char * filen,char * orig)2852 ShowErrors(char *filen, char *orig)
2853 {
2854     char           *temp;
2855     char            buffer[LINEMAX + 1];
2856     char            buffer2[LINEMAX + 1];
2857     char            filenamebuf[LINEMAX + 1];
2858     char           *ptr_char;
2859     int             j, linepos;
2860     FILE           *fp, *fp2;
2861 
2862     temp = changesuffix(filen, ".err");
2863     if ((fp = fopen(temp, "r")) != NULL) {
2864         if (orig) {
2865             fprintf(stderr, "Errors in source file %s:\n", orig);
2866         } else {
2867             /* We're printing linking errors, better print a key! */
2868             fprintf(stderr, "Key to filenames:\n");
2869             for (j = 0; j < nfiles; j++) {
2870                 fprintf(stderr, "%s = %s\n", filelist[j], original_filenames[j]);
2871             }
2872         }
2873 
2874         while (fgets(buffer, LINEMAX, fp) != NULL) {
2875             fprintf(stderr, "%s", buffer);
2876 
2877             /* Dig into asm source file and show the corresponding line.. */
2878             if (strstr(buffer, " line ") != NULL ) {    /* ..only if a line number is given */
2879                 linepos = atoi(strstr(buffer, " line ") + strlen(" line "));
2880                 strcpy(filenamebuf, strstr(buffer, "'") + strlen("'"));
2881                 ptr_char = strstr(filenamebuf,"::");
2882                 if (ptr_char) *ptr_char = 0;
2883                 ptr_char = strstr(filenamebuf, "'");        /* Find second ' */
2884                 if (ptr_char) *ptr_char = 0;                /* End filenamebuf at second ' or at end of string */
2885 
2886                 if ((linepos > 1) && ((fp2 = fopen(filenamebuf, "r")) != NULL)) {
2887                     for (j = 1; j < linepos; j++) {
2888                         if (NULL == fgets(buffer2, LINEMAX, fp2)) {
2889                             fprintf(stderr, "Error while reading string from %s\n", temp);
2890                             exit(1);
2891                         }
2892                     }
2893                     fprintf(stderr, "                   ^ ---- %s", fgets(buffer2, LINEMAX, fp2));
2894                     fclose(fp2);
2895                 }
2896             }
2897 
2898         }
2899         fclose(fp);
2900 
2901     }
2902     free(temp);        /* Free temp buffer */
2903 
2904 }
2905 
2906 
2907 /** \brief Get a temporary filename
2908 */
tempname(char * filen)2909 void tempname(char *filen)
2910 {
2911 #ifdef _WIN32
2912 
2913     char   *ptr;
2914 
2915     if ((ptr = _tempnam(".\\", tmpnambuf)) == NULL) {
2916         fprintf(stderr, "Failed to create temporary filename\n");
2917         exit(1);
2918     }
2919     strcpy(filen, ptr);
2920     free(ptr);
2921 
2922 #elif defined(__MSDOS__) && defined(__TURBOC__)
2923     /* Both predefined by Borland's Turbo C/C++ and Borland C/C++ */
2924 
2925     if (ptr = getenv("TEMP")) {    /* From MS-DOS 5, C:\TEMP, C:\DOS,
2926                                     * C:\WINDOWS\TEMP or whatever or
2927                                     * nothing */
2928         strcpy(filen, ptr);    /* Directory is not guaranteed to
2929                                 * exist */
2930         strcat(filen, "\\");
2931         tmpnam(filen + strlen(filen));    /* Adds strings like
2932                                            * TMP1.$$$, TMP2.$$$ */
2933     }
2934     /* Allways starts at TMP1.$$$. Does not */
2935     else            /* check if file already exists. So is  */
2936         tmpnam(filen);    /* not suitable for executing zcc more  */
2937     if (ptr = find_file_ext(filen))    /* than once without cleaning out
2938                                         * files. */
2939         *ptr = 0;    /* Don't want to risk too long filenames */
2940 #else
2941     strcpy(filen, "/tmp/tmpXXXXXXXX");
2942 
2943     /* Prevent linker warning: the use of mktemp is dangerous */
2944     /* mktemp(filen);                                         */
2945     if ( ( mkstemp(filen) == -1 ) || ( unlink(filen) == -1 ) ) {   /* Automatic delete of file by unlink */
2946         fprintf(stderr, "Failed to create temporary filename\n");
2947         exit(1);
2948     }
2949 #endif
2950 }
2951 
2952 /*
2953 *    Find a config file to use:
2954 *
2955 *    Scheme is as follows:
2956 *    Use ZCCFILE for compatibility
2957 *    If not, use ZCCCFG/zcc.cfg
2958 *        or  ZCCCFG/argv[1]
2959 *    Or as a first resort argv[1]
2960 *    Returns gc (or exits)
2961 *
2962 *    If ZCCCFG doesn't exist then we take the c_install_dir/lib/config/zcc.cfg
2963 */
find_zcc_config_fileFile(const char * program,char * arg,int gc,char * buf,size_t buflen)2964 int find_zcc_config_fileFile(const char *program, char *arg, int gc, char *buf, size_t buflen)
2965 {
2966     FILE           *fp;
2967     char           *cfgfile;
2968 
2969     /* Scan for an option file on the command line */
2970     if (arg[0] == '+') {
2971         snprintf(buf, buflen, "%s", arg + 1);
2972         gc++;        /* Increment first arg to search from */
2973         if (strstr(arg, ".cfg") != NULL) {
2974             if ((fp = fopen(buf, "r")) != NULL) {
2975                 /* Local config file */
2976                 fclose(fp);
2977                 return (gc);
2978             }
2979         }
2980         cfgfile = getenv("ZCCCFG");
2981         if (cfgfile != NULL) {
2982             if (strlen(cfgfile) > ( FILENAME_MAX - strlen(arg+1) - strlen("/.cfg") )) {
2983                 fprintf(stderr, "Possibly corrupt env variable ZCCCFG\n");
2984                 exit(1);
2985             }
2986             /* Config file in config directory */
2987             snprintf(buf, buflen, "%s/%s.cfg", cfgfile, arg + 1);
2988             return (gc);
2989         } else {
2990             snprintf(buf, buflen, "%s/lib/config/%s.cfg", c_install_dir, arg + 1);
2991         }
2992         /*
2993          * User supplied invalid config file, let it fall over back
2994          * when
2995          */
2996         return (gc);
2997     }
2998     /* Without a config file, we should just print usage and then exit */
2999     fprintf(stderr, "A config file must be specified with +file as the first argument\n\n");
3000     print_help_text(program);
3001     exit(1);
3002 }
3003 
3004 
3005 /* Parse options - rewritten to use qstrtok which is like strtok but understands quoting */
parse_option(char * option)3006 void parse_option(char *option)
3007 {
3008     char           *ptr;
3009 
3010     if (option != NULL) {
3011         ptr = qstrtok(option, " \t\r\n");
3012         while (ptr != NULL) {
3013             if (ptr[0] == '-') {
3014                 parse_cmdline_arg(strip_inner_quotes(ptr));
3015             } else {
3016                 add_file_to_process(strip_outer_quotes(ptr));
3017             }
3018             ptr = qstrtok(NULL, " \t\r\n");
3019         }
3020     }
3021 }
3022 
add_zccopt(char * fmt,...)3023 void add_zccopt(char *fmt, ...)
3024 {
3025     char   buf[4096];
3026     size_t len = zccopt ? strlen(zccopt) : 0;
3027     size_t extra;
3028     va_list ap;
3029 
3030     va_start(ap, fmt);
3031     extra = vsnprintf(buf, sizeof(buf), fmt, ap);
3032     va_end(ap);
3033 
3034     zccopt = realloc(zccopt, len + extra + 1);
3035     strcpy(zccopt + len, buf);
3036 }
3037 
3038 /* From: http://creativeandcritical.net/str-replace-c/ */
replace_str(const char * str,const char * old,const char * new)3039 char *replace_str(const char *str, const char *old, const char *new)
3040 {
3041     char *ret, *r;
3042     const char *p, *q;
3043     size_t oldlen = strlen(old);
3044     size_t count, retlen, newlen = strlen(new);
3045 
3046     if (oldlen != newlen) {
3047         for (count = 0, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen)
3048             count++;
3049         /* this is undefined if p - str > PTRDIFF_MAX */
3050         retlen = p - str + strlen(p) + count * (newlen - oldlen);
3051     }
3052     else
3053         retlen = strlen(str);
3054 
3055     ret = mustmalloc(retlen + 1);
3056 
3057     for (r = ret, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) {
3058         /* this is undefined if q - p > PTRDIFF_MAX */
3059         ptrdiff_t l = q - p;
3060         memcpy(r, p, l);
3061         r += l;
3062         memcpy(r, new, newlen);
3063         r += newlen;
3064     }
3065     strcpy(r, p);
3066 
3067     return ret;
3068 }
3069 
setup_default_configuration()3070 static void setup_default_configuration()
3071 {
3072     char    buf[1024];
3073     arg_t  *pargs = config;
3074 
3075     while (pargs->setfunc) {
3076         if (pargs->defvalue) {
3077             snprintf(buf, sizeof(buf), "%s %s", pargs->name, pargs->defvalue);
3078             parse_configfile_line(buf);
3079         }
3080         pargs++;
3081     }
3082 }
3083 
print_specs()3084 static void print_specs()
3085 {
3086     arg_t  *pargs = config;
3087     int     i;
3088 
3089     while (pargs->setfunc) {
3090         if ((pargs->flags & AF_DEPRECATED) == 0 && pargs->setfunc == SetStringConfig) {
3091             if (*(char **)pargs->data != NULL && strlen(*(char **)pargs->data)) {
3092                 printf("%-20s\t%s\n", pargs->name, *(char **)pargs->data);
3093             }
3094             else {
3095                 printf("%-20s\t[undefined]\n", pargs->name);
3096             }
3097         }
3098         else if (pargs->setfunc == AddArray) {
3099             for (i = 0; i < *pargs->num_ptr; i++) {
3100                 printf("%-20s\t%s\n", pargs->name, (*(char ***)pargs->data)[i]);
3101             }
3102         }
3103         pargs++;
3104     }
3105 }
3106 
3107 
isquote(unsigned char c)3108 static int isquote(unsigned char c)
3109 {
3110     return ((c == '"') || (c == '\''));
3111 }
3112 
3113 
3114 /* strtok with quoting */
qstrtok(char * s,const char * delim)3115 static char *qstrtok(char *s, const char *delim)
3116 {
3117     static char *start = NULL;
3118     char *ret;
3119     uint32_t quote_mask = 0;
3120     uint32_t quote_type = 0;
3121     int type = 0;
3122 
3123     /* check for new string */
3124     if (s != NULL)
3125     {
3126         /* clear inquote indicators */
3127         quote_mask = quote_type = 0;
3128 
3129         /* skip initial delimiters */
3130         for (start = s; *start; ++start)
3131             if ((strchr(delim, *start) == NULL) || isquote(*start))
3132                 break;
3133 
3134         if (*start == '\0') start = NULL;
3135     }
3136 
3137     /* check if current string is done */
3138     if (start == NULL) return NULL;
3139 
3140     /* look for next token in current string */
3141     for (ret = start; *start; ++start) {
3142         if (quote_mask) {
3143             /* inside quote, ignore delim */
3144             if (isquote(*start)) {
3145                 type = (*start == '"');
3146                 if (type == (quote_type & 0x01))
3147                 {
3148                     /* undoing one level of quote */
3149                     quote_mask >>= 1;
3150                     quote_type >>= 1;
3151                 } else {
3152                     /* adding a level of quote */
3153                     if (quote_mask & 0x80000000UL)
3154                     {
3155                         fprintf(stderr, "Error: Reached maximum quoting level\n");
3156                         exit(1);
3157                     }
3158 
3159                     quote_mask = (quote_mask << 1) + 1;
3160                     quote_type = (quote_type << 1) + type;
3161                 }
3162             }
3163         } else {
3164             /* behave like strtok, delim takes precedence over quoting */
3165             if (strchr(delim, *start))
3166                 break;
3167             /* check for quoting */
3168             if (isquote(*start)) {
3169                 quote_mask = 1;
3170                 quote_type = (*start == '"');
3171             }
3172         }
3173     }
3174 
3175     if (*start == '\0')
3176         start = NULL;
3177     else
3178         *start++ = '\0';
3179     return ret;
3180 }
3181 
3182 
3183 /* strip away first level of quotes inside string */
strip_inner_quotes(char * p)3184 static char *strip_inner_quotes(char *p)
3185 {
3186     char *first, *last, *temp;
3187     size_t len;
3188 
3189     len = strlen(p);
3190 
3191     first = strchr(p, '"');
3192     temp = strchr(p, '\'');
3193 
3194     if ((first == NULL) || ((temp != NULL) && (first > temp)))
3195         first = temp;
3196 
3197     last = strrchr(p, '"');
3198     temp = strrchr(p, '\'');
3199 
3200     if ((last == NULL) || ((temp != NULL) && (last < temp)))
3201         last = temp;
3202 
3203     if ((first != NULL) && (first != last) && (*first == *last)) {
3204         memmove(first, first + 1, last - first - 1);
3205         memmove(last - 1, last, p + len - last - 1);
3206         p[len - 2] = '\0';
3207     }
3208 
3209     return p;
3210 }
3211 
3212 
3213 /* strip away outer quotes if string is quoted */
strip_outer_quotes(char * p)3214 static char *strip_outer_quotes(char *p)
3215 {
3216     size_t q = strlen(p);
3217 
3218     if (isquote(*p) && (p[0] == p[q - 1])) {
3219         p[q - 1] = '\0';
3220         p++;
3221     }
3222 
3223     return p;
3224 }
3225 
3226 
zcc_vasprintf(char ** s,const char * fmt,va_list ap)3227 static int zcc_vasprintf(char **s, const char *fmt, va_list ap)
3228 {
3229     FILE       *fp;
3230     va_list     saveap;
3231     size_t      req;
3232     char       *ret;
3233 
3234     /* MSC Visual Studio 2010 does not have va_copy and va_list is a simple pointer */
3235 #ifdef _MSC_VER
3236     saveap = ap;
3237 #else
3238     va_copy(saveap, ap);
3239 #endif
3240 
3241     /* This isn't performant, but we don't use it that much */
3242     if (
3243 #ifndef WIN32
3244     (fp = fopen("/dev/null", "w")) != NULL
3245 #else
3246         (fp = fopen("NUL", "w")) != NULL
3247 #endif
3248         ) {
3249         req = vfprintf(fp, fmt, ap);
3250         fclose(fp);
3251         ret = calloc(req + 1, sizeof(char));
3252         req = vsnprintf(ret, req + 1, fmt, saveap);
3253         *s = ret;
3254     }
3255     else {
3256         *s = NULL;
3257         req = -1;
3258     }
3259     return req;
3260 }
3261 
3262 
3263 
zcc_asprintf(char ** s,const char * fmt,...)3264 static int zcc_asprintf(char **s, const char *fmt, ...)
3265 {
3266     va_list arg;
3267     int     res;
3268 
3269     va_start(arg, fmt);
3270     res = zcc_vasprintf(s, fmt, arg);
3271     va_end(arg);
3272     return res;
3273 }
3274 
3275 
3276 
3277 
3278 /*
3279 * zcc_getdelim()
3280 * Copyright (C) 2003 ETC s.r.o.
3281 *
3282 * This program is free software; you can redistribute it and/or
3283 * modify it under the terms of the GNU General Public License
3284 * as published by the Free Software Foundation; either version 2
3285 * of the License, or (at your option) any later version.
3286 *
3287 * This program is distributed in the hope that it will be useful,
3288 * but WITHOUT ANY WARRANTY; without even the implied warranty of
3289 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
3290 * GNU General Public License for more details.
3291 *
3292 * You should have received a copy of the GNU General Public License
3293 * along with this program; if not, write to the Free Software
3294 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
3295 * 02111-1307, USA.
3296 *
3297 * Written by Marcel Telka <marcel@telka.sk>, 2003.
3298 *
3299 */
3300 
3301 #ifndef GETDELIM_BUFFER
3302 #define GETDELIM_BUFFER 128
3303 #endif
3304 
zcc_getdelim(char ** lineptr,unsigned int * n,int delimiter,FILE * stream)3305 static int zcc_getdelim(char **lineptr, unsigned int *n, int delimiter, FILE *stream)
3306 {
3307 
3308     char *p, *np;
3309     int c;
3310     unsigned int len = 0;
3311 
3312     if (!lineptr || !n)
3313         return -1;
3314 
3315     /* allocate initial buffer */
3316     if (!*lineptr || !*n) {
3317         np = realloc(*lineptr, GETDELIM_BUFFER);
3318         if (!np)
3319             return -1;
3320         *n = GETDELIM_BUFFER;
3321         *lineptr = np;
3322     }
3323 
3324     p = *lineptr;
3325 
3326     /* read characters from stream */
3327     while ((c = fgetc(stream)) != EOF) {
3328         if (len >= *n) {
3329             np = realloc(*lineptr, *n * 2);
3330             if (!np)
3331                 return -1;
3332             p = np + (p - *lineptr);
3333             *lineptr = np;
3334             *n *= 2;
3335         }
3336         *p++ = (char)c;
3337         len++;
3338         if (delimiter == c)
3339             break;
3340     }
3341 
3342     /* end of file without any bytes read */
3343     if ((c == EOF) && (len == 0))
3344         return -1;
3345 
3346     /* trailing '\0' */
3347     if (len >= *n) {
3348         np = realloc(*lineptr, *n + 1);
3349         if (!np)
3350             return -1;
3351         p = np + (p - *lineptr);
3352         *lineptr = np;
3353         *n += 1;
3354     }
3355     *p = '\0';
3356 
3357     return len;
3358 }
3359 
3360 
3361 
3362 /*
3363 * Local Variables:
3364 *  indent-tabs-mode:nil
3365 *  require-final-newline:t
3366 *  c-basic-offset: 4
3367 *  eval: (c-set-offset 'case-label 0)
3368 *  eval: (c-set-offset 'substatement-open 0)
3369 *  eval: (c-set-offset 'access-label 0)
3370 *  eval: (c-set-offset 'class-open 4)
3371 *  eval: (c-set-offset 'class-close 4)
3372 * End:
3373 */
3374