/* * 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); }
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; }
/* * 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; }