/** * mds — A micro-display server * Copyright © 2014, 2015, 2016, 2017 Mattias Andrée (m@maandree.se) * * 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 . */ #include "callables.h" #include #include /** * Map, by index, from argument count, to list of callable's names */ static char ***restrict names = NULL; /** * If `callable_list[callabes[i][j]]` and `callable_include_stack_list[callabes[i][j]]` * describe the callable named by `named[i][j]` with either `i` parameters, or the * number of parameters specified by the suffix in `named[i][j]` */ static size_t **restrict callables = NULL; /** * Map the the number of elements in the, by index, corresponding element in `names` */ static size_t *restrict bucket_sizes = NULL; /** * List of callables */ static mds_kbdc_tree_t **restrict callable_list = NULL; /** * List of callables' include-stacks */ static mds_kbdc_include_stack_t **restrict callable_include_stack_list = NULL; /** * The number of buckets in `names` and `bucket_sizes` */ static size_t buckets = 0; /** * The index of the next item in `callable_list` and `callable_include_stack_list` */ static size_t list_ptr = 0; /** * Destroy the callable storage */ void callables_terminate(void) { size_t i, j, n; char **bucket; for (i = 0; i < buckets; i++) { bucket = names[i]; for (j = 0, n = bucket_sizes[i]; j < n; j++) free(bucket[j]); free(bucket); free(callables[i]); } for (i = 0; i < list_ptr; i++) mds_kbdc_include_stack_free(callable_include_stack_list[i]); free(callables), callables = NULL; free(names), names = NULL; free(bucket_sizes), bucket_sizes = NULL; free(callable_list), callable_list = NULL; free(callable_include_stack_list), callable_include_stack_list = NULL; buckets = list_ptr = 0; } /** * Store a callable * * @param name The name of the callable * @param arg_count The number of arguments the callable takes * if `name` is suffixless, otherwise zero * @param callable The callable * @param callable_include_stack The include-stack for the callable * @return Zero on success, -1 on error */ int callables_set(const char *restrict name, size_t arg_count, mds_kbdc_tree_t *restrict callable, mds_kbdc_include_stack_t *restrict callable_include_stack) { #define _yrealloc(var, elements, type) (yrealloc(tmp_##var, var, elements, type)) char *dupname = NULL; char ***tmp_names = NULL; size_t **tmp_callables = NULL; size_t *tmp_bucket_sizes = NULL; char **old_names = NULL; size_t *old_callables = NULL; mds_kbdc_tree_t **tmp_callable_list = NULL; mds_kbdc_include_stack_t **tmp_callable_include_stack_list = NULL; int saved_errno; fail_if (xstrdup(dupname, name)); if (arg_count >= buckets) { fail_if (_yrealloc(names, arg_count + 1, char**)); fail_if (_yrealloc(callables, arg_count + 1, size_t*)); fail_if (_yrealloc(bucket_sizes, arg_count + 1, size_t)); memset(names + buckets, 0, (arg_count + 1 - buckets) * sizeof(char**)); memset(callables + buckets, 0, (arg_count + 1 - buckets) * sizeof(size_t*)); memset(bucket_sizes + buckets, 0, (arg_count + 1 - buckets) * sizeof(size_t)); buckets = arg_count + 1; } fail_if (xxrealloc(old_names, names[arg_count], bucket_sizes[arg_count] + 1, char*)); fail_if (xxrealloc(old_callables, callables[arg_count], bucket_sizes[arg_count] + 1, size_t)); names[arg_count][bucket_sizes[arg_count]] = dupname, dupname = NULL; callables[arg_count][bucket_sizes[arg_count]] = list_ptr; bucket_sizes[arg_count]++; fail_if (_yrealloc(callable_list, list_ptr + 1, mds_kbdc_tree_t*)); fail_if (_yrealloc(callable_include_stack_list, list_ptr + 1, mds_kbdc_include_stack_t*)); callable_list[list_ptr] = callable; callable_include_stack_list[list_ptr] = callable_include_stack; list_ptr++; return 0; fail: saved_errno = errno; free(dupname); if (old_names) names[arg_count] = old_names; if (old_callables) callables[arg_count] = old_callables; return errno = saved_errno, -1; #undef _yrealloc } /** * Get a stored callable * * @param name The name of the callable * @param arg_count The number of arguments the callable takes * if `name` is suffixless, otherwise zero * @param callable Output parameter for the callable, `NULL` if not found * @param callable_include_stack Output parameter for the include-stack for the callable */ void callables_get(const char *restrict name, size_t arg_count, mds_kbdc_tree_t **restrict callable, mds_kbdc_include_stack_t **restrict callable_include_stack) { char **restrict names_; size_t i, n; *callable = NULL; *callable_include_stack = NULL; if (arg_count >= buckets) return; names_ = names[arg_count]; for (i = 0, n = bucket_sizes[arg_count]; i < n; i++) { if (strcmp(names_[i], name)) continue; i = callables[arg_count][i]; *callable = callable_list[i]; *callable_include_stack = callable_include_stack_list[i]; return; } }