cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

lcc.c (33711B)


      1/*
      2 * lcc [ option ]... [ file | -llib ]...
      3 * front end for the ANSI C compiler
      4 */
      5static char rcsid[] = "$Id: lcc.c,v 2.0 " BUILDDATE " " BUILDTIME " gbdk-2020 Exp $";
      6
      7#include <stdio.h>
      8#include <stdarg.h>
      9#include <stdlib.h>
     10#include <stdbool.h>
     11#include <string.h>
     12#include <assert.h>
     13#include <ctype.h>
     14#include <signal.h>
     15
     16#ifdef _WIN32
     17# include <io.h>
     18# include <process.h>
     19#else
     20# include <unistd.h>
     21# include <sys/types.h>
     22# include <sys/wait.h>
     23#endif
     24
     25#include "gb.h"
     26#include "list.h"
     27#include "targets.h"
     28
     29#ifndef TEMPDIR
     30#define TEMPDIR "/tmp"
     31#endif
     32
     33void *alloc(int);
     34extern char *basepath(char *);
     35extern char *path_stripext(char *);
     36extern char *path_newext(char *, char *);
     37static int callsys(char *[]);
     38extern char *concat(const char *, const char *);
     39static void compose(char *[], List, List, List);
     40static void error(char *, char *);
     41static char *exists(char *);
     42static char *first(char *);
     43static int filename(char *, char *);
     44static void help(void);
     45static void initinputs(void);
     46static void interrupt(int);
     47static void opt(char *);
     48extern int main(int, char *[]);
     49extern char *replace(const char *, int, int);
     50static void rm(List);
     51extern char *strsave(const char *);
     52extern char *stringf(const char *, ...);
     53extern int suffix(char *, char *[], int);
     54extern char *tempname(char *);
     55
     56static bool arg_has_searchkey(char *, char *);
     57
     58// Adds linker default required vars if not present (defined by user)
     59static void Fixllist();
     60
     61static void handle_autobanking(void);
     62
     63
     64// These get populated from _class using finalise() in gb.c
     65extern char *cpp[], *include[], *com[], *as[], *bankpack[], *ld[], *ihxcheck[], *mkbin[], *postproc[], inputs[], *suffixes[], *rom_extension;
     66extern arg_entry *llist0_defaults;
     67extern int llist0_defaults_len;
     68
     69extern int option(char *);
     70extern void set_gbdk_dir(char*);
     71
     72void finalise(void);
     73
     74static int errcnt;		/* number of errors */
     75static int Eflag;		/* -E specified */
     76static int Sflag;		/* -S specified */
     77static int cflag;		/* -c specified */
     78static int Kflag;		/* -K specified */
     79static int autobankflag;	/* -K specified */
     80int verbose;		/* incremented for each -v */
     81static List bankpack_flags;	/* bankpack flags */
     82static List ihxchecklist;	/* ihxcheck flags */
     83static List mkbinlist;		/* loader files, flags */
     84
     85// Index entries for llist[]
     86#define L_ARGS 0
     87#define L_FILES 1
     88#define L_LKFILES 2
     89static List llist[3];       /* [2] = .lkfiles, [1] = linker object file list, [0] = linker flags */
     90
     91static List alist;		/* assembler flags */
     92List clist;		/* compiler flags */
     93static List plist;		/* preprocessor flags */
     94static List ilist;		/* list of additional includes from LCCINPUTS */
     95static List rmlist;		/* list of files to remove */
     96static char *outfile;		/* ld output file or -[cS] object file */
     97static int ac;			/* argument count */
     98static char **av;		/* argument vector */
     99char *tempdir = TEMPDIR;	/* directory for temporary files */
    100char *progname;
    101static List lccinputs;		/* list of input directories */
    102char bankpack_newext[1024] = {'\0'};
    103static int ihx_inputs = 0;  // Number of ihx files present in input list
    104static char ihxFile[256] = {'\0'};
    105static char binFile[256] = {'\0'};
    106
    107
    108int main(int argc, char *argv[]) {
    109	int i, j, nf;
    110
    111	progname = argv[0];
    112	ac = argc + 50;
    113	av = alloc(ac * sizeof(char *));
    114	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
    115		signal(SIGINT, interrupt);
    116	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
    117		signal(SIGTERM, interrupt);
    118#ifdef SIGHUP
    119	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
    120		signal(SIGHUP, interrupt);
    121#endif
    122	if (getenv("TMP"))
    123		tempdir = getenv("TMP");
    124	else if (getenv("TEMP"))
    125		tempdir = getenv("TEMP");
    126	else if (getenv("TMPDIR"))
    127		tempdir = getenv("TMPDIR");
    128	assert(tempdir);
    129
    130	// Remove trailing slashes
    131	i = strlen(tempdir);
    132	for (; (i > 0) && ((tempdir[i - 1] == '/') || (tempdir[i - 1] == '\\')); i--)
    133		tempdir[i - 1] = '\0';
    134	if (argc <= 1) {
    135		help();
    136		exit(0);
    137	}
    138//	clist = append("-D__LCC__", 0);
    139	initinputs();
    140	if (getenv("GBDKDIR"))
    141		option(stringf("--prefix=%s", getenv("GBDKDIR")));
    142	else
    143		set_gbdk_dir(argv[0]);
    144	for (nf = 0, i = j = 1; i < argc; i++) {
    145		if (strcmp(argv[i], "-o") == 0) {
    146			if (++i < argc) {
    147				// Don't allow output file to have ".c" or ".i" extension (first two in suffixes[])
    148				if (suffix(argv[i], suffixes, 2) != SUFX_NOMATCH) {
    149					error("-o would overwrite %s", argv[i]);
    150					exit(8);
    151				}
    152				// Valid output file found
    153				outfile = argv[i];
    154				continue;
    155			}
    156			else {
    157				error("unrecognized option `%s'", argv[i - 1]);
    158				exit(8);
    159			}
    160		}
    161		else if (strcmp(argv[i], "-target") == 0) {
    162			if (argv[i + 1] && *argv[i + 1] != '-')
    163				i++;
    164			continue;
    165		}
    166		else if (*argv[i] == '-' && argv[i][1] != 'l') {
    167			opt(argv[i]);
    168			continue;
    169		}
    170		else if (*argv[i] != '-') {
    171			// Count number of (.ihx) files
    172			if (suffix(argv[i], (char * []){EXT_IHX}, 1) != SUFX_NOMATCH)
    173				ihx_inputs++;
    174			// Count number of (.c, .i, .asm, .s) files
    175			else if (suffix(argv[i], suffixes, 3) != SUFX_NOMATCH)
    176				nf++;
    177		}
    178		argv[j++] = argv[i];
    179	}
    180
    181	// Ignore -o output request if:
    182	// * compile only (-c) *OR* compile to ASM (-S) is specified
    183	// * and there are 2 or more source files (.c, .i, .asm, .s) in the input list (what "nf" seems to count)
    184	// Instead, it will generate output matching each input filename
    185	if ((cflag || Sflag) && outfile && nf != 1) {
    186		fprintf(stderr, "%s: -o %s ignored\n", progname, outfile);
    187		outfile = 0;
    188	}
    189
    190	// When .ihx is an input only ihxcheck and makebin will be called.
    191	// Warn that all source files won't be processed
    192	if ((ihx_inputs > 0) && (nf > 0)) {
    193		fprintf(stderr, "%s: Warning: .ihx file present as input, all other input files ignored\n", progname);
    194	}
    195
    196	// Add includes
    197	argv[j] = 0;
    198
    199	// This copies settings from port:platform "class" structure
    200	// into command strings used for compose()
    201	finalise();
    202
    203	for (i = 0; include[i]; i++)
    204		clist = append(include[i], clist);
    205	if (ilist) {
    206		List b = ilist;
    207		do {
    208			b = b->link;
    209			clist = append(b->str, clist);
    210		} while (b != ilist);
    211	}
    212	ilist = 0;
    213	for (i = 1; argv[i]; i++)
    214		// Process arguments
    215		if (*argv[i] == '-')
    216			opt(argv[i]);
    217		else {
    218			// Process filenames
    219			char *name = exists(argv[i]);
    220			if (name) {
    221				if (strcmp(name, argv[i]) != 0
    222					|| ((nf > 1) && (suffix(name, suffixes, 3) != SUFX_NOMATCH)) ) // Does it match: .c, .i, .asm, .s
    223
    224					fprintf(stderr, "%s:\n", name);
    225				// Send input filename argument to "filename processor"
    226				// which will add them to llist[n] in some form most of the time
    227				filename(name, 0);
    228			}
    229			else
    230				error("can't find `%s'", argv[i]);
    231		}
    232
    233
    234	// Perform Link / ihxcheck / makebin stages (unless some conditions prevent it)
    235	if (errcnt == 0 && !Eflag && !cflag && !Sflag &&
    236		(llist[L_FILES] || llist[L_LKFILES] || ((ihxFile[0] != '\0') && ihx_inputs))) {
    237
    238		int target_is_ihx = 0;
    239
    240		// if outfile is not specified, set it to default rom extension for active port:platform
    241		if(!outfile)
    242			outfile = concat("a", rom_extension);
    243
    244		// If an .ihx file is present as input skip link related stages
    245		if (ihx_inputs > 0) {
    246
    247			// Only one .ihx can be used for input, warn that others will be ignored
    248			if (ihx_inputs > 1)
    249				fprintf(stderr, "%s: Warning: Multiple (%d) .ihx files present as input, only one (%s) will be used\n", progname, ihx_inputs, ihxFile);
    250		}
    251		else {
    252
    253			//file.gb to file.ihx (don't use tmpfile because maps and other stuffs are created there)
    254			// Check to see if output target is a .ihx file
    255			target_is_ihx = (suffix(outfile, (char *[]){EXT_IHX}, 1) != SUFX_NOMATCH);
    256
    257			// Build ihx file name from output name
    258			sprintf(ihxFile, "%s%s", path_stripext(outfile), EXT_IHX);
    259
    260			// Only remove .ihx from the delete-list if it's not the final target
    261			if (!target_is_ihx)
    262				append(ihxFile, rmlist);
    263
    264			// If auto bank assignment is enabled, modify obj files before linking
    265			// Will alter: llist[L_FILES] and llist[L_LKFILES]
    266			if (autobankflag)
    267				handle_autobanking();
    268
    269			// Copy any pending linkerfiles into the linker list (with "-f" as preceding arg)
    270			llist[L_FILES] = list_add_to_another(llist[L_FILES], llist[L_LKFILES], NULL, "-f");
    271			// Call linker (add output ihxfile in compose $3)
    272			Fixllist();   // Fixlist adds required default linker vars if not added by user
    273			compose(ld, llist[L_ARGS], llist[L_FILES], append(ihxFile, 0));
    274
    275			if (callsys(av))
    276				errcnt++;
    277		} // end: non-ihx input file handling
    278
    279		// ihxcheck (test for multiple writes to the same ROM address)
    280		if (!Kflag) {
    281			compose(ihxcheck, ihxchecklist, append(ihxFile, 0), 0);
    282			if (callsys(av))
    283				errcnt++;
    284		}
    285
    286		// No need to makebin (.ihx -> .gb [or other rom_extension]) if .ihx is final target
    287		if (!target_is_ihx)
    288		{
    289			if(errcnt == 0)
    290			{
    291                // makebin - use output filename unless there is a post-process step
    292                if (strlen(postproc) == 0)
    293                    sprintf(binFile, "%s", outfile);
    294                else
    295                    sprintf(binFile, "%s", path_newext(outfile, EXT_ROM));
    296
    297                compose(mkbin, mkbinlist, append(ihxFile, 0), append(binFile, 0));
    298                if (callsys(av))
    299                    errcnt++;
    300
    301                // post-process step (such as makecom), if applicable
    302                if ((strlen(postproc) != 0) && (errcnt == 0)) {
    303                    compose(postproc, append(binFile, 0), append(outfile, 0), 0);
    304                    if (callsys(av))
    305                        errcnt++;
    306                }
    307			}
    308		}
    309	}
    310	rm(rmlist);
    311    if (verbose > 0)
    312        fprintf(stderr, "\n");
    313	return errcnt ? EXIT_FAILURE : EXIT_SUCCESS;
    314}
    315
    316
    317
    318// Check whether string "arg" has "searchkey" at the first possible
    319// occurrence of searchkey's starting character in arg (typically "." or "_")
    320static bool arg_has_searchkey(char * arg, char * searchkey) {
    321	char * str_start = strchr(arg, searchkey[0]);
    322
    323	if (str_start)
    324		return (strncmp(str_start, searchkey, strlen(searchkey)) == 0);
    325	else
    326		return false;
    327}
    328
    329
    330// Adds linker default required vars if not present (defined by user)
    331// Uses data from targets.c for per port/platform defaults
    332static void Fixllist()
    333{
    334	int c;
    335
    336	// Iterate through linker list entries
    337	if(llist[L_ARGS]) {
    338		List b = llist[L_ARGS];
    339		do {
    340			b = b->link;
    341			// Only -g and -b settings are supported at this time
    342			if(b->str[1] == 'g' || b->str[1] == 'b')
    343			{
    344				// '-g' and '-b' now have their values separated by a space.
    345				// That splits them into two consecutive llist items,
    346				// so try advancing to the next item to access it's value.
    347				// Example:  b = "-g", next = ".STACK=0xE000"
    348				if (b != llist[L_ARGS])
    349					b = b->link;
    350				else
    351					break; // end of list
    352
    353				// Check current linker arg to see if any default settings are present
    354				// If they do, flag them as present so they don't need to be added later
    355				for (c = 0; c < llist0_defaults_len; c++)
    356					if (arg_has_searchkey(b->str, llist0_defaults[c].searchkey))
    357						llist0_defaults[c].found = true;
    358			}
    359		} while (b != llist[L_ARGS]);
    360	}
    361
    362	// Add required default settings to the linker list if they weren't found
    363	for (c = 0; c < llist0_defaults_len; c++)
    364		if (llist0_defaults[c].found == false) {
    365			// Add the entry to the linker llist[L_ARGS], flag first then value
    366			llist[L_ARGS] = append(llist0_defaults[c].addflag,  llist[L_ARGS]);
    367			llist[L_ARGS] = append(llist0_defaults[c].addvalue, llist[L_ARGS]);
    368		}
    369}
    370
    371
    372/* alloc - allocate n bytes or die */
    373void *alloc(int n) {
    374	static char *avail, *limit;
    375
    376	n = (n + sizeof(char *) - 1)&~(sizeof(char *) - 1);
    377	if (n >= limit - avail) {
    378		avail = malloc(n + 4 * 1024);
    379		assert(avail);
    380		limit = avail + n + 4 * 1024;
    381	}
    382	avail += n;
    383	return avail - n;
    384}
    385
    386
    387/* basepath - return base name for name, e.g. /usr/drh/foo.c => foo */
    388char *basepath(char *name) {
    389	char *s, *b, *t = 0;
    390
    391	for (b = s = name; *s; s++)
    392		if (*s == '/' || *s == '\\') {
    393			b = s + 1;
    394			t = 0;
    395		}
    396		else if (*s == '.')
    397			t = s;
    398	s = strsave(b);
    399	if (t)
    400		s[t - b] = 0;
    401	return s;
    402}
    403
    404// path_stripext - return a new string of path [name] with extension removed
    405//              e.g. /usr/drh/foo.c => /usr/drh/foo
    406char *path_stripext(char *name) {
    407	char * copy_str = strsave(name);
    408	char * end_str = copy_str + strlen(copy_str);
    409
    410	// Work from end of string backward,
    411	// truncate string at first "." char
    412	while (end_str > copy_str) {
    413		if (*end_str == '.') {
    414			*end_str = '\0';
    415			break;
    416		}
    417		end_str--;
    418	}
    419	return copy_str;
    420}
    421
    422
    423// path_newext - return a new string of path [name] with extension replaced
    424//              e.g. /usr/drh/foo.c => /usr/drh/foo
    425char *path_newext(char *name, char *new_ext) {
    426     return stringf("%s%s", path_stripext(name), new_ext);
    427}
    428
    429
    430// Check if an extension matches the end of a filename
    431int matches_ext(const char * filename, const char * ext)
    432{
    433	// Only test match if filename is larger than extension
    434	// Then check the end of the filename for [ext] length of chars
    435	if (strlen(filename) >= strlen(ext))
    436		return strncmp(filename + strlen(filename) - strlen(ext), ext, strlen(ext)) == 0;
    437	else return 0;
    438}
    439
    440
    441#ifndef WIN32
    442#define _P_WAIT 0
    443
    444static int _spawnvp(int mode, const char *cmdname, char *argv[]) {
    445	int status;
    446	pid_t pid, n;
    447
    448	switch (pid = fork()) {
    449	case -1:
    450		fprintf(stderr, "%s: no more processes\n", progname);
    451		return 100;
    452	case 0:
    453		execv(cmdname, argv);
    454		fprintf(stderr, "%s: ", progname);
    455		perror(cmdname);
    456		fflush(stdout);
    457		exit(100);
    458	}
    459	while ((n = wait(&status)) != pid && n != -1)
    460		;
    461	if (n == -1)
    462		status = -1;
    463	if (status & 0377) {
    464		fprintf(stderr, "%s: fatal error in %s\n", progname, cmdname);
    465		status |= 0400;
    466	}
    467	return (status >> 8) & 0377;
    468}
    469#endif
    470
    471/* removes quotes from src and stores it on dst */
    472void removeQuotes(char* src, char* dst)
    473{
    474	while(*src != '\0')
    475	{
    476		if(*src != '\"')
    477    {
    478      if(*dst != *src)
    479				*(dst) = *src;
    480      dst ++;
    481    }
    482		src ++;
    483	}
    484  if(*dst != '\0')
    485	  *dst = '\0';
    486}
    487
    488/* turns "C:\Users\Zalo\Desktop\gb\gbdk 2020\build\gbdk\"bin/sdcpp
    489   into  "C:\Users\Zalo\Desktop\gb\gbdk 2020\build\gbdk\bin/sdcpp" */
    490void fixQuotes(char* str)
    491{
    492	while(*str != '\"' && *str != '\0')
    493		str ++;
    494
    495	char* src = str;
    496	if(*src == '\"')
    497	{
    498		*(str ++) = '\"';
    499		while(*src != '\0')
    500		{
    501			if(*src != '\"')
    502				*(str ++) = *src;
    503			src ++;
    504		}
    505		*(str ++) = '\"';
    506		*(str ++) = '\0';
    507	}
    508}
    509
    510/* callsys - execute the command described by av[0...], return status */
    511static int callsys(char **av) {
    512	int i, status = 0;
    513	static char **argv;
    514	static int argc;
    515
    516	for (i = 0; av[i] != NULL; i++)
    517		;
    518	if (i + 1 > argc) {
    519		argc = i + 1;
    520		if (argv == NULL)
    521			argv = malloc(argc * sizeof *argv);
    522		else {
    523			char **argv0 = argv;
    524			argv = realloc(argv0, argc * sizeof *argv);
    525			if (argv == NULL)
    526				free(argv0);
    527		}
    528		assert(argv);
    529	}
    530	for (i = 0; status == 0 && av[i] != NULL; ) {
    531		int j = 0;
    532		char *s;
    533		for (; av[i] != NULL && (s = strchr(av[i], '\n')) == NULL; i++)
    534			argv[j++] = av[i];
    535		if (s != NULL) {
    536			if (s > av[i])
    537				argv[j++] = stringf("%.*s", s - av[i], av[i]);
    538			if (s[1] != '\0')
    539				av[i] = s + 1;
    540			else
    541				i++;
    542		}
    543		argv[j] = NULL;
    544		if (verbose > 0) {
    545			int k;
    546			fprintf(stderr, "%s", argv[0]);
    547			for (k = 1; argv[k] != NULL; k++)
    548				fprintf(stderr, " %s", argv[k]);
    549			fprintf(stderr, "\n");
    550		}
    551		if (verbose < 2)
    552		{
    553			char argv_0_no_quotes[256];
    554			removeQuotes(argv[0], argv_0_no_quotes);
    555			for(char** it = argv; *it != 0; it ++)
    556			{
    557#ifdef __WIN32__
    558				fixQuotes(*it); //On windows quotes must be kept, and fixed
    559#else
    560        removeQuotes(*it, *it); //On macos, quotes must be fully removed from args
    561#endif
    562			}
    563			//For future reference:
    564			//_spawnvp requires _FileName to not have quotes
    565			//_Arguments must have quotes on windows, but not in macos
    566			//Quoted strings must begin and end with quotes, no quotes in the middle
    567			status = _spawnvp(_P_WAIT, argv_0_no_quotes, argv);
    568		}
    569		if (status == -1) {
    570			fprintf(stderr, "%s: ", progname);
    571			perror(argv[0]);
    572		}
    573	}
    574	return status;
    575}
    576
    577/* concat - return concatenation of strings s1 and s2 */
    578char *concat(const char *s1, const char *s2) {
    579	int n = strlen(s1);
    580	char *s = alloc(n + strlen(s2) + 1);
    581
    582	strcpy(s, s1);
    583	strcpy(s + n, s2);
    584	return s;
    585}
    586
    587/* compose - compose cmd into av substituting a, b, c for $1, $2, $3, resp. */
    588static void compose(char *cmd[], List a, List b, List c) {
    589	int i, j;
    590	List lists[3];
    591
    592	lists[0] = a;
    593	lists[1] = b;
    594	lists[2] = c;
    595	for (i = j = 0; cmd[i]; i++) {
    596		char *s = strchr(cmd[i], '$');
    597		if (s && isdigit(s[1])) {
    598			int k = s[1] - '0';
    599			assert(k >= 1 && k <= 3);
    600			b = lists[k - 1];
    601			if (b) {
    602				b = b->link;
    603				av[j] = alloc(strlen(cmd[i]) + strlen(b->str) - 1);
    604				strncpy(av[j], cmd[i], s - cmd[i]);
    605				av[j][s - cmd[i]] = '\0';
    606				strcat(av[j], b->str);
    607				strcat(av[j++], s + 2);
    608				while (b != lists[k - 1]) {
    609					b = b->link;
    610					assert(j < ac);
    611					av[j++] = b->str;
    612				};
    613			}
    614		}
    615		else if (*cmd[i]) {
    616			assert(j < ac);
    617			av[j++] = cmd[i];
    618		}
    619	}
    620	av[j] = NULL;
    621}
    622
    623/* error - issue error msg according to fmt, bump error count */
    624static void error(char *fmt, char *msg) {
    625	fprintf(stderr, "%s: ", progname);
    626	fprintf(stderr, fmt, msg);
    627	fprintf(stderr, "\n");
    628	errcnt++;
    629}
    630
    631/* exists - if `name' readable return its path name or return null */
    632static char *exists(char *name) {
    633	List b;
    634
    635	if ((name[0] == '/' || name[0] == '\\' || name[2] == ':')
    636		&& access(name, 4) == 0)
    637		return name;
    638	if (!(name[0] == '/' || name[0] == '\\' || name[2] == ':')
    639		&& (b = lccinputs))
    640		do {
    641			b = b->link;
    642			if (b->str[0]) {
    643				char buf[1024];
    644				sprintf(buf, "%s/%s", b->str, name);
    645				if (access(buf, 4) == 0)
    646					return strsave(buf);
    647			}
    648			else if (access(name, 4) == 0)
    649				return name;
    650		} while (b != lccinputs);
    651		if (verbose > 1)
    652			return name;
    653		return 0;
    654}
    655
    656/* first - return first component in semicolon separated list */
    657static char *first(char *list) {
    658	char *s = strchr(list, ';');
    659
    660	if (s) {
    661		char buf[1024];
    662		size_t len = s - list;
    663		if(len >= sizeof(buf)) len = sizeof(buf)-1;
    664		strncpy(buf, list, len);
    665		buf[len] = '\0';
    666		return strsave(buf);
    667	}
    668	else
    669		return list;
    670}
    671
    672/* filename - process file name argument `name', return status */
    673static int filename(char *name, char *base) {
    674	int status = 0;
    675	static char *stemp, *itemp;
    676
    677	if (base == 0)
    678		base = basepath(name);
    679
    680	// Handle all available suffixes except .gb (last in list)
    681	switch (suffix(name, suffixes, 5)) {
    682	case 0:	/* C source files */
    683		{
    684			char *ofile;
    685			if ((cflag || Sflag) && outfile)
    686				ofile = outfile;
    687			else if (cflag)
    688				ofile = concat(base, EXT_O);
    689			else if (Sflag) {
    690				// When compiling to asm only, set outfile as .asm
    691				ofile = concat(base, EXT_ASM);
    692			}
    693			else
    694			{
    695				ofile = tempname(EXT_O);
    696
    697				char* ofileBase = basepath(ofile);
    698				rmlist = append(stringf("%s/%s%s", tempdir, ofileBase, EXT_ASM), rmlist);
    699				rmlist = append(stringf("%s/%s%s", tempdir, ofileBase, ".lst"), rmlist);
    700				rmlist = append(stringf("%s/%s%s", tempdir, ofileBase, ".sym"), rmlist);
    701				rmlist = append(stringf("%s/%s%s", tempdir, ofileBase, ".adb"), rmlist);
    702			}
    703
    704			compose(com, clist, append(name, 0), append(ofile, 0));
    705			status = callsys(av);
    706			if (!find(ofile, llist[L_FILES]))
    707				llist[L_FILES] = append(ofile, llist[L_FILES]);
    708		}
    709		break;
    710	case 2:	/* assembly language files */
    711		if (Eflag)
    712			break;
    713		if (!Sflag) {
    714			char *ofile;
    715			if (cflag && outfile)
    716				ofile = outfile;
    717			else if (cflag)
    718				ofile = concat(base, EXT_O);
    719			else
    720				ofile = tempname(EXT_O);
    721			compose(as, alist, append(name, 0), append(ofile, 0));
    722			status = callsys(av);
    723			if (!find(ofile, llist[L_FILES]))
    724				llist[L_FILES] = append(ofile, llist[L_FILES]);
    725		}
    726		break;
    727	case 3:	/* object files */
    728		if (!find(name, llist[L_FILES]))
    729			llist[L_FILES] = append(name, llist[L_FILES]);
    730		break;
    731	case 4: // .ihx files
    732		// Apply "name" as .ihx file (there can be only one as input)
    733		strncpy(ihxFile, name, sizeof(ihxFile) - 1);
    734		break;
    735	default:
    736		if (Eflag) {
    737			compose(cpp, plist, append(name, 0), 0);
    738			status = callsys(av);
    739		}
    740		llist[L_FILES] = append(name, llist[L_FILES]);
    741		break;
    742	}
    743	if (status)
    744		errcnt++;
    745	return status;
    746}
    747
    748/* help - print help message */
    749static void help(void) {
    750	static char *msgs[] = {
    751"", " [ option | file ]...\n",
    752"	except for -l, options are processed left-to-right before files\n",
    753"	unrecognized options are taken to be linker options\n",
    754"-A	warn about nonANSI usage; 2nd -A warns more\n",
    755"-b	emit expression-level profiling code; see bprint(1)\n",
    756#ifdef sparc
    757"-Bstatic -Bdynamic	specify static or dynamic libraries\n",
    758#endif
    759"-Bdir/	use the compiler named `dir/rcc'\n",
    760"-c	compile only\n",
    761"-dn	set switch statement density to `n'\n",
    762"-debug	Turns on --debug for compiler, -y (.cdb) and -j (.noi) for linker\n",
    763"-Dname -Dname=def	define the preprocessor symbol `name'\n",
    764"-E	run only the preprocessor on the named C programs and unsuffixed files\n",
    765"-g	produce symbol table information for debuggers\n",
    766"-help or -?	print this message\n",
    767"-Idir	add `dir' to the beginning of the list of #include directories\n",
    768"-K don't run ihxcheck test on linker ihx output\n",
    769"-lx	search library `x'\n",
    770"-m	select port and platform: \"-m[port]:[plat]\" ports:sm83,z80 plats:ap,duck,gb,sms,gg\n",
    771"-N	do not search the standard directories for #include files\n",
    772"-n	emit code to check for dereferencing zero pointers\n",
    773"-no-crt do not auto-include the gbdk crt0.o runtime in linker list\n",
    774"-no-libs do not auto-include the gbdk libs in linker list\n",
    775"-O	is ignored\n",
    776"-o file	leave the output in `file'\n",
    777"-P	print ANSI-style declarations for globals\n",
    778"-p -pg	emit profiling code; see prof(1) and gprof(1)\n",
    779"-S	compile to assembly language\n",
    780"-autobank auto-assign banks set to 255 (bankpack)\n"
    781#ifdef linux
    782"-static	specify static libraries (default is dynamic)\n",
    783#endif
    784"-t -tname	emit function tracing calls to printf or to `name'\n",
    785"-target name	is ignored\n",
    786"-tempdir=dir	place temporary files in `dir/'", "\n"
    787"-Uname	undefine the preprocessor symbol `name'\n",
    788"-v	show commands as they are executed; 2nd -v suppresses execution\n",
    789"-w	suppress warnings\n",
    790"-Woarg	specify system-specific `arg'\n",
    791"-W[pfablim]arg	pass `arg' to the preprocessor, compiler, assembler, bankpack, linker, ihxcheck, or makebin\n",
    792	0 };
    793	int i;
    794	char *s;
    795
    796	msgs[0] = progname;
    797	for (i = 0; msgs[i]; i++) {
    798		fprintf(stderr, "%s", msgs[i]);
    799		if (strncmp("-tempdir", msgs[i], 8) == 0 && tempdir)
    800			fprintf(stderr, "; default=%s", tempdir);
    801	}
    802#define xx(v) if ((s = getenv(#v))) fprintf(stderr, #v "=%s\n", s)
    803	xx(LCCINPUTS);
    804	xx(LCCDIR);
    805#ifdef WIN32
    806	xx(include);
    807	xx(lib);
    808#endif
    809#undef xx
    810}
    811
    812/* initinputs - if LCCINPUTS or include is defined, use them to initialize various lists */
    813static void initinputs(void) {
    814	char *s = getenv("LCCINPUTS");
    815	List list, b;
    816
    817	if (s == 0 || (s = inputs)[0] == 0)
    818		s = ".";
    819	if (s) {
    820		lccinputs = path2list(s);
    821		b = lccinputs;
    822		if (b)
    823			do {
    824				b = b->link;
    825				if (strcmp(b->str, ".") != 0) {
    826					ilist = append(concat("-I", b->str), ilist);
    827					if (strstr(com[1], "win32") == NULL)
    828						llist[L_ARGS] = append(concat("-L", b->str), llist[L_ARGS]);
    829				}
    830				else
    831					b->str = "";
    832			} while (b != lccinputs);
    833	}
    834#ifdef WIN32
    835	if (list = b = path2list(getenv("include")))
    836		do {
    837			b = b->link;
    838			ilist = append(stringf("-I\"%s\"", b->str), ilist);
    839		} while (b != list);
    840#endif
    841}
    842
    843/* interrupt - catch interrupt signals */
    844static void interrupt(int n) {
    845	rm(rmlist);
    846	exit(n = 100);
    847}
    848
    849/* opt - process option in arg */
    850static void opt(char *arg) {
    851	switch (arg[1]) {	/* multi-character options */
    852	case 'W':	/* -Wxarg */
    853		if (arg[2] && arg[3])
    854			switch (arg[2]) {
    855			case 'o':
    856				if (option(&arg[3]))
    857					return;
    858				break;
    859			case 'p': /* Preprocessor */
    860				clist = append(&arg[3], clist);
    861				return;
    862			case 'f': /* Compiler */
    863				if (strcmp(&arg[3], "-C") || option("-b")) {
    864					clist = append(&arg[3], clist);
    865					return;
    866				}
    867				break; /* and fall thru */
    868			case 'a': /* Assembler */
    869				alist = append(&arg[3], alist);
    870				return;
    871			case 'i': /* ihxcheck arg list */
    872				ihxchecklist = append(&arg[3], ihxchecklist);
    873				return;
    874			case 'b': /* auto bankpack_flags arg list */
    875				bankpack_flags = append(&arg[3], bankpack_flags);
    876				// -Wb-ext=[.some-extension]
    877				// If bankpack is going to rewrite input object files to a new extension
    878				// then save that extension for rewriting the linker list (llist[L_FILES])
    879				if (strstr(&arg[3], "-ext=") != NULL) {
    880					if (arg[8]) {
    881						sprintf(bankpack_newext, "%s", &arg[8]);
    882					}
    883				}
    884				return;
    885			case 'l': /* Linker */
    886				if(arg[4] == 'y' && (arg[5] == 't' || arg[5] == 'o' || arg[5] == 'a' || arg[5] == 'p') && (arg[6] != '\0' && arg[6] != ' '))
    887					goto makebinoption; //automatically pass -yo -ya -yt -yp options to makebin (backwards compatibility)
    888				{
    889					// If using linker file for sdldgb (-f file[.lk]).
    890					// Starting at arg[5] should be name of the linkerfile
    891					if ((arg[4] == 'f') && (arg[5])) {
    892						// Items in llist[L_LKFILES] get added to llist[L_FILES] right before the linker is called.
    893						// That avoids sending to bankpack with the "-f" flag mixed in with the names of the .o object files.
    894						llist[L_LKFILES] = append(stringf(&arg[5]), llist[L_LKFILES]);
    895					} else {
    896						//sdldgb requires spaces between -k and the path
    897						llist[L_ARGS] = append(stringf("%c%c", arg[3], arg[4]), llist[L_ARGS]);     //splitting the args into 2 works on Win and Linux
    898						if (arg[5]) {
    899							llist[L_ARGS] = append(&arg[5], llist[L_ARGS]);  // Add filename separately if present
    900						}
    901					}
    902				}
    903				return;
    904			case 'm': /* Makebin */
    905			makebinoption:{
    906				char *tmp = malloc(256);
    907				char *tmp2 = malloc(256);
    908				tmp2[0] = '\0'; // Zero out second arg by default
    909				if (arg[4] == 'y') {
    910					sprintf(tmp, "%c%c%c", arg[3], arg[4], arg[5]); //-yo -ya -yt -yl -yk -yn -yp
    911					if (!(arg[5] == 'c' || arg[5] == 'C' || arg[5] == 's'  || arg[5] == 'S' || arg[5] == 'j' || arg[5] == 'p')) // Don't add second arg for -yc -yC -ys -yS -yj
    912						sprintf(tmp2, "%s", &arg[6]);
    913					// -yp of SDCC 4.1.0's makebin erroneously does not use a space between flag and it's value
    914					// So append trailing values to first arg that would otherwise go in the second arg
    915					if (arg[5] == 'p')
    916						sprintf(tmp, "%s", &arg[3]);
    917
    918					// If MBC option is present for makebin (-Wl-yt <n> or -Wm-yt <n>) then make a copy for bankpack to use
    919					if (arg[5] == 't')
    920						bankpack_flags = append(&arg[3], bankpack_flags);
    921				} else if ((arg[4] == 'x') && arg[5] && arg[6]) {
    922					// SMS options
    923					// Print "-" plus first two option chars into first arg
    924					// and any trailing option chars into a separate arg
    925					sprintf(tmp, "%c%c%c", arg[3], arg[4], arg[5]);  //-xo -xj -xv
    926					if(arg[6])
    927						sprintf(tmp2, "%s", &arg[6]);
    928				} else {
    929					sprintf(tmp, "%c%c", arg[3], arg[4]); //-s
    930					if(arg[5])
    931						sprintf(tmp2, "%s", &arg[5]);
    932				}
    933				mkbinlist = append(tmp, mkbinlist);
    934				if (tmp2[0] != '\0')  // Only append second argument if it's populated
    935					mkbinlist = append(tmp2, mkbinlist);
    936				}return;
    937			}
    938		fprintf(stderr, "%s: %s ignored\n", progname, arg);
    939		return;
    940	case 'd':	/* -dn */
    941		if (strcmp(arg, "-debug") == 0) {
    942			// Load default debug options
    943			clist    = append("--debug", clist);  // Debug for sdcc compiler
    944			llist[L_ARGS] = append("-y", llist[L_ARGS]);    // Enable .cdb output for sdldgb linker
    945			llist[L_ARGS] = append("-j", llist[L_ARGS]);    // Enable .noi output
    946			return;
    947		}
    948
    949		arg[1] = 's';
    950		clist = append(arg, clist);
    951		return;
    952	case 't':	/* -t -tname -tempdir=dir */
    953		if (strncmp(arg, "-tempdir=", 9) == 0)
    954			tempdir = arg + 9;
    955		else
    956			clist = append(arg, clist);
    957		return;
    958	case 'p':	/* -p -pg */
    959		if (option(arg))
    960			clist = append(arg, clist);
    961		else
    962			fprintf(stderr, "%s: %s ignored\n", progname, arg);
    963		return;
    964	case 'D':	/* -Dname -Dname=def */
    965	case 'U':	/* -Uname */
    966	case 'I':	/* -Idir */
    967		clist = append(arg, clist);
    968		return;
    969	case 'K':
    970		Kflag++;
    971		return;
    972	case 'a':
    973		if (strcmp(arg, "-autobank") == 0) {
    974			autobankflag++;
    975			return;
    976		}
    977	case 'n':
    978		if (strcmp(arg, "-no-crt") == 0) {
    979			option(arg);  // Clear crt0 entry in linker compose string
    980			return;
    981		}
    982		else if (strcmp(arg, "-no-libs") == 0) {
    983			option(arg);  // Clear libs entry in linker compose string
    984			return;
    985		}
    986	case 'B':	/* -Bdir -Bstatic -Bdynamic */
    987#ifdef sparc
    988		if (strcmp(arg, "-Bstatic") == 0 || strcmp(arg, "-Bdynamic") == 0)
    989			llist[L_FILES] = append(arg, llist[L_FILES]);
    990		else
    991#endif
    992		{
    993			static char *path;
    994			if (path)
    995				error("-B overwrites earlier option", 0);
    996			path = arg + 2;
    997			if (strstr(com[1], "win32") != NULL)
    998				com[0] = concat(replace(path, '/', '\\'), concat("rcc", EXT_IHX));
    999			else
   1000				com[0] = concat(path, "rcc");
   1001			if (path[0] == 0)
   1002				error("missing directory in -B option", 0);
   1003		}
   1004		return;
   1005	case 'h':
   1006		if (strcmp(arg, "-help") == 0) {
   1007			static int printed = 0;
   1008	case '?':
   1009		if (!printed)
   1010			help();
   1011		printed = 1;
   1012		return;
   1013		}
   1014#ifdef linux
   1015	case 's':
   1016		if (strcmp(arg, "-static") == 0) {
   1017			if (!option(arg))
   1018				fprintf(stderr, "%s: %s ignored\n", progname, arg);
   1019			return;
   1020		}
   1021#endif
   1022	}
   1023	if (arg[2] == 0)
   1024		switch (arg[1]) {	/* single-character options */
   1025		case 'S':        // Requested compile to assembly only
   1026			Sflag++;
   1027			option(arg); // Update composing the compile stage, use of -S instead of -c
   1028			return;
   1029		case 'O':
   1030			fprintf(stderr, "%s: %s ignored\n", progname, arg);
   1031			return;
   1032		case 'A': case 'n': case 'w': case 'P':
   1033			clist = append(arg, clist);
   1034			return;
   1035		case 'g': case 'b':
   1036			if (option(arg))
   1037				clist = append(arg[1] == 'g' ? "-g2" : arg, clist);
   1038			else
   1039				fprintf(stderr, "%s: %s ignored\n", progname, arg);
   1040			return;
   1041		case 'G':
   1042			if (option(arg)) {
   1043				clist = append("-g3", clist);
   1044				llist[L_ARGS] = append("-N", llist[L_ARGS]);
   1045			}
   1046			else
   1047				fprintf(stderr, "%s: %s ignored\n", progname, arg);
   1048			return;
   1049		case 'E':
   1050			Eflag++;
   1051			return;
   1052		case 'c':
   1053			cflag++;
   1054			return;
   1055		case 'N':
   1056			if (strcmp(basepath(cpp[0]), "gcc-cpp") == 0)
   1057				clist = append("-nostdinc", clist);
   1058			include[0] = 0;
   1059			ilist = 0;
   1060			return;
   1061		case 'v':
   1062			if (verbose++ == 0) {
   1063#if 0
   1064				/* GBDK removed */
   1065				if (strcmp(basepath(cpp[0]), "gcc-cpp") == 0)
   1066					clist = append(arg, clist);
   1067				clist = append(arg, clist);
   1068#endif
   1069				fprintf(stderr, "%s %s\n", progname, rcsid);
   1070			}
   1071			return;
   1072		}
   1073	if (option(arg))
   1074		return;
   1075	if (cflag || Sflag || Eflag)
   1076		fprintf(stderr, "%s: %s ignored\n", progname, arg);
   1077	else
   1078		llist[L_FILES] = append(arg, llist[L_FILES]);
   1079}
   1080
   1081
   1082/* replace - copy str, then replace occurrences of from with to, return the copy */
   1083char *replace(const char *str, int from, int to) {
   1084	char *s = strsave(str), *p = s;
   1085
   1086	for (; (p = strchr(p, from)) != NULL; p++)
   1087		*p = to;
   1088	return s;
   1089}
   1090
   1091/* rm - remove files in list */
   1092static void rm(List list) {
   1093	if (list) {
   1094		List b = list;
   1095		if (verbose)
   1096			fprintf(stderr, "rm");
   1097		do {
   1098			if (verbose)
   1099				fprintf(stderr, " %s", b->str);
   1100			if (verbose < 2)
   1101				remove(b->str);
   1102		} while ((b = b->link) != list);
   1103		if (verbose)
   1104			fprintf(stderr, "\n");
   1105	}
   1106}
   1107
   1108/* strsave - return a saved copy of string str */
   1109char *strsave(const char *str) {
   1110	return strcpy(alloc(strlen(str) + 1), str);
   1111}
   1112
   1113/* stringf - format and return a string */
   1114char *stringf(const char *fmt, ...) {
   1115	char buf[1024];
   1116	va_list ap;
   1117	int n;
   1118
   1119	va_start(ap, fmt);
   1120	n = vsnprintf(buf, sizeof(buf), fmt, ap);
   1121	va_end(ap);
   1122	return strsave(buf);
   1123}
   1124
   1125/* suffix - if one of tails[0..n-1] holds a proper suffix of name, return its index */
   1126int suffix(char *name, char *tails[], int n) {
   1127	int i, len = strlen(name);
   1128
   1129	for (i = 0; i < n; i++) {
   1130		char *s = tails[i], *t;
   1131		for (; (t = strchr(s, ';')); s = t + 1) {
   1132			int m = t - s;
   1133			if (len > m && strncmp(&name[len - m], s, m) == 0)
   1134				return i;
   1135		}
   1136		if (*s) {
   1137			int m = strlen(s);
   1138			if (len > m && strncmp(&name[len - m], s, m) == 0)
   1139				return i;
   1140		}
   1141	}
   1142	return SUFX_NOMATCH;
   1143}
   1144
   1145/* tempname - generate a temporary file name in tempdir with given suffix */
   1146char *tempname(char *suffix) {
   1147	static int n;
   1148	char *name = stringf("%s/lcc%d%d%s", tempdir, getpid(), n++, suffix);
   1149
   1150	if (strstr(com[1], "win32") != NULL)
   1151		name = replace(name, '/', '\\');
   1152	rmlist = append(name, rmlist);
   1153	return name;
   1154}
   1155
   1156
   1157// Performs the autobanking stage
   1158//
   1159// Should be called prior to doing compose() for the linker
   1160//
   1161static void handle_autobanking(void) {
   1162
   1163    // bankpack will be populated if supported by active port:platform
   1164    if (bankpack[0][0] != '\0') {
   1165
   1166        char * bankpack_linkerfile_name = tempname(EXT_LK);
   1167        rmlist = append(bankpack_linkerfile_name, rmlist); // Delete the linkerfile when done
   1168        // Always use a linkerfile when using bankpack through lcc
   1169        // Writes all input object files out to [bankpack_linkerfile_name]
   1170        bankpack_flags = append(stringf("%s%s","-lkout=", bankpack_linkerfile_name), bankpack_flags);
   1171
   1172        // Add linkerfile entries (usually *.lk) to the bankpack arg list if any are present
   1173        bankpack_flags = list_add_to_another(bankpack_flags, llist[L_LKFILES], "-lkin=", NULL);
   1174
   1175        // Prepare the bankpack command line, then execute it
   1176        compose(bankpack, bankpack_flags, llist[L_FILES], 0);
   1177        if (callsys(av))
   1178            errcnt++;
   1179
   1180        // Clear out the objects file and linkerfiles from their lists
   1181        // Then replace them with the filename passed to bankpack for "-lkout="
   1182        llist[L_FILES]   = list_remove_all(llist[L_FILES]);
   1183        llist[L_LKFILES] = list_remove_all(llist[L_LKFILES]);
   1184        llist[L_LKFILES] = append(stringf("%s", bankpack_linkerfile_name), llist[L_LKFILES]);
   1185    }
   1186    else
   1187        fprintf(stderr, "Warning: bankpack enabled but not supported by active port:platform\n");
   1188}