Merging master and adopting its main.c

This commit is contained in:
Wolfgang Bumiller 2012-07-28 21:14:03 +02:00
commit e3dc56fc92
7 changed files with 470 additions and 84 deletions

6
code.c
View file

@ -69,7 +69,7 @@ void code_init() {
int i = 0;
/* omit creation of null code */
if (opts_omit_nullcode)
if (OPTS_FLAG(OMIT_NULL_BYTES))
return;
/*
@ -158,7 +158,7 @@ bool code_write(const char *filename) {
size_t it = 2;
/* see proposal.txt */
if (opts_omit_nullcode) {}
if (OPTS_FLAG(OMIT_NULL_BYTES)) {}
code_header.statements.offset = sizeof(prog_header);
code_header.statements.length = code_statements_elements;
code_header.defs.offset = code_header.statements.offset + (sizeof(prog_section_statement) * code_statements_elements);
@ -175,7 +175,7 @@ bool code_write(const char *filename) {
code_header.crc16 = 0; /* TODO: */
code_header.entfield = 0; /* TODO: */
if (opts_darkplaces_stringtablebug) {
if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
util_debug("GEN", "Patching stringtable for -fdarkplaces-stringtablebug\n");
/* >= + P */

49
doc/gmqcc.1 Normal file
View file

@ -0,0 +1,49 @@
.\" Process with groff -man -Tascii file.3
.TH GMQCC 1 2012-07-12 "" "gmqcc Manual"
.SH NAME
gmqcc \- A Quake C compiler which tries to reduce suckiness.
.SH SYNOPSIS
.B gmqcc
[\fIOPTIONS\fR] [\fIfiles...\fR]
.SH DESCRIPTION
Traditionally, a QC compiler reads the file \fIprogs.src\fR which
in its first line contains the output filename, and the rest is a
list of QC source files that are to be compiled in order.
\fBgmqcc\fR optionally takes options to specify the output and
input files on the commandline, and also accepts assembly files.
.SH OPTIONS
\fBgmqcc\fR mostly tries to mimick gcc's commandline handling, though
there are also traditional long-options available.
.TP
.B "-h, --help"
Show a usage message and exit.
.TP
.BI "-o, --output=" filename
Specify the output filename. Defaults to progs.dat. This will overwrite
the output file listed in a \fIprogs.src\fR file in case such a file is used.
.TP
.BI "-O" n
Specify the optimization level, similar to gcc.
.TP
.BI "-a" filename
Append the specified files to the list of files to assemble using the QC-Assembler.
.TP
.BI "-s" filename
Append the specified file which is to be interpreted as a \fIprogs.src\fR file.
.TP
.BI "-std=" standard
Use the specified standard for parsing QC code. The following standards are available:
.IR gmqcc , qcc , fteqcc
.TP
.BI -W warning "\fR, " "" -Wno- warning
Enable or disable a warning.
.TP
.B -Wall
Enable all warnings. Overrides preceding -W parameters.
.TP
.B -fdarkplaces-string-table-bug
Patch the output file to work around a string-table bug in certain darkplaces versions.
.TP
.B -fomit-nullbytes
Changes the output format to be more efficient. Requires a patched engine. See the
proposal for a better file structure in the gmqcc source tree.

7
flags.def Normal file
View file

@ -0,0 +1,7 @@
#ifndef GMQCC_DEFINE_FLAG
#define GMQCC_DEFINE_FLAG(x)
#endif
GMQCC_DEFINE_FLAG(OVERLAP_LOCALS)
GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG)
GMQCC_DEFINE_FLAG(OMIT_NULL_BYTES)

88
gmqcc.h
View file

@ -198,6 +198,9 @@ void util_debug (const char *, const char *, ...);
int util_getline (char **, size_t *, FILE *);
void util_endianswap (void *, int, int);
size_t util_strtocmd (const char *, char *, size_t);
size_t util_strtononcmd (const char *, char *, size_t);
uint32_t util_crc32(const char *, int, register const short);
#ifdef NOTRACK
@ -621,20 +624,6 @@ void asm_init (const char *, FILE **);
void asm_close(FILE *);
void asm_parse(FILE *);
/*===================================================================*/
/*============================= main.c ==============================*/
/*===================================================================*/
enum {
COMPILER_QCC, /* circa QuakeC */
COMPILER_FTEQCC, /* fteqcc QuakeC */
COMPILER_QCCX, /* qccx QuakeC */
COMPILER_GMQCC /* this QuakeC */
};
extern bool opts_debug;
extern bool opts_memchk;
extern bool opts_darkplaces_stringtablebug;
extern bool opts_omit_nullcode;
extern int opts_compiler;
/*===================================================================*/
/*============================= ast.c ===============================*/
/*===================================================================*/
#define MEM_VECTOR_PROTO(Towner, Tmem, mem) \
@ -928,4 +917,75 @@ prog_section_def* prog_getdef (qc_program *prog, qcint off);
qcany* prog_getedict (qc_program *prog, qcint e);
qcint prog_tempstring(qc_program *prog, const char *_str);
/*===================================================================*/
/*======================= main.c commandline ========================*/
/*===================================================================*/
#if 0
/* Helpers to allow for a whole lot of flags. Otherwise we'd limit
* to 32 or 64 -f options...
*/
typedef struct {
size_t idx; /* index into an array of 32 bit words */
uint8_t bit; /* index _into_ the 32 bit word, thus just uint8 */
} longbit;
#define LONGBIT(bit) { ((bit)/32), ((bit)%32) }
#else
typedef uint32_t longbit;
#define LONGBIT(bit) (bit)
#endif
/* Used to store the list of flags with names */
typedef struct {
const char *name;
longbit bit;
} opts_flag_def;
/*===================================================================*/
/* list of -f flags, like -fdarkplaces-string-table-bug */
enum {
# define GMQCC_DEFINE_FLAG(X) X,
# include "flags.def"
# undef GMQCC_DEFINE_FLAG
COUNT_FLAGS
};
static const opts_flag_def opts_flag_list[] = {
# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(X) },
# include "flags.def"
# undef GMQCC_DEFINE_FLAG
{ NULL, LONGBIT(0) }
};
enum {
# define GMQCC_DEFINE_FLAG(X) X,
# include "warns.def"
# undef GMQCC_DEFINE_FLAG
COUNT_WARNINGS
};
static const opts_flag_def opts_warn_list[] = {
# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(X) },
# include "warns.def"
# undef GMQCC_DEFINE_FLAG
{ NULL, LONGBIT(0) }
};
/* other options: */
enum {
COMPILER_QCC, /* circa QuakeC */
COMPILER_FTEQCC, /* fteqcc QuakeC */
COMPILER_QCCX, /* qccx QuakeC */
COMPILER_GMQCC /* this QuakeC */
};
extern uint32_t opts_O; /* -Ox */
extern const char *opts_output; /* -o file */
extern int opts_standard;
extern bool opts_debug;
extern bool opts_memchk;
/*===================================================================*/
#define OPTS_FLAG(i) (!! (opts_flags[(i)/32] & (1<< ((i)%32))))
extern uint32_t opts_flags[1 + (COUNT_FLAGS / 32)];
#define OPTS_WARN(i) (!! (opts_warn[(i)/32] & (1<< ((i)%32))))
extern uint32_t opts_warn[1 + (COUNT_WARNINGS / 32)];
#endif

365
main.c
View file

@ -22,83 +22,328 @@
*/
#include "gmqcc.h"
static const char *output = "progs.dat";
static const char *input = NULL;
uint32_t opts_flags[1 + (COUNT_FLAGS / 32)];
uint32_t opts_warn [1 + (COUNT_WARNINGS / 32)];
#define OptReq(opt, body) \
case opt: \
if (argv[0][2]) argarg = argv[0]+2; \
else { \
if (argc < 2) { \
printf("option -%c requires an argument\n", opt); \
exit(1); \
} \
argarg = argv[1]; \
--argc; \
++argv; \
} \
do { body } while (0); \
break;
uint32_t opts_O = 1;
const char *opts_output = "progs.dat";
int opts_standard = COMPILER_GMQCC;
bool opts_debug = false;
bool opts_memchk = false;
#define LongReq(opt, body) \
if (!strcmp(argv[0], opt)) { \
if (argc < 2) { \
printf("option " opt " requires an argument\n"); \
exit(1); \
} \
argarg = argv[1]; \
--argc; \
++argv; \
do { body } while (0); \
break; \
} else if (!strncmp(argv[0], opt "=", sizeof(opt "="))) \
{ \
argarg = argv[0] + sizeof(opt "="); \
do { body } while (0); \
break; \
typedef struct { char *filename; int type; } argitem;
VECTOR_MAKE(argitem, items);
#define TYPE_QC 0
#define TYPE_ASM 1
#define TYPE_SRC 2
static const char *app_name;
static int usage() {
printf("usage: %s [options] [files...]", app_name);
printf("options:\n"
" -h, --help show this help message\n"
" -debug turns on compiler debug messages\n"
" -memchk turns on compiler memory leak check\n");
printf(" -o, --output=file output file, defaults to progs.dat\n"
" -a filename add an asm file to be assembled\n"
" -s filename add a progs.src file to be used\n");
printf(" -f<flag> enable a flag\n"
" -fno-<flag> disable a flag\n"
" -std standard select one of the following standards\n"
" -std=qcc original QuakeC\n"
" -std=fteqcc fteqcc QuakeC\n"
" -std=gmqcc this compiler (default)\n");
printf(" -W<warning> enable a warning\n"
" -Wno-<warning> disable a warning\n"
" -Wall enable all warnings\n");
printf("\n");
printf("flags:\n"
" -fdarkplaces-string-table-bug\n"
" patch the string table to work with some bugged darkplaces versions\n"
" -fomit-nullbytes\n"
" omits certain null-bytes for a smaller output - requires a patched engine\n"
);
return -1;
}
static bool options_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
size_t i;
for (i = 0; i < listsize; ++i) {
if (!strcmp(name, list[i].name)) {
longbit lb = list[i].bit;
#if 0
if (on)
flags[lb.idx] |= (1<<(lb.bit));
else
flags[lb.idx] &= ~(1<<(lb.bit));
#else
if (on)
flags[0] |= (1<<lb);
else
flags[0] &= ~(1<<(lb));
#endif
return true;
}
}
return false;
}
static bool options_setflag(const char *name, bool on) {
return options_setflag_all(name, on, opts_flags, opts_flag_list, COUNT_FLAGS);
}
static bool options_setwarn(const char *name, bool on) {
return options_setflag_all(name, on, opts_warn, opts_warn_list, COUNT_WARNINGS);
}
static bool options_witharg(int *argc_, char ***argv_, char **out) {
int argc = *argc_;
char **argv = *argv_;
if (argv[0][2]) {
*out = argv[0]+2;
return true;
}
/* eat up the next */
if (argc < 2) /* no parameter was provided */
return false;
*out = argv[1];
--*argc_;
++*argv_;
return true;
}
static bool options_long_witharg_all(const char *optname, int *argc_, char ***argv_, char **out, int ds, bool split) {
int argc = *argc_;
char **argv = *argv_;
size_t len = strlen(optname);
if (strncmp(argv[0]+ds, optname, len))
return false;
/* it's --optname, check how the parameter is supplied */
if (argv[0][ds+len] == '=') {
/* using --opt=param */
*out = argv[0]+ds+len+1;
return true;
}
bool parser_compile(const char *filename, const char *datfile);
int main(int argc, char **argv) {
const char *argarg;
char opt;
if (!split || argc < ds) /* no parameter was provided, or only single-arg form accepted */
return false;
util_debug("COM", "starting ...\n");
/* using --opt param */
*out = argv[1];
--*argc_;
++*argv_;
return true;
}
static bool options_long_witharg(const char *optname, int *argc_, char ***argv_, char **out) {
return options_long_witharg_all(optname, argc_, argv_, out, 2, true);
}
static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, char **out) {
return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
}
static bool options_parse(int argc, char **argv) {
bool argend = false;
size_t itr;
char buffer[1024];
while (!argend && argc > 1) {
char *argarg;
argitem item;
++argv;
--argc;
--argc;
++argv;
while (argc > 0) {
if (argv[0][0] == '-') {
opt = argv[0][1];
switch (opt)
{
OptReq('o', output = argarg; );
case '-':
LongReq("--output", output = argarg; );
default:
printf("Unrecognized option: %s\n", argv[0]);
/* All gcc-type long options */
if (options_long_gcc("std", &argc, &argv, &argarg)) {
if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default"))
opts_standard = COMPILER_GMQCC;
else if (!strcmp(argarg, "qcc"))
opts_standard = COMPILER_QCC;
else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc"))
opts_standard = COMPILER_FTEQCC;
else if (!strcmp(argarg, "qccx"))
opts_standard = COMPILER_QCCX;
else {
printf("Unknown standard: %s\n", argarg);
return false;
}
continue;
}
if (!strcmp(argv[0]+1, "debug")) {
opts_debug = true;
continue;
}
if (!strcmp(argv[0]+1, "memchk")) {
opts_memchk = true;
continue;
}
switch (argv[0][1]) {
/* -h, show usage but exit with 0 */
case 'h':
usage();
exit(0);
break;
/* handle all -fflags */
case 'f':
util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
if (!strcmp(argv[0]+2, "HELP")) {
printf("Possible flags:\n");
for (itr = 0; itr < COUNT_FLAGS; ++itr) {
util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
printf(" -f%s\n", buffer);
}
exit(0);
}
else if (!strncmp(argv[0]+2, "NO-", 3)) {
if (!options_setflag(argv[0]+5, false)) {
printf("unknown flag: %s\n", argv[0]+2);
return false;
}
}
else if (!options_setflag(argv[0]+2, true)) {
printf("unknown flag: %s\n", argv[0]+2);
return false;
}
break;
case 'W':
util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
if (!strcmp(argv[0]+2, "HELP")) {
printf("Possible warnings:\n");
for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
printf(" -W%s\n", buffer);
}
exit(0);
}
else if (!strcmp(argv[0]+2, "all")) {
for (itr = 0; itr < sizeof(opts_warn)/sizeof(opts_warn[0]); ++itr)
opts_warn[itr] = 0xFFFFFFFFL;
break;
}
if (!strncmp(argv[0]+2, "no-", 3)) {
if (!options_setwarn(argv[0]+5, false)) {
printf("unknown warning: %s\n", argv[0]+2);
return false;
}
}
else if (!options_setwarn(argv[0]+2, true)) {
printf("unknown warning: %s\n", argv[0]+2);
return false;
}
break;
case 'O':
if (!options_witharg(&argc, &argv, &argarg)) {
printf("option -O requires a numerical argument\n");
return false;
}
opts_O = atoi(argarg);
break;
case 'o':
if (!options_witharg(&argc, &argv, &argarg)) {
printf("option -o requires an argument: the output file name\n");
return false;
}
opts_output = argarg;
break;
case 'a':
case 's':
item.type = argv[0][1] == 'a' ? TYPE_ASM : TYPE_SRC;
if (!options_witharg(&argc, &argv, &argarg)) {
printf("option -a requires a filename %s\n",
(argv[0][1] == 'a' ? "containing QC-asm" : "containing a progs.src formatted list"));
return false;
}
item.filename = argarg;
items_add(item);
break;
case '-':
if (!argv[0][2]) {
/* anything following -- is considered a non-option argument */
argend = true;
break;
}
/* All long options without arguments */
else if (!strcmp(argv[0]+2, "help")) {
usage();
exit(0);
}
else {
/* All long options with arguments */
if (options_long_witharg("output", &argc, &argv, &argarg))
opts_output = argarg;
else
{
printf("Unknown parameter: %s\n", argv[0]);
return false;
}
}
break;
default:
printf("Unknown parameter: %s\n", argv[0]);
return false;
}
}
else
{
if (input) {
printf("Onlyh 1 input file allowed\n");
exit(1);
}
input = argv[0];
/* it's a QC filename */
argitem item;
item.filename = argv[0];
item.type = TYPE_QC;
items_add(item);
}
--argc;
++argv;
}
return true;
}
int main(int argc, char **argv) {
size_t itr;
app_name = argv[0];
if (!options_parse(argc, argv)) {
return usage();
}
if (!input) {
printf("must specify an input file\n");
for (itr = 0; itr < COUNT_FLAGS; ++itr) {
printf("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
}
for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
printf("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
}
printf("output = %s\n", opts_output);
printf("optimization level = %i\n", (int)opts_O);
printf("standard = %i\n", opts_standard);
if (items_elements) {
printf("Mode: manual\n");
printf("There are %lu items to compile:\n", (unsigned long)items_elements);
for (itr = 0; itr < items_elements; ++itr) {
printf(" item: %s (%s)\n",
items_data[itr].filename,
( (items_data[itr].type == TYPE_QC ? "qc" :
(items_data[itr].type == TYPE_ASM ? "asm" :
(items_data[itr].type == TYPE_SRC ? "progs.src" :
("unknown"))))));
}
} else {
printf("Mode: progs.src\n");
}
if (!parser_compile(input, output)) {
printf("There were compile errors\n");
}
util_debug("COM", "starting ...\n");
/* stuff */
util_debug("COM", "cleaning ...\n");

34
util.c
View file

@ -371,10 +371,30 @@ int util_getline(char **lineptr, size_t *n, FILE *stream) {
return (ret = pos - *lineptr);
}
/* TODO: opts.c? when it gets large enugh */
/* global options */
bool opts_debug = false;
bool opts_memchk = false;
bool opts_darkplaces_stringtablebug = false;
bool opts_omit_nullcode = false;
int opts_compiler = COMPILER_GMQCC;
size_t util_strtocmd(const char *in, char *out, size_t outsz) {
size_t sz = 1;
for (; *in && sz < outsz; ++in, ++out, ++sz) {
if (*in == '-')
*out = '_';
else if (isalpha(*in) && !isupper(*in))
*out = *in + 'A' - 'a';
else
*out = *in;
}
*out = 0;
return sz-1;
}
size_t util_strtononcmd(const char *in, char *out, size_t outsz) {
size_t sz = 1;
for (; *in && sz < outsz; ++in, ++out, ++sz) {
if (*in == '_')
*out = '-';
else if (isalpha(*in) && isupper(*in))
*out = *in + 'a' - 'A';
else
*out = *in;
}
*out = 0;
return sz-1;
}

5
warns.def Normal file
View file

@ -0,0 +1,5 @@
#ifndef GMQCC_DEFINE_FLAG
#define GMQCC_DEFINE_FLAG(x)
#endif
GMQCC_DEFINE_FLAG(UNUSED_VARIABLE)