blob: 02130abd431e32f08f5790f745269433f90fac01 [file] [log] [blame]
/* ----------------------------------------------------------------------------
libconfig - A library for processing structured configuration files
Copyright (C) 2005-2020 Mark A Lindner
This file is part of libconfig.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, see
<http://www.gnu.org/licenses/>.
----------------------------------------------------------------------------
*/
#include "scanctx.h"
#include "strvec.h"
#include "wincompat.h"
#include "util.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
/* ------------------------------------------------------------------------- */
static const char *err_bad_include = "cannot open include file";
static const char *err_include_too_deep = "include file nesting too deep";
/* ------------------------------------------------------------------------- */
void libconfig_scanctx_init(struct scan_context *ctx, const char *top_filename)
{
__zero(ctx);
if(top_filename)
{
ctx->top_filename = strdup(top_filename);
libconfig_strvec_append(&(ctx->filenames), ctx->top_filename);
}
}
/* ------------------------------------------------------------------------- */
const char **libconfig_scanctx_cleanup(struct scan_context *ctx)
{
int i;
for(i = 0; i < ctx->stack_depth; ++i)
{
struct include_stack_frame *frame = &(ctx->include_stack[i]);
if(frame->current_stream)
fclose(frame->current_stream);
__delete(frame->files);
}
__delete(libconfig_strbuf_release(&(ctx->string)));
return(libconfig_strvec_release(&(ctx->filenames)));
}
/* ------------------------------------------------------------------------- */
FILE *libconfig_scanctx_push_include(struct scan_context *ctx, void *prev_buffer,
const char *path, const char **error)
{
struct include_stack_frame *frame;
const char **files = NULL, **f;
FILE *fp;
if(ctx->stack_depth == MAX_INCLUDE_DEPTH)
{
*error = err_include_too_deep;
return(NULL);
}
*error = NULL;
if(ctx->config->include_fn)
files = ctx->config->include_fn(ctx->config, ctx->config->include_dir,
path, error);
if(*error || !files)
{
libconfig_strvec_delete(files);
return(NULL);
}
if(!*files)
{
libconfig_strvec_delete(files);
return(NULL);
}
frame = &(ctx->include_stack[ctx->stack_depth]);
for(f = files; *f; ++f)
libconfig_strvec_append(&(ctx->filenames), *f);
frame->files = files;
frame->current_file = NULL;
frame->current_stream = NULL;
frame->parent_buffer = prev_buffer;
++(ctx->stack_depth);
fp = libconfig_scanctx_next_include_file(ctx, error);
if(!fp)
(void)libconfig_scanctx_pop_include(ctx);
return(fp);
}
/* ------------------------------------------------------------------------- */
FILE *libconfig_scanctx_next_include_file(struct scan_context *ctx,
const char **error)
{
struct include_stack_frame *include_frame;
*error = NULL;
if(ctx->stack_depth == 0)
return(NULL);
include_frame = &(ctx->include_stack[ctx->stack_depth - 1]);
if(include_frame->current_file)
++(include_frame->current_file);
else
include_frame->current_file = include_frame->files;
if(include_frame->current_stream)
{
fclose(include_frame->current_stream);
include_frame->current_stream = NULL;
}
if(!*(include_frame->current_file))
return(NULL);
include_frame->current_stream = fopen(*(include_frame->current_file), "rt");
if(!include_frame->current_stream)
*error = err_bad_include;
return(include_frame->current_stream);
}
/* ------------------------------------------------------------------------- */
void *libconfig_scanctx_pop_include(struct scan_context *ctx)
{
struct include_stack_frame *frame;
if(ctx->stack_depth == 0)
return(NULL); /* stack underflow */
frame = &(ctx->include_stack[--(ctx->stack_depth)]);
__delete(frame->files);
frame->files = NULL;
if(frame->current_stream)
{
fclose(frame->current_stream);
frame->current_stream = NULL;
}
return(frame->parent_buffer);
}
/* ------------------------------------------------------------------------- */
char *libconfig_scanctx_take_string(struct scan_context *ctx)
{
char *r = libconfig_strbuf_release(&(ctx->string));
return(r ? r : strdup(""));
}
/* ------------------------------------------------------------------------- */
const char *libconfig_scanctx_current_filename(struct scan_context *ctx)
{
if(ctx->stack_depth > 0)
return(*(ctx->include_stack[ctx->stack_depth - 1].current_file));
return(ctx->top_filename);
}
/* ------------------------------------------------------------------------- */