/*
* Copyright (C) 2014 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <limits.h>
#include <ctype.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "parser_helper.h"
#include <collections/array.h>
typedef struct private_parser_helper_t private_parser_helper_t;
typedef struct parser_helper_file_t parser_helper_file_t;
struct private_parser_helper_t {
/**
* Public interface.
*/
parser_helper_t public;
/**
* Stack of included files, as parser_helper_file_t.
*/
array_t *files;
/**
* Helper for parsing strings.
*/
bio_writer_t *writer;
};
struct parser_helper_file_t {
/**
* File name
*/
char *name;
/**
* File stream
*/
FILE *file;
/**
* Enumerator of paths matching the most recent inclusion pattern.
*/
enumerator_t *matches;
};
/**
* Destroy the given file data.
*/
static void parser_helper_file_destroy(parser_helper_file_t *this)
{
if (this->file)
{
fclose(this->file);
}
free(this->name);
DESTROY_IF(this->matches);
free(this);
}
/**
* Returns the current file, if any.
*/
static parser_helper_file_t *current_file(private_parser_helper_t *this)
{
parser_helper_file_t *file;
array_get(this->files, ARRAY_TAIL, &file);
if (file->name)
{
return file;
}
return NULL;
}
METHOD(parser_helper_t, file_next, FILE*,
private_parser_helper_t *this)
{
parser_helper_file_t *file, *next;
struct stat st;
char *name;
array_get(this->files, ARRAY_TAIL, &file);
if (!file->matches && file->name)
{
array_remove(this->files, ARRAY_TAIL, NULL);
parser_helper_file_destroy(file);
/* continue with previous includes, if any */
array_get(this->files, ARRAY_TAIL, &file);
}
if (file->matches)
{
while (file->matches->enumerate(file->matches, &name, NULL))
{
INIT(next,
.name = strdup(name),
.file = fopen(name, "r"),
);
if (next->file && fstat(fileno(next->file), &st) == 0 &&
S_ISREG(st.st_mode))
{
array_insert(this->files, ARRAY_TAIL, next);
return next->file;
}
PARSER_DBG2(&this->public, "unable to open '%s'", name);
parser_helper_file_destroy(next);
}
file->matches->destroy(file->matches);
file->matches = NULL;
}
return NULL;
}
METHOD(parser_helper_t, file_include, void,
private_parser_helper_t *this, char *pattern)
{
parser_helper_file_t *file;
char pat[PATH_MAX];
array_get(this->files, ARRAY_TAIL, &file);
if (!pattern || !*pattern)
{
PARSER_DBG1(&this->public, "no include pattern specified, ignored");
file->matches = enumerator_create_empty();
return;
}
if (!file->name || path_absolute(pattern))
{ /* absolute path */
if (snprintf(pat, sizeof(pat), "%s", pattern) >= sizeof(pat))
{
PARSER_DBG1(&this->public, "include pattern too long, ignored");
file->matches = enumerator_create_empty();
return;
}
}
else
{ /* base relative paths to the directory of the current file */
char *dir = path_dirname(file->name);
if (snprintf(pat, sizeof(pat), "%s%s%s", dir, DIRECTORY_SEPARATOR,
pattern) >= sizeof(pat))
{
PARSER_DBG1(&this->public, "include pattern too long, ignored");
free(dir);
file->matches = enumerator_create_empty();
return;
}
free(dir);
}
file->matches = enumerator_create_glob(pat);
if (!file->matches)
{ /* if glob(3) is not available, try to load pattern directly */
file->matches = enumerator_create_single(strdup(pat), free);
}
}
METHOD(parser_helper_t, string_init, void,
private_parser_helper_t *this)
{
chunk_t data;
data = this->writer->extract_buf(this->writer);
chunk_free(&data);
}
METHOD(parser_helper_t, string_add, void,
private_parser_helper_t *this, char *str)
{
this->writer->write_data(this->writer, chunk_from_str(str));
}
METHOD(parser_helper_t, string_get, char*,
private_parser_helper_t *this)
{
chunk_t data;
this->writer->write_data(this->writer, chunk_from_chars('\0'));
data = this->writer->extract_buf(this->writer);
return data.ptr;
}
METHOD(parser_helper_t, destroy, void,
private_parser_helper_t *this)
{
array_destroy_function(this->files, (void*)parser_helper_file_destroy, NULL);
this->writer->destroy(this->writer);
free(this);
}
/**
* Described in header
*/
void parser_helper_log(int level, parser_helper_t *ctx, char *fmt, ...)
{
private_parser_helper_t *this = (private_parser_helper_t*)ctx;
parser_helper_file_t *file;
char msg[8192];
va_list args;
int line;
va_start(args, fmt);
vsnprintf(msg, sizeof(msg), fmt, args);
va_end(args);
file = current_file(this);
line = ctx->get_lineno ? ctx->get_lineno(ctx->scanner) : 0;
if (file)
{
dbg(DBG_CFG, level, "%s:%d: %s", file->name, line, msg);
}
else
{
dbg(DBG_CFG, level, "%s", msg);
}
}
/**
* Described in header
*/
parser_helper_t *parser_helper_create(void *context)
{
private_parser_helper_t *this;
parser_helper_file_t *sentinel;
INIT(this,
.public = {
.context = context,
.file_include = _file_include,
.file_next = _file_next,
.string_init = _string_init,
.string_add = _string_add,
.string_get = _string_get,
.destroy = _destroy,
},
.files = array_create(0, 0),
.writer = bio_writer_create(0),
);
INIT(sentinel,
.name = NULL,
);
array_insert(this->files, ARRAY_TAIL, sentinel);
return &this->public;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>