Пример #1
0
int platform_vasprintf(char **dat, const char *fmt, va_list args) {
    int     ret;
    int     len;
    char   *tmp = NULL;
    char    buf[128];
    va_list cpy;

    va_copy(cpy, args);
    len = vsnprintf(buf, sizeof(buf), fmt, cpy);
    va_end (cpy);

    if (len < (int)sizeof(buf)) {
        *dat = util_strdup(buf);
        return len;
    }

    tmp = (char*)mem_a(len + 1);
    if ((ret = vsnprintf(tmp, len + 1, fmt, args)) != len) {
        mem_d(tmp);
        *dat = NULL;
        return -1;
    }

    *dat = tmp;
    return len;
}
Пример #2
0
char *correct_str(correction_t *corr, correct_trie_t* table, const char *ident) {
    char **e1      = NULL;
    char **e2      = NULL;
    char  *e1ident = NULL;
    char  *e2ident = NULL;
    size_t e1rows  = 0;
    size_t e2rows  = 0;
    size_t *bits   = NULL;

    /* needs to be allocated for free later */
    if (correct_find(table, ident))
        return correct_pool_claim(ident);

    if ((e1rows = correct_size(ident))) {
        if (vec_size(corr->edits) > 0)
            e1 = corr->edits[0];
        else {
            e1 = correct_edit(ident, &bits);
            vec_push(corr->edits, e1);
            vec_push(corr->lens,  bits);
        }

        if ((e1ident = correct_maximum(table, e1, e1rows)))
            return correct_pool_claim(e1ident);
    }

    e2 = correct_known(corr, table, e1, e1rows, &e2rows);
    if (e2rows && ((e2ident = correct_maximum(table, e2, e2rows))))
        return correct_pool_claim(e2ident);


    return util_strdup(ident);
}
Пример #3
0
/*
 * Actual loading subsystem, this finds the ini or cfg file, and properly
 * loads it and executes it to set compiler options.
 */
void opts_ini_init(const char *file) {
    /*
     * Possible matches are:
     *  gmqcc.ini
     *  gmqcc.cfg
     */
    char       *error = NULL;
    char       *parse_file = NULL;
    size_t     line;
    fs_file_t  *ini;

    if (!file) {
        /* try ini */
        if (!(ini = fs_file_open((file = "gmqcc.ini"), "r")))
            /* try cfg */
            if (!(ini = fs_file_open((file = "gmqcc.cfg"), "r")))
                return;
    } else if (!(ini = fs_file_open(file, "r")))
        return;

    con_out("found ini file `%s`\n", file);

    parse_file = util_strdup(file);
    if ((line = opts_ini_parse(ini, &opts_ini_load, &error, &parse_file)) != 0) {
        /* there was a parse error with the ini file */
        con_printmsg(LVL_ERROR, parse_file, line, 0 /*TODO: column for ini error*/, "error", error);
        vec_free(error);
    }
    mem_d(parse_file);

    fs_file_close(ini);
}
Пример #4
0
void store_msg(char *msg)
{
	struct settings *s = sett_get();
	char *t = util_strdup(msg);
	struct irc_message m = util_irc_message_parse(t);

	if(!strcmp(m.tokarr[m.cmd], "PRIVMSG")){
		char path[1024], buf[1024];

		store_format_parse(path, sizeof path, s->sfmt, m.tokarr[m.middle]);
		util_mkdir_r(path);

		util_irc_prefix_construct(buf, sizeof buf, m.prefix);
		strcat(buf, ": ");
		strcat(buf, m.tokarr[m.trailing]);
		store_store(path, buf);
	}
	else if(s->sjoin && (!strcmp(m.tokarr[m.cmd], "JOIN") || !strcmp(m.tokarr[m.cmd], "PART"))){
		char path[1024], buf[1024];

		store_format_parse(path, sizeof path, s->sfmt, m.tokarr[m.middle]);
		util_mkdir_r(path);

		util_irc_prefix_construct(buf, sizeof buf, m.prefix);
		strcat(buf, " ");
		strcat(buf, (m.tokarr[m.cmd][1] == 'J') ? "joined." : "left.");
		store_store(path, buf);
	}

	if(state_is_away())
		state_buffer(m.tokarr[m.middle], msg);

	free(t);
}
Пример #5
0
/* Creates a new structure in the given schema with the given name, returning
   a pointer to it. Notably, this function DOES NOT perform error-checking, so
   if you do not perform error-checking yourself, you may end up with
   duplicate structure names. */
