/* ---------------------------------------------------------------------------- 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 . ---------------------------------------------------------------------------- */ #include "scanctx.h" #include "strvec.h" #include "wincompat.h" #include "util.h" #include #include #include /* ------------------------------------------------------------------------- */ 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); } /* ------------------------------------------------------------------------- */