Commit 0679cf7c authored by Anders Blomdell's avatar Anders Blomdell
Browse files

Rudimentary data structures

parent 83cf5fbb
LIBRARIES=libmoberg.so
CCFLAGS+=-Wall -Werror -I. -g
LDFLAGS+=-Lbuild/ -lmoberg
CCFLAGS+=-Wall -Werror -I$(shell pwd) -g
LDFLAGS+=-L$(shell pwd)/build/ -lmoberg
MODULES:=$(wildcard modules/*)
export CCFLAGS LDFLAGS
LDFLAGS_parse_config=-ldl -export-dynamic
all: $(LIBRARIES:%=build/%) $(MODULES) test/test_c parse_config
all: $(LIBRARIES:%=build/%) $(MODULES) parse_config
echo $(MODULES)
echo $(CCFLAGS)
build/libmoberg.so: moberg.c Makefile | build
$(CC) -o $@ $(CCFLAGS) -lxdg-basedir -shared -fPIC -I. $<
$(CC) -o $@ $(CCFLAGS) -shared -fPIC -I. $< \
$(filter %.o,$^) -lxdg-basedir -ldl
build:
mkdir $@
build/lib build:
mkdir -p $@
%: build/%.o Makefile
$(CC) $(LDFLAGS) $(LDFLAGS_$(*)) -o $@ $(filter %.o,$^)
......@@ -20,6 +22,9 @@ build:
build/%.o: %.c Makefile
$(CC) $(CCFLAGS) -c -o $@ $<
build/lib/%.o: %.c Makefile | build/lib
$(CC) $(CCFLAGS) -c -fPIC -o $@ $<
.PHONY: $(MODULES)
$(MODULES):
......@@ -28,18 +33,19 @@ $(MODULES):
.PHONY: test
test: all
$(MAKE) -C test test
LD_LIBRARY_PATH=build \
valgrind ./parse_config test/*/*.conf
LD_LIBRARY_PATH=build HOME=$$(pwd)/test \
valgrind -q --error-exitcode=1 test/test_c
# LD_LIBRARY_PATH=build HOME=$$(pwd)/test \
# valgrind -q --error-exitcode=1 test/test_c
test/test_c: test/test_c.c Makefile
$(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $<
clean:
rm build/*
build/libmoberg.so: build/lib/moberg_driver.o
build/libmoberg.so: build/lib/moberg_config_parser.o
build/parse_config.o: moberg_config_parser.h
parse_config: build/moberg_driver.o
parse_config: build/parse_config.o
......@@ -10,35 +10,52 @@
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include "moberg.h"
#include <moberg.h>
#include <moberg_config.h>
#include <moberg_config_parser.h>
struct moberg_digital_in_t {
int index;
char *driver;
};
struct moberg_t {
struct moberg {
struct {
int count;
struct moberg_digital_in_t *value;
} digital_in;
};
static void parse_config_at(struct moberg_t *config,
int dirfd,
const char *pathname)
static void parse_config_at(
struct moberg *moberg,
int dirfd,
const char *pathname)
{
if (dirfd >= 0) {
int fd = openat(dirfd, pathname, O_RDONLY);
if (fd >= 0) {
printf("Parsing... %s %d %d\n", pathname, dirfd, fd);
struct stat statbuf;
if (fstat(fd, &statbuf) == 0) {
char *buf = malloc(statbuf.st_size + 1);
if (buf) {
if (read(fd, buf, statbuf.st_size) == statbuf.st_size) {
buf[statbuf.st_size] = 0;
}
printf("Parsing... %s %d %d\n", pathname, dirfd, fd);
struct moberg_config *config;
config = moberg_config_parse(buf);
if (config) {
}
free(buf);
}
}
close(fd);
}
}
}
static int conf_filter(const struct dirent *entry)
static int conf_filter(
const struct dirent *entry)
{
char *dot = strrchr(entry->d_name, '.');
if (dot != NULL && strcmp(dot, ".conf") == 0) {
......@@ -48,44 +65,71 @@ static int conf_filter(const struct dirent *entry)
}
}
static void parse_config_dir_at(struct moberg_t *config,
int dirfd)
static void parse_config_dir_at(
struct moberg *config,
int dirfd)
{
if (dirfd >= 0) {
struct dirent **entry = NULL;
int n = scandirat(dirfd, ".", &entry, conf_filter, alphasort);
for (int i = 0 ; i < n ; i++) {
parse_config_at(config, dirfd, entry[i]->d_name);
free(entry[i]);
}
free(entry);
}
}
const struct moberg_t *moberg_init()
struct moberg *moberg_new(
struct moberg_config *config)
{
struct moberg_t *result = malloc(sizeof(struct moberg_t));
const char * const *config_paths = xdgSearchableConfigDirectories(NULL);
const char * const *path;
for (path = config_paths ; *path ; path++) {
int dirfd1 = open(*path, O_DIRECTORY);
if (dirfd >= 0) {
parse_config_at(result, dirfd1, "moberg.conf");
int dirfd2 = openat(dirfd1, "moberg.d", O_DIRECTORY);
if (dirfd2 >= 0) {
parse_config_dir_at(result, dirfd2);
close(dirfd2);
struct moberg *result = malloc(sizeof(*result));
if (result) {
if (! config) {
/* Parse default configuration(s) */
const char * const *config_paths = xdgSearchableConfigDirectories(NULL);
const char * const *path;
for (path = config_paths ; *path ; path++) {
int dirfd1 = open(*path, O_DIRECTORY);
if (dirfd >= 0) {
parse_config_at(result, dirfd1, "moberg.conf");
int dirfd2 = openat(dirfd1, "moberg.d", O_DIRECTORY);
if (dirfd2 >= 0) {
parse_config_dir_at(result, dirfd2);
close(dirfd2);
}
close(dirfd1);
}
free((char*)*path);
}
close(dirfd1);
free((const char **)config_paths);
/* Read environment default */
/* Parse environment overrides */
}
}
free((const char **)config_paths);
/* Read local default */
/* Read environment default */
/* Parse environment overrides */
return result;
}
void moberg_free(struct moberg *moberg)
{
free(moberg);
}
enum moberg_status moberg_start(
struct moberg *moberg,
FILE *f)
{
return moberg_OK;
}
enum moberg_status moberg_stop(
struct moberg *moberg,
FILE *f)
{
return moberg_OK;
}
#ifndef __MOBERG_H__
#define __MOBERG_H__
struct moberg_t;
struct moberg_digital_in_t;
#include <stdio.h>
#include <moberg_config.h>
struct moberg;
enum moberg_status { moberg_OK };
const struct moberg_t *moberg_init();
struct moberg *moberg_new(struct moberg_config *config);
void moberg_free(struct moberg *moberg);
struct moberg_digital_in_t *moberg_open_digital_in(
const struct moberg_t *handle,
/* Input/output functions */
enum moberg_status moberg_analog_in(
double *value,
struct moberg *moberg,
int channel);
enum moberg_status moberg_analog_out(
double value,
struct moberg *moberg,
int channel);
enum moberg_status moberg_digital_in(
int *value,
struct moberg *moberg,
int channel);
enum moberg_status moberg_digital_out(
int value,
struct moberg *moberg,
int channel);
enum moberg_status moberg_encoder_in(
long *value,
struct moberg *moberg,
int channel);
/* Install functionality */
enum moberg_status moberg_start(
struct moberg *moberg,
FILE *f);
enum moberg_status moberg_stop(
struct moberg *moberg,
FILE *f);
#endif
#ifndef __MOBERG_CONFIG_H__
#define __MOBERG_CONFIG_H__
struct moberg_config;
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <moberg_config_parser.h>
#include <moberg_config_parser_module.h>
#include <moberg_driver.h>
#include <moberg_device.h>
typedef enum moberg_config_parser_token_kind kind_t;
typedef struct moberg_config_parser_token token_t;
typedef struct moberg_config_parser_ident ident_t;
#define MAX_EXPECTED 10
typedef struct moberg_config_parser_context {
const char *buf; /* Pointer to data to be parsed */
const char *p; /* current parse location */
token_t token;
struct {
int n;
const char *what[MAX_EXPECTED];
} expected;
} context_t;
static inline int acceptsym(context_t *c,
kind_t kind,
token_t *token)
{
return moberg_config_parser_acceptsym(c, kind, token);
}
static inline int acceptkeyword(context_t *c,
const char *keyword)
{
return moberg_config_parser_acceptkeyword(c, keyword);
}
static const void nextsym_ident(context_t *c)
{
c->token.kind = tok_IDENT;
c->token.u.ident.length = 1;
c->token.u.ident.value = c->p;
c->p++;
while (*c->p) {
switch (*c->p) {
case 'a'...'z':
case 'A'...'Z':
case '0'...'9':
case '_':
c->p++;
c->token.u.ident.length++;
break;
default:
return;
}
}
}
static const void nextsym_integer(context_t *c)
{
c->token.kind = tok_INTEGER;
c->token.u.integer.value = 0;
while (*c->p && '0' <= *c->p && *c->p <= '9') {
c->token.u.integer.value *= 10;
c->token.u.integer.value += *c->p - '0';
c->p++;
}
}
static const void nextsym_string(context_t *c)
{
c->token.kind = tok_STRING;
c->p++;
c->token.u.string.value = c->p;
c->token.u.string.length = 0;
while (*c->p && *c->p != '"') {
if (*c->p == '\\') {
c->token.u.string.length++;
c->p++;
}
if (*c->p) {
c->token.u.string.length++;
c->p++;
}
}
c->p++;
}
static int nextsym(context_t *c)
{
c->token.kind = tok_none;
while (c->p && *c->p && c->token.kind == tok_none) {
if (c->p[0] == '/' && c->p[1] == '*') {
/* Skip comment */
c->p += 2;
while (*c->p && (c->p[0] != '*' || c->p[1] != '/')) {
c->p++;
}
c->p += 2;
continue;
}
switch (*c->p) {
case ' ':
case '\t':
case '\n':
case '\r':
/* Skip whitespace */
c->p++;
break;
case '(':
c->token.kind = tok_LPAREN;
c->p++;
break;
case ')':
c->token.kind = tok_RPAREN;
c->p++;
break;
case '{':
c->token.kind = tok_LBRACE;
c->p++;
break;
case '}':
c->token.kind = tok_RBRACE;
c->p++;
break;
case '[':
c->token.kind = tok_LBRACKET;
c->p++;
break;
case ']':
c->token.kind = tok_RBRACKET;
c->p++;
break;
case '=':
c->token.kind = tok_EQUAL;
c->p++;
break;
case ',':
c->token.kind = tok_COMMA;
c->p++;
break;
case ':':
c->token.kind = tok_COLON;
c->p++;
break;
case ';':
c->token.kind = tok_SEMICOLON;
c->p++;
break;
case '"':
nextsym_string(c);
break;
case 'a'...'z':
case 'A'...'Z':
case '_':
nextsym_ident(c);
break;
case '0'...'9':
nextsym_integer(c);
break;
default:
printf("UNEXPECTED %c\n\n", *c->p);
c->p++;
break;
}
}
if (c->token.kind != tok_none) {
return 1;
} else {
c->token.kind = tok_EOF;
return 0;
}
}
static int peeksym(context_t *c,
kind_t kind,
token_t *token)
{
if (c->token.kind == kind) {
if (token) {
*token = c->token;
}
return 1;
}
return 0;
}
int moberg_config_parser_acceptsym(context_t *c,
kind_t kind,
token_t *token)
{
if (c->token.kind == kind) {
if (token) {
*token = c->token;
}
nextsym(c);
c->expected.n = 0;
return 1;
}
if (c->expected.n < MAX_EXPECTED) {
const char *what = NULL;
switch (kind) {
case tok_none: break;
case tok_EOF: what = "<EOF>"; break;
case tok_LPAREN: what = "("; break;
case tok_RPAREN: what = ")"; break;
case tok_LBRACE: what = "{"; break;
case tok_RBRACE: what = "}"; break;
case tok_LBRACKET: what = "["; break;
case tok_RBRACKET: what = "]"; break;
case tok_EQUAL: what = "="; break;
case tok_COMMA: what = ","; break;
case tok_COLON: what = ":"; break;
case tok_SEMICOLON: what = ";"; break;
case tok_INTEGER: what = "<INTEGER>"; break;
case tok_IDENT: what = "<IDENT>"; break;
case tok_STRING: what = "<STRING>"; break;
}
if (what) {
c->expected.what[c->expected.n] = what;
c->expected.n++;
}
}
return 0;
}
int moberg_config_parser_acceptkeyword(context_t *c,
const char *keyword)
{
token_t t;
if (peeksym(c, tok_IDENT, &t) &&
strncmp(keyword, t.u.ident.value, t.u.ident.length) == 0) {
nextsym(c);
c->expected.n = 0;
return 1;
}
if (c->expected.n < MAX_EXPECTED) {
c->expected.what[c->expected.n] = keyword;
c->expected.n++;
}
return 0;
}
void moberg_config_parser_failed(
struct moberg_config_parser_context *c,
FILE *f)
{
fprintf(f, "EXPECTED ");
for (int i = 0 ; i < c->expected.n; i++) {
if (i > 0) {
fprintf(f, " | ");
}
fprintf(f, "'%s'", c->expected.what[i]);
}
fprintf(f, "\nGOT: ");
switch (c->token.kind) {
case tok_none: break;
case tok_EOF: fprintf(f, "<EOF>"); break;
case tok_LPAREN: fprintf(f, "("); break;
case tok_RPAREN: fprintf(f, ")"); break;
case tok_LBRACE: fprintf(f, "{"); break;
case tok_RBRACE: fprintf(f, "}"); break;
case tok_LBRACKET: fprintf(f, "["); break;
case tok_RBRACKET: fprintf(f, "]"); break;
case tok_EQUAL: fprintf(f, "="); break;
case tok_COMMA: fprintf(f, ","); break;
case tok_COLON: fprintf(f, ":"); break;
case tok_SEMICOLON: fprintf(f, ";"); break;
case tok_INTEGER:
fprintf(f, "%d (<INTEGER>)", c->token.u.integer.value);
break;
case tok_IDENT:
fprintf(f, "%.*s (<IDENT>)",
c->token.u.ident.length, c->token.u.ident.value);
break;
case tok_STRING:
fprintf(f, "\"%.*s'\" (<STRING>)",
c->token.u.string.length, c->token.u.string.value);
break;
}
fprintf(f, "\n%s\n", c->p);
}
static int parse_map_range(context_t *c)
{
token_t low, high;
if (! acceptsym(c, tok_LBRACKET, NULL)) { goto err; }
if (! acceptsym(c, tok_INTEGER, &low)) { goto err; }
if (acceptsym(c, tok_COLON, NULL)) {
if (! acceptsym(c, tok_INTEGER, &high)) { goto err; }
} else {
high = low;
}
if (! acceptsym(c, tok_RBRACKET, NULL)) { goto err; }
return 1;
err:
moberg_config_parser_failed(c, stderr);
exit(1);
return 0;
}
static int parse_map(context_t *c,
struct moberg_driver *driver)
{
if (acceptkeyword(c, "analog_in") ||
acceptkeyword(c, "analog_out") ||
acceptkeyword(c, "digital_in") ||
acceptkeyword(c, "digital_out") ||
acceptkeyword(c, "encoder_in")) {
if (! parse_map_range(c)) { goto err; }
if (! acceptsym(c, tok_EQUAL, NULL)) { goto err; }
driver->module.parse_map(c, 0);
if (! acceptsym(c, tok_SEMICOLON, NULL)) { goto err; }
} else {
goto err;
}
return 1;
err:
moberg_config_parser_failed(c, stderr);
exit(1);
return 0;
}
struct moberg_device *parse_device(context_t *c,
struct moberg_driver *driver)
{
struct moberg_device *result = moberg_device_new(driver);
if (! result) {
goto err;
}
if (! acceptsym(c, tok_LBRACE, NULL)) { goto syntax_err; }
for (;;) {
if (acceptkeyword(c, "config")) {