diff options
author | Mattias Andrée <m@maandree.se> | 2025-03-27 18:36:26 +0100 |
---|---|---|
committer | Mattias Andrée <m@maandree.se> | 2025-03-27 18:36:26 +0100 |
commit | 037b945a9f253b97faffc02d8475574e75203516 (patch) | |
tree | b008e7d77e9daaeaaa8e7854728d715df5aafb77 /redshift/config-ini.c | |
parent | todo list housekeeping (diff) | |
download | redshift-ng-037b945a9f253b97faffc02d8475574e75203516.tar.gz redshift-ng-037b945a9f253b97faffc02d8475574e75203516.tar.bz2 redshift-ng-037b945a9f253b97faffc02d8475574e75203516.tar.xz |
one dir per subproject
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'redshift/config-ini.c')
-rw-r--r-- | redshift/config-ini.c | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/redshift/config-ini.c b/redshift/config-ini.c new file mode 100644 index 0000000..13aaa57 --- /dev/null +++ b/redshift/config-ini.c @@ -0,0 +1,267 @@ +/*- + * redshift-ng - Automatically adjust display colour temperature according the Sun + * + * Copyright (c) 2009-2018 Jon Lund Steffensen <jonlst@gmail.com> + * Copyright (c) 2014-2016, 2025 Mattias Andrée <m@maandree.se> + * + * redshift-ng is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * redshift-ng is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with redshift-ng. If not, see <http://www.gnu.org/licenses/>. + */ +#include "common.h" + + +/** + * Paths, in order of priority, to test when looking for + * the configuration file for redshift + */ +static const struct env_path paths[] = { + {0, "XDG_CONFIG_HOME", "/redshift-ng/redshift.conf"}, + {0, "XDG_CONFIG_HOME", "/redshift/redshift.conf"}, + {0, "XDG_CONFIG_HOME", "/redshift.conf"}, +#if defined(WINDOWS) + {0, "localappdata", "/redshift-ng/redshift.conf"}, + {0, "localappdata", "/redshift/redshift.conf"}, + {0, "localappdata", "/redshift.conf"}, +#endif + {0, "HOME", "/.config/redshift-ng/redshift.conf"}, + {0, "HOME", "/.config/redshift/redshift.conf"}, + {0, "HOME", "/.config/redshift.conf"}, + {0, "HOME", "/.redshift.conf"}, + {0, NULL, "/.config/redshift-ng/redshift.conf"}, + {0, NULL, "/.config/redshift/redshift.conf"}, + {0, NULL, "/.config/redshift.conf"}, + {0, NULL, "/.redshift.conf"}, + {1, "XDG_CONFIG_DIRS", "/redshift-ng/redshift.conf"}, + {1, "XDG_CONFIG_DIRS", "/redshift/redshift.conf"}, + {1, "XDG_CONFIG_DIRS", "/redshift.conf"}, +#if !defined(WINDOWS) + {0, "", "/etc/redshift-ng/redshift.conf"}, + {0, "", "/etc/redshift/redshift.conf"}, + {0, "", "/etc/redshift.conf"} +#endif +}; + + +/** + * Open the configuration file for reading + * + * @param path The path to the configuration file, or `NULL` if the + * application should look for it in the default paths + * @param path_out Output parameter for the configuration file path + * @param pathbuf_out Output parameter for the memory allocation for `*path_out`; + * will be set to `NULL` unless `path` is `NULL`; shall be + * free(3)d by the caller + * @param should_close_out Output parameter for whether the file should be closed + * @return `FILE` object for the reading the file; `NULL` if not + * found and `path` is `NULL` + */ +static FILE * +open_config_file(const char *path, const char **path_out, char **pathbuf_out, int *should_close_out) +{ + FILE *f = NULL; + size_t i; +#ifndef WINDOWS + const char *s; + int fd, old_fd = -1; +#endif + + *path_out = path; + *pathbuf_out = NULL; + *should_close_out = 1; + + if (!path) { + for (i = 0; !f && i < ELEMSOF(paths); i++) + f = try_path_fopen(&paths[i], path_out, pathbuf_out); + if (f) + weprintf(_("Found configuration file `%s'."), *path_out); + else + weprintf(_("No configuration file found.")); + } else if (!strcmp(path, "/dev/null")) { /* needed to allow /dev/null to be specified on Windows */ + return NULL; + } else if (!strcmp(path, "-")) { + *should_close_out = 0; + return stdin; +#ifndef WINDOWS + } else if (!strcmp(path, "/dev/stdin")) { + *should_close_out = 0; + return stdin; + } else if (!strcmp(path, "/dev/stdout")) { + fd = STDOUT_FILENO; + goto use_fd; + } else if (!strcmp(path, "/dev/stderr")) { + fd = STDERR_FILENO; + goto use_fd; + } else if (!strncmp(path, "/dev/fd/", sizeof("/dev/fd/") - 1U)) { + s = &path[sizeof("/dev/fd/") - 1U]; +# if defined(__linux__) + goto parse_fd; + } else if (!strncmp(path, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1U)) { + s = &path[sizeof("/proc/self/fd/") - 1U]; + parse_fd: +# endif + fd = 0; + if (!*s) + goto fallback; + while (isdigit(*s)) { + if (fd > (INT_MAX - (*s & 15)) / 10) + goto fallback; + fd = fd * 10 + (*s & 15); + } + if (*s) + goto fallback; + use_fd: + if (fd > 2) { + fd = dup(old_fd = fd); + if (fd < 0) + eprintf("dup %i:", old_fd); + } + f = fdopen(fd, "r"); + if (!f) { + if (old_fd < 0) + eprintf("fdopen %i \"r\":", fd); + else + eprintf("fdopen <duplicate of %i> \"r\":", old_fd); + } +#endif + } else { +#ifndef WINDOWS + fallback: +#endif + f = fopen(path, "r"); + if (!f) + eprintf("fopen %s \"r\":", path); + } + + return f; +} + + +const char * +config_ini_init(struct config_ini_state *state, const char *path, char **pathbuf_out) +{ + struct config_ini_section *section = NULL; + struct config_ini_setting *setting; + char *line = NULL, *s, *p, *value, *end; + char *next_line = NULL, *name; + size_t size = 0; + ssize_t len = 0; /* initialised to silence false warning from clang */ + FILE *f; + int should_close; + + state->sections = NULL; + *pathbuf_out = NULL; + + f = open_config_file(path, &path, pathbuf_out, &should_close); + if (!f) + return NULL; + +#ifndef WINDOWS +again: +#endif + while ((s = next_line) || (len = getline(&line, &size, f)) >= 0) { + if (!s && (s = line, strlen(s) != (size_t)len)) + eprintf(_("Config file contains NUL byte.")); + + s = ltrim(s); + next_line = NULL; + end = &s[strcspn(s, "\r\n")]; + if (*end) { + p = end; + do { + *p++ = '\0'; + } while (*p == '\r' || *p == '\n'); + if (*p) + next_line = p; + } + rtrim(s, end); + + switch (*s) { + case ';': /* comment line */ + case '#': /* comment line */ + case '\0': /* blank line */ + break; + + case '[': /* "[%s]", section */ + end = strchr(name = &s[1], ']'); + if (!end || end[1] || end == name) + eprintf(_("Malformed section header in config file.")); + *end = '\0'; + section = emalloc(sizeof(*section)); + section->name = estrdup(name); + section->settings = NULL; + section->next = state->sections; + state->sections = section; + break; + + default: /* "%s = %s", name, value */ + value = p = strchr(name = s, '='); + if (!value || value == name) + eprintf(_("Malformed assignment in config file.")); + *value++ = '\0'; + if (!section) + eprintf(_("Assignment outside section in config file.")); + setting = emalloc(sizeof(*setting)); + setting->name = estrdup(rtrim(name, p)); + setting->value = estrdup(rtrim(ltrim(value), NULL)); + setting->next = section->settings; + section->settings = setting; + break; + } + } + if (ferror(f)) { +#ifndef WINDOWS + if (errno == EINTR) { + clearerr(f); + goto again; + } +#endif + eprintf("getline %s:", path); + } + + free(line); + if (should_close) + fclose(f); + + return path; +} + + +void +config_ini_free(struct config_ini_state *state) +{ + struct config_ini_section *section, *section_next; + struct config_ini_setting *setting, *setting_next; + for (section = state->sections; section; section = section_next) { + section_next = section->next; + for (setting = section->settings; setting; setting = setting_next) { + setting_next = setting->next; + free(setting->name); + free(setting->value); + free(setting); + } + free(section->name); + free(section); + } +} + + +struct config_ini_section * +config_ini_get_section(struct config_ini_state *state, const char *name) +{ + /* TODO deal with multiple section definitions */ + struct config_ini_section *section; + for (section = state->sections; section; section = section->next) + if (!strcasecmp(section->name, name)) + return section; + return NULL; +} |