#include <sys/param.h>
#include <ctype.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "figpar.h"
#include "string_m.h"
struct figpar_config figpar_dummy_config = {0, NULL, {0}, NULL};
struct figpar_config *
get_config_option(struct figpar_config options[], const char *directive)
{
uint32_t n;
if (options == NULL || directive == NULL)
return (&figpar_dummy_config);
for (n = 0; options[n].directive != NULL; n++)
if (strcmp(options[n].directive, directive) == 0)
return (&(options[n]));
figpar_dummy_config.directive = NULL;
figpar_dummy_config.type = 0;
figpar_dummy_config.action = NULL;
figpar_dummy_config.value.u_num = 0;
return (&figpar_dummy_config);
}
int
parse_config(struct figpar_config options[], const char *path,
int (*unknown)(struct figpar_config *option, uint32_t line,
char *directive, char *value), uint16_t processing_options)
{
uint8_t bequals;
uint8_t bsemicolon;
uint8_t case_sensitive;
uint8_t comment = 0;
uint8_t end;
uint8_t found;
uint8_t have_equals = 0;
uint8_t quote;
uint8_t require_equals;
uint8_t strict_equals;
char p[2];
char *directive;
char *t;
char *value;
int error;
int fd;
ssize_t r = 1;
uint32_t dsize;
uint32_t line = 1;
uint32_t n;
uint32_t vsize;
uint32_t x;
off_t charpos;
off_t curpos;
char rpath[PATH_MAX];
if (options == NULL && unknown == NULL)
return (-1);
bequals = (processing_options & FIGPAR_BREAK_ON_EQUALS) == 0 ? 0 : 1;
bsemicolon =
(processing_options & FIGPAR_BREAK_ON_SEMICOLON) == 0 ? 0 : 1;
case_sensitive =
(processing_options & FIGPAR_CASE_SENSITIVE) == 0 ? 0 : 1;
require_equals =
(processing_options & FIGPAR_REQUIRE_EQUALS) == 0 ? 0 : 1;
strict_equals =
(processing_options & FIGPAR_STRICT_EQUALS) == 0 ? 0 : 1;
directive = value = 0;
vsize = dsize = 0;
if (realpath(path, rpath) == 0)
return (-1);
if ((fd = open(rpath, O_RDONLY)) < 0)
return (-1);
while (r != 0) {
r = read(fd, p, 1);
while (r != 0 && (isspace(*p) || *p == '#' || comment ||
(bsemicolon && *p == ';'))) {
if (*p == '#')
comment = 1;
else if (*p == '\n') {
comment = 0;
line++;
}
r = read(fd, p, 1);
}
if (r == 0) {
close(fd);
return (0);
}
if ((curpos = lseek(fd, 0, SEEK_CUR)) == -1) {
close(fd);
return (-1);
}
curpos--;
for (n = 0; r != 0; n++) {
if (isspace(*p))
break;
if (bequals && *p == '=') {
have_equals = 1;
break;
}
if (bsemicolon && *p == ';')
break;
r = read(fd, p, 1);
}
if (n == 0 && r == 0) {
close(fd);
return (0);
}
if (lseek(fd, curpos, SEEK_SET) == -1) {
close(fd);
return (-1);
}
if (n > dsize) {
if ((directive = realloc(directive, n + 1)) == NULL) {
close(fd);
return (-1);
}
dsize = n;
}
r = read(fd, directive, n);
if (bequals && *p == '=') {
if (lseek(fd, 1, SEEK_CUR) != -1)
r = read(fd, p, 1);
if (strict_equals && isspace(*p))
*p = '\n';
}
directive[n] = '\0';
if (!case_sensitive)
strtolower(directive);
if (!(bsemicolon && *p == ';') &&
!(strict_equals && *p == '=')) {
while (r != 0 && isspace(*p) && *p != '\n')
r = read(fd, p, 1);
}
if (r != 0 && bequals && *p == '=' && !strict_equals) {
have_equals = 1;
r = read(fd, p, 1);
while (r != 0 && isspace(*p) && *p != '\n')
r = read(fd, p, 1);
}
if (r == 0 || *p == '\n' || *p == '#' ||
(bsemicolon && *p == ';')) {
if (value == NULL && (value = malloc(1)) == NULL) {
close(fd);
return (-1);
}
value[0] = '\0';
goto call_function;
}
if ((curpos = lseek(fd, 0, SEEK_CUR)) == -1) {
close(fd);
return (-1);
}
curpos--;
quote = 0;
end = 0;
while (r != 0 && end == 0) {
if (*p != '\"' && *p != '#' && *p != '\n' &&
(!bsemicolon || *p != ';')) {
r = read(fd, p, 1);
continue;
}
if ((charpos = lseek(fd, 0, SEEK_CUR)) == -1) {
close(fd);
return (-1);
}
charpos--;
if (lseek(fd, -2, SEEK_CUR) == -1) {
close(fd);
return (-1);
}
r = read(fd, p, 1);
for (n = 1; *p == '\\'; n++) {
if (lseek(fd, -2, SEEK_CUR) == -1) {
close(fd);
return (-1);
}
r = read(fd, p, 1);
}
if (lseek(fd, charpos, SEEK_SET) == -1) {
close(fd);
return (-1);
}
r = read(fd, p, 1);
if ((n & 1) == 1) {
switch (*p) {
case '\"':
quote = !quote;
break;
case '#':
if (!quote)
end = 1;
break;
case '\n':
end = 1;
case ';':
if (!quote && bsemicolon)
end = 1;
break;
}
} else if (*p == '\n')
line++;
r = read(fd, p, 1);
}
if ((charpos = lseek(fd, 0, SEEK_CUR)) == -1) {
close(fd);
return (-1);
}
n = (uint32_t)(charpos - curpos);
if (r != 0)
n--;
if (lseek(fd, curpos, SEEK_SET) == -1) {
close(fd);
return (-1);
}
if (n > vsize) {
if ((value = realloc(value, n + 1)) == NULL) {
close(fd);
return (-1);
}
vsize = n;
}
r = read(fd, value, n);
value[n] = '\0';
t = value + n;
while (isspace(*--t))
*t = '\0';
x = strcount(value, "\\\"");
if (x != 0 && (n + x) > vsize) {
if ((value = realloc(value, n + x + 1)) == NULL) {
close(fd);
return (-1);
}
vsize = n + x;
}
if (replaceall(value, "\\\"", "\\\\\"") < 0) {
close(fd);
return (-1);
}
if (replaceall(value, "\\\n", "") < 0) {
close(fd);
return (-1);
}
strexpand(value);
call_function:
if (require_equals && !have_equals)
return (-1);
found = have_equals = 0;
if (options == NULL && unknown != NULL) {
error = unknown(NULL, line, directive, value);
if (error != 0) {
close(fd);
return (error);
}
continue;
}
for (n = 0; options[n].directive != NULL; n++) {
error = fnmatch(options[n].directive, directive,
FNM_NOESCAPE);
if (error == 0) {
found = 1;
if (options[n].action != NULL) {
error = options[n].action(
&options[n],
line, directive, value);
if (error != 0) {
close(fd);
return (error);
}
}
} else if (error != FNM_NOMATCH) {
close(fd);
return (-1);
}
}
if (!found && unknown != NULL) {
error = unknown(NULL, line, directive, value);
if (error != 0) {
close(fd);
return (error);
}
}
}
close(fd);
return (0);
}