diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | expac.c | 253 |
2 files changed, 195 insertions, 62 deletions
@@ -6,8 +6,8 @@ PREFIX ?= /usr/local MANPREFIX ?= $(PREFIX)/share/man # compiler flags -CPPFLAGS := -DVERSION=\"$(VERSION)\" $(CPPFLAGS) -CFLAGS := -std=c99 -g -pedantic -Wall -Wextra $(CFLAGS) +CPPFLAGS := -DVERSION=\"$(VERSION)\" -D_GNU_SOURCE $(CPPFLAGS) +CFLAGS := -std=c11 -g -pedantic -Wall -Wextra -Wno-missing-field-initializers $(CFLAGS) LDLIBS = -lalpm DISTFILES = expac.c README.pod @@ -24,10 +24,11 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#define _GNU_SOURCE #include <alpm.h> #include <ctype.h> +#include <errno.h> #include <getopt.h> +#include <glob.h> #include <stdint.h> #include <stdbool.h> #include <stdio.h> @@ -66,6 +67,187 @@ int opt_pkgcounter = 0; typedef const char *(*extractfn)(void*); +typedef struct config_t { + char **repos; + int size; + int capacity; +} config_t; + +static inline void freep(void *p) { free(*(void **)p); } +static inline void fclosep(FILE **p) { if (*p) fclose(*p); } +static inline void globfreep(glob_t *p) { globfree(p); } +#define _cleanup_(x) __attribute__((cleanup(x))) +#define _cleanup_free_ _cleanup_(freep) + +static size_t strtrim(char *str) { + char *left = str, *right; + + if (!str || *str == '\0') { + return 0; + } + + while (isspace((unsigned char)*left)) { + left++; + } + if (left != str) { + memmove(str, left, (strlen(left) + 1)); + left = str; + } + + if (*str == '\0') { + return 0; + } + + right = strchr(str, '\0') - 1; + while (isspace((unsigned char)*right)) { + right--; + } + *++right = '\0'; + + return right - left; +} + +int is_section(const char *s, int n) { + return s[0] == '[' && s[n-1] == ']'; +} + +static int config_add_repo(config_t *config, char *reponame) { + /* first time setup */ + if (config->repos == NULL) { + config->repos = calloc(10, sizeof(char*)); + if (config->repos == NULL) { + return -ENOMEM; + } + + config->size = 0; + config->capacity = 10; + } + + /* grow when needed */ + if (config->size == config->capacity) { + void *ptr; + + ptr = realloc(config->repos, config->capacity * 2.5 * sizeof(char*)); + if (ptr == NULL) { + return -ENOMEM; + } + + config->repos = ptr; + } + + config->repos[config->size] = strdup(reponame); + ++config->size; + + return 0; +} + +void config_reset(config_t *config) { + if (config == NULL) + return; + + for (int i = 0; i < config->size; ++i) + free(config->repos[i]); + + free(config->repos); +} + +static int parse_one_file(config_t *config, const char *filename, char **section); + +static int parse_include(config_t *config, const char *include, char **section) { + _cleanup_(globfreep) glob_t globbuf = {}; + + if (glob(include, GLOB_NOCHECK, NULL, &globbuf) != 0) { + fprintf(stderr, "warning: globbing failed on '%s': out of memory\n", + include); + return -ENOMEM; + } + + for (size_t i = 0; i < globbuf.gl_pathc; ++i) + parse_one_file(config, globbuf.gl_pathv[i], section); + + return 0; +} + +static char *split_keyval(char *line, const char *sep) { + strsep(&line, sep); + return line; +} + +int parse_one_file(config_t *config, const char *filename, char **section) { + _cleanup_(fclosep) FILE *fp = NULL; + _cleanup_free_ char *line = NULL; + size_t n = 0; + int in_options = 0; + + fp = fopen(filename, "r"); + if (fp == NULL) + return -errno; + + for (;;) { + ssize_t len; + + errno = 0; + len = getline(&line, &n, fp); + if (len < 0) { + if (errno != 0) + return -errno; + + /* EOF */ + break; + } + + len = strtrim(line); + if (len == 0 || line[0] == '#') + continue; + + if (is_section(line, len)) { + + free(*section); + *section = strndup(&line[1], len - 2); + if (*section == NULL) + return -ENOMEM; + + in_options = strcmp(*section, "options") == 0; + + if (!in_options) { + int r; + + r = config_add_repo(config, *section); + if (r < 0) + return r; + + continue; + } + } + + if (in_options) { + char *val; + + if (memchr(line, '=', len)) { + val = split_keyval(line, "="); + + strtrim(val); + strtrim(line); + + if (strcmp(line, "Include") == 0) { + int k; + + k = parse_include(config, val, section); + if (k < 0) + return k; + } + } + } + } + + return 0; +} + +int config_parse(config_t *config, const char *filename) { + _cleanup_free_ char *section = NULL; + + return parse_one_file(config, filename, §ion); +} static const char *alpm_backup_get_name(void *b) { alpm_backup_t *bkup = b; @@ -110,33 +292,6 @@ static char *size_to_string(off_t pkgsize) return out; } -static char *strtrim(char *str) { - char *pch = str; - - if (!str || *str == '\0') { - return str; - } - - while (isspace((unsigned char)*pch)) { - pch++; - } - if (pch != str) { - memmove(str, pch, (strlen(pch) + 1)); - } - - if (*str == '\0') { - return str; - } - - pch = (str + (strlen(str) - 1)); - while (isspace((unsigned char)*pch)) { - pch--; - } - *++pch = '\0'; - - return str; -} - static char *format_optdep(alpm_depend_t *optdep) { char *out; @@ -150,9 +305,8 @@ static char *format_optdep(alpm_depend_t *optdep) { static alpm_handle_t *alpm_init(void) { alpm_handle_t *alpm = NULL; enum _alpm_errno_t alpm_errno = 0; - FILE *fp; - char line[PATH_MAX]; - char *ptr, *section = NULL; + config_t config = { NULL, 0, 0 }; + int r; alpm = alpm_initialize("/", "/var/lib/pacman", &alpm_errno); if (!alpm) { @@ -162,39 +316,18 @@ static alpm_handle_t *alpm_init(void) { db_local = alpm_get_localdb(alpm); - fp = fopen("/etc/pacman.conf", "r"); - if (!fp) { - perror("fopen: /etc/pacman.conf"); - return alpm; + r = config_parse(&config, "/etc/pacman.conf"); + if (r < 0) { + fprintf(stderr, "error: failed to parse config: %s\n", strerror(-r)); + return NULL; } - while (fgets(line, PATH_MAX, fp)) { - strtrim(line); - - if (strlen(line) == 0 || line[0] == '#') { - continue; - } - if ((ptr = strchr(line, '#'))) { - *ptr = '\0'; - } - - if (line[0] == '[' && line[strlen(line) - 1] == ']') { - ptr = &line[1]; - if (section) { - free(section); - } - - section = strdup(ptr); - section[strlen(section) - 1] = '\0'; - - if (strcmp(section, "options") != 0) { - alpm_register_syncdb(alpm, section, 0); - } - } + for (int i = 0; i < config.size; ++i) { + alpm_register_syncdb(alpm, config.repos[i], 0); } - free(section); - fclose(fp); + config_reset(&config); + return alpm; } |