From 237412362b6c9476ff98c7eaf63d63ebea136be2 Mon Sep 17 00:00:00 2001 From: pommicket Date: Wed, 2 Nov 2022 21:00:33 -0400 Subject: multiline config value strings, fix search_cwd (debug feature) --- config.c | 130 +++++++++++++++++++++++++++++++++++++++++++-------------------- gl.c | 25 +++++++----- main.c | 52 ++++++++++++++----------- ted.c | 15 +++++--- ted.h | 3 +- text.c | 2 +- 6 files changed, 149 insertions(+), 78 deletions(-) diff --git a/config.c b/config.c index fdbd05f..ee147de 100644 --- a/config.c +++ b/config.c @@ -394,7 +394,7 @@ static void config_init_options(void) { void config_read(Ted *ted, ConfigPart **parts, char const *filename) { FILE *fp = fopen(filename, "rb"); if (!fp) { - ted_seterr(ted, "Couldn't open config file %s.", filename); + ted_seterr(ted, "Couldn't open config file %s: %s.", filename, strerror(errno)); return; } @@ -488,9 +488,61 @@ static int config_part_qsort_cmp(const void *av, const void *bv) { return config_part_cmp(av, bv, true); } -static void config_parse_line(ConfigReader *cfg, Settings *settings, const ConfigPart *part, const char *line) { +static i64 config_read_string(Ted *ted, ConfigReader *cfg, char **ptext) { + char *p; + int backslashes = 0; + u32 start_line = cfg->line_number; + char *start = *ptext + 1; + for (p = start; ; ++p) { + bool done = false; + switch (*p) { + case '\\': + ++backslashes; + break; + case '"': + if (backslashes % 2 == 0) + done = true; + break; + case '\n': + ++cfg->line_number; + break; + case '\0': + cfg->line_number = start_line; + config_err(cfg, "String doesn't end."); + *ptext += strlen(*ptext); + return -1; + } + if (done) break; + } + + i64 str_idx = -1; + if (ted->nstrings < TED_MAX_STRINGS) { + char *str = strn_dup(start, (size_t)(p - start)); + str_idx = ted->nstrings; + ted->strings[ted->nstrings++] = str; + } + *ptext = p + 1; + return str_idx; +} + +// reads a single "line" of the config file, but it may include a multiline string, +// so it may read multiple lines. +static void config_parse_line(ConfigReader *cfg, Settings *settings, const ConfigPart *part, char **pline) { + char *line = *pline; Ted *ted = cfg->ted; + char *newline = strchr(line, '\n'); + if (!newline) { + config_err(cfg, "No newline at end of file?"); + *pline += strlen(*pline); + return; + } + + if (newline) *newline = '\0'; + char *carriage_return = strchr(line, '\r'); + if (carriage_return) *carriage_return = '\0'; + *pline = newline + 1; + if (part->section == 0) { // there was an error reading this section. don't bother with anything else. return; @@ -509,9 +561,9 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi return; } - char const *key = line; + char *key = line; *equals = '\0'; - char const *value = equals + 1; + char *value = equals + 1; while (isspace(*key)) ++key; while (isspace(*value)) ++value; if (equals != line) { @@ -556,31 +608,20 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi value = endp; } else if (*value == '"') { // string argument - int backslashes = 0; - char const *p; - for (p = value + 1; *p; ++p) { - bool done = false; - switch (*p) { - case '\\': - ++backslashes; - break; - case '"': - if (backslashes % 2 == 0) - done = true; - break; - } - if (done) break; - } - if (!*p) { - config_err(cfg, "String doesn't end."); - break; - } - if (ted->nstrings < TED_MAX_STRINGS) { - char *str = strn_dup(value + 1, (size_t)(p - (value + 1))); - argument = ted->nstrings | ARG_STRING; - ted->strings[ted->nstrings++] = str; + + // restore newline to handle multi-line strings + // a little bit hacky oh well + *newline = '\n'; + argument = config_read_string(ted, cfg, &value); + + newline = strchr(value, '\n'); + if (!newline) { + config_err(cfg, "No newline at end of file?"); + *pline += strlen(*pline); + return; } - value = p + 1; + *newline = '\0'; + *pline = newline + 1; } while (isspace(*value)) ++value; // skip past space following argument if (*value == ':') { @@ -634,6 +675,26 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi is_bool = true; boolean = false; } + + if (value[0] == '"') { + // restore newline to handle multi-line strings + // a little bit hacky oh well + *newline = '\n'; + + i64 string = config_read_string(ted, cfg, &value); + + newline = strchr(value, '\n'); + if (!newline) { + config_err(cfg, "No newline at end of file?"); + *pline += strlen(*pline); + return; + } + *newline = '\0'; + *pline = newline + 1; + if (string >= 0 && string < TED_MAX_STRINGS) { + value = ted->strings[string]; + } + } // go through all options bool recognized = false; @@ -747,22 +808,11 @@ void config_parse(Ted *ted, ConfigPart **pparts) { arr_add(part->text, '\0'); // null termination char *line = part->text; while (*line) { - char *newline = strchr(line, '\n'); - if (!newline) { - config_err(cfg, "No newline at end of file?"); - break; - } - - if (newline) *newline = '\0'; - char *carriage_return = strchr(line, '\r'); - if (carriage_return) *carriage_return = '\0'; - - config_parse_line(cfg, settings, part, line); + config_parse_line(cfg, settings, part, &line); if (cfg->error) break; ++cfg->line_number; - line = newline + 1; } } diff --git a/gl.c b/gl.c index 8326de6..1d6cb14 100644 --- a/gl.c +++ b/gl.c @@ -78,7 +78,7 @@ static void gl_get_procs(void) { } // compile a GLSL shader -static GLuint gl_compile_shader(char const *code, GLenum shader_type) { +static GLuint gl_compile_shader(char error_buf[256], char const *code, GLenum shader_type) { GLuint shader = glCreateShader(shader_type); glShaderSource(shader, 1, &code, NULL); glCompileShader(shader); @@ -87,14 +87,17 @@ static GLuint gl_compile_shader(char const *code, GLenum shader_type) { if (status == GL_FALSE) { char log[1024] = {0}; glGetShaderInfoLog(shader, sizeof log - 1, NULL, log); - debug_println("Error compiling shader: %s", log); + if (error_buf) + str_printf(error_buf, 256, "Error compiling shader: %s", log); + else + debug_println("Error compiling shader: %s", log); return 0; } return shader; } // link together GL shaders -static GLuint gl_link_program(GLuint *shaders, size_t count) { +static GLuint gl_link_program(char error_buf[256], GLuint *shaders, size_t count) { GLuint program = glCreateProgram(); if (program) { for (size_t i = 0; i < count; ++i) { @@ -110,7 +113,11 @@ static GLuint gl_link_program(GLuint *shaders, size_t count) { if (status == GL_FALSE) { char log[1024] = {0}; glGetProgramInfoLog(program, sizeof log - 1, NULL, log); - debug_println("Error linking shaders: %s", log); + if (error_buf) { + str_printf(error_buf, 256, "Error linking shaders: %s", log); + } else { + debug_println("Error linking shaders: %s", log); + } glDeleteProgram(program); return 0; } @@ -118,11 +125,11 @@ static GLuint gl_link_program(GLuint *shaders, size_t count) { return program; } -static GLuint gl_compile_and_link_shaders(char const *vshader_code, char const *fshader_code) { +static GLuint gl_compile_and_link_shaders(char error_buf[256], char const *vshader_code, char const *fshader_code) { GLuint shaders[2]; - shaders[0] = gl_compile_shader(vshader_code, GL_VERTEX_SHADER); - shaders[1] = gl_compile_shader(fshader_code, GL_FRAGMENT_SHADER); - GLuint program = gl_link_program(shaders, 2); + shaders[0] = gl_compile_shader(error_buf, vshader_code, GL_VERTEX_SHADER); + shaders[1] = gl_compile_shader(error_buf, fshader_code, GL_FRAGMENT_SHADER); + GLuint program = gl_link_program(error_buf, shaders, 2); if (shaders[0]) glDeleteShader(shaders[0]); if (shaders[1]) glDeleteShader(shaders[1]); if (program) { @@ -180,7 +187,7 @@ static void gl_geometry_init(void) { }\n\ "; - gl_geometry_program = gl_compile_and_link_shaders(vshader_code, fshader_code); + gl_geometry_program = gl_compile_and_link_shaders(NULL, vshader_code, fshader_code); gl_geometry_v_pos = gl_attrib_loc(gl_geometry_program, "v_pos"); gl_geometry_v_color = gl_attrib_loc(gl_geometry_program, "v_color"); diff --git a/main.c b/main.c index 4579c55..0bd2c85 100644 --- a/main.c +++ b/main.c @@ -1,10 +1,10 @@ /* FUTURE FEATURES: -- [/path//extensions] - custom shaders - texture, time, time since last save - config variables - config multi-line strings +- plugins? */ #include "base.h" @@ -272,7 +272,7 @@ int main(int argc, char **argv) { #endif PROFILE_TIME(init_start) PROFILE_TIME(basic_init_start) - + #if __unix__ { struct sigaction act = {0}; @@ -330,6 +330,7 @@ int main(int argc, char **argv) { // make sure signal handler has access to ted. error_signal_handler_ted = ted; + fs_get_cwd(ted->start_cwd, sizeof ted->start_cwd); { // get local and global data directory #if _WIN32 wchar_t *appdata = NULL; @@ -395,7 +396,7 @@ int main(int argc, char **argv) { char *last_slash = strrchr(executable_path, '/'); if (last_slash) { *last_slash = '\0'; - ted->search_cwd = streq(cwd, executable_path); + ted->search_start_cwd = streq(cwd, executable_path); } } #endif @@ -438,27 +439,36 @@ int main(int argc, char **argv) { PROFILE_TIME(window_end) PROFILE_TIME(gl_start) - gl_version_major = 4; - gl_version_minor = 3; - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, gl_version_major); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, gl_version_minor); -#if DEBUG - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); -#endif - SDL_GLContext *glctx = SDL_GL_CreateContext(window); - if (!glctx) { - debug_println("Couldn't get GL 4.3 context. Falling back to 2.0."); - gl_version_major = 2; - gl_version_minor = 0; - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, gl_version_major); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, gl_version_minor); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); - glctx = SDL_GL_CreateContext(window); + SDL_GLContext *glctx = NULL; + { // get OpenGL context + int gl_versions[][2] = { + {4,3}, + {3,0}, + {2,0}, + {0,0}, + }; + for (int i = 0; gl_versions[i][0]; ++i) { + gl_version_major = gl_versions[i][0]; + gl_version_minor = gl_versions[i][1]; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, gl_version_major); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, gl_version_minor); + #if DEBUG + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); + #endif + glctx = SDL_GL_CreateContext(window); + if (!glctx) { + debug_println("Couldn't get GL %d.%d context. Falling back to %d.%d.", + gl_versions[i][0], gl_versions[i][1], gl_versions[i+1][0], gl_versions[i+1][1]); + gl_version_major = 3; + gl_version_minor = 0; + } + } + if (!glctx) die("%s", SDL_GetError()); + gl_get_procs(); } - gl_get_procs(); - + #if DEBUG if (gl_version_major * 100 + gl_version_minor >= 403) { GLint flags = 0; diff --git a/ted.c b/ted.c index 73df785..2d19a02 100644 --- a/ted.c +++ b/ted.c @@ -66,9 +66,9 @@ static bool ted_is_regular_buffer(Ted *ted, TextBuffer *buffer) { // Check the various places a file could be, and return the full path. static Status ted_get_file(Ted const *ted, char const *name, char *out, size_t outsz) { - if (ted->search_cwd && fs_file_exists(name)) { - // check in current working directory - str_cpy(out, outsz, name); + if (ted->search_start_cwd && fs_file_exists(name)) { + // check in start_cwd + path_full(ted->start_cwd, name, out, outsz); return true; } if (*ted->local_data_dir) { @@ -380,9 +380,12 @@ void ted_load_configs(Ted *ted, bool reloading) { ConfigPart *parts = NULL; config_read(ted, &parts, global_config_filename); config_read(ted, &parts, local_config_filename); - if (ted->search_cwd) { - // read config in cwd - config_read(ted, &parts, TED_CFG); + if (ted->search_start_cwd) { + // read config in start_cwd + char start_cwd_filename[TED_PATH_MAX]; + strbuf_printf(start_cwd_filename, "%s" PATH_SEPARATOR_STR TED_CFG, ted->start_cwd); + + config_read(ted, &parts, start_cwd_filename); } config_parse(ted, &parts); diff --git a/ted.h b/ted.h index 9f49fdd..8681ab6 100644 --- a/ted.h +++ b/ted.h @@ -353,7 +353,8 @@ typedef struct Ted { TextBuffer argument_buffer; // used for command selector double error_time; // time error box was opened (in seconds -- see time_get_seconds) double cursor_error_time; // time which the cursor error animation started (cursor turns red, e.g. when there's no autocomplete suggestion) - bool search_cwd; // should the working directory be searched for files? set to true if the executable isn't "installed" + bool search_start_cwd; // should start_cwd be searched for files? set to true if the executable isn't "installed" + char start_cwd[TED_PATH_MAX]; bool quit; // if set to true, the window will close next frame. NOTE: this doesn't check for unsaved changes!! bool find; // is the find or find+replace menu open? bool replace; // is the find+replace menu open? diff --git a/text.c b/text.c index 1aaebe8..144a7a0 100644 --- a/text.c +++ b/text.c @@ -112,7 +112,7 @@ void main() {\n\ }\n\ "; - text_program = gl_compile_and_link_shaders(vshader_code, fshader_code); + text_program = gl_compile_and_link_shaders(NULL, vshader_code, fshader_code); text_v_pos = gl_attrib_loc(text_program, "v_pos"); text_v_color = gl_attrib_loc(text_program, "v_color"); text_v_tex_coord = gl_attrib_loc(text_program, "v_tex_coord"); -- cgit v1.2.3