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/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <libgen.h>
#include "ini.h" #include "ini.h"
#include "keys.h" #include "keys.h"
@ -22,6 +23,90 @@
#include "error.h" #include "error.h"
#include "config.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. */ /* Return up to two keycodes associated with the given name. */
static int lookup_keycodes(const char *name, uint8_t *code1, uint8_t *code2) 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; s = dot+1;
} }
if (parse_kvp(s, &keyname, &descstr) < 0) { parse_kvp(s, &keyname, &descstr);
err("Invalid key value pair.");
return -1;
}
idx = config_get_layer_index(config, layername); idx = config_get_layer_index(config, layername);
if (idx == -1) { if (idx == -1) {
@ -216,24 +297,19 @@ static void parse_globals(const char *path, struct config *config, struct ini_se
size_t i; size_t i;
for (i = 0; i < section->nr_entries;i++) { for (i = 0; i < section->nr_entries;i++) {
char *key, *val;
struct ini_entry *ent = &section->entries[i]; 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")) if (!strcmp(ent->key, "macro_timeout"))
config->macro_timeout = atoi(val); config->macro_timeout = atoi(ent->val);
else if (!strcmp(key, "macro_repeat_timeout")) else if (!strcmp(ent->key, "macro_repeat_timeout"))
config->macro_repeat_timeout = atoi(val); config->macro_repeat_timeout = atoi(ent->val);
else if (!strcmp(key, "layer_indicator")) else if (!strcmp(ent->key, "layer_indicator"))
config->layer_indicator = atoi(val); config->layer_indicator = atoi(ent->val);
else else
fprintf(stderr, "\tERROR %s:%zd: %s is not a valid global option.\n", fprintf(stderr, "\tERROR %s:%zd: %s is not a valid global option.\n",
path, path,
ent->lnum, ent->lnum,
key); ent->key);
} }
} }
@ -241,12 +317,16 @@ int config_parse(struct config *config, const char *path)
{ {
size_t i; size_t i;
char *content;
struct ini *ini; struct ini *ini;
struct ini_section *section; struct ini_section *section;
config_init(config); 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; return -1;
/* First pass: create all layers based on section headers. */ /* 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]; char entry[MAX_EXP_LEN];
struct ini_entry *ent = &section->entries[j]; 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) 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 = * Parse a value of the form 'key = value'. The value may contain =
* and the key may itself be = as a special case. The returned * and the key may itself be = as a special case. value may be NULL
* values are pointers within the modified input string. * 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 *last_space = NULL;
char *c = s; char *c = s;
@ -31,6 +31,8 @@ int parse_kvp(char *s, char **key, char **value)
if (*c == '=') if (*c == '=')
c++; c++;
*key = s;
*value = NULL;
while (*c) { while (*c) {
switch (*c) { switch (*c) {
case '=': case '=':
@ -38,18 +40,11 @@ int parse_kvp(char *s, char **key, char **value)
*last_space = 0; *last_space = 0;
else else
*c = 0; *c = 0;
c++;
while (*c && *c == ' ') while (*++c == ' ');
c++;
if (!*s)
return -1;
*key = s;
*value = c; *value = c;
return;
return 0;
case ' ': case ' ':
if (!last_space) if (!last_space)
last_space = c; last_space = c;
@ -61,36 +56,19 @@ int parse_kvp(char *s, char **key, char **value)
c++; c++;
} }
return -1;
} }
static void read_file(const char *path, char *buf, size_t buf_sz) /*
{ * The result is statically allocated and should not be freed by the caller.
struct stat st; * The returned struct is only valid until the next invocation. The
size_t sz; * input string may be modified and should only be freed after the
int fd; * returned ini struct is no longer required.
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);
}
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; int ln = 0;
size_t n = 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] == ']') { if (line[len-1] == ']') {
assert(n < MAX_SECTIONS); assert(n < MAX_SECTIONS);
section = &ini->sections[n++]; section = &ini.sections[n++];
line[len-1] = 0; line[len-1] = 0;
@ -148,39 +126,22 @@ int parse(char *s, struct ini *ini, const char *default_section_name)
if (!section) { if (!section) {
if(default_section_name) { if(default_section_name) {
section = &ini->sections[n++]; section = &ini.sections[n++];
strcpy(section->name, default_section_name); strcpy(section->name, default_section_name);
section->nr_entries = 0; section->nr_entries = 0;
section->lnum = 0; section->lnum = 0;
} else } else
return -1; return NULL;
} }
assert(section->nr_entries < MAX_SECTION_ENTRIES); assert(section->nr_entries < MAX_SECTION_ENTRIES);
ent = &section->entries[section->nr_entries++]; ent = &section->entries[section->nr_entries++];
parse_kvp(line, &ent->key, &ent->val);
ent->line = line;
ent->lnum = ln; ent->lnum = ln;
} }
ini->nr_sections = n; 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;
return &ini; return &ini;
} }

@ -12,7 +12,9 @@
#define MAX_SECTION_ENTRIES 128 #define MAX_SECTION_ENTRIES 128
struct ini_entry { struct ini_entry {
char *line; char *key;
char *val;
size_t lnum; // The line number in the original source file. 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] * [section]
* *
* # Comment * # Comment
* *
* entry1 * key1 = val1
* entry2 * key2 = val2
* key3
* *
* [section2] * [section2]
* ... * ...
@ -49,15 +52,17 @@ struct ini {
* sripped of leading whitespace. If a default * sripped of leading whitespace. If a default
* section name is supplied then entries not * section name is supplied then entries not
* listed under an explicit heading will be * 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 * The returned result is statically allocated and only
* valid until the next invocation. It should not be * valid until the next invocation. It should not be
* freed. * 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 #endif

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

Loading…
Cancel
Save