ParsedStruct *new_struct(ParsedSchema *schema, char *name)
{
  const size_t arr_size = 8;
  ParsedStruct *ret, *array;
  if (schema->num_structs == schema->structs_alloc) {
    array = realloc(schema->structs, 
                    (size_t)(schema->structs_alloc *= 2) 
                     * sizeof *array);
    if (!array) return NULL;
    schema->structs = array;
  } 
  ret = schema->structs + schema->num_structs;
  ret->schema_index = schema->num_structs;
  ret->name = util_strdup(name);
  ret->num_scalars = ret->num_children = 0;
  ret->offset = 0;
  ret->meta.max_size = 0;
  ret->scalars_alloc = ret->children_alloc = (int)arr_size;
  ret->scalars = malloc(arr_size * sizeof *ret->scalars);
  ret->children = malloc(arr_size * sizeof *ret->children);
  if (!ret->scalars || !ret->children) {
    free(ret->scalars);
    free(ret->children);
    return NULL;
  }
  schema->num_structs++;
  return ret;
}
Пример #6
0
/*
 * Chops a substring from an existing string by creating a
 * copy of it and null terminating it at the required position.
 */
char *util_strchp(const char *s, const char *e) {
    const char *c = NULL;
    if (!s || !e)
        return NULL;

    c = s;
    while (c != e)
        c++;

    return util_strdup(s);
}
Пример #7
0
/* Adds a text field to the given structure. Fields may name-collide. */
int add_text_field(ParsedStruct *strct, char *name, int nullable)
{
  int i = strct->num_children;
  if (strct->num_children == strct->children_alloc)
    if (!realloc_struct_children(strct)) return 0;
  strct->children[i].name = util_strdup(name);
  if (!strct->children[i].name) return 0;
  strct->children[i].nullable = nullable;
  strct->children[i].tag = CHILD_TEXT;
  strct->children[i].meta.embeddable = 0;
  strct->num_children++;
  return 1;
}
Пример #8
0
/* Adds an enumerated field to the given structure. As above, performs no
   error-checking, so fields might name-collide. */
int add_enum_field(ParsedStruct *strct, char *name, ParsedEnum *field)
{
  int i = strct->num_scalars;
  if (strct->num_scalars == strct->scalars_alloc) 
    if (!realloc_struct_scalars(strct)) return 0;
  strct->scalars[i].name = util_strdup(name);
  if (!strct->scalars[i].name) return 0;
  strct->scalars[i].offset = strct->offset;
  strct->scalars[i].type.tag = SCALAR_ENUM;
  strct->scalars[i].type.enum_type = field;
  strct->offset += 1;
  strct->num_scalars++;
  return 1;
}
Пример #9
0
/* Adds an enumerated value to the given enumeration. Performs no error-
   checking, so the names of values may collide. */
int add_enumerated_value(ParsedEnum *enm, char *name)
{
  char **array;
  int i = enm->num_values;
  if (enm->num_values == enm->values_alloc) {
    array = realloc(enm->values,
                    (size_t)(enm->values_alloc *= 2) * sizeof *array);
    if (!array) return 0;
    enm->values = array;
  }
  enm->values[i] = util_strdup(name);
  if (!enm->values[i]) return 0;
  enm->num_values++;
  return 1;
}
Пример #10
0
/* Adds a list of structures field to the given structure. Fields may
   name-collide. */
int add_list_of_structs_field(ParsedStruct *strct, char *name, int nullable,
                              ParsedStruct *field)
{
  int i = strct->num_children;
  if (strct->num_children == strct->children_alloc)
    if (!realloc_struct_children(strct)) return 0;
  strct->children[i].name = util_strdup(name);
  if (!strct->children[i].name) return 0;
  strct->children[i].nullable = nullable;
  strct->children[i].tag = CHILD_STRUCT_LIST;
  strct->children[i].type.struct_list = field;
  strct->children[i].meta.embeddable = 0;
  strct->num_children++;
  return 1;
}
Пример #11
0
/* Adds a scalar field (NOT an enumeration) to the given structure. Fields
   may name-collide. */
int add_scalar_field(ParsedStruct *strct, char *name, ScalarTag field)
{
  int i = strct->num_scalars;
  if (field == SCALAR_ENUM) return 0; /* This function is not for adding
                                         enumerated fields */
  if (strct->num_scalars == strct->scalars_alloc) 
    if (!realloc_struct_scalars(strct)) return 0;
  strct->scalars[i].name = util_strdup(name);
  if (!strct->scalars[i].name) return 0;
  strct->scalars[i].offset = strct->offset;
  strct->scalars[i].type.tag = field;
  strct->scalars[i].type.enum_type = NULL;
  strct->offset += field_length(field);
  strct->num_scalars++;
  return 1;
}
Пример #12
0
static int add_list_of_scalars_or_enums_field(ParsedStruct *strct, char *name, 
                                              int nullable, ScalarTag tag, 
                                              ParsedEnum *enm)
{
  int i = strct->num_children;
  if (strct->num_children == strct->children_alloc)
    if (!realloc_struct_children(strct)) return 0;
  strct->children[i].name = util_strdup(name);
  if (!strct->children[i].name) return 0;
  strct->children[i].nullable = nullable;
  strct->children[i].tag = CHILD_SCALAR_LIST;
  strct->children[i].type.scalar_list.tag = tag;
  strct->children[i].type.scalar_list.enum_type = enm;
  strct->num_children++;
  return 1;
}
Пример #13
0
/* As above, this function does not perform error-checking over the name
   of the enumeration. */
