Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_FS_1_19910112
-
kcc-6/kcc/cc.c
There are 9 other files named cc.c in the archive. Click here to see a list.
/* CC.C - KCC Main program
**
** (c) Copyright Ken Harrenstien 1989
** All changes after v.187, 26-May-1988
** (c) Copyright Ken Harrenstien, SRI International 1985, 1986
** All changes after v.74, 8-Aug-1985
**
** Original version (c) 1981 K. Chen
*/
#include "ccsite.h"
#include "cc.h"
#include "ccchar.h"
#include <string.h>
#include <stdlib.h> /* For EXIT_ values and malloc decls */
#include <time.h> /* For clock() to get runtime */
/* Imported functions */
extern SYMBOL *symfidstr(); /* CCSYM */
extern char *execargs(); /* CCASMB */
extern int asmb(); /* CCASMB */
extern void runlink(); /* CCASMB */
extern char *estrcpy(); /* CCASMB */
extern char *fstrcpy(); /* CCASMB */
extern int makprefile(); /* CCOUT */
extern NODE *extdef(), *tntdef(); /* CCDECL */
extern void syminit(), ppinit(), lexinit(), initpar(),
outinit(), outdone(),
ppdefine(), passthru(),
symdump(), typedump(),
gencode();
extern int unlink(), stat(); /* Syscalls */
/* Internal functions */
static void showcpu(), setcpu(), cindfiles();
static void coptimize(), cdebug(), ctargmach(), cportlev(), cwarnlev(),
cverbose(), ciload();
static int cswitch(), cfile(), files(), mainsymp(), needcomp();
static int chkmacname();
static char mainname[FNAMESIZE] = {0}; /* Name of module containing "main" */
static char *savname = NULL; /* Pointer to desired -o filename */
static int vrbarg = 0; /* Patch 1 to show args on stderr */
main(argc, argv)
int argc;
char **argv;
{
int ac; /* temp copy of argc */
char **av; /* temp copy of argv */
char *nextprog = NULL; /* Set to program to chain through, if any */
int toterrs = 0; /* Total # errors for all files */
int nfiles = 0; /* # files to try compiling */
int asmfiles = 0; /* # files for which assembly was deferred */
/* Initialize KCC command switch values.
** All are either initially 0, or given default values in CCDATA.
*/
link = assemble = delete = 1;
coptimize("all"); /* Turn on all optimizer flags */
/* Get command line arguments */
if (argc <= 1) { /* No command line? */
nextprog = execargs(&argc, &argv); /* Try getting from RPG/CCL */
if (argc > 1)
link = 0; /* Got stuff, so act as if -c given */
else { /* Sigh, tell user where help lives */
fstrcpy(mainname, hfpaths[0], "CC.DOC");
jmsg("Bad usage, see %s for help.", mainname);
return EXIT_FAILURE;
}
}
/* Have initial command line; now scan for any indirect files (@file) */
cindfiles(&argc, &argv);
/* Now have complete command line, report it if desired.
** This debugging switch needs to be patched in by hand, because at this
** point it cannot have been set yet from the command line!
*/
if (vrbarg) {
fprintf(stderr, "KCC args (%d):", argc);
for (ac = 0; ac < argc; ++ac)
fprintf(stderr, " %s", argv[ac] ? argv[ac] : "<null>");
fprintf(stderr,"\n");
}
/* Now process command line. First scan for all switches */
for (av = argv+1, ac = argc; --ac > 0; ++av)
if (**av == '-') {
if (cswitch(*av, &ac, &av)) /* Process a switch */
*av = NULL; /* OK to zap it now */
} else ++nfiles; /* Assume a filename spec */
/* Now finalize after all switches scanned */
setcpu(); /* Set all CPU feature flags */
if (nfiles == 0) /* This sometimes happens */
jerr("No filenames specified");
/* If no errors, now scan for all filenames, and process them. */
if ((toterrs += nerrors) == 0)
for (av = argv+1, ac = argc; --ac > 0; ++av)
if (*av && **av != '-') {
if (cfile(*av) == 0) /* Compile a file */
asmfiles++; /* Count deferred assemblies */
nfiles++;
toterrs += nerrors;
}
/* Now see what to invoke next, if any. Note runlink() never returns. */
if (toterrs) /* If any errors, */
link = 0; /* never invoke loader. */
if (asmfiles || link || nextprog)
runlink(link, /* Whether to invoke loader or not */
argc-1, argv+1, /* Loader args (.REL files) */
(savname ? savname /* Loader arg: output file name */
: mainname),
nextprog); /* Chained program to invoke next */
return (toterrs ? EXIT_FAILURE : EXIT_SUCCESS);
}
/* CINDFILES() - Scan an argv array for indirect file specs and
** expand them into a new argv array.
*/
static void
cindfiles(aac, aav)
int *aac;
char ***aav;
{
static int dynarr = 0; /* Set if array was malloced */
register int i, cnt;
register char *cp;
FILE *f;
char *buf;
int bufsiz;
#define NINDARGS 500
int locac;
char *locav[NINDARGS];
int ac = *aac;
char **newav, **avp, **av = *aav;
/* Scan current array and process any indirect files. */
for (i = 0; i < ac; ++i) if (av[i] && av[i][0] == '@') {
if ((f = fopen(&av[i][1], "r")) == NULL) {
errfopen("indirect", av[i]); /* Couldn't open, tell user */
av[i] = NULL;
continue;
}
/* Read all of file into a memory block */
bufsiz = cnt = 0;
buf = cp = NULL;
while (!feof(f)) {
if (++cnt >= bufsiz) { /* Ensure have room in buffer */
char *nbuf;
if ((nbuf = realloc(buf, bufsiz += 1000)) == NULL) {
jerr("Out of memory for indirect file \"%s\"", av[i]);
--cnt;
break; /* Leave loop now */
}
cp = (buf = nbuf) + (cnt-1);
}
*cp++ = getc(f);
}
fclose(f);
if (cp == NULL) { /* If got nothing, scan for next file */
av[i] = NULL;
continue;
}
cp[-1] = '\0'; /* Ensure buffer tied off with null */
/* Now scan through the buffer to find arguments, dropping NULLs in
** to split them up, and add pointers to our local array.
** "cnt" has the # chars in the buffer, including a terminating null.
*/
locac = 0;
cp = buf;
for (; --cnt > 0; ++cp) {
if (!isgraph(*cp))
continue; /* Ignore whitespace/cntrls */
if (*cp == '-' && (cnt <= 0 || cp[1] == '\n'))
continue; /* Ignore T20 "line continuation" */
if (*cp == ';') {
while (--cnt > 0 && *++cp != '\n');
continue; /* Ignore ;-commented lines */
}
if (*cp == '!') {
while (--cnt > 0 && *++cp != '!' && *cp != '\n');
continue; /* Ignore !-commented phrases/lines */
}
/* Start scanning over an argument */
if (locac >= NINDARGS) {
jerr("More than %d args in indirect file \"%s\"",
NINDARGS, av[i]);
break;
}
locav[locac++] = cp; /* Remember ptr to arg */
while (--cnt >= 0 && isgraph(*++cp));
if (cnt >= 0)
*cp = '\0'; /* Terminate arg with null */
}
/* Now combine new args with old args. New table size is
** # old args (minus current arg),
** plus # new args (plus ending null pointer).
*/
if (!(newav = (char **)malloc((ac+locac)*sizeof(char *)))) {
jerr("Out of memory for args of indirect file \"%s\"", av[i]);
av[i] = NULL;
continue;
}
avp = newav;
for (cnt = 0; cnt < i; ++cnt) /* Copy already checked args */
*avp++ = av[cnt];
for (cnt = 0; cnt < locac; ++cnt) /* Copy new args */
*avp++ = locav[cnt];
for (cnt = i+1; cnt < ac; ++cnt) /* Copy old unchecked args */
*avp++ = av[cnt];
*avp = NULL; /* Last one always null */
ac = (ac - 1) + locac; /* Old args minus 1, plus new args */
if (!dynarr) ++dynarr; /* If old not dynamic, say new one is */
else free((char *)av); /* else must free up old dynamic array */
av = newav; /* Pointer to new array */
--i; /* Compensate for loop increment */
}
*aac = ac; /* Return new values (normally the same) */
*aav = av;
}
/* CSWITCH - Scan argv array for command line switches
**
** Returns 0 if should keep this switch spec around (used for -l).
** Otherwise, OK to zap switch spec, don't need to keep around.
*/
static int
cswitch(s, aac, aav)
char *s;
int * aac; /* Passed along just for stupid -o switch */
char * **aav;
{
while (*++s) switch (*s) {
case 'a': /* -a<file> Set #asm tmp file name */
asmtfile = ++s; /* rest of arg is filename */
return 1;
case 'A': /* -A<file> Set asm hdr file name */
asmhfile = ++s; /* rest of arg is filename */
return 1;
case 'c': /* -c Compile only */
link = 0; /* Don't run linking loader */
break;
case 'C': /* -C Pass on comments during -E */
keepcmts = 1; /* pass comments through to stdout */
break;
case 'd': /* -d Debug (same as -d=all) */
if (s[1] == '=') { /* -d=<flags> If extended syntax, */
++s;
cdebug(++s); /* go hack rest of arg string. */
return 1;
} else cdebug("all"); /* Else just turn everything on */
break;
case 'D': /* -D<ident> Define a macro */
if (!chkmacname(s)) /* -D<ident>=<def> */
return 1;
if (npredef < MAXPREDEF)
predefs[npredef++] = ++s; /* rest of arg is def string */
else jerr("More than %d predefined macros", MAXPREDEF);
return 1; /* so don't use as switches */
case 'E': /* -E Run through preproc. only */
prepf = 1;
delete = assemble = link = 0;
break;
#if SYS_CSI /* Temporary for compatibility, flush later */
case 'g': /* -g Debugging: link ddt.rel */
ldddtf = 1; /* added 09/15/89 by MVS */
break;
#endif
case 'H': /* -H<path> Specify #include <> path */
if (nhfpaths < MAXINCDIR-1)
hfpaths[nhfpaths++] = ++s; /* Remember the search path */
else jerr("More than %d -H paths", MAXINCDIR);
return 1;
case 'h': /* -h<path> Specify <sys/ > path */
if (nhfsypaths < MAXINCDIR-1)
hfsypaths[nhfsypaths++] = ++s; /* Remember search path */
else jerr("More than %d -h paths", MAXINCDIR);
return 1;
case 'i': /* -i Loader: extended addressing */
if (s[1] == '=') { /* -i=<flags> If extended syntax, */
++s;
ciload(++s); /* go hack rest of arg string. */
return(1);
} else ciload("ext"); /* Else just say "extended" */
break;
case 'I': /* -I<path> Add an #include "" path */
if (nincpaths < MAXINCDIR-1)
incpaths[nincpaths++] = ++s; /* Remember the search path */
else jerr("More than %d -I paths", MAXINCDIR);
return 1;
case 'l': /* -lxxx Loader: Search library */
return 0; /* Just skip over this switch */
case 'L': /* -L<path> Specify library path */
/* -L=<string> Specify LINK cmds */
if (s[1] == '=')
return 0; /* Just skip over -L= for now */
libpath = ++s; /* Set library path */
return 1;
/* This switch is semi-obsolete. -x=macro is preferred. */
case 'm': /* -m Use MACRO */
tgasm = TGASM_MACRO; /* for target asm, not FAIL */
break;
/* This switch is obsolete. -O= is preferred. */
case 'n': /* -n No optimize */
coptimize(""); /* Turn off all optimizations */
break; /* just as if -O= given. */
case 'O': /* -O Optimize (same as -O=all) */
if (s[1] == '=') { /* -O=<flags> If extended syntax, */
++s;
coptimize(++s); /* go hack rest of arg string. */
return 1;
} else coptimize("all"); /* Else just turn everything on */
break;
case 'o': /* -o=<filename> Loader: output file */
if (s[1] == '=') s += 2; /* -o <filename> Permit old syntax */
else {
**aav = NULL; /* Flush this arg */
++(*aav); /* Point to next one */
if (--(*aac) <= 0 || !(s = **aav))
jerr("No filename arg for -o");
}
savname = s;
return 1; /* Can flush arg from switch list */
case 'P': /* -P Port level (same as -P=) */
if (s[1] == '=') { /* -P=<flags> If extended syntax, */
++s;
cportlev(++s); /* go hack rest of arg string. */
return(1);
} else cportlev(""); /* Else just use basic level */
break;
#if SYS_CSI
case 'p': /* -p Turn on Bliss Profiler (link locals)*/
profbliss = 1; /* added 09/15/89 by MVS */
break;
#endif
case 'q': /* -q Conditional compilation */
condccf = 1;
break;
/* Obsolete switch. */
case 's': /* -s Dump symtab (same as -d=sym) */
cdebug("sym");
break;
case 'S': /* -S Do not delete asm source */
delete = 0;
link = assemble = 0; /* don't link or assemble either */
break;
case 'U': /* -U<ident> Undefine macro */
if (!chkmacname(s)) /* Check out identifier syntax */
return 1;
if (npreundef < MAXPREDEF)
preundefs[npreundef++] = ++s;
else jerr("More than %d -U macro undefinitions", MAXPREDEF);
return 1;
case 'v': /* -v Verbosity level (same as -v=) */
if (s[1] == '=') { /* -v=<flags> If extended syntax, */
++s;
cverbose(++s); /* go hack rest of arg string. */
return(1);
} else cverbose("all"); /* Else just use basic level */
break;
case 'w': /* -w Suppress warning messages */
if (s[1] == '=') { /* -w=<flags> If extended syntax, */
++s;
cwarnlev(++s); /* go hack rest of arg string. */
return(1);
}
#if SYS_CSI /* Temporary for compatibility, flush later */
if (isdigit(s[1])) wrnlev = toint(*++s);
else
#endif
cwarnlev("all"); /* Else just use basic level */
break;
case 'x': /* -x=<flags> Cross-compilation sws */
if (s[1] == '=') { /* If extended syntax, */
++s;
ctargmach(++s); /* go hack rest of arg string. */
return 1;
}
jerr("Syntax for -x is \"-x=flag\"");
return 1;
default:
jerr("Unknown switch: \"-%c\"", *s);
return 1;
}
return 1;
}
/* Command switch auxiliary routines.
**
** The standard way for extending KCC switch capabilities is by using
** parcswi() to implement the following keyword-based syntax, as exemplified by
** the -O switch:
** -O=<flag>+<flag>+<flag>...
** The flags are handled in the order given; all are cleared
** at the start. Using a '-' instead of '+' as the separator will cause
** the next flag to be turned OFF instead of ON. Either the flag name
** "all" or just the switch "-O" will cause all flags to be turned on.
** The handling of flag keywords is governed by a table of "flagent"
** structures.
*/
struct flagent { /* Structure of an entry in flag table */
char *name; /* Flag name */
int *fladdr; /* Address of runtime flag value */
int flval; /* Value to set it to if user gives flag */
};
/* PARCSWI - Parse an extended-style command switch.
** A NULL name entry marks end of table.
** An entry with a NULL flag address is the special "all" indicator.
*/
static char *cmpname();
static void errfswi();
static void
parcswi(s, ftab, resetf)
char *s;
struct flagent *ftab; /* Pointer to array of flagents */
int resetf; /* True if want all flags cleared initially */
{
int onoff, c, i;
char *cp;
/* First turn off all flags */
if (resetf)
for (i = 0; ftab[i].name; ++i)
if (ftab[i].fladdr) *(ftab[i].fladdr) = 0;
while (c = *s) {
onoff = 1; /* First do separator */
if (c == '-') { onoff = 0; ++s; }
else if (c == '+') ++s;
/* Look up switch in table */
for (i = 0; cp = ftab[i].name; i++) {
cp = cmpname(s, cp);
if (*cp == '\0' || *cp == '+' || *cp == '-')
break;
}
if (cp) { /* Found one? */
s = cp;
if (ftab[i].fladdr) /* Single flag */
*(ftab[i].fladdr) = (onoff ? ftab[i].flval : 0);
else for (i = 0; ftab[i].name; ++i) /* Hack "all" */
if (ftab[i].fladdr)
*(ftab[i].fladdr) = (onoff ? ftab[i].flval : 0);
} else { /* Nope, error. Find end of flag name */
for (cp = s; *cp && *cp != '+' && *cp != '-'; ++cp);
c = *cp; /* Remember last char */
*cp = '\0'; /* and temporarily zap it */
errfswi(s, ftab); /* Give user error message */
s = cp; /* Restore zapped char */
*s = c; /* And carry on from that point */
}
}
}
static void
errfswi(s, ftab)
char *s;
struct flagent *ftab; /* Pointer to array of flagents */
{
char emsg[1000]; /* Lots of room for temp string */
char *cp = emsg;
for (; ftab->name; ++ftab) /* Build string of flag names */
*cp++ = ' ', cp = estrcpy(cp, ftab->name);
jerr("Unknown flag \"%s\" (choices are:%s)", s, emsg);
}
/* CMPNAME - String comparison, returns pointer to first non-matching char
*/
static char *
cmpname(str, tst)
char *str,*tst;
{
if (*str == *tst)
while (*++str == *++tst)
if (*str == 0) break;
return str;
}
/* CHKMACNAME - Verify syntax for -D and -U macro names.
*/
static int
chkmacname(s)
char *s;
{
char *cp = s;
int typ = *s; /* 'D' or 'U' */
if (!iscsymf(*++cp)) {
jerr("Bad syntax for -%c macro name: \"%s\"", typ, cp);
return 0;
}
while (iscsym(*++cp)) ; /* Skip over ident name */
if (*cp && (typ == 'U' || (*cp != '='))) {
jerr("Bad syntax for -%c macro name: \"%s\"", typ, s+1);
return 0;
}
return 1;
}
/* COPTIMIZE - Set -O optimization switches
** Flags are used to provide finer degrees of control over the
** optimization process instead of just turning everything on or
** off; this makes debugging easier.
*/
struct flagent copttab[] = {
"all", NULL, 0, /* First element is special */
"parse", &optpar, 1, /* Parse tree optimization */
"gen", &optgen, 1, /* Code generator optimizations */
"object", &optobj, 1, /* Object code (peephole) optimizations */
NULL, NULL, NULL
};
static void
coptimize(s)
char *s;
{
parcswi(s, copttab, 1); /* Reset switches and parse */
}
/* CDEBUG - Set -d debug switches.
** This is exactly like COPTIMIZE only the switches here are for
** controlling what sorts of debug checks or output are produced.
** The syntax is:
** -d=<flag>+<flag>+<flag>...
** The flags are handled in the order given; all are cleared
** at the start. Using a '-' instead of '+' as the separator will cause
** the next flag to be turned OFF instead of ON. Either the flag name
** "all" or just the switch "-d" will cause all flags to be turned on.
*/
struct flagent cdebtab[] = {
"all", NULL, 0, /* First element is special */
"parse", &debpar, 1, /* Parse tree output */
"gen", &debgen, 1, /* Code generator output */
"pho", &debpho, 1, /* Peephole optimizer output */
"sym", &debsym, 1, /* Symbol table output */
NULL, NULL, NULL
};
static void
cdebug(s)
char *s;
{
parcswi(s, cdebtab, 1); /* Reset switches and parse */
}
/* CWARNLEV - Set -w warning message suppression switches.
** Same syntax as for -O and -d. -w alone is same as "all".
*/
static struct flagent cwlevtab[] = {
"all", &wrnlev, WLEV_ALL, /* Suppress everything */
"note", &wrnlev, WLEV_NOTE, /* Suppress notes */
"advise", &wrnlev, WLEV_ADVISE, /* Suppress notes & advice */
"warn", &wrnlev, WLEV_WARN, /* Suppress n & a & warnings */
NULL, NULL, NULL
};
static void
cwarnlev(s)
char *s;
{
parcswi(s, cwlevtab, 1); /* Reset switches and parse */
}
/* CTARGMACH - Set -x cross-compilation switches.
** Same syntax as for -O and -d.
** There is no "all" and no flags are reset. -x alone does nothing.
** Note that the value for the CPU type switches is not 1, so that
** we can distinguish between a default setting (1) and a switch setting (2).
*/
struct flagent ctgmtab[] = {
"tops20", &tgsys, TGSYS_TOPS20, /* 5 choices of system */
"tops10", &tgsys, TGSYS_TOPS10,
"waits", &tgsys, TGSYS_WAITS,
"tenex", &tgsys, TGSYS_TENEX,
"its", &tgsys, TGSYS_ITS,
"ka", &tgcpu[TGCPU_KA], 2, /* 5 choices of CPU */
"ki", &tgcpu[TGCPU_KI], 2,
"ks", &tgcpu[TGCPU_KS], 2,
"kl0", &tgcpu[TGCPU_KL0], 2,
"klx", &tgcpu[TGCPU_KLX], 2,
"fail", &tgasm, TGASM_FAIL, /* 3 choices of assembler */
"macro", &tgasm, TGASM_MACRO,
"midas", &tgasm, TGASM_MIDAS,
"ch7", &tgcsize, 7, /* Size of chars, in bits */
NULL, NULL, NULL
};
static void
ctargmach(s)
char *s;
{
register int i;
parcswi(s, ctgmtab, 0); /* Don't reset switches; parse */
tgcpw = TGSIZ_WORD/tgcsize; /* Ensure right vars set if charsize */
tgcmask = (1<<tgcsize)-1; /* was specified. */
for (i = TGCPU_N; --i >= 0;) /* See if any CPU types given */
if (tgcpu[i] == 2) {
for (i = TGCPU_N; --i >= 0;) /* If so, flush defaults */
if (tgcpu[i] == 1) tgcpu[i] = 0;
break;
}
setcpu(); /* Set other stuff based on this */
}
/* SETCPU - sets necessary flags and stuff based on CPU/SYS selection
** Primarily sets "tgmachuse" structure.
*/
static void
setcpu()
{
if (tgcpu[TGCPU_KLX] == 2) /* If user said -x=kx, then ensure loading */
ldextf++; /* is done as for -i=extend ! */
tgmachuse.dmovx = !(tgcpu[TGCPU_KA]); /* All but KA */
tgmachuse.fixflt = !(tgcpu[TGCPU_KA]); /* All but KA */
tgmachuse.adjsp = !(tgcpu[TGCPU_KA] || tgcpu[TGCPU_KI]);
tgmachuse.adjbp = !(tgcpu[TGCPU_KA] || tgcpu[TGCPU_KI]);
tgmachuse.dfl_s = (tgcpu[TGCPU_KA]); /* KA */
tgmachuse.dfl_h = ! tgmachuse.dfl_s; /* Use hardware, if no soft */
tgmachuse.dfl_g = 0; /* Not implem yet (maybe never) */
tgmachuse.exadr = tgcpu[TGCPU_KLX];
tgmachuse.mapch = /* Map char set if sys source != target and one
** of the systems is WAITS */
#if SYS_WTS
(tgsys != TGSYS_WAITS);
#else
(tgsys == TGSYS_WAITS);
#endif
/* Map double format if CPU source != target and one of the
** machines is a KA-10. +1 means map dfl_h to dfl_s; -1 means
** the opposite.
*/
tgmachuse.mapdbl =
#if CPU_KA
(tgmachuse.dfl_s ? 0 : -1);
#else
(tgmachuse.dfl_s ? 1 : 0);
#endif
}
/* CPORTLEV - Set -P portability level switches.
** Same syntax as for -O and -d.
** There is no "all". -P alone resets everything.
*/
struct flagent cplevtab[] = {
"kcc", &clevkcc, 1, /* Enable KCC extensions to C */
"base", &clevel, CLEV_BASE, /* Allow only very portable code */
"carm", &clevel, CLEV_CARM, /* Allow full CARM implementation */
"ansi", &clevel, CLEV_ANSI, /* Parse CARM+ANSI implementation */
"stdc", &clevel, CLEV_STDC, /* Parse full ANSI implementation */
NULL, NULL, NULL
};
static void
cportlev(s)
char *s;
{
parcswi(s, cplevtab, 1); /* Reset switches and parse */
}
/* CVERBOSE - Set -v verboseness switches.
** Same syntax as for -O etc.
** -v alone is same as "all".
*/
struct flagent cverbtab[] = {
"all", NULL, 0, /* First element is special */
"fundef", &vrbfun, 1, /* Print function names as we go */
"stats", &vrbsta, 1, /* Print statistics at end */
"args", &vrbarg, 1, /* Print KCC command line args */
"load", &vrbld, 1, /* Print linking loader commands */
NULL, NULL, NULL
};
static void
cverbose(s)
char *s;
{
parcswi(s, cverbtab, 1); /* Reset switches and parse */
}
/* CILOAD - Set -i loading control switches.
** Same syntax as for -O etc.
** -i alone is same as "ext". There is no "all".
*/
struct flagent cildtab[] = {
"extend", &ldextf, 1, /* Load into non-zero section (1) */
"psect", &ldpsectf, 1, /* Use PSECTs for loading */
"ddt", &ldddtf, 1, /* Tell LINK to add DDT (sigh) */
NULL, NULL, NULL
};
static void
ciload(s)
char *s;
{
parcswi(s, cildtab, 1); /* Reset switches and parse */
}
/* CFILE(filename) - Compile or otherwise process a file.
** Return value indicates whether file was assembled:
** -2 No assembly attempted (may or may not be error).
** -1 Assembly failed.
** 0 Assembly deferred, must call runasmlnk() later.
** +1 Assembled into .REL file.
*/
static int
cfile(arg)
char *arg;
{
int preflg; /* Set if asm prefix file produced */
int mainflg; /* Set if module contains "main" */
int asmdflg = -2; /* Set to result of assembly attempt */
clock_t startime;
if (vrbsta)
startime = clock(); /* Mark cpu time */
nerrors = 0;
if (!files(arg)) /* If couldn't open files or file is .REL, */
return asmdflg; /* just return. */
if (!prepf) fprintf(stderr, "KCC:\t%s\n", inpfmodule);
syminit(); /* Set up symbol tables */
ppinit(); /* Initialize the input preprocessor */
ppdefine(npreundef,preundefs, /* then can do initial -U undefs */
npredef, predefs); /* and initial -D definitions */
lexinit(); /* Initialize the input lexer */
initpar(); /* Initialize the input parser */
if (prepf) { /* If only preprocessor output (-E) */
passthru(stdout); /* send it through specially */
fclose(in); /* and then close input */
if (debsym) {
symdump(minsym->Snext, "external"); /* symbol table dump */
typedump();
fclose(fsym);
}
}
else { /* Normal compilation processing */
NODE *n;
outinit(); /* Initialize for assembler code output */
#if 0 /* No longer supported */
entdefs(); /* Special hack: process any "entry" defs. */
#endif
while (!eof) { /* Process each external definition */
savelits = 0; /* Reset string literal pool */
nodeinit(); /* Reset parse-tree node table */
curfn = NULL; /* Not in any function */
n = extdef(); /* Call parser, get one external definition */
if (debpar)
nodedump(n); /* Output dump of parse tree if debugging */
gencode(n); /* Call code generator on parse tree */
}
fclose(in); /* All done with input stream, close it. */
curfn = NULL; /* Not in any function */
fline = 0; /* Avoid showing context on errs after this */
while (n = tntdef()) { /* Output any remaining tentative defs */
if (debpar)
nodedump(n);
gencode(n);
nodeinit(); /* Reset parse-tree node table after each */
}
if (mainflg = mainsymp()) /* See if "main" defined in module */
strcpy(mainname, inpfmodule); /* Yes, save module name! */
outdone(mainflg); /* Output assembler postamble stuff */
fclose(out); /* and close assembler output file. */
if (debsym) {
symdump(minsym->Snext, "external"); /* symbol table dump */
typedump();
fclose(fsym);
}
if (debpar) fclose(fdeb); /* Close parse tree debug file */
if (debpho) fclose(fpho); /* Close peephole debug file */
preflg = makprefile(prefname); /* Make prefix file for asm if nec */
if (!nerrors && assemble)
asmdflg = asmb(inpfmodule, /* Assemble into this .REL file, */
(preflg ? prefname : (char *)NULL), /* from this */
outfname); /* and this. */
if (delete && asmdflg != 0) {
unlink(outfname); /* delete assembler file */
if (preflg) unlink(prefname); /* both of them */
}
}
if (nerrors) /* Report errors */
jmsg("%d error%s detected", nerrors, nerrors==1 ? "" : "s" );
else if (vrbsta) showcpu(startime); /* or say how much cpu we used */
return asmdflg; /* Return assembly result */
}
/* Auxiliary - returns true if main() was defined in this module */
static int
mainsymp()
{
SYMBOL *s;
return ((s = symfidstr("main")) && s->Sclass == SC_EXTDEF);
}
/* FILES - parse a filename argument and set up I/O streams.
**
** Note that "prefname" is set up here, but not opened.
** This is because we may not need to use it; the call to
** "makprefile()" will do so if necessary.
*/
static int
files(fname)
char *fname;
{
extern char *fnparse(); /* All from CCASMB */
extern int fnxisrel();
extern int fnmarkin();
int cextf;
char *cp;
char cname[FNAMESIZE]; /* Name of .C source file */
char rname[FNAMESIZE]; /* Name of .REL binary file */
char ext[FNAMESIZE]; /* Temp to hold parsed extension */
/*
** Parse filename into its various pieces, mainly to get module name.
** As a special case, it will work to specify a device (logical) name
** with no filename, e.g. you can say
** @CC FOO:
** where FOO: => <DIR>NAME.C, and the module name will be FOO.
*/
if (cp = fnparse(fname, inpfdir, inpfmodule, ext, inpfsuf)) {
jerr("Bad filename arg (%s): \"%s\"", cp, fname); /* Ugh */
return 0; /* and don't try to compile */
}
/* Check for .REL extension and avoid compiling if so */
if (fnxisrel(ext))
return 0;
/* Check for .C extension. "cextf" is set if we have it. */
cextf = (ext[0] == '.'
&& toupper(ext[1]) == 'C'
&& !ext[2]);
/* Now compose source filename with ".C" appended if necessary */
if (cextf) strcpy(cname, fname); /* Found .C, just copy filename */
else {
estrcpy(estrcpy(estrcpy(estrcpy(cname, /* Rebuild filename */
inpfdir), inpfmodule), ".c"), inpfsuf);
}
#if SYS_T20+SYS_10X+SYS_ITS /* Only TOPS-10 systems need to retain */
inpfsuf[0] = '\0'; /* input suffix; all others must flush it. */
#endif
/* If no ".C" was specified, and the -q conditional compile flag was
** set, we assume that we are to check the .C and .REL extensions of
** this file to determine whether compilation is necessary.
*/
if (!cextf && condccf) {
strcpy(rname, inpfmodule); /* Make the .REL filename */
strcat(rname, ".rel");
if (!needcomp(cname, rname))
return 0; /* Doesn't need to be compiled! */
}
/*
** Now that we've figured out what the name of the file is, we can try
** to open it. First we try the filename as given, and if that doesn't
** work (most likely because no .C was specified) then we try the
** one we constructed by adding .C.
*/
strcpy(inpfname, fname); /* Try filename as given */
in = fopen(inpfname, "r");
if (in == NULL) {
strcpy(inpfname, cname); /* then constructed filename */
in = fopen(inpfname, "r");
if (in == NULL) {
errfopen("input", inpfname);
return 0;
}
}
fnmarkin(in, inpfname); /* Let CCASMB mark new input file */
/* Compose symbol table dump output filename, if desired */
if (debsym) {
strcpy(symfname, inpfmodule);
strcat(symfname, ".cym");
if ((fsym = fopen(symfname, "w")) == NULL) {
errfopen("symbol table", symfname);
return 0;
}
}
/* If we are only doing pre-processing, then no other filenames are
** needed, and we can return now.
*/
if (prepf) return 1;
/*
** The output file is merely ".FAI" etc concatenated to the stripped
** filename we calculated above, in the current directory.
*/
cp = (tgasm==TGASM_FAIL ? ".fai" : /* Get right extension */
(tgasm==TGASM_MACRO ? ".mac" :
(tgasm==TGASM_MIDAS ? ".mid" : "")));
strcat(strcpy(outfname, inpfmodule), cp); /* Compose output filename */
if ((out = fopen(outfname, "w")) == NULL) {
errfopen("output", outfname);
return 0;
}
/* Compose assembler predefinition filename for later */
strcat(strcpy(prefname, inpfmodule), ".pre");
/* Now open various other debugging output files */
if (debpar) { /* debugging output goes here */
strcpy(debfname, inpfmodule);
strcat(debfname, ".deb");
if ((fdeb = fopen(debfname, "w")) == NULL) {
errfopen("parser debugging output", debfname);
return 0;
}
}
if (debpho) { /* Peephole debugging output goes here */
strcpy(phofname, inpfmodule);
strcat(phofname, ".pho");
if ((fpho = fopen(phofname, "w")) == NULL) {
errfopen("peephole debugging output", phofname);
return 0;
}
}
return 1;
}
/* NEEDCOMP - Auxiliary for above.
** Takes source and binary filenames, returns TRUE if
** source needs compiling (is newer than binary).
*/
#include <sys/types.h>
#include <sys/stat.h>
static int
needcomp(src, rel)
char *src, *rel; /* source and binary filenames */
{
struct stat sbuf, rbuf;
if (stat(src, &sbuf) < 0)
return 1; /* No source?? Try compiling anyway */
if (stat(rel, &rbuf) < 0)
return 1; /* No .REL, so must compile */
return (sbuf.st_mtime > rbuf.st_mtime); /* Compare last mod times */
}
/* ----------------------------------- */
/* show how much cpu we used */
/* ----------------------------------- */
static void
showcpu(otim)
clock_t otim; /* previous val of clock() */
{
float secs;
secs = (float)(clock() - otim) / CLOCKS_PER_SEC; /* Find # secs used */
fprintf(stderr,"Processed %d lines in %.2f seconds (%d lines/min)\n",
tline, secs, (int)((tline*60)/secs));
}
/* Target Machine routines. Eventually this page may become a separate file
** called CCTARG.
*/
/* TGMAPCH - Map input char to target machine char set
** Called by CCLEX for char and string constants.
*/
int
tgmapch(c)
{
#if SYS_WTS /* Running on WAITS */
if (tgsys != TGSYS_WAITS) { /* Convert from WAITS ASCII to USASCII */
switch (c) {
case '\b': return '\010'; /* BS is 0177 on WAITS */
case '}': return '\175';
case '~': return '\176';
}
}
#else /* Running on anything but WAITS */
if (tgsys == TGSYS_WAITS) { /* Convert from USASCII to WAITS ASCII */
switch (c) {
case '\b': return '\177'; /* BS is 0177 on WAITS */
case '}': return '\176'; /* } is ~ */
case '~': return '\032'; /* ~ is ^Z (ctrl-Z) */
}
}
#endif
return c;
}