/* Copyright (c) 1988,89,91 by Sozobon, Limited. Author: Tony Andrews * * Permission is granted to anyone to use this software for any purpose * on any computer system, and to redistribute it freely, with the * following restrictions: * 1) No charge may be made other than reasonable charges for reproduction. * 2) Modified versions must be clearly marked as such. * 3) The authors are not responsible for any harmful consequences * of using this software, even if they result from defects in it. */ static char Version[] = "cc: version 2.0 Copyright (c) 1988,89,91 by Sozobon, Limited."; /* * cc - C compiler driver program * * Parses command line for options and file names. Then calls the * various passes of the compiler as needed. */ #include #if MINIX || UNIXHOST #include #include #include #include #else #include #include #include #endif #if MINIX || UNIXHOST #define SEPCHAR '/' #else #define SEPCHAR '\\' #endif #ifndef MINIX #define rindex strrchr #define index strchr #endif #ifndef TRUE #define FALSE (0) #define TRUE !FALSE #endif #define MAXOPT 16 /* max. number of options to any one pass */ /* * Compiler pass information */ #if MINIX || UNIXHOST #define CC "hcc" #else #define CC "hcc.ttp" #endif char *hccopt[MAXOPT]; int hcc_cnt = 0; /* number of options to hcc */ /* * Optimizer information */ #if MINIX || UNIXHOST #define OPT "top" #else #define OPT "top.ttp" #endif char *topopt[MAXOPT]; int top_cnt = 0; /* number of options to top */ /* * Assembler information */ #if MINIX || UNIXHOST #define ASM "jas" #define CPP "cpp" #else #define ASM "jas.ttp" #endif /* * Loader information */ #ifdef MINIX #define LD "sld" #else #ifdef UNIXHOST #define LD "xld" #else #define LD "ld.ttp" #endif #endif char *lsize = NULL; /* process size (from the =N option) */ #ifdef MINIX #define CSU (pflag ? "mcrtso.o" : "crtso.o") /* C startup code */ #define LIBC (pflag ? "libp/libc.a" : "libc.a") /* C runtime library */ #define LIBM (pflag ? "libp/libm.a" : "libm.a") /* math library */ #else #define CSU (pflag ? "dstartp.o" : "dstart.o") /* C startup code */ #define LIBC (pflag ? "dlibsp.a" : "dlibs.a") /* C runtime library */ #define LIBM (pflag ? "libmp.a" : "libm.a") /* math library */ #endif /* * Path information */ char *path; /* where to find executables */ char *lib = NULL; /* where to find library stuff */ char *tmp = NULL; /* where to put temporary files */ /* * Temp file names */ char Sfile[128]; /* temp file for assembly code */ char OSfile[128]; /* temp file for optimized assembly */ char Lfile[128]; /* the loader command file */ /* * Default paths for executables and libraries */ #ifdef MINIX #define DEFPATH "/usr/sozobon/bin" #define DEFLIB "/usr/sozobon/lib" #define DEFTMP "/tmp" #else #ifdef UNIXHOST #define DEFPATH "/home/hans/sozobon/bin" #define DEFLIB "/home/hans/sozobon/lib" #define DEFTMP "/tmp" #else #define DEFPATH "\\sozobon\\bin" #define DEFLIB "\\sozobon\\lib" #define DEFTMP "." #endif #endif /* * Boolean options */ int mflag = 0; /* generate a load map */ int vflag = 0; /* show what we're doing w/ version numbers */ int nflag = 0; /* ...but don't really do it */ int Sflag = 0; /* generate assembly files */ int cflag = 0; /* generate ".s" files only */ int Oflag = 0; /* run the optimizer */ int sflag = 0; /* don't generate a symbol table in executables */ int pflag = 0; /* enable execution profiling */ int rflag = 0; /* enable floating point (Real numbers) */ int fflag = 0; /* scan given file for file names */ int tflag = 0; /* don't remove temporary files */ int gflag = 0; /* disable certain confusing optimizations */ /* * We build lists of the various file types we're given. Within each * type, MAXF says how many we can deal with. */ #define MAXF 60 int ncfiles = 0; /* .c files */ char *cfiles[MAXF]; int nsfiles = 0; /* .s files */ char *sfiles[MAXF]; int nofiles = 0; /* .o files */ char *ofiles[MAXF]; int nlfiles = 0; /* .a or .lib files (or files with no suffix) */ char *lfiles[MAXF]; #ifdef MINIX #define DEFOUT "a.out" /* default output file */ #else #define DEFOUT "a.ttp" /* default output file */ #endif char *output = NULL; /* output file */ /* * Argument vectors for the programs we'll run. */ #define MAXARG 60 char *av[MAXARG]; int ac; #define BEGARG() (ac = 0) #define ADDARG(x) (av[ac++] = (x)) #define ENDARG() (av[ac++] = NULL) extern int errno; int got_sig = FALSE; /* TRUE if we got a signal */ usage() { #define P(s) fprintf(stderr, s) P("Sozobon C Compiler Options:\n"); P("-c compile, but don't link\n"); P("-S don't assemble, leave .s files around (-A)\n"); P("-O run the assembly code optimizer\n"); P("-v show what cc is doing\n"); P("-n like -v, but don't really run anything\n"); P("-f file read a list of input files from 'file'\n"); P("-Tdir put temp files in 'dir' (-K)\n"); P("-t don't remove temp files when done (-H)\n"); P("-p enable execution profiling\n"); P("-g compile program for use with the debugger\n"); P("-Ilib add dir. 'lib' to the header search list\n"); P("-Dsym define the pre-processor symbol 'sym' as 1\n"); P("-Dsym=val or as 'val'\n"); P("-Usym un-define the built-in symbol 'sym'\n"); P("-o file use the file 'f' for the loader output (-X)\n"); P("-r link the floating point library (-Q)\n"); P("-lxx load the library libxx.a (-J)\n"); P("-m generate a load map\n"); P("-s don't generate a symbol table (-Y)\n"); P("=nnn make the stack/heap size nnn bytes (Minix only)\n"); P("-R ignored (for Minix compatibility)\n"); P("-w ignored (for Minix compatibility)\n"); #undef P exit(1); } main(argc, argv) int argc; char *argv[]; { #if MINIX || UNIXHOST int catch(); /* signal handler */ #endif extern char *chsuf(); register int i; register char *s; register int endopt; int domsg = FALSE; if (argc == 1) usage(); #ifdef MINIX /* * This is one of the places where ACK blew it. We have to check * to see if we're in the background before setting up our own * signal handler. ACK catches the signal anyway, so interrupting * a foreground process kills any background compiles, too. */ if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, catch); #endif #ifdef UNIXHOST signal(SIGINT, catch); #endif for (i=1; i < argc ;i++) { if (argv[i][0] == '-') { /* option */ endopt = FALSE; for (s = &argv[i][1]; *s && !endopt ;s++) { switch (*s) { case 'c': case 'C': cflag = TRUE; break; case 'S': case 'a': case 'A': Sflag = TRUE; break; case 'v': case 'V': vflag = TRUE; break; case 'n': case 'N': nflag = TRUE; break; case 'm': case 'M': mflag = TRUE; break; case 's': case 'y': case 'Y': sflag = TRUE; break; case 'r': case 'q': case 'Q': rflag = TRUE; break; case 'p': case 'P': pflag = TRUE; break; case 'g': case 'G': gflag = TRUE; break; case 'o': case 'x': case 'X': output = argv[++i]; endopt = TRUE; break; case 'l': case 'j': case 'J': keeplib(s+1); endopt = TRUE; break; case 'f': case 'F': getfiles(argv[++i]); endopt = TRUE; break; /* * Minix compatibility */ case 'R': /* ignored */ case 'w': case 'W': break; case 'T': case 'k': case 'K': tmp = s+1; endopt = TRUE; break; case 't': case 'h': case 'H': tflag = TRUE; break; /* * Options for other passes. */ case 'I': /* compiler options */ case 'D': case 'U': hccopt[hcc_cnt++] = argv[i]; endopt = TRUE; break; case 'z': case 'Z': *s = 'O'; /* fall through */ case 'O': /* * Enable the optimizer, possibly with * options specifically for it. */ Oflag = TRUE; if (s[1] != '\0') topopt[top_cnt++] = argv[i]; endopt = TRUE; break; default: usage(); } } } else if (argv[i][0] == '=') { /* heap/stack size for ld */ lsize = argv[i]; } else /* input file */ keepfile(argv[i]); } doinit(); if ((ncfiles + nsfiles) > 1) domsg = TRUE; if (vflag && !nflag) printf("%s\n", Version); for (i = 0; i < ncfiles ;i++) { if (domsg) printf("%s:\n", cfiles[i]); docomp(cfiles[i]); doopt(cfiles[i]); doasm(cfiles[i], TRUE); } for (i = 0; i < nsfiles ;i++) { if (domsg) printf("%s:\n", sfiles[i]); doasm(sfiles[i], FALSE); } dold(); /* run the loader */ exit(0); } /* * doinit() - set up some variables before getting started */ doinit() { char *getenv(); if ((path = getenv("PATH")) == NULL) path = DEFPATH; if ((lib = getenv("LIB")) == NULL) lib = DEFLIB; if (tmp == NULL) { if ((tmp = getenv("TMPDIR")) == NULL) tmp = DEFTMP; } sprintf(Sfile, "%s%cC%05d.s", tmp, SEPCHAR, getpid()); sprintf(OSfile, "%s%cCO%05d.s", tmp, SEPCHAR, getpid()); sprintf(Lfile, "%s%cCL%05d.ld", tmp, SEPCHAR, getpid()); } #if MINIX || UNIXHOST catch() { signal(SIGINT, catch); got_sig = TRUE; } #endif /* * getfiles(f) - read file names from the given file */ getfiles(f) char *f; { char *strsave(); char name[64]; FILE *fp; if ((fp = fopen(f, "r")) == NULL) { fprintf(stderr, "cc: can't open file '%s'\n", f); return; } while (scanf("%s", name)) keepfile(strsave(name)); fclose(fp); } /* * keeplib(l) - save a library reference * * Given an option like "-lm", keeplib saves a reference to a library file * named "libm.a". The usual places will be searched for the library when * the time comes. keeplib() should be called with the "-l" stripped off. */ keeplib(l) char *l; { char *strsave(); char name[32]; sprintf(name, "lib%s.a", l); keepfile(strsave(name)); } /* * keepfile(f) - remember the filename 'f' in the appropriate place */ keepfile(f) char *f; { char *p, *rindex(); if ((p = rindex(f, '.')) == NULL) { /* no suffix */ lfiles[nlfiles++] = f; return; } if ((strcmp(p, ".c") == 0) || (strcmp(p, ".C") == 0)) { cfiles[ncfiles++] = f; return; } if ((strcmp(p, ".s") == 0) || (strcmp(p, ".S") == 0)) { sfiles[nsfiles++] = f; return; } if ((strcmp(p, ".o") == 0) || (strcmp(p, ".O") == 0)) { ofiles[nofiles++] = f; return; } if ((strcmp(p, ".a") == 0) || (strcmp(p, ".A") == 0)) { lfiles[nlfiles++] = f; return; } fprintf(stderr, "cc: unknown file suffix '%s'\n", f); exit(1); } /* * chsuf(f, suf) - change the suffix of file 'f' to 'suf'. * * Space for the new string is obtained using malloc(). */ char * chsuf(f, suf) char *f; char *suf; { char *malloc(); char *s, *p; p = s = malloc(strlen(f) + strlen(suf) + 1); strcpy(p, f); for (; *p ; p++) { if (*p == '.') break; } while (*suf) *p++ = *suf++; *p = '\0'; return s; } /* * isfile(f) - return true if the given file exists */ int isfile(f) char *f; { struct stat sbuf; if (stat(f, &sbuf) < 0) return FALSE; return TRUE; } /* * findfile(e, b) * * Finds a file in one of the directories given in the environment * variable whose value is pointed to by 'e'. Looks for the file * given by the name 'b'. * * Returns a pointer to a static area containing the pathname of the * file, if found, NULL otherwise. */ char * findfile(e, b) char *e; register char *b; { static char file[256]; char env[256]; register char *p; /* * Make a copy of the value of the env. variable. Convert all * delimiters to nulls. */ if (e != NULL) { strcpy(env, e); for (p = env; *p ;p++) { #if MINIX || UNIXHOST if (*p == ':') #else if (*p == ';' || *p == ',') #endif *p = '\0'; } p[1] = '\0'; /* double null terminator */ } else env[1] = env[0] = '\0'; p = env; /* * An initial null means to try the current directory. */ if (*p == '\0') { if (isfile(b)) return b; p++; } while (*p) { sprintf(file, "%s%c%s", p, SEPCHAR, b); if (isfile(file)) return file; while (*p++ != '\0') ; } return NULL; /* give up */ } #if MINIX || UNIXHOST /* * docmd(path, argv) - run a command * * We check for an interrupt before and after executing the command. * The sub-processes we run are the only very time-consuming thing we * do in 'cc', so there's not much point in checking for signals in * other places. When a signal comes in, we just delete the temp files. */ int docmd(path, argv) char *path; char *argv[]; { void cleanup(); int i; int pid, status; char buf[512]; int dosystem = FALSE; if (vflag || nflag) { fprintf(stderr, "%s", path); for (i=1; argv[i] != NULL ;i++) fprintf(stderr, " %s", argv[i]); fputc('\n', stderr); fflush(stderr); if (nflag) return 0; } /* * Figure out whether exec() is okay or we need a shell. */ for (i=1; argv[i] != NULL ;i++) if (argv[i][0] == '>' || argv[i][0] == '<') dosystem = TRUE; if (got_sig) cleanup(); if (dosystem) { strcpy(buf, path); for (i=1; argv[i] != NULL ;i++) { strcat(buf, " "); strcat(buf, argv[i]); } return system(buf); } else { /* * Fork and exec the command. */ if ((pid = fork()) < 0) { fprintf(stderr, "cc: can't fork\n"); return -1; } if (pid == 0) { /* the child */ execv(path, argv); fprintf(stderr, "cc: Can't exec '%s'\n", path); exit(1); } else { /* the parent */ if (wait(&status) == -1 && errno != EINTR) { fprintf(stderr,"cc: wait failed (%d)\n", errno); return -1; } else if ((status & 0xff) == 0) return (status >> 8); } } if (got_sig) cleanup(); return -1; } void cleanup() { unlink(Sfile); unlink(OSfile); unlink(Lfile); exit(1); } #else /* * docmd(path, argv) - run a command */ int docmd(path, argv) char *path; char *argv[]; { int i; char cmdline[130]; char *cmd; cmd = &cmdline[1]; *cmd = '\0'; for (i=1; argv[i] != NULL ;i++) { if (i > 1) strcat(cmd, " "); strcat(cmd, argv[i]); } cmdline[0] = strlen(cmd); if (vflag || nflag) { fprintf(stderr, "%s %s\n", path, cmd); fflush(stderr); if (nflag) return 0; } i = Pexec(0, path, cmdline, 0L); return i; } #endif /* * docomp(f) - run the compiler on the given .c file */ docomp(f) char *f; { int i; char *cpath, *sf; char tbuf[128]; char sbuf[128]; int notmp; /* don't use temp directory */ /* * Don't use a temp directory if all we want is the assembly file, * and we're not optimizing. */ notmp = (Sflag && !Oflag); if ((cpath = findfile(path, CC)) == NULL) { fprintf(stderr, "cc: can't find compiler program '%s'\n", CC); exit(1); } BEGARG(); ADDARG(CC); if (!notmp) { sprintf(tbuf, "-T%s", tmp); ADDARG(tbuf); } if (pflag) ADDARG("-P"); for (i=0; i < hcc_cnt ;i++) ADDARG(hccopt[i]); ADDARG(f); ENDARG(); sf = chsuf(f, ".s"); if (notmp) strcpy(sbuf, sf); else sprintf(sbuf, "%s%c%s", tmp, SEPCHAR, sf); free(sf); if (docmd(cpath, av)) { fremove(sbuf); fprintf(stderr, "cc: compiler failed\n"); exit(1); } if (!notmp) frename(sbuf, Sfile); /* fix the temp file name */ } /* * doopt(f) - run the optimizer * * Only optimize files that were produced by the compiler. */ doopt(f) char *f; { int i; char *opath; char *sf; char *of; /* the output file name */ if (!Oflag) return; if ((opath = findfile(path, OPT)) == NULL) { fprintf(stderr, "cc: can't find optimizer program '%s'\n", OPT); exit(1); } sf = chsuf(f, ".s"); BEGARG(); ADDARG(OPT); for (i=0; i < top_cnt ;i++) ADDARG(topopt[i]); /* * The -g option turns off certain optimizations that make the * debugger more difficult to use. These mostly involve messing * with the stack frame, and make it harder to get accurate * stack backtraces while stepping through a program. */ if (gflag) ADDARG("-g"); ADDARG(Sfile); /* the input file */ of = (Sflag) ? sf : OSfile; /* what's the output file name? */ ADDARG(of); ENDARG(); if (docmd(opath, av)) { if (Sflag) { /* error is fatal if we wanted the assembly */ fremove(Sfile); fremove(of); fprintf(stderr, "cc: optimizer failed (aborting)\n"); exit(1); } else { /* try to press on... */ /* * We continue by pretending we optimized the * assembly file when we really didn't. */ frename(Sfile, of); fprintf(stderr, "cc: optimizer failed (continuing)\n"); } } free(sf); fremove(Sfile); } /* * doasm() - run the assembler * * If 'istmp' is TRUE, the file we were given is a temporary. If we aren't * working on a temp file, we may need to run the assembly through the C * preprocessor. If the first character in the file is '#', we do it. */ doasm(f, istmp) char *f; int istmp; { char *strrchr(), *getenv(); int i; char *dpath; char *sf; /* file.s */ char *infile; /* the assembler input file */ char *outfile; /* the assembler output */ if (Sflag) return; outfile = chsuf(f, ".o"); /* * We want the -o option to be able to specify the output file * from the assembler. This only makes sense if -c is given, and * there is only one input file. */ if (cflag && (output != NULL) && ((ncfiles+nsfiles) == 1)) outfile = output; /* * Assume a temp file for now. May change... */ infile = (Oflag) ? OSfile : Sfile; /* * If this isn't a temp file, we may have to run the C preprocessor * on it. If the first character of the file isn't '#', then we can * skip it and just run the assembler. */ if (!istmp) { #ifdef MINIX int fd; char c; if ((fd = open(f, 0)) < 0) { fprintf(stderr, "cc: can't open file '%s'\n", f); exit(1); } if (read(fd, &c, 1) != 1) { fprintf(stderr, "cc: can't read file '%s'\n", f); exit(1); } close(fd); if (c == '#') { if ((dpath = findfile(lib, CPP)) == NULL) { fprintf(stderr, "cc: can't find C preprocessor '%s'\n", CPP); exit(1); } BEGARG(); ADDARG(dpath); /* * The -P option removes "#file" and "#line" directives * from the generated output. */ ADDARG("-P"); /* * Include any pre-processor directives (-D, -I, or -U) */ for (i=0; i < hcc_cnt ;i++) ADDARG(hccopt[i]); ADDARG(f); ADDARG(">"); ADDARG(Sfile); ENDARG(); if (docmd(dpath, av)) { fprintf(stderr, "cc: preprocessor failed '%s'\n", f); fremove(Sfile); exit(1); } infile = Sfile; } else infile = f; #else infile = f; #endif } if ((dpath = findfile(path, ASM)) == NULL) { fprintf(stderr, "cc: can't find assembler program '%s'\n", ASM); exit(1); } BEGARG(); ADDARG(ASM); ADDARG(infile); ADDARG("-o"); ADDARG(outfile); ENDARG(); if (docmd(dpath, av)) { fprintf(stderr, "cc: assembler failed '%s'\n", infile); if (infile != f) fremove(infile); exit(1); } free(outfile); if (infile != f) fremove(infile); } /* * dold() - run the loader */ dold() { FILE *fp, *fopen(); int i; char *lpath; char *s; char *l; if (cflag || Sflag) return; /* * Construct loader command file */ if ((fp = fopen(Lfile, "w")) == NULL) { fprintf(stderr, "cc: can't open loader temp file\n"); exit(1); } if ((lpath = findfile(lib, CSU)) == NULL) { fprintf(stderr, "cc: can't find C startup code '%s'\n", CSU); exit(1); } fprintf(fp, "%s\n", lpath); for (i = 0; i < ncfiles ;i++) { s = chsuf(cfiles[i], ".o"); fprintf(fp, "%s\n", s); free(s); } for (i = 0; i < nsfiles ;i++) { s = chsuf(sfiles[i], ".o"); fprintf(fp, "%s\n", s); free(s); } for (i = 0; i < nofiles ;i++) fprintf(fp, "%s\n", ofiles[i]); for (i = 0; i < nlfiles ;i++) { if (isfile(lfiles[i])) { fprintf(fp, "%s\n", lfiles[i]); } else { lpath = findfile(lib, lfiles[i]); if (lpath == NULL) { fprintf(stderr, "cc: can't find library '%s'\n", lfiles[i]); exit(1); } fprintf(fp, "%s\n", lpath); } } if (rflag) { if ((lpath = findfile(lib, LIBM)) == NULL) { fprintf(stderr, "cc: can't find floating point library '%s'\n", LIBM); exit(1); } fprintf(fp, "%s\n", lpath); } if ((lpath = findfile(lib, LIBC)) == NULL) { fprintf(stderr, "cc: can't find C runtime library '%s'\n",LIBC); exit(1); } fprintf(fp, "%s\n", lpath); fclose(fp); if ((lpath = findfile(path, LD)) == NULL) { fprintf(stderr, "cc: can't find loader program '%s'\n", LD); exit(1); } BEGARG(); ADDARG(LD); ADDARG("-p"); ADDARG("-b"); if (lsize != NULL) ADDARG(lsize); if (mflag) ADDARG("-m"); if (!sflag) ADDARG("-t"); if (rflag) { ADDARG("-u"); ADDARG("__printf"); ADDARG("-u"); ADDARG("__scanf"); } ADDARG("-o"); ADDARG((output != NULL) ? output : DEFOUT); ADDARG("-f"); ADDARG(Lfile); if (docmd(lpath, av)) { fprintf(stderr, "cc: loader failed\n"); fremove(Lfile); fremove(output); exit(1); } for (i = 0; i < ncfiles ;i++) { s = chsuf(cfiles[i], ".o"); fremove(s); free(s); } for (i = 0; i < nsfiles ;i++) { s = chsuf(sfiles[i], ".o"); fremove(s); free(s); } fremove(Lfile); } int frename(f1, f2) char *f1, *f2; { if (vflag || nflag) fprintf(stderr, "mv %s %s\n", f1, f2); #if MINIX || UNIXHOST unlink(f2); link(f1, f2); unlink(f1); #else rename(f1, f2); #endif } fremove(f) char *f; { if (vflag || nflag) fprintf(stderr, "rm %s\n", f); if (!tflag) unlink(f); } char * strsave(string) char *string; { char *malloc(), *strcpy(); return (strcpy(malloc((unsigned)(strlen(string)+1)), string)); }