ParsedEnum *new_enum(ParsedSchema *schema, char *name)
{
  const size_t arr_size = 8;
  ParsedEnum *ret, *array;
  if (schema->num_enums == schema->enums_alloc) {
    array = realloc(schema->enums,
                    (size_t)(schema->enums_alloc *= 2) * sizeof *array);
    if (!array) return NULL;
    schema->enums = array;
  }
  ret = schema->enums + schema->num_enums;
  ret->name = util_strdup(name);
  ret->num_values = 0;
  ret->values_alloc = (int)arr_size;
  ret->values = malloc(arr_size * sizeof *ret->values);
  if (!ret->values) return NULL;
  schema->num_enums++;
  return ret;
}
Пример #14
0
static bool task_template_parse(const char *file, task_template_t *tmpl, fs_file_t *fp, size_t *pad) {
    char  *data = NULL;
    char  *back = NULL;
    size_t size = 0;
    size_t line = 1;

    if (!tmpl)
        return false;

    /* top down parsing */
    while (fs_file_getline(&back, &size, fp) != FS_FILE_EOF) {
        /* skip whitespace */
        data = back;
        if (*data && (*data == ' ' || *data == '\t'))
            data++;

        switch (*data) {
            /*
             * Handle comments inside task tmpl files.  We're strict
             * about the language for fun :-)
             */
            case '/':
                if (data[1] != '/') {
                    con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
                        "invalid character `/`, perhaps you meant `//` ?");

                    mem_d(back);
                    return false;
                }
            case '#':
                break;

            /*
             * Empty newlines are acceptable as well, so we handle that here
             * despite being just odd since there should't be that many
             * empty lines to begin with.
             */
            case '\r':
            case '\n':
                break;


            /*
             * Now begin the actual "tag" stuff.  This works as you expect
             * it to.
             */
            case 'D':
            case 'T':
            case 'C':
            case 'E':
            case 'I':
            case 'F':
                if (data[1] != ':') {
                    con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
                        "expected `:` after `%c`",
                        *data
                    );
                    goto failure;
                }
                if (!task_template_generate(tmpl, *data, file, line, &data[3], pad)) {
                    con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl compile error",
                        "failed to generate for given task\n"
                    );
                    goto failure;
                }
                break;

            /*
             * Match requires it's own system since we allow multiple M's
             * for multi-line matching.
             */
            case 'M':
            {
                char *value = &data[3];
                if (data[1] != ':') {
                    con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
                        "expected `:` after `%c`",
                        *data
                    );
                    goto failure;
                }

                /*
                 * Value will contain a newline character at the end, we need to strip
                 * this otherwise kaboom, seriously, kaboom :P
                 */
                if (strrchr(value, '\n'))
                    *strrchr(value, '\n')='\0';
                else /* cppcheck: possible null pointer dereference */
                    exit(EXIT_FAILURE);

                vec_push(tmpl->comparematch, util_strdup(value));

                break;
            }

            default:
                con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
                    "invalid tag `%c`", *data
                );
                goto failure;
            /* no break required */
        }

        /* update line and free old sata */
        line++;
        mem_d(back);
        back = NULL;
    }
    if (back)
        mem_d(back);
    return true;

failure:
    mem_d (back);
    return false;
}
Пример #15
0
qc_program_t* prog_load(const char *filename, bool skipversion)
{
    prog_header_t header;
    qc_program_t *prog;
    FILE *file = fopen(filename, "rb");

    /* we need all those in order to support INSTR_STATE: */
    bool            has_self      = false,
                    has_time      = false,
                    has_think     = false,
                    has_nextthink = false,
                    has_frame     = false;

    if (!file)
        return nullptr;

    if (fread(&header, sizeof(header), 1, file) != 1) {
        loaderror("failed to read header from '%s'", filename);
        fclose(file);
        return nullptr;
    }

    util_swap_header(header);

    if (!skipversion && header.version != 6) {
        loaderror("header says this is a version %i progs, we need version 6\n", header.version);
        fclose(file);
        return nullptr;
    }

    prog = (qc_program_t*)mem_a(sizeof(qc_program_t));
    if (!prog) {
        fclose(file);
        fprintf(stderr, "failed to allocate program data\n");
        return nullptr;
    }
    memset(prog, 0, sizeof(*prog));

    prog->entityfields = header.entfield;
    prog->crc16 = header.crc16;

    prog->filename = util_strdup(filename);
    if (!prog->filename) {
        loaderror("failed to store program name");
        goto error;
    }

#define read_data(hdrvar, progvar, reserved)                           \
    if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) {            \
        loaderror("seek failed");                                      \
        goto error;                                                    \
    }                                                                  \
    prog->progvar.resize(header.hdrvar.length + reserved);             \
    if (fread(                                                         \
            &prog->progvar[0],                                         \
            sizeof(prog->progvar[0]),                                  \
            header.hdrvar.length,                                      \
            file                                                       \
        )!= header.hdrvar.length                                       \
    ) {                                                                \
        loaderror("read failed");                                      \
        goto error;                                                    \
    }
