/**
* mds — A micro-display server
* Copyright © 2014 Mattias Andrée (maandree@member.fsf.org)
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "include-stack.h"
#include <alloca.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
/**
* Variable whether the latest created error is stored
*/
static mds_kbdc_parse_error_t* error;
/**
* The `result` parameter of root procedure that requires the include stack
*/
static mds_kbdc_parsed_t* result;
/**
* The original value of `result->pathname`
*/
static char* original_pathname;
/**
* The original value of `result->source_code`
*/
static mds_kbdc_source_code_t* original_source_code;
/**
* Stack of visited include-statements
*/
static const mds_kbdc_tree_include_t** restrict includes = NULL;
/**
* The number elements allocated for `includes`
*/
static size_t includes_size = 0;
/**
* The number elements stored in `includes`
*/
size_t includes_ptr = 0;
/**
* The latest saved include-stack
*/
static mds_kbdc_include_stack_t* latest_save = NULL;
/**
* Add “included from here”-notes
*
* @param ptr The number of “included from here”-notes
* @return Zero on success, -1 on error
*/
int mds_kbdc_include_stack_dump(size_t ptr)
{
char* old_pathname = result->pathname;
mds_kbdc_source_code_t* old_source_code = result->source_code;
while (ptr--)
{
result->pathname = ptr ? includes[ptr - 1]->filename : original_pathname;
result->source_code = ptr ? includes[ptr - 1]->source_code : original_source_code;
NEW_ERROR_WITHOUT_INCLUDES(includes[ptr], NOTE, "included from here");
}
result->pathname = old_pathname;
result->source_code = old_source_code;
return 0;
pfail:
result->pathname = old_pathname;
result->source_code = old_source_code;
return -1;
}
/**
* Mark the root of the tree as included
*
* @param result_ The `result` parameter of root procedure that requires the include stack
*/
void mds_kbdc_include_stack_begin(mds_kbdc_parsed_t* restrict result_)
{
latest_save = NULL;
result = result_;
original_pathname = result_->pathname;
original_source_code = result_->source_code;
}
/**
* Mark the root of the tree as no longer being visited,
* and release clean up after the use of this module
*/
void mds_kbdc_include_stack_end(void)
{
latest_save = NULL;
result->pathname = original_pathname;
result->source_code = original_source_code;
free(includes), includes = NULL;
includes_size = includes_ptr = 0;
}
/**
* Mark an include-statement as visited
*
* @param tree The visisted include-statement
* @param data Output parameter with stack information that should be passed to
* the corresponding call to `mds_kbdc_include_stack_pop`, `*data`
* is undefined on error
* @return Zero on success, -1 on error
*/
int mds_kbdc_include_stack_push(const mds_kbdc_tree_include_t* restrict tree, void** data)
{
const mds_kbdc_tree_include_t** old = NULL;
int saved_errno;
if (includes_ptr == includes_size)
fail_if (xxrealloc(old, includes, includes_size += 4, mds_kbdc_tree_include_t*));
fail_if (xmalloc(*data, 3, void*));
((char**)(*data))[0] = result->pathname;
((mds_kbdc_source_code_t**)(*data))[1] = result->source_code;
((mds_kbdc_include_stack_t**)(*data))[2] = latest_save;
includes[includes_ptr++] = tree;
result->pathname = tree->filename;
result->source_code = tree->source_code;
latest_save = NULL;
return 0;
pfail:
saved_errno = errno;
free(old);
return errno = saved_errno, -1;
}
/**
* Undo the lasted not-undone call to `mds_kbdc_include_stack_push`
*
* This function is guaranteed not to modify `errno`
*
* @param data `*data` from `mds_kbdc_include_stack_push`
*/
void mds_kbdc_include_stack_pop(void* data)
{
int saved_errno = errno;
result->pathname = ((char**)data)[0];
result->source_code = ((mds_kbdc_source_code_t**)data)[1];
latest_save = ((mds_kbdc_include_stack_t**)data)[2];
includes_ptr--;
free(data);
errno = saved_errno;
}
/**
* Save the current include-stack
*
* @return The include-stack, `NULL` on error
*/
mds_kbdc_include_stack_t* mds_kbdc_include_stack_save(void)
{
int saved_errno;
if (latest_save)
{
latest_save->duplicates++;
return latest_save;
}
latest_save = malloc(sizeof(mds_kbdc_include_stack_t));
if (latest_save == NULL)
return NULL;
latest_save->stack = NULL;
latest_save->ptr = includes_ptr;
latest_save->duplicates = 0;
if (latest_save->ptr == 0)
return latest_save;
fail_if (xmalloc(latest_save->stack, latest_save->ptr, const mds_kbdc_tree_include_t*));
memcpy(latest_save->stack, includes, latest_save->ptr * sizeof(const mds_kbdc_tree_include_t*));
return latest_save;
pfail:
saved_errno = errno;
free(latest_save->stack);
latest_save = NULL;
errno = saved_errno;
return NULL;
}
/**
* Restore a previous include-stack
*
* @param stack The include-stack
* @return Zero on success, -1 on error
*/
int mds_kbdc_include_stack_restore(mds_kbdc_include_stack_t* restrict stack)
{
const mds_kbdc_tree_include_t** new;
latest_save = stack;
if (stack->ptr > includes_size)
{
new = realloc(includes, stack->ptr * sizeof(const mds_kbdc_tree_include_t*));
if (new == NULL)
return -1;
includes = new;
includes_size = stack->ptr;
}
memcpy(includes, stack->stack, stack->ptr * sizeof(const mds_kbdc_tree_include_t*));
includes_ptr = stack->ptr;
return 0;
}
/**
* Destroy a previous include-stack and free its allocation
*
* @param stack The include-stack
*/
void mds_kbdc_include_stack_free(mds_kbdc_include_stack_t* restrict stack)
{
if ((stack == NULL) || (stack->duplicates--))
return;
free(stack->stack);
free(stack);
if (latest_save == stack)
latest_save = NULL;
}