quakeforge/tools/qfcc/source/qfcc.c
Adam Olsen c218ede288 Add support for checkfunction, which is a builtin that searches for
another builtin by name, and returns it.

Soon I'll change all our new builtins to by allocated dynamically, as
well as changing the number checkfunction uses, and happily break
everything that uses them :D
2001-08-04 09:11:50 +00:00

1144 lines
24 KiB
C

/*
qfcc.c
QuakeForge Code Compiler (main program)
Copyright (C) 1996-1997 id Software, Inc.
Copyright (C) 2001 Jeff Teunissen <deek@dusknet.dhs.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public
License along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
$Id$
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <QF/crc.h>
#include <QF/hash.h>
#include <QF/qendian.h>
#include <QF/sys.h>
#include "qfcc.h"
options_t options;
char sourcedir[1024];
char destfile[1024];
char debugfile[1024];
float pr_globals[MAX_REGS];
int numpr_globals;
char strings[MAX_STRINGS];
int strofs;
dstatement_t statements[MAX_STATEMENTS];
int numstatements;
int statement_linenums[MAX_STATEMENTS];
dfunction_t functions[MAX_FUNCTIONS];
int numfunctions;
ddef_t globals[MAX_GLOBALS];
int numglobaldefs;
int num_localdefs;
ddef_t fields[MAX_FIELDS];
int numfielddefs;
char precache_sounds[MAX_SOUNDS][MAX_DATA_PATH];
int precache_sounds_block[MAX_SOUNDS];
int numsounds;
char precache_models[MAX_MODELS][MAX_DATA_PATH];
int precache_models_block[MAX_SOUNDS];
int nummodels;
char precache_files[MAX_FILES][MAX_DATA_PATH];
int precache_files_block[MAX_SOUNDS];
int numfiles;
/*
WriteFiles
Generates files.dat, which contains all of the data files actually used by
the game, to be processed by qfiles
*/
void
WriteFiles (void)
{
FILE *f;
int i;
char filename[1024];
sprintf (filename, "%s%cfiles.dat", sourcedir, PATH_SEPARATOR);
f = fopen (filename, "w");
if (!f)
Error ("Couldn't open %s", filename);
fprintf (f, "%i\n", numsounds);
for (i = 0; i < numsounds; i++)
fprintf (f, "%i %s\n", precache_sounds_block[i], precache_sounds[i]);
fprintf (f, "%i\n", nummodels);
for (i = 0; i < nummodels; i++)
fprintf (f, "%i %s\n", precache_models_block[i], precache_models[i]);
fprintf (f, "%i\n", numfiles);
for (i = 0; i < numfiles; i++)
fprintf (f, "%i %s\n", precache_files_block[i], precache_files[i]);
fclose (f);
}
/*
CopyString
Return an offset from the string heap
*/
static hashtab_t *strings_tab;
static const char *
stings_get_key (void *_str, void *unsued)
{
return (char*)_str;
}
int
CopyString (const char *str)
{
int old;
if (!strings_tab) {
strings_tab = Hash_NewTable (16381, stings_get_key, 0, 0);
}
old = strofs;
strcpy (strings + strofs, str);
strofs += strlen (str) + 1;
Hash_Add (strings_tab, strings + old);
return old;
}
int
ReuseString (const char *str)
{
char *s;
if (!strings_tab)
return CopyString (str);
s = Hash_Find (strings_tab, str);
if (s)
return s - strings;
return CopyString (str);
}
void
PrintStrings (void)
{
int i, l, j;
for (i = 0; i < strofs; i += l) {
l = strlen (strings + i) + 1;
printf ("%5i : ", i);
for (j = 0; j < l; j++) {
if (strings[i + j] == '\n') {
putchar ('\\');
putchar ('n');
} else {
putchar (strings[i + j]);
}
}
printf ("\n");
}
}
void
PrintFunctions (void)
{
int i, j;
dfunction_t *d;
for (i = 0; i < numfunctions; i++) {
d = &functions[i];
printf ("%s : %s : %i %i (", strings + d->s_file, strings + d->s_name,
d->first_statement, d->parm_start);
for (j = 0; j < d->numparms; j++)
printf ("%i ", d->parm_size[j]);
printf (")\n");
}
}
void
PrintFields (void)
{
int i;
ddef_t *d;
for (i = 0; i < numfielddefs; i++) {
d = &fields[i];
printf ("%5i : (%i) %s\n", d->ofs, d->type, strings + d->s_name);
}
}
void
PrintGlobals (void)
{
int i;
ddef_t *d;
for (i = 0; i < numglobaldefs; i++) {
d = &globals[i];
printf ("%5i : (%i) %s\n", d->ofs, d->type, strings + d->s_name);
}
}
void
InitData (void)
{
int i;
numstatements = 1;
strofs = 1;
numfunctions = 1;
numglobaldefs = 1;
numfielddefs = 1;
def_ret.ofs = OFS_RETURN;
for (i = 0; i < MAX_PARMS; i++)
def_parms[i].ofs = OFS_PARM0 + 3 * i;
}
void
WriteData (int crc)
{
def_t *def;
ddef_t *dd;
dprograms_t progs;
pr_debug_header_t debug;
FILE *h;
int i;
for (def = pr.def_head.def_next; def; def = def->def_next) {
if (def->scope)
continue;
if (def->type->type == ev_func) {
// df = &functions[numfunctions];
// numfunctions++;
} else if (def->type->type == ev_field) {
dd = &fields[numfielddefs];
numfielddefs++;
dd->type = def->type->aux_type->type;
dd->s_name = ReuseString (def->name);
dd->ofs = G_INT (def->ofs);
}
dd = &globals[numglobaldefs];
numglobaldefs++;
dd->type = def->type->type;
if (!def->initialized
&& def->type->type != ev_func
&& def->type->type != ev_field && def->scope == NULL)
dd->type |= DEF_SAVEGLOBAL;
dd->s_name = ReuseString (def->name);
dd->ofs = def->ofs;
}
// PrintStrings ();
// PrintFunctions ();
// PrintFields ();
// PrintGlobals ();
strofs = (strofs + 3) & ~3;
if (options.quiet < 2) {
printf ("%6i strofs\n", strofs);
printf ("%6i statements\n", numstatements);
printf ("%6i functions\n", numfunctions);
printf ("%6i globaldefs\n", numglobaldefs);
printf ("%6i locals size\n", num_localdefs);
printf ("%6i fielddefs\n", numfielddefs);
printf ("%6i pr_globals\n", numpr_globals);
}
h = SafeOpenWrite (destfile);
SafeWrite (h, &progs, sizeof (progs));
progs.ofs_strings = ftell (h);
progs.numstrings = strofs;
SafeWrite (h, strings, strofs);
progs.ofs_statements = ftell (h);
progs.numstatements = numstatements;
for (i = 0; i < numstatements; i++) {
statements[i].op = LittleShort (statements[i].op);
statements[i].a = LittleShort (statements[i].a);
statements[i].b = LittleShort (statements[i].b);
statements[i].c = LittleShort (statements[i].c);
}
SafeWrite (h, statements, numstatements * sizeof (dstatement_t));
progs.ofs_functions = ftell (h);
progs.numfunctions = numfunctions;
for (i = 0; i < numfunctions; i++) {
functions[i].first_statement = LittleLong (functions[i].first_statement);
functions[i].parm_start = LittleLong (functions[i].parm_start);
functions[i].s_name = LittleLong (functions[i].s_name);
functions[i].s_file = LittleLong (functions[i].s_file);
functions[i].numparms = LittleLong (functions[i].numparms);
functions[i].locals = LittleLong (functions[i].locals);
}
SafeWrite (h, functions, numfunctions * sizeof (dfunction_t));
progs.ofs_globaldefs = ftell (h);
progs.numglobaldefs = numglobaldefs;
for (i = 0; i < numglobaldefs; i++) {
globals[i].type = LittleShort (globals[i].type);
globals[i].ofs = LittleShort (globals[i].ofs);
globals[i].s_name = LittleLong (globals[i].s_name);
}
SafeWrite (h, globals, numglobaldefs * sizeof (ddef_t));
progs.ofs_fielddefs = ftell (h);
progs.numfielddefs = numfielddefs;
for (i = 0; i < numfielddefs; i++) {
fields[i].type = LittleShort (fields[i].type);
fields[i].ofs = LittleShort (fields[i].ofs);
fields[i].s_name = LittleLong (fields[i].s_name);
}
SafeWrite (h, fields, numfielddefs * sizeof (ddef_t));
progs.ofs_globals = ftell (h);
progs.numglobals = numpr_globals;
for (i = 0; i < numpr_globals; i++)
((int *) pr_globals)[i] = LittleLong (((int *) pr_globals)[i]);
SafeWrite (h, pr_globals, numpr_globals * 4);
if (options.quiet < 2)
printf ("%6i TOTAL SIZE\n", (int) ftell (h));
progs.entityfields = pr.size_fields;
progs.version = options.version;
progs.crc = crc;
// byte swap the header and write it out
for (i = 0; i < sizeof (progs) / 4; i++)
((int *) &progs)[i] = LittleLong (((int *) &progs)[i]);
fseek (h, 0, SEEK_SET);
SafeWrite (h, &progs, sizeof (progs));
fclose (h);
if (!options.debug) {
return;
}
h = SafeOpenRead (destfile);
debug.version = LittleLong (PROG_DEBUG_VERSION);
CRC_Init (&debug.crc);
while ((i = fgetc (h)) != EOF)
CRC_ProcessByte (&debug.crc, i);
fclose (h);
debug.crc = LittleShort (debug.crc);
debug.you_tell_me_and_we_will_both_know = 0;
h = SafeOpenWrite (debugfile);
SafeWrite (h, &debug, sizeof (debug));
debug.auxfunctions = LittleLong (ftell (h));
debug.num_auxfunctions = LittleLong (num_auxfunctions);
for (i = 0; i < num_auxfunctions; i++) {
auxfunctions[i].function = LittleLong (auxfunctions[i].function);
auxfunctions[i].source_line = LittleLong (auxfunctions[i].source_line);
auxfunctions[i].line_info = LittleLong (auxfunctions[i].line_info);
auxfunctions[i].local_defs = LittleLong (auxfunctions[i].local_defs);
auxfunctions[i].num_locals = LittleLong (auxfunctions[i].num_locals);
}
SafeWrite (h, auxfunctions, num_auxfunctions * sizeof (auxfunctions[0]));
debug.linenos = LittleLong (ftell (h));
debug.num_linenos = LittleLong (num_linenos);
for (i = 0; i < num_linenos; i++) {
linenos[i].fa.addr = LittleLong (linenos[i].fa.addr);
linenos[i].line = LittleLong (linenos[i].line);
}
SafeWrite (h, linenos, num_linenos * sizeof (linenos[0]));
debug.locals = LittleLong (ftell (h));
debug.num_locals = LittleLong (num_locals);
for (i = 0; i < num_locals; i++) {
locals[i].type = LittleShort (locals[i].type);
locals[i].ofs = LittleShort (locals[i].ofs);
locals[i].s_name = LittleLong (locals[i].s_name);
}
SafeWrite (h, locals, num_locals * sizeof (locals[0]));
fseek (h, 0, SEEK_SET);
SafeWrite (h, &debug, sizeof (debug));
fclose (h);
}
/*
PR_String
Returns a string suitable for printing (no newlines, max 60 chars length)
*/
char *
PR_String (char *string)
{
static char buf[80];
char *s;
s = buf;
*s++ = '"';
while (string && *string) {
if (s == buf + sizeof (buf) - 2)
break;
if (*string == '\n') {
*s++ = '\\';
*s++ = 'n';
} else if (*string == '"') {
*s++ = '\\';
*s++ = '"';
} else {
*s++ = *string;
}
string++;
if (s - buf > 60) {
*s++ = '.';
*s++ = '.';
*s++ = '.';
break;
}
}
*s++ = '"';
*s++ = 0;
return buf;
}
def_t *
PR_DefForFieldOfs (gofs_t ofs)
{
def_t *d;
for (d = pr.def_head.def_next; d; d = d->def_next) {
if (d->type->type != ev_field)
continue;
if (*((int *) &pr_globals[d->ofs]) == ofs)
return d;
}
Error ("PR_DefForFieldOfs: couldn't find %i", ofs);
return NULL;
}
/*
PR_ValueString
Return a string describing *data in a type-specific manner
*/
char *
PR_ValueString (etype_t type, void *val)
{
static char line[256];
def_t *def;
dfunction_t *f;
switch (type) {
case ev_string:
sprintf (line, "%s", PR_String (strings + *(int *) val));
break;
case ev_entity:
sprintf (line, "entity %i", *(int *) val);
break;
case ev_func:
if (!(f = functions + *(int *) val))
sprintf (line, "undefined function");
else
sprintf (line, "%s()", strings + f->s_name);
break;
case ev_field:
def = PR_DefForFieldOfs (*(int *) val);
sprintf (line, ".%s", def->name);
break;
case ev_void:
sprintf (line, "void");
break;
case ev_float:
sprintf (line, "%5.1f", *(float *) val);
break;
case ev_vector:
sprintf (line, "'%5.1f %5.1f %5.1f'", ((float *) val)[0],
((float *) val)[1], ((float *) val)[2]);
break;
case ev_pointer:
sprintf (line, "pointer");
break;
default:
sprintf (line, "bad type %i", type);
break;
}
return line;
}
/*
PR_GlobalString
Return a string with a description and the contents of a global, padded
to 20 field width
*/
char *
PR_GlobalStringNoContents (gofs_t ofs)
{
int i;
def_t *def;
void *val;
static char line[128];
val = (void *) &pr_globals[ofs];
def = pr_global_defs[ofs];
if (!def) {
// Error ("PR_GlobalString: no def for %i", ofs);
sprintf (line, "%i(\?\?\?)", ofs);
} else {
sprintf (line, "%i(%s)", ofs, def->name);
}
for (i = strlen (line); i < 16; i++) {
strcat (line, " ");
}
strcat (line, " ");
return line;
}
char *
PR_GlobalString (gofs_t ofs)
{
char *s;
int i;
def_t *def;
void *val;
static char line[128];
val = (void *) &pr_globals[ofs];
if (!(def = pr_global_defs[ofs]))
return PR_GlobalStringNoContents (ofs);
if (def->initialized && def->type->type != ev_func) {
s = PR_ValueString (def->type->type, &pr_globals[ofs]);
sprintf (line, "%i(%s)", ofs, s);
} else {
sprintf (line, "%i(%s)", ofs, def->name);
}
for (i = strlen (line); i < 16; i++) {
strcat (line, " ");
}
strcat (line, " ");
return line;
}
/*
============
PR_PrintOfs
============
*/
void
PR_PrintOfs (gofs_t ofs)
{
printf ("%s\n", PR_GlobalString (ofs));
}
/*
=================
PR_PrintStatement
=================
*/
void
PR_PrintStatement (dstatement_t *s)
{
int i;
opcode_t *op;
op = PR_Opcode (s->op);
printf ("%4i : %4i : %s ", (int) (s - statements),
statement_linenums[s - statements], op->opname);
for (i = strlen (op->opname); i < 10; i++) {
printf (" ");
}
if (s->op == OP_IF || s->op == OP_IFNOT) {
printf ("%sbranch %i", PR_GlobalString (s->a), s->b);
} else if (s->op == OP_GOTO) {
printf ("branch %i", s->a);
} else if ((unsigned) (s->op - OP_STORE_F) < 6) {
printf ("%s", PR_GlobalString (s->a));
printf ("%s", PR_GlobalStringNoContents (s->b));
} else {
if (s->a)
printf ("%s", PR_GlobalString (s->a));
if (s->b)
printf ("%s", PR_GlobalString (s->b));
if (s->c)
printf ("%s", PR_GlobalStringNoContents (s->c));
}
printf ("\n");
}
/*
PR_PrintDefs
*/
void
PR_PrintDefs (void)
{
def_t *d;
for (d = pr.def_head.def_next; d; d = d->def_next)
PR_PrintOfs (d->ofs);
}
/*
PR_BeginCompilation
called before compiling a batch of files, clears the pr struct
*/
void
PR_BeginCompilation (void *memory, int memsize)
{
int i;
pr.memory = memory;
pr.max_memory = memsize;
numpr_globals = RESERVED_OFS;
pr.def_tail = &pr.def_head;
for (i = 0; i < RESERVED_OFS; i++)
pr_global_defs[i] = &def_void;
// link the function type in so state forward declarations match proper type
pr.types = &type_function;
type_function.next = NULL;
pr_error_count = 0;
}
void
PR_RelocateRefs (def_t *def)
{
statref_t *ref;
for (ref = def->refs; ref; ref = ref->next) {
switch (ref->field) {
case 0:
ref->statement->a = def->ofs;
break;
case 1:
ref->statement->b = def->ofs;
break;
case 2:
ref->statement->c = def->ofs;
break;
default:
abort();
}
}
}
/*
PR_FinishCompilation
called after all files are compiled to check for errors.
Returns false if errors were detected.
*/
qboolean
PR_FinishCompilation (void)
{
def_t *d;
qboolean errors = false;
function_t *f;
def_t *def;
expr_t e;
// check to make sure all functions prototyped have code
if (options.undefined_function_warning)
for (d = pr.def_head.def_next; d; d = d->def_next) {
if (d->type->type == ev_func && !d->scope) { // function args ok
// f = G_FUNCTION(d->ofs);
// if (!f || (!f->code && !f->builtin))
if (!d->initialized) {
warning (0, "function %s was not defined\n", d->name);
}
}
}
if (errors)
return !errors;
if (options.debug) {
e.type = ex_string;
e.e.string_val = debugfile;
PR_ReuseConstant (&e, PR_GetDef (&type_string, ".debug_file", 0,
&numpr_globals));
}
for (def = pr.def_head.def_next; def; def = def->def_next) {
if (def->scope)
continue;
PR_RelocateRefs (def);
}
for (f = pr_functions; f; f = f->next) {
if (f->builtin)
continue;
if (f->def->num_locals > num_localdefs)
num_localdefs = f->def->num_locals;
f->dfunc->parm_start = numpr_globals;
for (def = f->def->scope_next; def; def = def->scope_next) {
def->ofs += numpr_globals;
PR_RelocateRefs (def);
}
}
numpr_globals += num_localdefs;
return !errors;
}
//=============================================================================
/*
PR_WriteProgdefs
Writes the global and entity structures out.
Returns a crc of the header, to be stored in the progs file for comparison
at load time.
*/
int
PR_WriteProgdefs (char *filename)
{
def_t *d;
FILE *f;
unsigned short crc;
int c;
if (!options.quiet)
printf ("writing %s\n", filename);
f = fopen (filename, "w");
// print global vars until the first field is defined
fprintf (f, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{\tint\tpad[%i];\n", RESERVED_OFS);
for (d = pr.def_head.def_next; d; d = d->def_next) {
if (!strcmp (d->name, "end_sys_globals"))
break;
switch (d->type->type) {
case ev_float:
fprintf (f, "\tfloat\t%s;\n", d->name);
break;
case ev_vector:
fprintf (f, "\tvec3_t\t%s;\n", d->name);
d = d->def_next->def_next->def_next; // skip the elements
break;
case ev_string:
fprintf (f, "\tstring_t\t%s;\n", d->name);
break;
case ev_func:
fprintf (f, "\tfunc_t\t%s;\n", d->name);
break;
case ev_entity:
fprintf (f, "\tint\t%s;\n", d->name);
break;
default:
fprintf (f, "\tint\t%s;\n", d->name);
break;
}
}
fprintf (f, "} globalvars_t;\n\n");
// print all fields
fprintf (f, "typedef struct\n{\n");
for (d = pr.def_head.def_next; d; d = d->def_next) {
if (!strcmp (d->name, "end_sys_fields"))
break;
if (d->type->type != ev_field)
continue;
switch (d->type->aux_type->type) {
case ev_float:
fprintf (f, "\tfloat\t%s;\n", d->name);
break;
case ev_vector:
fprintf (f, "\tvec3_t\t%s;\n", d->name);
d = d->def_next->def_next->def_next; // skip the elements
break;
case ev_string:
fprintf (f, "\tstring_t\t%s;\n", d->name);
break;
case ev_func:
fprintf (f, "\tfunc_t\t%s;\n", d->name);
break;
case ev_entity:
fprintf (f, "\tint\t%s;\n", d->name);
break;
default:
fprintf (f, "\tint\t%s;\n", d->name);
break;
}
}
fprintf (f, "} entvars_t;\n\n");
fclose (f);
// do a crc of the file
CRC_Init (&crc);
f = fopen (filename, "r+");
while ((c = fgetc (f)) != EOF)
CRC_ProcessByte (&crc, (byte) c);
fprintf (f, "#define PROGHEADER_CRC %i\n", crc);
fclose (f);
return crc;
}
void
PrintFunction (const char *name)
{
int i;
dstatement_t *ds;
dfunction_t *df;
for (i = 0; i < numfunctions; i++)
if (!strcmp (name, strings + functions[i].s_name))
break;
if (i == numfunctions)
Error ("No function names \"%s\"", name);
df = functions + i;
printf ("Statements for %s:\n", name);
ds = statements + df->first_statement;
while (1) {
PR_PrintStatement (ds);
if (!ds->op)
break;
ds++;
}
}
void
PR_PrintFunction (def_t *def)
{
def_t *d;
statref_t *r;
printf ("%s\n", def->name);
for (d = def->scope_next; d; d = d->scope_next) {
printf ("%s: %d %d %d\n",
d->name ? d->name : "<temp>",
d->ofs, d->type->type, type_size[d->type->type]);
for (r = d->refs; r; r = r->next)
printf (" %d", r->statement - statements);
printf ("\n");
}
}
//============================================================================
/*
main
The nerve center of our little operation
*/
int
main (int argc, char **argv)
{
char *src;
char filename[1024];
int p, crc;
double start, stop;
int no_cpp = 0;
start = Sys_DoubleTime ();
myargc = argc;
myargv = argv;
options.version = PROG_VERSION;
if (CheckParm ("-h") || CheckParm ("--help")) {
printf ("%s - A compiler for the QuakeC language\n", argv[0]);
printf ("Usage: %s [options]\n", argv[0]);
printf (
"Options: \n"
" -s, --source <dir> look for progs.src in directory <dir>\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
" --cow allow assignment to initialized globals\n"
" --id only support id (progs version 6) features\n"
" --warn=error treat warnings as errors\n"
" --undefined-function-warning warn when a function isn't defined\n"
);
return 1;
}
if (CheckParm ("-V") || CheckParm ("--version")) {
printf ("%s version %s\n", PACKAGE, VERSION);
return 1;
}
if ((p = CheckParm ("--source")) && p < argc - 1) {
strcpy (sourcedir, argv[p + 1]);
} else {
if ((p = CheckParm ("-s")) && p < argc - 1) {
strcpy (sourcedir, argv[p + 1]);
} else {
strcpy (sourcedir, ".");
}
}
if (CheckParm ("--quiet=2")) { //FIXME: getopt, need getopt </#5>
options.quiet = 2;
}
if (CheckParm ("--quiet")) {
options.quiet = 1;
}
if (CheckParm ("--cow")) {
options.cow = 1;
}
if (CheckParm ("--id")) {
options.version = PROG_ID_VERSION;
}
if (CheckParm ("--debug")) {
options.debug = 1;
}
if (CheckParm ("--no-cpp")) {
no_cpp = 1;
}
// FIXME eww, really must go to getopt
if (CheckParm ("--warn=error")) {
options.warn_error = 1;
}
if (CheckParm ("--undefined-function-warning")) {
options.undefined_function_warning = 1;
}
if (strcmp (sourcedir, ".")) {
printf ("Source directory: %s\n", sourcedir);
}
PR_Opcode_Init_Tables ();
InitData ();
sprintf (filename, "%s/progs.src", sourcedir);
LoadFile (filename, (void *) &src);
if (!(src = COM_Parse (src)))
Error ("No destination filename. qfcc --help for info.\n");
strcpy (destfile, com_token);
if (!options.quiet) {
printf ("outputfile: %s\n", destfile);
}
if (options.debug) {
char *s;
strcpy (debugfile, com_token);
s = debugfile + strlen (debugfile);
while (s-- > debugfile) {
if (*s == '.')
break;
if (*s == '/' || *s == '\\') {
s = debugfile + strlen (debugfile);
break;
}
}
strcpy (s, ".sym");
if (!options.quiet)
printf ("debug file: %s\n", debugfile);
}
pr_dumpasm = false;
PR_BeginCompilation (malloc (0x100000), 0x100000);
// compile all the files
while ((src = COM_Parse (src))) {
#ifdef NEW_PARSER
#ifdef USE_CPP
pid_t pid;
char *temp1;
char *temp2 = strrchr (argv[0], PATH_SEPARATOR);
char tempname[1024];
int tempfd;
#endif
int error;
extern FILE *yyin;
int yyparse(void);
extern void clear_frame_macros (void);
//extern int yydebug;
//yydebug = 1;
sprintf (filename, "%s%c%s", sourcedir, PATH_SEPARATOR, com_token);
if (!options.quiet)
printf ("compiling %s\n", filename);
#ifdef USE_CPP
if (!no_cpp) {
temp1 = getenv ("TMPDIR");
if ((!temp1) || (!temp1[0])) {
temp1 = getenv ("TEMP");
if ((!temp1) || (!temp1[0])) {
temp1 = "/tmp";
}
}
snprintf (tempname, sizeof (tempname), "%s%c%sXXXXXX", temp1,
PATH_SEPARATOR, temp2 ? temp2 + 1 : argv[0]);
tempfd = mkstemp (tempname);
if ((pid = fork ()) == -1) {
perror ("fork");
return 1;
}
if (!pid) { // we're a child, check for abuse
execlp ("cpp", "-D__QFCC__=1", "-o", tempname, filename, NULL);
printf ("Child shouldn't reach here\n");
exit (1);
} else { // give parental guidance (or bury it in the back yard)
int status;
pid_t rc;
// printf ("pid = %d\n", pid);
if ((rc = waitpid (0, &status, 0 | WUNTRACED)) != pid) {
if (rc == -1) {
perror ("wait");
exit (1);
}
printf ("*** Uhh, dude, the wrong child (%d) just died.\n"
"*** Don't ask me, I can't figure it out either.\n",
rc);
exit (1);
}
if (WIFEXITED (status)) {
if (WEXITSTATUS (status)) {
printf ("cpp returned error code %d",
WEXITSTATUS (status));
exit (1);
}
} else {
printf ("cpp returned prematurely.");
exit (1);
}
}
yyin = fdopen (tempfd, "r+t");
} else {
yyin = fopen (filename, "rt");
}
#else
yyin = fopen (filename, "rt");
#endif
s_file = ReuseString (filename);
pr_source_line = 1;
clear_frame_macros ();
error = yyparse () || pr_error_count;
fclose (yyin);
#ifdef USE_CPP
if (!no_cpp) {
if (unlink (tempname)) {
perror ("unlink");
exit (1);
}
}
#endif
if (error)
return 1;
#else
char *src2;
sprintf (filename, "%s%c%s", sourcedir, PATH_SEPARATOR, com_token);
if (!options.quiet)
printf ("compiling %s\n", filename);
LoadFile (filename, (void *) &src2);
if (!PR_CompileFile (src2, filename))
return 1;
#endif
}
if (!PR_FinishCompilation ())
Error ("compilation errors");
// write progdefs.h
crc = PR_WriteProgdefs ("progdefs.h");
// write data file
WriteData (crc);
// write files.dat
WriteFiles ();
stop = Sys_DoubleTime ();
if (options.quiet < 2)
printf ("Compilation time: %.3f seconds.\n", (stop - start));
return 0;
}