#define read_data1(x)    read_data(x, x, 0)
#define read_data2(x, y) read_data(x, x, y)

    read_data (statements, code, 0);
    read_data1(defs);
    read_data1(fields);
    read_data1(functions);
    read_data1(strings);
    read_data2(globals, 2); /* reserve more in case a RETURN using with the global at "the end" exists */

    util_swap_statements(prog->code);
    util_swap_defs_fields(prog->defs);
    util_swap_defs_fields(prog->fields);
    util_swap_functions(prog->functions);
    util_swap_globals(prog->globals);

    fclose(file);

    /* profile counters */
    memset(vec_add(prog->profile, prog->code.size()), 0, sizeof(prog->profile[0]) * prog->code.size());

    /* Add tempstring area */
    prog->tempstring_start = prog->strings.size();
    prog->tempstring_at = prog->strings.size();

    prog->strings.resize(prog->strings.size() + 16*1024, '\0');

    /* spawn the world entity */
    vec_push(prog->entitypool, true);
    memset(vec_add(prog->entitydata, prog->entityfields), 0, prog->entityfields * sizeof(prog->entitydata[0]));
    prog->entities = 1;

    /* cache some globals and fields from names */
    for (auto &it : prog->defs) {
        const char *name = prog_getstring(prog, it.name);
        if (!strcmp(name, "self")) {
            prog->cached_globals.self = it.offset;
            has_self = true;
        }
        else if (!strcmp(name, "time")) {
            prog->cached_globals.time = it.offset;
            has_time = true;
        }
    }
    for (auto &it : prog->fields) {
        const char *name = prog_getstring(prog, it.name);
        if (!strcmp(name, "think")) {
            prog->cached_fields.think = it.offset;
            has_think = true;
        }
        else if (!strcmp(name, "nextthink")) {
            prog->cached_fields.nextthink = it.offset;
            has_nextthink = true;
        }
        else if (!strcmp(name, "frame")) {
            prog->cached_fields.frame  = it.offset;
            has_frame = true;
        }
    }
    if (has_self && has_time && has_think && has_nextthink && has_frame)
        prog->supports_state = true;

    return prog;

