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; }
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); }
/* * 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); }
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); }
/* 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; }
/* * 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); }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
/* 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; }
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; }
/* 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; }
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; }
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; }
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; }
/* * 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; }
/* * 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; }
static GMQCC_INLINE char *correct_pool_claim(const char *data) { char *claim = util_strdup(data); return claim; }
/* * 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; }
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; }
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; }