config: Add support for including other files

master
Raheman Vaiya 4 years ago
parent 4084a9d500
commit b0d9cce64a
  1. 125
      src/config.c
  2. 81
      src/ini.c
  3. 19
      src/ini.h
  4. 2
      src/keyd.c

@ -15,6 +15,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <libgen.h>
#include "ini.h"
#include "keys.h"
@ -22,6 +23,90 @@
#include "error.h"
#include "config.h"
#define MAX_FILE_SZ 65536
#define MAX_LINE_LEN 256
static char *read_file(const char *path)
{
const char include_prefix[] = "include ";
static char buf[MAX_FILE_SZ];
char line[MAX_LINE_LEN];
int sz = 0;
FILE *fh = fopen(path, "r");
if (!fh) {
fprintf(stderr, "\tERROR: Failed to open %s\n", path);
return NULL;
}
while (fgets(line, sizeof line, fh)) {
int len = strlen(line);
if (line[len-1] != '\n') {
fprintf(stderr, "\tERROR: Maximum line length exceed (%d).\n", MAX_LINE_LEN);
goto fail;
}
if ((len+sz) > MAX_FILE_SZ) {
fprintf(stderr, "\tERROR: Max file size (%d) exceeded.\n", MAX_FILE_SZ);
goto fail;
}
if (strstr(line, include_prefix) == line) {
int fd;
char include_path[PATH_MAX];
char resolved_path[PATH_MAX];
line[len-1] = 0;
char *tmp = strdup(path);
char *dir = dirname(tmp);
snprintf(include_path,
sizeof include_path,
"%s/%s", dir, line+sizeof(include_prefix)-1);
if(!realpath(include_path, resolved_path)) {
fprintf(stderr, "\tERROR: Failed to resolve include path: %s\n", include_path);
free(tmp);
continue;
}
if (strstr(resolved_path, dir) != resolved_path) {
fprintf(stderr, "\tERROR: Naughty path detected: %s\n", include_path);
free(tmp);
continue;
}
free(tmp);
fd = open(include_path, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "\tERROR: Failed to include %s\n", include_path);
perror("open");
} else {
int n;
while ((n = read(fd, buf+sz, sizeof(buf)-sz)) > 0)
sz += n;
close(fd);
}
} else {
strcpy(buf+sz, line);
sz += len;
}
}
fclose(fh);
return buf;
fail:
fclose(fh);
return NULL;
}
/* Return up to two keycodes associated with the given name. */
static int lookup_keycodes(const char *name, uint8_t *code1, uint8_t *code2)
{
@ -103,11 +188,7 @@ int config_add_entry(struct config *config, const char *exp)
s = dot+1;
}
if (parse_kvp(s, &keyname, &descstr) < 0) {
err("Invalid key value pair.");
return -1;
}
parse_kvp(s, &keyname, &descstr);
idx = config_get_layer_index(config, layername);
if (idx == -1) {
@ -216,24 +297,19 @@ static void parse_globals(const char *path, struct config *config, struct ini_se
size_t i;
for (i = 0; i < section->nr_entries;i++) {
char *key, *val;
struct ini_entry *ent = &section->entries[i];
if (parse_kvp(ent->line, &key, &val)) {
fprintf(stderr, "\tERROR %s:%zd: malformed config entry\n", path, ent->lnum);
continue;
}
if (!strcmp(key, "macro_timeout"))
config->macro_timeout = atoi(val);
else if (!strcmp(key, "macro_repeat_timeout"))
config->macro_repeat_timeout = atoi(val);
else if (!strcmp(key, "layer_indicator"))
config->layer_indicator = atoi(val);
if (!strcmp(ent->key, "macro_timeout"))
config->macro_timeout = atoi(ent->val);
else if (!strcmp(ent->key, "macro_repeat_timeout"))
config->macro_repeat_timeout = atoi(ent->val);
else if (!strcmp(ent->key, "layer_indicator"))
config->layer_indicator = atoi(ent->val);
else
fprintf(stderr, "\tERROR %s:%zd: %s is not a valid global option.\n",
path,
ent->lnum,
key);
ent->key);
}
}
@ -241,12 +317,16 @@ int config_parse(struct config *config, const char *path)
{
size_t i;
char *content;
struct ini *ini;
struct ini_section *section;
config_init(config);
if (!(ini = ini_parse_file(path, NULL)))
if (!(content = read_file(path)))
return -1;
if (!(ini = ini_parse_string(content, NULL)))
return -1;
/* First pass: create all layers based on section headers. */
@ -282,10 +362,15 @@ int config_parse(struct config *config, const char *path)
char entry[MAX_EXP_LEN];
struct ini_entry *ent = &section->entries[j];
snprintf(entry, sizeof entry, "%s.%s", layername, ent->line);
if (!ent->val) {
fprintf(stderr, "\tERROR parsing %s:%zd: invalid key value pair.\n", path, ent->lnum);
continue;
}
snprintf(entry, sizeof entry, "%s.%s = %s", layername, ent->key, ent->val);
if (config_add_entry(config, entry) < 0)
fprintf(stderr, "\tERROR %s:%zd: %s\n", path, ent->lnum, errstr);
fprintf(stderr, "\tERROR parsing %s:%zd: %s\n", path, ent->lnum, errstr);
}
}