error:
    if (prog->filename)
        mem_d(prog->filename);
    vec_free(prog->entitydata);
    vec_free(prog->entitypool);
    mem_d(prog);

    fclose(file);
    return nullptr;
}
Пример #16
0
static char *opts_ini_load(const char *section, const char *name, const char *value, char **parse_file) {
    char *error = NULL;
    bool  found = false;

    /*
     * undef all of these because they may still be defined like in my
     * case they where.
     */
    #undef GMQCC_TYPE_FLAGS
    #undef GMQCC_TYPE_OPTIMIZATIONS
    #undef GMQCC_TYPE_WARNS

    /* deal with includes */
    if (!strcmp(section, "includes")) {
        static const char *include_error_beg = "failed to open file `";
        static const char *include_error_end = "' for inclusion";
        fs_file_t *file = fs_file_open(value, "r");
        found = true;
        if (!file) {
            vec_append(error, strlen(include_error_beg), include_error_beg);
            vec_append(error, strlen(value), value);
            vec_append(error, strlen(include_error_end), include_error_end);
        } else {
            if (opts_ini_parse(file, &opts_ini_load, &error, parse_file) != 0)
                found = false;
            /* Change the file name */
            mem_d(*parse_file);
            *parse_file = util_strdup(value);
            fs_file_close(file);
        }
    }

    /* flags */
    #define GMQCC_TYPE_FLAGS
    #define GMQCC_DEFINE_FLAG(X)                                       \
    if (!strcmp(section, "flags") && !strcmp(name, #X)) {              \
        opts_set(opts.flags, X, opts_ini_bool(value));                 \
        found = true;                                                  \
    }
    #include "opts.def"

    /* warnings */
    #define GMQCC_TYPE_WARNS
    #define GMQCC_DEFINE_FLAG(X)                                       \
    if (!strcmp(section, "warnings") && !strcmp(name, #X)) {           \
        opts_set(opts.warn, WARN_##X, opts_ini_bool(value));           \
        found = true;                                                  \
    }
    #include "opts.def"

    /* Werror-individuals */
    #define GMQCC_TYPE_WARNS
    #define GMQCC_DEFINE_FLAG(X)                                       \
    if (!strcmp(section, "errors") && !strcmp(name, #X)) {             \
        opts_set(opts.werror, WARN_##X, opts_ini_bool(value));         \
        found = true;                                                  \
    }
    #include "opts.def"

    /* optimizations */
    #define GMQCC_TYPE_OPTIMIZATIONS
    #define GMQCC_DEFINE_FLAG(X,Y)                                     \
    if (!strcmp(section, "optimizations") && !strcmp(name, #X)) {      \
        opts_set(opts.optimization, OPTIM_##X, opts_ini_bool(value));  \
        found = true;                                                  \
    }
    #include "opts.def"

    /* nothing was found ever! */
    if (!found) {
        if (strcmp(section, "includes") &&
            strcmp(section, "flags")    &&
            strcmp(section, "warnings") &&
            strcmp(section, "optimizations"))
        {
            static const char *invalid_section = "invalid_section `";
            vec_append(error, strlen(invalid_section), invalid_section);
            vec_append(error, strlen(section), section);
            vec_push(error, '`');
        } else if (strcmp(section, "includes")) {
            static const char *invalid_variable = "invalid_variable `";
            static const char *in_section = "` in section: `";
            vec_append(error, strlen(invalid_variable), invalid_variable);
            vec_append(error, strlen(name), name);
            vec_append(error, strlen(in_section), in_section);
            vec_append(error, strlen(section), section);
            vec_push(error, '`');
        } else {
            static const char *expected_something = "expected something";
            vec_append(error, strlen(expected_something), expected_something);
        }
    }
    vec_push(error, '\0');
    return error;
}
Пример #17
0
/*
 * This executes the QCVM task for a specificly compiled progs.dat
 * using the template passed into it for call-flags and user defined
 * messages IF the procedure type is -execute, otherwise it matches
 * the preprocessor output.
 */
static bool task_trymatch(size_t i, char ***line) {
    bool             success = true;
    bool             process = true;
    int              retval  = EXIT_SUCCESS;
    fs_file_t       *execute;
    char             buffer[4096];
    task_template_t *tmpl = task_tasks[i].tmpl;

    memset  (buffer,0,sizeof(buffer));

    if (!strcmp(tmpl->proceduretype, "-execute")) {
        /*
         * Drop the execution flags for the QCVM if none where
         * actually specified.
         */
        if (!strcmp(tmpl->executeflags, "$null")) {
            util_snprintf(buffer,  sizeof(buffer), "%s %s",
                task_bins[TASK_EXECUTE],
                tmpl->tempfilename
            );
        } else {
            util_snprintf(buffer,  sizeof(buffer), "%s %s %s",
                task_bins[TASK_EXECUTE],
                tmpl->executeflags,
                tmpl->tempfilename
            );
        }

        execute = (fs_file_t*)popen(buffer, "r");
        if (!execute)
            return false;
    } else if (!strcmp(tmpl->proceduretype, "-pp")) {
        /*
         * we're preprocessing, which means we need to read int
         * the produced file and do some really weird shit.
         */
        if (!(execute = fs_file_open(tmpl->tempfilename, "r")))
            return false;

        process = false;
    } else {
        /*
         * we're testing diagnostic output, which means it will be
         * in runhandles[2] (stderr) since that is where the compiler
         * puts it's errors.
         */
        if (!(execute = fs_file_open(task_tasks[i].stderrlogfile, "r")))
            return false;

        process = false;
    }

    /*
     * Now lets read the lines and compare them to the matches we expect
     * and handle accordingly.
     */
    {
        char  *data    = NULL;
        size_t size    = 0;
        size_t compare = 0;

        while (fs_file_getline(&data, &size, execute) != FS_FILE_EOF) {
            if (!strcmp(data, "No main function found\n")) {
                con_err("test failure: `%s` (No main function found) [%s]\n",
                    tmpl->description,
                    tmpl->rulesfile
                );
                if (!process)
                    fs_file_close(execute);
                else
                    pclose((FILE*)execute);
                return false;
            }

            /*
             * Trim newlines from data since they will just break our
             * ability to properly validate matches.
             */
            if  (strrchr(data, '\n'))
                *strrchr(data, '\n') = '\0';

            /*
             * We remove the file/directory and stuff from the error
             * match messages when testing diagnostics.
             */
            if(!strcmp(tmpl->proceduretype, "-diagnostic")) {
                if (strstr(data, "there have been errors, bailing out"))
                    continue; /* ignore it */
                if (strstr(data, ": error: ")) {
                    char *claim = util_strdup(data + (strstr(data, ": error: ") - data) + 9);
                    mem_d(data);
                    data = claim;
                }
            }

            /*
             * We need to ignore null lines for when -pp is used (preprocessor), since
             * the preprocessor is likely to create empty newlines in certain macro
             * instantations, otherwise it's in the wrong nature to ignore empty newlines.
             */
            if (!strcmp(tmpl->proceduretype, "-pp") && !*data)
                continue;

            if (vec_size(tmpl->comparematch) > compare) {
                if (strcmp(data, tmpl->comparematch[compare++])) {
                    success = false;
                }
            } else {
                success = false;
            }

            /*
             * Copy to output vector for diagnostics if execution match
             * fails.
             */
            vec_push(*line, data);

            /* reset */
            data = NULL;
            size = 0;
        }

        if (compare != vec_size(tmpl->comparematch))
            success = false;

        mem_d(data);
        data = NULL;
    }

    if (process)
        retval = pclose((FILE*)execute);
    else
        fs_file_close(execute);

    return success && retval == EXIT_SUCCESS;
}
Пример #18
0
/*
 * Read a directory and searches for all template files in it
 * which is later used to run all tests.
 */
static bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
    bool             success = true;
    fs_dir_t        *dir;
    fs_dirent_t     *files;
    struct stat      directory;
    char             buffer[4096];
    size_t           found = 0;
    char           **directories = NULL;
    char            *claim = util_strdup(curdir);
    size_t           i;

    vec_push(directories, claim);
    dir = fs_dir_open(claim);

    /*
     * Generate a list of subdirectories since we'll be checking them too
     * for tmpl files.
     */
    while ((files = fs_dir_read(dir))) {
        util_asprintf(&claim, "%s/%s", curdir, files->d_name);
        if (stat(claim, &directory) == -1) {
            fs_dir_close(dir);
            mem_d(claim);
            return false;
        }

        if (S_ISDIR(directory.st_mode) && files->d_name[0] != '.') {
            vec_push(directories, claim);
        } else {
            mem_d(claim);
            claim = NULL;
        }
    }
    fs_dir_close(dir);

    /*
     * Now do all the work, by touching all the directories inside
     * test as well and compile the task templates into data we can
     * use to run the tests.
     */
    for (i = 0; i < vec_size(directories); i++) {
        dir = fs_dir_open(directories[i]);

        while ((files = fs_dir_read(dir))) {
            util_snprintf(buffer, sizeof(buffer), "%s/%s", directories[i], files->d_name);
            if (stat(buffer, &directory) == -1) {
                con_err("internal error: stat failed, aborting\n");
                abort();
            }

            if (S_ISDIR(directory.st_mode))
                continue;

            /*
             * We made it here, which concludes the file/directory is not
             * actually a directory, so it must be a file :)
             */
            if (strcmp(files->d_name + strlen(files->d_name) - 5, ".tmpl") == 0) {
                task_template_t *tmpl = task_template_compile(files->d_name, directories[i], pad);
                char             buf[4096]; /* one page should be enough */
                const char      *qcflags = NULL;
                task_t           task;

                found ++;
                if (!tmpl) {
                    con_err("error compiling task template: %s\n", files->d_name);
                    success = false;
                    continue;
                }
                /*
                 * Generate a temportary file name for the output binary
                 * so we don't trample over an existing one.
                 */
                tmpl->tempfilename = NULL;
                util_asprintf(&tmpl->tempfilename, "%s/TMPDAT.%s.dat", directories[i], files->d_name);

                /*
                 * Additional QCFLAGS enviroment variable may be used
                 * to test compile flags for all tests.  This needs to be
                 * BEFORE other flags (so that the .tmpl can override them)
                 */
                qcflags = platform_getenv("QCFLAGS");

                /*
                 * Generate the command required to open a pipe to a process
                 * which will be refered to with a handle in the task for
                 * reading the data from the pipe.
                 */
                if (strcmp(tmpl->proceduretype, "-pp")) {
                    if (qcflags) {
                        if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
                            util_snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s",
                                task_bins[TASK_COMPILE],
                                directories[i],
                                tmpl->sourcefile,
                                qcflags,
                                tmpl->compileflags,
                                tmpl->tempfilename
                            );
                        } else {
                            util_snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s %s -o %s",
                                task_bins[TASK_COMPILE],
                                curdir,
                                defs,
                                directories[i],
                                tmpl->sourcefile,
                                qcflags,
                                tmpl->compileflags,
                                tmpl->tempfilename
                            );
                        }
                    } else {
                        if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
                            util_snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s",
                                task_bins[TASK_COMPILE],
                                directories[i],
                                tmpl->sourcefile,
                                tmpl->compileflags,
                                tmpl->tempfilename
                            );
                        } else {
                            util_snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s -o %s",
                                task_bins[TASK_COMPILE],
                                curdir,
                                defs,
                                directories[i],
                                tmpl->sourcefile,
                                tmpl->compileflags,
                                tmpl->tempfilename
                            );
                        }
                    }
                } else {
                    /* Preprocessing (qcflags mean shit all here we don't allow them) */
                    if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
                        util_snprintf(buf, sizeof(buf), "%s -E %s/%s -o %s",
                            task_bins[TASK_COMPILE],
                            directories[i],
                            tmpl->sourcefile,
                            tmpl->tempfilename
                        );
                    } else {
                        util_snprintf(buf, sizeof(buf), "%s -E %s/%s %s/%s -o %s",
                            task_bins[TASK_COMPILE],
                            curdir,
                            defs,
                            directories[i],
                            tmpl->sourcefile,
                            tmpl->tempfilename
                        );
                    }
                }

                /*
                 * The task template was compiled, now lets create a task from
                 * the template data which has now been propagated.
                 */
                task.tmpl = tmpl;
                if (!(task.runhandles = task_popen(buf, "r"))) {
                    con_err("error opening pipe to process for test: %s\n", tmpl->description);
                    success = false;
                    continue;
                }

                /*
                 * Open up some file desciptors for logging the stdout/stderr
                 * to our own.
                 */
                util_snprintf(buf,  sizeof(buf), "%s.stdout", tmpl->tempfilename);
                task.stdoutlogfile = util_strdup(buf);
                if (!(task.stdoutlog     = fs_file_open(buf, "w"))) {
                    con_err("error opening %s for stdout\n", buf);
                    continue;
                }

                util_snprintf(buf,  sizeof(buf), "%s.stderr", tmpl->tempfilename);
                task.stderrlogfile = util_strdup(buf);
                if (!(task.stderrlog = fs_file_open(buf, "w"))) {
                    con_err("error opening %s for stderr\n", buf);
                    continue;
                }

                vec_push(task_tasks, task);
            }
        }

        fs_dir_close(dir);
        mem_d(directories[i]); /* free claimed memory */
    }
    vec_free(directories);

    return success;
}
Пример #19
0
static GMQCC_INLINE char *correct_pool_claim(const char *data) {
    char *claim = util_strdup(data);
    return claim;
}
Пример #20
0
/*
 * This is very much like a compiler code generator :-).  This generates
 * a value from some data observed from the compiler.
 */
