summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--expac.c253
2 files changed, 195 insertions, 62 deletions
diff --git a/Makefile b/Makefile
index bee264c..85b47a9 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/expac.c b/expac.c
index 0c1a2e0..b94760c 100644
--- a/expac.c
+++ b/expac.c
@@ -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, &section);
+}
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;
}