/* qfcc.c QuakeForge Code Compiler (main program) Copyright (C) 1996-1997 id Software, Inc. Copyright (C) 2001 Jeff Teunissen Copyright (C) 2001 Bill Currie 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 #endif #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #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; const char *big_function = 0; 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->constant && 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.verbosity > 1) { printf ("%6i strofs\n", strofs); printf ("%6i statements\n", numstatements); printf ("%6i functions\n", numfunctions); printf ("%6i globaldefs\n", numglobaldefs); printf ("%6i locals size (%s)\n", num_localdefs, big_function); 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.verbosity > 1) printf ("%6i TOTAL SIZE\n", (int) ftell (h)); progs.entityfields = pr.size_fields; progs.version = options.code.progsversion; 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.code.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->constant && 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) { int i; 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.warnings.undefined_function) for (d = pr.def_head.def_next; d; d = d->def_next) { if (d->type->type == ev_func && !d->scope) { // function args ok if (d->used) { if (!d->initialized) { warning (0, "function %s was called but not defined\n", d->name); } } } } if (errors) return !errors; if (options.code.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->locals > num_localdefs) { num_localdefs = f->def->locals; big_function = f->def->name; } 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.verbosity) 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 : "", 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"); } } /* "Options: \n" " -s, --source look for progs.src in directory \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" */ qboolean ParseParmOptions (const char *options, const char *name) { const char *start; char *where, *terminator; // Options are comma-separated, so bail if the string has a comma if ((!(*name)) || strchr (name, ',')) return false; for (start = options;; start = terminator) { if (!(where = strstr (start, name))) break; terminator = where + strlen (name); if ((where == start) || (*(where - 1) == ',')) if ((*terminator == ',') || (*terminator == '\0')) return true; } return false; } //============================================================================ /* 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.code.progsversion = PROG_VERSION; options.warnings.uninited_variable = 1; options.verbosity = 2; if (CheckParm ("-h") || CheckParm ("--help")) { printf ("%s - QuakeForge Code Compiler\n", argv[0]); printf ("Usage: %s [options]\n", argv[0]); printf ( "Options: \n" " -s, --source look for progs.src in directory \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" " --no-undefined-function-warning don't 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 options.verbosity = 0; } if (CheckParm ("--quiet")) { options.verbosity = 1; } if (CheckParm ("--cow")) { options.code.cow = 1; options.warnings.cow = 1; } if (CheckParm ("--id")) { options.code.progsversion = PROG_ID_VERSION; } if (CheckParm ("--debug")) { options.code.debug = 1; } if (CheckParm ("--no-cpp")) { no_cpp = 1; } // FIXME eww, really must go to getopt if (CheckParm ("--warn=error")) { options.warnings.promote = 1; } if (CheckParm ("--warn=nocow")) { options.warnings.cow = 0; } options.warnings.undefined_function = 1; if (CheckParm ("--no-undefined-function-warning")) { options.warnings.undefined_function = 0; } 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 = Parse (src))) Error ("No destination filename. qfcc --help for info.\n"); strcpy (destfile, com_token); if (options.verbosity) { printf ("outputfile: %s\n", destfile); } if (options.code.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.verbosity) printf ("debug file: %s\n", debugfile); } PR_BeginCompilation (); // compile all the files while ((src = Parse (src))) { #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.verbosity) 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; } 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.verbosity > 1) printf ("Compilation time: %0.3g seconds.\n", (stop - start)); return 0; } /* static void usage (int status) { printf ("%s - the QuakeForge Code Compiler\n", PROGRAM); printf ("Usage: %s [options]\n", argv[0]); printf ( "Options:\n" " -s, --source DIR look for progs.src in DIR instead of \".\"\n" " -q, --quiet NUM Inhibit usual output\n" " -g, --debug Output symbol information\n" " --code= Set code generation options\n" " --warn= Set warning options\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n\n" "For help on options for --code and --warn, see the qfcc(1) man page\n" ); exit (status); } */