@ -18,11 +18,11 @@
/*
* Parse a value of the form 'key = value'. The value may contain =
* and the key may itself be = as a special case. The returned
* values are pointers within the modified input string.
* and the key may itself be = as a special case. value may be NULL
* if '=' is not present in the original string.
*/
int parse_kvp(char *s, char **key, char **value)
void parse_kvp(char *s, char **key, char **value)
{
char *last_space = NULL;
char *c = s;
@ -31,6 +31,8 @@ int parse_kvp(char *s, char **key, char **value)
if (*c == '=')
c++;
*key = s;
*value = NULL;
while (*c) {
switch (*c) {
case '=':
@ -38,18 +40,11 @@ int parse_kvp(char *s, char **key, char **value)
*last_space = 0;
else
*c = 0;
c++;
while (*c && *c == ' ')
c++;
while (*++c == ' ');
if (!*s)
return -1;
*key = s;
*value = c;
return 0;
return;
case ' ':
if (!last_space)
last_space = c;
@ -61,36 +56,19 @@ int parse_kvp(char *s, char **key, char **value)
c++;
}
return -1;
}
static void read_file(const char *path, char *buf, size_t buf_sz)
{
struct stat st;
size_t sz;
int fd;
ssize_t n = 0, nr;
if (stat(path, &st)) {
perror("stat");
exit(-1);
}
sz = st.st_size;
assert(sz < buf_sz);
fd = open(path, O_RDONLY);
while ((nr = read(fd, buf + n, sz - n))) {
n += nr;
}
buf[sz] = '\0';
close(fd);
}
/*
* The result is statically allocated and should not be freed by the caller.
* The returned struct is only valid until the next invocation. The
* input string may be modified and should only be freed after the
* returned ini struct is no longer required.
*/
int parse(char *s, struct ini *ini, const char *default_section_name)
struct ini *ini_parse_string(char *s, const char *default_section_name)
{
static struct ini ini;
int ln = 0;
size_t n = 0;
@ -130,7 +108,7 @@ int parse(char *s, struct ini *ini, const char *default_section_name)
if (line[len-1] == ']') {
assert(n < MAX_SECTIONS);
section = &ini->sections[n++];
section = &ini.sections[n++];
line[len-1] = 0;
@ -148,39 +126,22 @@ int parse(char *s, struct ini *ini, const char *default_section_name)
if (!section) {
if(default_section_name) {
section = &ini->sections[n++];
section = &ini.sections[n++];
strcpy(section->name, default_section_name);
section->nr_entries = 0;
section->lnum = 0;
} else
return -1;
return NULL;
}
assert(section->nr_entries < MAX_SECTION_ENTRIES);
ent = &section->entries[section->nr_entries++];
ent->line = line;
parse_kvp(line, &ent->key, &ent->val);
ent->lnum = ln;
}
ini->nr_sections = n;
return 0;
}
/*
* The result is statically allocated and should not be freed by the caller.
* The returned ini struct is only valid until the next invocation.
*/
struct ini *ini_parse_file(const char *path, const char *default_section_name)
{
static char buf[MAX_INI_SIZE];
static struct ini ini;
read_file(path, buf, sizeof buf);
if (parse(buf, &ini, default_section_name) < 0)
return NULL;
ini.nr_sections = n;
return &ini;
}

@ -12,7 +12,9 @@
#define MAX_SECTION_ENTRIES 128
struct ini_entry {
char *line;
char *key;
char *val;
size_t lnum; // The line number in the original source file.
};
@ -32,14 +34,15 @@ struct ini {
};
/*
* Reads a file of the form:
* Reads a string of the form:
*
* [section]
*
* # Comment
*
* entry1
* entry2
* key1 = val1
* key2 = val2
* key3
*
* [section2]
* ...
@ -49,15 +52,17 @@ struct ini {
* sripped of leading whitespace. If a default
* section name is supplied then entries not
* listed under an explicit heading will be
* returned under the named section.
* returned under the named section. If
* no value is specified, val is NULL in
* the corresponding entry.
*
* The returned result is statically allocated and only
* valid until the next invocation. It should not be
* freed.
*/
struct ini *ini_parse_file(const char *path, const char *default_section_name);
struct ini *ini_parse_string(char *s, const char *default_section_name);
int parse_kvp(char *s, char **key, char **value);
void parse_kvp(char *s, char **key, char **value);
#endif

@ -599,4 +599,6 @@ int main(int argc, char *argv[])
chgid();
loop(0);
}
return 0;
}

Loading…
Cancel
Save