static bool task_template_generate(task_template_t *tmpl, char tag, const char *file, size_t line, char *value, size_t *pad) {
    size_t desclen = 0;
    size_t filelen = 0;
    char **destval = NULL;

    if (!tmpl)
        return false;

    switch(tag) {
        case 'D': destval = &tmpl->description;    break;
        case 'T': destval = &tmpl->proceduretype;  break;
        case 'C': destval = &tmpl->compileflags;   break;
        case 'E': destval = &tmpl->executeflags;   break;
        case 'I': destval = &tmpl->sourcefile;     break;
        case 'F': destval = &tmpl->testflags;      break;
        default:
            con_printmsg(LVL_ERROR, __FILE__, __LINE__, 0, "internal error",
                "invalid tag `%c:` during code generation\n",
                tag
            );
            return false;
    }

    /*
     * Ensure if for the given tag, there already exists a
     * assigned value.
     */
    if (*destval) {
        con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "compile error",
            "tag `%c:` already assigned value: %s\n",
            tag, *destval
        );
        return false;
    }

    /*
     * Strip any whitespace that might exist in the value for assignments
     * like "D:      foo"
     */
    if (value && *value && (*value == ' ' || *value == '\t'))
        value++;
    else if (!value)
        exit(EXIT_FAILURE);

    /*
     * Value will contain a newline character at the end, we need to strip
     * this otherwise kaboom, seriously, kaboom :P
     */
    if (strchr(value, '\n'))
        *strrchr(value, '\n')='\0';

    /*
     * Now allocate and set the actual value for the specific tag. Which
     * was properly selected and can be accessed with *destval.
     */
    *destval = util_strdup(value);


    if (*destval == tmpl->description) {
        /*
         * Create some padding for the description to align the
         * printing of the rules file.
         */
        if ((desclen = strlen(tmpl->description)) > pad[0])
            pad[0] = desclen;
    }

    if ((filelen = strlen(file)) > pad[2])
        pad[2] = filelen;

    return true;
}
Пример #21
0
static task_template_t *task_template_compile(const char *file, const char *dir, size_t *pad) {
    /* a page should be enough */
    char             fullfile[4096];
    size_t           filepadd = 0;
    fs_file_t       *tempfile = NULL;
    task_template_t *tmpl     = NULL;

    util_snprintf(fullfile,    sizeof(fullfile), "%s/%s", dir, file);

    tempfile = fs_file_open(fullfile, "r");
    tmpl     = (task_template_t*)mem_a(sizeof(task_template_t));
    task_template_nullify(tmpl);

    /*
     * Create some padding for the printing to align the
     * printing of the rules file to the console.
     */
    if ((filepadd = strlen(fullfile)) > pad[1])
        pad[1] = filepadd;

    tmpl->rulesfile = util_strdup(fullfile);

    /*
     * Esnure the file even exists for the task, this is pretty useless
     * to even do.
     */
    if (!tempfile) {
        con_err("template file: %s does not exist or invalid permissions\n",
            file
        );
        goto failure;
    }

    if (!task_template_parse(file, tmpl, tempfile, pad)) {
        con_err("template parse error: error during parsing\n");
        goto failure;
    }

    /*
     * Regardless procedure type, the following tags must exist:
     *  D
     *  T
     *  C
     *  I
     */
    if (!tmpl->description) {
        con_err("template compile error: %s missing `D:` tag\n", file);
        goto failure;
    }
    if (!tmpl->proceduretype) {
        con_err("template compile error: %s missing `T:` tag\n", file);
        goto failure;
    }
    if (!tmpl->compileflags) {
        con_err("template compile error: %s missing `C:` tag\n", file);
        goto failure;
    }
    if (!tmpl->sourcefile) {
        con_err("template compile error: %s missing `I:` tag\n", file);
        goto failure;
    }

    /*
     * Now lets compile the template, compilation is really just
     * the process of validating the input.
     */
    if (!strcmp(tmpl->proceduretype, "-compile")) {
        if (tmpl->executeflags)
            con_err("template compile warning: %s erroneous tag `E:` when only compiling\n", file);
        if (tmpl->comparematch)
            con_err("template compile warning: %s erroneous tag `M:` when only compiling\n", file);
        goto success;
    } else if (!strcmp(tmpl->proceduretype, "-execute")) {
        if (!tmpl->executeflags) {
            /* default to $null */
            tmpl->executeflags = util_strdup("$null");
        }
        if (!tmpl->comparematch) {
            con_err("template compile error: %s missing `M:` tag (use `$null` for exclude)\n", file);
            goto failure;
        }
    } else if (!strcmp(tmpl->proceduretype, "-fail")) {
        if (tmpl->executeflags)
            con_err("template compile warning: %s erroneous tag `E:` when only failing\n", file);
        if (tmpl->comparematch)
            con_err("template compile warning: %s erroneous tag `M:` when only failing\n", file);
    } else if (!strcmp(tmpl->proceduretype, "-diagnostic")) {
        if (tmpl->executeflags)
            con_err("template compile warning: %s erroneous tag `E:` when only diagnostic\n", file);
        if (!tmpl->comparematch) {
            con_err("template compile error: %s missing `M:` tag (use `$null` for exclude)\n", file);
            goto failure;
        }
    } else if (!strcmp(tmpl->proceduretype, "-pp")) {
        if (tmpl->executeflags)
            con_err("template compile warning: %s erroneous tag `E:` when only preprocessing\n", file);
        if (!tmpl->comparematch) {
            con_err("template compile error: %s missing `M:` tag (use `$null` for exclude)\n", file);
            goto failure;
        }
    } else {
        con_err("template compile error: %s invalid procedure type: %s\n", file, tmpl->proceduretype);
        goto failure;
    }

success:
    fs_file_close(tempfile);
    return tmpl;

failure:
    /*
     * The file might not exist and we jump here when that doesn't happen
     * so the check to see if it's not null here is required.
     */
    if (tempfile)
        fs_file_close(tempfile);
    mem_d (tmpl);

    return NULL;
}
Пример #22
0
qc_program* prog_load(const char *filename)
{
    qc_program *prog;
    prog_header header;
    FILE *file;

    file = util_fopen(filename, "rb");
    if (!file)
        return NULL;

    if (fread(&header, sizeof(header), 1, file) != 1) {
        loaderror("failed to read header from '%s'", filename);
        fclose(file);
        return NULL;
    }

    if (header.version != 6) {
        loaderror("header says this is a version %i progs, we need version 6\n", header.version);
        fclose(file);
        return NULL;
    }

    prog = (qc_program*)mem_a(sizeof(qc_program));
    if (!prog) {
        fclose(file);
        printf("failed to allocate program data\n");
        return NULL;
    }
    memset(prog, 0, sizeof(*prog));

    prog->entityfields = header.entfield;
    prog->crc16 = header.crc16;

    prog->filename = util_strdup(filename);
    if (!prog->filename) {
        loaderror("failed to store program name");
        goto error;
    }

#define read_data(hdrvar, progvar, reserved)                           \
    if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) {            \
        loaderror("seek failed");                                      \
        goto error;                                                    \
    }                                                                  \
    if (fread(vec_add(prog->progvar, header.hdrvar.length + reserved), \
              sizeof(*prog->progvar),                                  \
              header.hdrvar.length, file)                              \
        != header.hdrvar.length)                                       \
    {                                                                  \
        loaderror("read failed");                                      \
        goto error;                                                    \
    }
