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}