quakeforge/tools/qfcc/source/qfcc.c
Bill Currie afc5b2827d Simplify def handling, use far data.
Since there is now a proper symbol table, defs are now just references to
memory locations and the symbol table takes care of duplicates.

Also, start using far data for ObjQC structures.

The qfo functions have been stubbed out until I figure out what to do with
object files in the new scheme.
2011-01-24 15:41:43 +09:00

1119 lines
25 KiB
C

/*
qfcc.c
QuakeForge Code Compiler (main program)
Copyright (C) 1996-1997 id Software, Inc.
Copyright (C) 2001 Jeff Teunissen <deek@quakeforge.net>
Copyright (C) 2001 Bill Currie <bill@taniwha.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
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static __attribute__ ((used)) const char rcsid[] = "$Id$";
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <QF/cbuf.h>
#include <QF/crc.h>
#include <QF/dstring.h>
#include <QF/hash.h>
#include <QF/qendian.h>
#include <QF/script.h>
#include <QF/sys.h>
#include <QF/va.h>
#include "qfcc.h"
#include "class.h"
#include "codespace.h"
#include "cpp.h"
#include "debug.h"
#include "def.h"
#include "defspace.h"
#include "emit.h"
#include "expr.h"
#include "function.h"
#include "idstuff.h"
#include "immediate.h"
#include "linker.h"
#include "method.h"
#include "obj_file.h"
#include "opcodes.h"
#include "options.h"
#include "reloc.h"
#include "strpool.h"
#include "struct.h"
#include "symtab.h"
#include "type.h"
options_t options;
const char *sourcedir;
const char *progs_src;
char debugfile[1024];
pr_info_t pr;
ddef_t *globals;
int numglobaldefs;
int num_localdefs;
const char *big_function = 0;
ddef_t *fields;
int numfielddefs;
hashtab_t *saved_strings;
static const char *
ss_get_key (void *s, void *unused)
{
return (const char *)s;
}
const char *
save_string (const char *str)
{
char *s;
if (!saved_strings)
saved_strings = Hash_NewTable (16381, ss_get_key, 0, 0);
s = Hash_Find (saved_strings, str);
if (s)
return s;
s = strdup (str);
Hash_Add (saved_strings, s);
return s;
}
const char *
make_string (char *token, char **end)
{
char s[2];
int c;
int i;
int mask;
int boldnext;
int quote;
static dstring_t *str;
if (!str)
str = dstring_newstr ();
dstring_clearstr (str);
s[1] = 0;
mask = 0x00;
boldnext = 0;
quote = *token++;
do {
c = *token++;
if (!c)
error (0, "EOF inside quote");
if (c == '\n')
error (0, "newline inside quote");
if (c == '\\') { // escape char
c = *token++;
if (!c)
error (0, "EOF inside quote");
switch (c) {
case '\\':
c = '\\';
break;
case 'n':
c = '\n';
break;
case '"':
c = '\"';
break;
case '\'':
c = '\'';
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
if (!options.qccx_escapes) {
for (i = c = 0; i < 3
&& *token >= '0'
&& *token <= '7'; i++, token++) {
c *= 8;
c += *token - '0';
}
if (!*token)
error (0, "EOF inside quote");
break;
}
case '8':
case '9':
c = 18 + c - '0';
break;
case 'x':
c = 0;
while (*token && isxdigit ((unsigned char)*token)) {
c *= 16;
if (*token <= '9')
c += *token - '0';
else if (*token <= 'F')
c += *token - 'A' + 10;
else
c += *token - 'a' + 10;
token++;
}
if (!*token)
error (0, "EOF inside quote");
break;
case 'a':
c = '\a';
break;
case 'b':
if (options.qccx_escapes)
mask ^= 0x80;
else
c = '\b';
break;
case 'e':
c = '\033';
break;
case 'f':
c = '\f';
break;
case 'r':
c = '\r';
break;
case 't':
c = '\t';
break;
case 'v':
c = '\v';
break;
case '^':
if (*token == '\"')
error (0, "Unexpected end of string after \\^");
boldnext = 1;
continue;
case '[':
c = 0x90; // gold [
break;
case ']':
c = 0x91; // gold ]
break;
case '.':
c = 28; // center dot
break;
case '<':
if (options.qccx_escapes)
c = 29; // brown left end
else
mask = 0x80;
continue;
case '-':
c = 30; // brown center bit
break;
case '>':
if (options.qccx_escapes)
c = 31; // broun right end
else
mask = 0x00;
continue;
case '(':
c = 128; // left slider end
break;
case '=':
c = 129; // slider center
break;
case ')':
c = 130; // right slider end
break;
case '{':
c = 0;
while (*token && *token != '}'
&& isdigit ((unsigned char)*token)) {
c *= 10;
c += *token++ - '0';
}
if (!*token)
error (0, "EOF inside quote");
if (*token != '}')
error (0, "non-digit inside \\{}");
else
token++;
if (c > 255)
warning (0, "\\{%d} > 255", c);
break;
default:
error (0, "Unknown escape char");
break;
}
} else if (c == quote) {
break;
}
if (boldnext)
c = c ^ 0x80;
boldnext = 0;
c = c ^ mask;
s[0] = c;
dstring_appendstr (str, s);
} while (1);
if (end)
*end = token;
return save_string (str->str);
}
#ifdef _WIN32
char *
fix_backslash (char *path)
{
char *s;
for (s = path; *s; s++)
if (*s == '\\')
*s = '/';
return path;
}
#endif
static void
InitData (void)
{
if (pr.code) {
codespace_delete (pr.code);
strpool_delete (pr.strings);
}
memset (&pr, 0, sizeof (pr));
pr.source_line = 1;
pr.error_count = 0;
pr.code = codespace_new ();
memset (codespace_newstatement (pr.code), 0, sizeof (dstatement_t));
pr.strings = strpool_new ();
pr.num_functions = 1;
pr.far_data = new_defspace ();
pr.near_data = new_defspace ();
pr.near_data->data = calloc (65536, sizeof (pr_type_t));
pr.near_data->max_size = 65536;
pr.near_data->grow = 0;
//FIXME pr.scope = new_scope (sc_global, pr.near_data, 0);
pr.entity_data = new_defspace ();
numglobaldefs = 1;
numfielddefs = 1;
}
static int
WriteData (int crc)
{
#if 0 //FIXME
def_t *def;
ddef_t *dd;
dprograms_t progs;
pr_debug_header_t debug;
QFile *h;
int i;
globals = calloc (pr.scope->num_defs + 1, sizeof (ddef_t));
fields = calloc (pr.scope->num_defs + 1, sizeof (ddef_t));
for (def = pr.scope->head; def; def = def->def_next) {
if (def->local || !def->name)
continue;
if (options.code.progsversion == PROG_ID_VERSION && *def->name == '.'
&& strcmp (def->name, ".imm") != 0
&& strcmp (def->name, ".debug_file") != 0)
continue;
if (def->type->type == ev_func) {
} else if (def->type->type == ev_field
&& strcmp (def->name, ".imm") != 0) {
dd = &fields[numfielddefs++];
def_to_ddef (def, dd, 1);
dd->ofs = G_INT (def->ofs);
}
dd = &globals[numglobaldefs++];
def_to_ddef (def, dd, 0);
if (!def->nosave
&& !def->constant
&& def->type->type != ev_func
&& def->type->type != ev_field && def->global)
dd->type |= DEF_SAVEGLOBAL;
}
pr.strings->size = (pr.strings->size + 3) & ~3;
if (options.verbosity >= 0) {
printf ("%6i strofs\n", pr.strings->size);
printf ("%6i statements\n", pr.code->size);
printf ("%6i functions\n", pr.num_functions);
printf ("%6i global defs\n", numglobaldefs);
if (!big_function)
big_function = "";
printf ("%6i locals size (%s)\n", num_localdefs, big_function);
printf ("%6i fielddefs\n", numfielddefs);
printf ("%6i globals\n", pr.near_data->size);
printf ("%6i entity fields\n", pr.entity_data->size);
}
if (!(h = Qopen (options.output_file, "wb")))
Sys_Error ("%s: %s\n", options.output_file, strerror(errno));
memset (&progs, 0, sizeof (progs));
Qwrite (h, &progs, sizeof (progs));
progs.ofs_strings = Qtell (h);
progs.numstrings = pr.strings->size;
Qwrite (h, pr.strings->strings, pr.strings->size);
progs.ofs_statements = Qtell (h);
progs.numstatements = pr.code->size;
for (i = 0; i < pr.code->size; i++) {
pr.code->code[i].op = LittleShort (pr.code->code[i].op);
pr.code->code[i].a = LittleShort (pr.code->code[i].a);
pr.code->code[i].b = LittleShort (pr.code->code[i].b);
pr.code->code[i].c = LittleShort (pr.code->code[i].c);
}
Qwrite (h, pr.code->code, pr.code->size * sizeof (dstatement_t));
{
dfunction_t *df;
progs.ofs_functions = Qtell (h);
progs.numfunctions = pr.num_functions;
for (i = 0, df = pr.functions + 1; i < pr.num_functions; i++, df++) {
df->first_statement = LittleLong (df->first_statement);
df->parm_start = LittleLong (df->parm_start);
df->s_name = LittleLong (df->s_name);
df->s_file = LittleLong (df->s_file);
df->numparms = LittleLong (df->numparms);
df->locals = LittleLong (df->locals);
}
Qwrite (h, pr.functions, pr.num_functions * sizeof (dfunction_t));
}
progs.ofs_globaldefs = Qtell (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);
}
Qwrite (h, globals, numglobaldefs * sizeof (ddef_t));
progs.ofs_fielddefs = Qtell (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);
}
Qwrite (h, fields, numfielddefs * sizeof (ddef_t));
progs.ofs_globals = Qtell (h);
progs.numglobals = pr.near_data->size;
for (i = 0; i < pr.near_data->size; i++)
G_INT (i) = LittleLong (G_INT (i));
Qwrite (h, pr.near_data->data, pr.near_data->size * 4);
if (options.verbosity >= -1)
printf ("%6i TOTAL SIZE\n", (int) Qtell (h));
progs.entityfields = pr.entity_data->size;
progs.version = options.code.progsversion;
progs.crc = crc;
// byte swap the header and write it out
for (i = 0; i < (int) sizeof (progs) / 4; i++)
((int *) &progs)[i] = LittleLong (((int *) &progs)[i]);
Qseek (h, 0, SEEK_SET);
Qwrite (h, &progs, sizeof (progs));
Qclose (h);
if (!options.code.debug) {
return 0;
}
if (!(h = Qopen (options.output_file, "rb")))
Sys_Error ("%s: %s\n", options.output_file, strerror(errno));
memset (&debug, 0, sizeof (debug));
debug.version = LittleLong (PROG_DEBUG_VERSION);
CRC_Init (&debug.crc);
while ((i = Qgetc (h)) != EOF)
CRC_ProcessByte (&debug.crc, i);
Qclose (h);
debug.crc = LittleShort (debug.crc);
debug.you_tell_me_and_we_will_both_know = 0;
if (!(h = Qopen (debugfile, "wb")))
Sys_Error ("%s: %s\n", options.output_file, strerror(errno));
Qwrite (h, &debug, sizeof (debug));
debug.auxfunctions = LittleLong (Qtell (h));
debug.num_auxfunctions = LittleLong (pr.num_auxfunctions);
for (i = 0; i < pr.num_auxfunctions; i++) {
pr.auxfunctions[i].function = LittleLong (pr.auxfunctions[i].function);
pr.auxfunctions[i].source_line = LittleLong (pr.auxfunctions[i].source_line);
pr.auxfunctions[i].line_info = LittleLong (pr.auxfunctions[i].line_info);
pr.auxfunctions[i].local_defs = LittleLong (pr.auxfunctions[i].local_defs);
pr.auxfunctions[i].num_locals = LittleLong (pr.auxfunctions[i].num_locals);
}
Qwrite (h, pr.auxfunctions,
pr.num_auxfunctions * sizeof (pr_auxfunction_t));
debug.linenos = LittleLong (Qtell (h));
debug.num_linenos = LittleLong (pr.num_linenos);
for (i = 0; i < pr.num_linenos; i++) {
pr.linenos[i].fa.addr = LittleLong (pr.linenos[i].fa.addr);
pr.linenos[i].line = LittleLong (pr.linenos[i].line);
}
Qwrite (h, pr.linenos, pr.num_linenos * sizeof (pr_lineno_t));
debug.locals = LittleLong (Qtell (h));
debug.num_locals = LittleLong (pr.num_locals);
for (i = 0; i < pr.num_locals; i++) {
pr.locals[i].type = LittleShort (pr.locals[i].type);
pr.locals[i].ofs = LittleShort (pr.locals[i].ofs);
pr.locals[i].s_name = LittleLong (pr.locals[i].s_name);
}
Qwrite (h, pr.locals, pr.num_locals * sizeof (ddef_t));
Qseek (h, 0, SEEK_SET);
Qwrite (h, &debug, sizeof (debug));
Qclose (h);
#endif
return 0;
}
static void
begin_compilation (void)
{
pr.func_tail = &pr.func_head;
pr.error_count = 0;
}
static void
setup_param_block (void)
{
static struct_def_t defs[] = {
{".zero", &type_zero},
{".return", &type_param},
{".param_0", &type_param},
{".param_1", &type_param},
{".param_2", &type_param},
{".param_3", &type_param},
{".param_4", &type_param},
{".param_5", &type_param},
{".param_6", &type_param},
{".param_7", &type_param},
};
size_t i;
symbol_t *sym;
pr.symtab = new_symtab (0, stab_global);
for (i = 0; i < sizeof (defs) / sizeof (defs[0]); i++) {
sym = new_symbol_type (defs[i].name, defs[i].type);
symtab_addsymbol (pr.symtab, sym);
}
}
static qboolean
finish_compilation (void)
{
//FIXME def_t *d;
qboolean errors = false;
function_t *f;
//FIXME def_t *def;
expr_t e;
//FIXME ex_label_t *l;
dfunction_t *df;
// check to make sure all functions prototyped have code
#if 0 //FIXME
if (options.warnings.undefined_function)
for (d = pr.scope->head; d; d = d->def_next) {
if (d->type->type == ev_func && d->global) {
// function args ok
if (d->used) {
if (!d->initialized) {
warning (0, "function %s was called but not defined\n",
d->name);
}
}
}
}
for (d = pr.scope->head; d; d = d->def_next) {
if (d->external && d->refs) {
if (strcmp (d->name, ".self") == 0) {
get_def (d->type, ".self", pr.scope, st_global);
} else if (strcmp (d->name, ".this") == 0) {
get_def (d->type, ".this", pr.scope, st_global);
} else {
errors = true;
error (0, "undefined global %s", d->name);
}
}
}
#endif
if (errors)
return !errors;
if (options.code.progsversion != PROG_ID_VERSION) {
e = *new_integer_expr (type_size (&type_param));
//FIXME ReuseConstant (&e, get_def (&type_integer, ".param_size", pr.scope,
//FIXME st_global));
}
if (options.code.debug) {
e = *new_string_expr (debugfile);
//FIXME ReuseConstant (&e, get_def (&type_string, ".debug_file", pr.scope,
//FIXME st_global));
}
//FIXME for (def = pr.scope->head; def; def = def->def_next)
//FIXME relocate_refs (def->refs, def->ofs);
pr.functions = calloc (pr.num_functions + 1, sizeof (dfunction_t));
for (df = pr.functions + 1, f = pr.func_head; f; df++, f = f->next) {
df->s_name = f->s_name;
df->s_file = f->s_file;
df->numparms = function_parms (f, df->parm_size);
//FIXME if (f->scope)
//FIXME df->locals = f->scope->space->size;
if (f->builtin) {
df->first_statement = -f->builtin;
continue;
}
if (!f->code)
continue;
df->first_statement = f->code;
if (options.code.local_merging) {
//FIXME if (f->scope->space->size > num_localdefs) {
//FIXME num_localdefs = f->scope->space->size;
//FIXME big_function = f->def->name;
//FIXME }
df->parm_start = pr.near_data->size;
} else {
//FIXME df->parm_start = defspace_new_loc (pr.near_data,
//FIXME f->scope->space->size);
//FIXME num_localdefs += f->scope->space->size;
}
//FIXME for (def = f->scope->head; def; def = def->def_next) {
//FIXME if (!def->local)
//FIXME continue;
//FIXME def->ofs += df->parm_start;
//FIXME relocate_refs (def->refs, def->ofs);
//FIXME }
}
if (options.code.local_merging) {
int ofs;
for (ofs = defspace_new_loc (pr.near_data, num_localdefs);
ofs < pr.near_data->size; ofs++)
pr.near_data->data[ofs].integer_var = 0;
}
//FIXME for (l = pr.labels; l; l = l->next)
//FIXME relocate_refs (l->refs, l->ofs);
return !errors;
}
const char *
strip_path (const char *filename)
{
const char *p = filename;
int i = options.strip_path;
while (i-- > 0) {
while (*p && *p != '/')
p++;
if (!*p)
break;
filename = ++p;
}
return filename;
}
static void
setup_sym_file (const char *output_file)
{
if (options.code.debug) {
char *s;
strcpy (debugfile, output_file);
s = debugfile + strlen (debugfile);
while (s-- > debugfile) {
if (*s == '.')
break;
if (*s == '/' || *s == '\\') {
s = debugfile + strlen (debugfile);
break;
}
}
strcpy (s, ".sym");
if (options.verbosity >= 1)
printf ("debug file: %s\n", debugfile);
}
}
static int
compile_to_obj (const char *file, const char *obj)
{
int err;
yyin = preprocess_file (file, 0);
if (!yyin)
return !options.preprocess_only;
InitData ();
setup_param_block ();
clear_frame_macros ();
clear_classes ();
clear_immediates ();
clear_selectors ();
chain_initial_types ();
begin_compilation ();
pr.source_file = ReuseString (strip_path (file));
err = yyparse () || pr.error_count;
fclose (yyin);
if (cpp_name && !options.save_temps) {
if (unlink (tempname->str)) {
perror ("unlink");
exit (1);
}
}
if (!err) {
qfo_t *qfo;
class_finish_module ();
qfo = qfo_from_progs (&pr);
err = qfo_write (qfo, obj);
qfo_delete (qfo);
}
return err;
}
static int
separate_compile (void)
{
const char **file;
const char **temp_files;
dstring_t *output_file = dstring_newstr ();
dstring_t *extension = dstring_newstr ();
char *f;
int err = 0;
int i;
if (options.compile && options.output_file && source_files[1]) {
fprintf (stderr,
"%s: cannot use -c and -o together with multiple files\n",
this_program);
return 1;
}
for (file = source_files, i = 0; *file; file++)
i++;
temp_files = calloc (i + 1, sizeof (const char*));
for (file = source_files, i = 0; *file; file++) {
dstring_clearstr (extension);
dstring_clearstr (output_file);
dstring_appendstr (output_file, *file);
f = output_file->str + strlen (output_file->str);
while (f >= output_file->str && *f != '.' && *f != '/')
f--;
if (f >= output_file->str && *f == '.') {
output_file->size -= strlen (f);
dstring_appendstr (extension, f);
*f = 0;
}
if (options.compile && options.output_file) {
dstring_clearstr (output_file);
dstring_appendstr (output_file, options.output_file);
} else {
dstring_appendstr (output_file, ".qfo");
}
if (strncmp (*file, "-l", 2)
&& (!strcmp (extension->str, ".r")
|| !strcmp (extension->str, ".qc")
|| !strcmp (extension->str, ".c"))) {
if (options.verbosity >= 2)
printf ("%s %s\n", *file, output_file->str);
temp_files[i++] = save_string (output_file->str);
err = compile_to_obj (*file, output_file->str) || err;
free ((char *)*file);
*file = strdup (output_file->str);
} else {
if (options.compile)
fprintf (stderr, "%s: %s: ignoring object file since linking "
"not done\n", this_program, *file);
}
}
if (!err && !options.compile) {
qfo_t *qfo;
linker_begin ();
for (file = source_files; *file; file++) {
if (strncmp (*file, "-l", 2)) {
if (strlen (*file) >= 2
&& strcmp (*file + strlen (*file) - 2, ".a") == 0) {
err = linker_add_lib (*file);
} else {
err = linker_add_object_file (*file);
}
} else {
err = linker_add_lib (*file);
}
if (err)
return err;
}
qfo = linker_finish ();
if (qfo) {
if (!options.output_file)
options.output_file = "progs.dat";
if (options.partial_link) {
qfo_write (qfo, options.output_file);
} else {
int crc = 0;
qfo_to_progs (qfo, &pr);
setup_sym_file (options.output_file);
finish_compilation ();
// write progdefs.h
if (options.progdefs_h)
crc = WriteProgdefs ("progdefs.h");
WriteData (crc);
}
} else {
err = 1;
}
if (!options.save_temps)
for (file = temp_files; *file; file++)
unlink (*file);
}
return err;
}
static const char *
load_file (const char *fname)
{
QFile *file;
char *src;
FILE *tmpfile;
tmpfile = preprocess_file (fname, "i1");
if (!tmpfile)
return 0;
file = Qfopen (tmpfile, "rb");
if (!file) {
perror (fname);
return 0;
}
src = malloc (Qfilesize (file) + 1);
src[Qfilesize (file)] = 0;
Qread (file, src, Qfilesize (file));
Qclose (file);
if (cpp_name && (!options.save_temps)) {
if (unlink (tempname->str)) {
perror ("unlink");
exit (1);
}
}
return src;
}
static void
parse_cpp_line (script_t *script, dstring_t *filename)
{
if (Script_TokenAvailable (script, 0)) {
Script_GetToken (script, 0);
// subtract 1 for \n
script->line = atoi (script->token->str) - 1;
if (Script_TokenAvailable (script, 0)) {
Script_GetToken (script, 0);
dstring_copystr (filename, script->token->str);
script->file = filename->str;
}
}
// There shouldn't be anything else, but just to be safe...
while (Script_TokenAvailable (script, 0))
Script_GetToken (script, 0);
}
static int
compile_file (const char *filename)
{
int err;
yyin = preprocess_file (filename, 0);
if (!yyin)
return !options.preprocess_only;
pr.source_file = ReuseString (strip_path (filename));
pr.source_line = 1;
clear_frame_macros ();
err = yyparse () || pr.error_count;
fclose (yyin);
if (cpp_name && (!options.save_temps)) {
if (unlink (tempname->str)) {
perror ("unlink");
exit (1);
}
}
return err;
}
static int
progs_src_compile (void)
{
dstring_t *filename = dstring_newstr ();
dstring_t *qc_filename = dstring_newstr ();
dstring_t *single_name = dstring_newstr ();
const char *src;
int crc = 0;
script_t *script;
FILE *single = 0;
if (options.verbosity >= 1 && strcmp (sourcedir, "")) {
printf ("Source directory: %s\n", sourcedir);
}
if (options.verbosity >= 1 && strcmp (progs_src, "progs.src")) {
printf ("progs.src: %s\n", progs_src);
}
if (*sourcedir)
dsprintf (filename, "%s/%s", sourcedir, progs_src);
else
dsprintf (filename, "%s", progs_src);
if (options.single_cpp) {
intermediate_file (single_name, filename->str, "i2", 1);
if (!options.save_temps) {
#ifdef _WIN32
mktemp (single_name->str);
#else
int tempfd = mkstemp (single_name->str);
single = fdopen (tempfd, "wb");
#endif
}
if (!single)
single = fopen (single_name->str, "wb");
if (!single) {
perror (single_name->str);
exit (1);
}
}
src = load_file (filename->str);
if (!src) {
fprintf (stderr, "couldn't open %s\n", filename->str);
unlink (single_name->str);
return 1;
}
script = Script_New ();
Script_Start (script, filename->str, src);
while (1) {
if (!Script_GetToken (script, 1)) {
fprintf (stderr,
"No destination filename. qfcc --help for info.\n");
return 1;
}
if (strcmp (script->token->str, "#") == 0) {
parse_cpp_line (script, filename);
continue;
}
break;
}
if (!options.output_file)
options.output_file = save_string (script->token->str);
if (options.verbosity >= 1) {
printf ("output file: %s\n", options.output_file);
}
setup_sym_file (options.output_file);
InitData ();
chain_initial_types ();
begin_compilation ();
if (!options.compile)
setup_param_block ();
// compile all the files
while (Script_GetToken (script, 1)) {
if (strcmp (script->token->str, "#") == 0) {
parse_cpp_line (script, filename);
continue;
}
while (1) {
if (*sourcedir)
dsprintf (qc_filename, "%s%c%s", sourcedir, PATH_SEPARATOR,
script->token->str);
else
dsprintf (qc_filename, "%s", script->token->str);
if (options.verbosity >= 2)
printf ("%s:%d: compiling %s\n", script->file, script->line,
qc_filename->str);
if (single) {
fprintf (single, "$frame_reset\n");
fprintf (single, "#line %d \"%s\"\n", script->line,
script->file);
fprintf (single, "#include \"%s\"\n", qc_filename->str);
} else {
if (compile_file (qc_filename->str))
return 1;
}
if (!Script_TokenAvailable (script, 0))
break;
Script_GetToken (script, 0);
}
}
if (single) {
int err;
fclose (single);
err = compile_file (single_name->str);
if (!options.save_temps) {
if (unlink (single_name->str)) {
perror ("unlink");
exit (1);
}
}
if (err)
return 1;
}
if (options.compile) {
qfo_t *qfo = qfo_from_progs (&pr);
qfo_write (qfo, options.output_file);
qfo_delete (qfo);
} else {
class_finish_module ();
if (!finish_compilation ()) {
fprintf (stderr, "compilation errors\n");
return 1;
}
// write progdefs.h
if (options.progdefs_h)
crc = WriteProgdefs ("progdefs.h");
// write data file
if (WriteData (crc))
return 1;
// write files.dat
if (options.files_dat)
if (WriteFiles (sourcedir))
return 1;
}
return 0;
}
/*
main
The nerve center of our little operation
*/
int
main (int argc, char **argv)
{
double start, stop;
int res;
start = Sys_DoubleTime ();
#ifdef _WIN32
if (putenv ("POSIXLY_INCORRECT_GETOPT=1")) {
fprintf (stderr, "Warning: putenv failed\n");
}
#endif
this_program = NORMALIZE (argv[0]);
DecodeArgs (argc, argv);
tempname = dstring_new ();
parse_cpp_name ();
opcode_init ();
InitData ();
init_types ();
clear_immediates ();
if (source_files) {
res = separate_compile ();
} else {
res = progs_src_compile ();
}
if (res)
return res;
stop = Sys_DoubleTime ();
if (options.verbosity >= 0)
printf ("Compilation time: %0.3g seconds.\n", (stop - start));
return 0;
}