#define read_data1(x)    read_data(x, x, 0)
#define read_data2(x, y) read_data(x, x, y)

    read_data (statements, code, 0);
    read_data1(defs);
    read_data1(fields);
    read_data1(functions);
    read_data1(strings);
    read_data2(globals, 2); /* reserve more in case a RETURN using with the global at "the end" exists */

    fclose(file);

    /* profile counters */
    memset(vec_add(prog->profile, vec_size(prog->code)), 0, sizeof(prog->profile[0]) * vec_size(prog->code));

    /* Add tempstring area */
    prog->tempstring_start = vec_size(prog->strings);
    prog->tempstring_at    = vec_size(prog->strings);
    memset(vec_add(prog->strings, 16*1024), 0, 16*1024);

    /* spawn the world entity */
    vec_push(prog->entitypool, true);
    memset(vec_add(prog->entitydata, prog->entityfields), 0, prog->entityfields * sizeof(prog->entitydata[0]));
    prog->entities = 1;

    return prog;

error:
    if (prog->filename)
        mem_d(prog->filename);
    vec_free(prog->code);
    vec_free(prog->defs);
    vec_free(prog->fields);
    vec_free(prog->functions);
    vec_free(prog->strings);
    vec_free(prog->globals);
    vec_free(prog->entitydata);
    vec_free(prog->entitypool);
    mem_d(prog);
    return NULL;
}