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