parent
1daa1e9b0f
commit
1036f1b00e
@ -0,0 +1,31 @@ |
|||||||
|
idf_build_get_property(target IDF_TARGET) |
||||||
|
|
||||||
|
if(${target} STREQUAL "linux") |
||||||
|
return() # This component is currently not supported by the POSIX/Linux simulator, but we may support it in the |
||||||
|
# future (TODO: IDF-8103) |
||||||
|
endif() |
||||||
|
|
||||||
|
set(argtable_srcs argtable3/arg_cmd.c |
||||||
|
argtable3/arg_date.c |
||||||
|
argtable3/arg_dbl.c |
||||||
|
argtable3/arg_dstr.c |
||||||
|
argtable3/arg_end.c |
||||||
|
argtable3/arg_file.c |
||||||
|
argtable3/arg_hashtable.c |
||||||
|
argtable3/arg_int.c |
||||||
|
argtable3/arg_lit.c |
||||||
|
argtable3/arg_rem.c |
||||||
|
argtable3/arg_rex.c |
||||||
|
argtable3/arg_str.c |
||||||
|
argtable3/arg_utils.c |
||||||
|
argtable3/argtable3.c) |
||||||
|
|
||||||
|
|
||||||
|
idf_component_register(SRCS "commands.c" |
||||||
|
"esp_console_repl.c" |
||||||
|
"split_argv.c" |
||||||
|
"linenoise/linenoise.c" |
||||||
|
${argtable_srcs} |
||||||
|
INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} |
||||||
|
REQUIRES vfs |
||||||
|
PRIV_REQUIRES driver) |
@ -0,0 +1,167 @@ |
|||||||
|
Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
<sheitmann@users.sourceforge.net> |
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions are met: |
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
* Redistributions in binary form must reproduce the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer in the |
||||||
|
documentation and/or other materials provided with the distribution. |
||||||
|
* Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
may be used to endorse or promote products derived from this software |
||||||
|
without specific prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
|
||||||
|
FreeBSD getopt library |
||||||
|
====================== |
||||||
|
|
||||||
|
Copyright (c) 2000 The NetBSD Foundation, Inc. |
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
This code is derived from software contributed to The NetBSD Foundation |
||||||
|
by Dieter Baron and Thomas Klausner. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
1. Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
2. Redistributions in binary form must reproduce the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer in the |
||||||
|
documentation and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
||||||
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
||||||
|
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||||
|
POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
|
||||||
|
Tcl library |
||||||
|
=========== |
||||||
|
|
||||||
|
This software is copyrighted by the Regents of the University of |
||||||
|
California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState |
||||||
|
Corporation and other parties. The following terms apply to all files |
||||||
|
associated with the software unless explicitly disclaimed in |
||||||
|
individual files. |
||||||
|
|
||||||
|
The authors hereby grant permission to use, copy, modify, distribute, |
||||||
|
and license this software and its documentation for any purpose, provided |
||||||
|
that existing copyright notices are retained in all copies and that this |
||||||
|
notice is included verbatim in any distributions. No written agreement, |
||||||
|
license, or royalty fee is required for any of the authorized uses. |
||||||
|
Modifications to this software may be copyrighted by their authors |
||||||
|
and need not follow the licensing terms described here, provided that |
||||||
|
the new terms are clearly indicated on the first page of each file where |
||||||
|
they apply. |
||||||
|
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY |
||||||
|
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
||||||
|
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY |
||||||
|
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE |
||||||
|
POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, |
||||||
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE |
||||||
|
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE |
||||||
|
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR |
||||||
|
MODIFICATIONS. |
||||||
|
|
||||||
|
GOVERNMENT USE: If you are acquiring this software on behalf of the |
||||||
|
U.S. government, the Government shall have only "Restricted Rights" |
||||||
|
in the software and related documentation as defined in the Federal |
||||||
|
Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you |
||||||
|
are acquiring the software on behalf of the Department of Defense, the |
||||||
|
software shall be classified as "Commercial Computer Software" and the |
||||||
|
Government shall have only "Restricted Rights" as defined in Clause |
||||||
|
252.227-7014 (b) (3) of DFARs. Notwithstanding the foregoing, the |
||||||
|
authors grant the U.S. Government and others acting in its behalf |
||||||
|
permission to use and distribute the software in accordance with the |
||||||
|
terms specified in this license. |
||||||
|
|
||||||
|
|
||||||
|
C Hash Table library |
||||||
|
==================== |
||||||
|
|
||||||
|
Copyright (c) 2002, Christopher Clark |
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions |
||||||
|
are met: |
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright |
||||||
|
notice, this list of conditions and the following disclaimer in the |
||||||
|
documentation and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
* Neither the name of the original author; nor the names of any contributors |
||||||
|
may be used to endorse or promote products derived from this software |
||||||
|
without specific prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER |
||||||
|
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
|
||||||
|
|
||||||
|
The Better String library |
||||||
|
========================= |
||||||
|
|
||||||
|
Copyright (c) 2014, Paul Hsieh |
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this |
||||||
|
list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, |
||||||
|
this list of conditions and the following disclaimer in the documentation |
||||||
|
and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
* Neither the name of bstrlib nor the names of its |
||||||
|
contributors may be used to endorse or promote products derived from |
||||||
|
this software without specific prior written permission. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,285 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2013-2019 Tom G. Huang |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_cmd: Provides the sub-command mechanism |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 2013-2019 Tom G. Huang |
||||||
|
* <tomghuang@gmail.com> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#include "argtable3.h" |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <assert.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#define MAX_MODULE_VERSION_SIZE 128 |
||||||
|
|
||||||
|
static arg_hashtable_t* s_hashtable = NULL; |
||||||
|
static char* s_module_name = NULL; |
||||||
|
static int s_mod_ver_major = 0; |
||||||
|
static int s_mod_ver_minor = 0; |
||||||
|
static int s_mod_ver_patch = 0; |
||||||
|
static char* s_mod_ver_tag = NULL; |
||||||
|
static char* s_mod_ver = NULL; |
||||||
|
|
||||||
|
void arg_set_module_name(const char* name) { |
||||||
|
size_t slen; |
||||||
|
|
||||||
|
xfree(s_module_name); |
||||||
|
slen = strlen(name); |
||||||
|
s_module_name = (char*)xmalloc(slen + 1); |
||||||
|
memset(s_module_name, 0, slen + 1); |
||||||
|
|
||||||
|
#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) |
||||||
|
strncpy_s(s_module_name, slen + 1, name, slen); |
||||||
|
#else |
||||||
|
memcpy(s_module_name, name, slen); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
void arg_set_module_version(int major, int minor, int patch, const char* tag) { |
||||||
|
size_t slen_tag, slen_ds; |
||||||
|
arg_dstr_t ds; |
||||||
|
|
||||||
|
s_mod_ver_major = major; |
||||||
|
s_mod_ver_minor = minor; |
||||||
|
s_mod_ver_patch = patch; |
||||||
|
|
||||||
|
xfree(s_mod_ver_tag); |
||||||
|
slen_tag = strlen(tag); |
||||||
|
s_mod_ver_tag = (char*)xmalloc(slen_tag + 1); |
||||||
|
memset(s_mod_ver_tag, 0, slen_tag + 1); |
||||||
|
|
||||||
|
#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) |
||||||
|
strncpy_s(s_mod_ver_tag, slen_tag + 1, tag, slen_tag); |
||||||
|
#else |
||||||
|
memcpy(s_mod_ver_tag, tag, slen_tag); |
||||||
|
#endif |
||||||
|
|
||||||
|
ds = arg_dstr_create(); |
||||||
|
arg_dstr_catf(ds, "%d.", s_mod_ver_major); |
||||||
|
arg_dstr_catf(ds, "%d.", s_mod_ver_minor); |
||||||
|
arg_dstr_catf(ds, "%d.", s_mod_ver_patch); |
||||||
|
arg_dstr_cat(ds, s_mod_ver_tag); |
||||||
|
|
||||||
|
xfree(s_mod_ver); |
||||||
|
slen_ds = strlen(arg_dstr_cstr(ds)); |
||||||
|
s_mod_ver = (char*)xmalloc(slen_ds + 1); |
||||||
|
memset(s_mod_ver, 0, slen_ds + 1); |
||||||
|
|
||||||
|
#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) |
||||||
|
strncpy_s(s_mod_ver, slen_ds + 1, arg_dstr_cstr(ds), slen_ds); |
||||||
|
#else |
||||||
|
memcpy(s_mod_ver, arg_dstr_cstr(ds), slen_ds); |
||||||
|
#endif |
||||||
|
|
||||||
|
arg_dstr_destroy(ds); |
||||||
|
} |
||||||
|
|
||||||
|
static unsigned int hash_key(const void* key) { |
||||||
|
const char* str = (const char*)key; |
||||||
|
int c; |
||||||
|
unsigned int hash = 5381; |
||||||
|
|
||||||
|
while ((c = *str++) != 0) |
||||||
|
hash = ((hash << 5) + hash) + (unsigned int)c; /* hash * 33 + c */ |
||||||
|
|
||||||
|
return hash; |
||||||
|
} |
||||||
|
|
||||||
|
static int equal_keys(const void* key1, const void* key2) { |
||||||
|
char* k1 = (char*)key1; |
||||||
|
char* k2 = (char*)key2; |
||||||
|
return (0 == strcmp(k1, k2)); |
||||||
|
} |
||||||
|
|
||||||
|
void arg_cmd_init(void) { |
||||||
|
s_hashtable = arg_hashtable_create(32, hash_key, equal_keys); |
||||||
|
} |
||||||
|
|
||||||
|
void arg_cmd_uninit(void) { |
||||||
|
arg_hashtable_destroy(s_hashtable, 1); |
||||||
|
} |
||||||
|
|
||||||
|
void arg_cmd_register(const char* name, arg_cmdfn* proc, const char* description) { |
||||||
|
arg_cmd_info_t* cmd_info; |
||||||
|
size_t slen_name; |
||||||
|
void* k; |
||||||
|
|
||||||
|
assert(strlen(name) < ARG_CMD_NAME_LEN); |
||||||
|
assert(strlen(description) < ARG_CMD_DESCRIPTION_LEN); |
||||||
|
|
||||||
|
/* Check if the command already exists. */ |
||||||
|
/* If the command exists, replace the existing command. */ |
||||||
|
/* If the command doesn't exist, insert the command. */ |
||||||
|
cmd_info = (arg_cmd_info_t*)arg_hashtable_search(s_hashtable, name); |
||||||
|
if (cmd_info) { |
||||||
|
arg_hashtable_remove(s_hashtable, name); |
||||||
|
cmd_info = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
cmd_info = (arg_cmd_info_t*)xmalloc(sizeof(arg_cmd_info_t)); |
||||||
|
memset(cmd_info, 0, sizeof(arg_cmd_info_t)); |
||||||
|
|
||||||
|
#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) |
||||||
|
strncpy_s(cmd_info->name, ARG_CMD_NAME_LEN, name, strlen(name)); |
||||||
|
strncpy_s(cmd_info->description, ARG_CMD_DESCRIPTION_LEN, description, strlen(description)); |
||||||
|
#else |
||||||
|
memcpy(cmd_info->name, name, strlen(name)); |
||||||
|
memcpy(cmd_info->description, description, strlen(description)); |
||||||
|
#endif |
||||||
|
|
||||||
|
cmd_info->proc = proc; |
||||||
|
|
||||||
|
slen_name = strlen(name); |
||||||
|
k = xmalloc(slen_name + 1); |
||||||
|
memset(k, 0, slen_name + 1); |
||||||
|
|
||||||
|
#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || (defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__)) |
||||||
|
strncpy_s((char*)k, slen_name + 1, name, slen_name); |
||||||
|
#else |
||||||
|
memcpy((char*)k, name, slen_name); |
||||||
|
#endif |
||||||
|
|
||||||
|
arg_hashtable_insert(s_hashtable, k, cmd_info); |
||||||
|
} |
||||||
|
|
||||||
|
void arg_cmd_unregister(const char* name) { |
||||||
|
arg_hashtable_remove(s_hashtable, name); |
||||||
|
} |
||||||
|
|
||||||
|
int arg_cmd_dispatch(const char* name, int argc, char* argv[], arg_dstr_t res) { |
||||||
|
arg_cmd_info_t* cmd_info = arg_cmd_info(name); |
||||||
|
|
||||||
|
assert(cmd_info != NULL); |
||||||
|
assert(cmd_info->proc != NULL); |
||||||
|
|
||||||
|
return cmd_info->proc(argc, argv, res); |
||||||
|
} |
||||||
|
|
||||||
|
arg_cmd_info_t* arg_cmd_info(const char* name) { |
||||||
|
return (arg_cmd_info_t*)arg_hashtable_search(s_hashtable, name); |
||||||
|
} |
||||||
|
|
||||||
|
unsigned int arg_cmd_count(void) { |
||||||
|
return arg_hashtable_count(s_hashtable); |
||||||
|
} |
||||||
|
|
||||||
|
arg_cmd_itr_t arg_cmd_itr_create(void) { |
||||||
|
return (arg_cmd_itr_t)arg_hashtable_itr_create(s_hashtable); |
||||||
|
} |
||||||
|
|
||||||
|
int arg_cmd_itr_advance(arg_cmd_itr_t itr) { |
||||||
|
return arg_hashtable_itr_advance((arg_hashtable_itr_t*)itr); |
||||||
|
} |
||||||
|
|
||||||
|
char* arg_cmd_itr_key(arg_cmd_itr_t itr) { |
||||||
|
return (char*)arg_hashtable_itr_key((arg_hashtable_itr_t*)itr); |
||||||
|
} |
||||||
|
|
||||||
|
arg_cmd_info_t* arg_cmd_itr_value(arg_cmd_itr_t itr) { |
||||||
|
return (arg_cmd_info_t*)arg_hashtable_itr_value((arg_hashtable_itr_t*)itr); |
||||||
|
} |
||||||
|
|
||||||
|
void arg_cmd_itr_destroy(arg_cmd_itr_t itr) { |
||||||
|
arg_hashtable_itr_destroy((arg_hashtable_itr_t*)itr); |
||||||
|
} |
||||||
|
|
||||||
|
int arg_cmd_itr_search(arg_cmd_itr_t itr, void* k) { |
||||||
|
return arg_hashtable_itr_search((arg_hashtable_itr_t*)itr, s_hashtable, k); |
||||||
|
} |
||||||
|
|
||||||
|
static const char* module_name(void) { |
||||||
|
if (s_module_name == NULL || strlen(s_module_name) == 0) |
||||||
|
return "<name>"; |
||||||
|
|
||||||
|
return s_module_name; |
||||||
|
} |
||||||
|
|
||||||
|
static const char* module_version(void) { |
||||||
|
if (s_mod_ver == NULL || strlen(s_mod_ver) == 0) |
||||||
|
return "0.0.0.0"; |
||||||
|
|
||||||
|
return s_mod_ver; |
||||||
|
} |
||||||
|
|
||||||
|
void arg_make_get_help_msg(arg_dstr_t res) { |
||||||
|
arg_dstr_catf(res, "%s v%s\n", module_name(), module_version()); |
||||||
|
arg_dstr_catf(res, "Please type '%s help' to get more information.\n", module_name()); |
||||||
|
} |
||||||
|
|
||||||
|
void arg_make_help_msg(arg_dstr_t ds, char* cmd_name, void** argtable) { |
||||||
|
arg_cmd_info_t* cmd_info = (arg_cmd_info_t*)arg_hashtable_search(s_hashtable, cmd_name); |
||||||
|
if (cmd_info) { |
||||||
|
arg_dstr_catf(ds, "%s: %s\n", cmd_name, cmd_info->description); |
||||||
|
} |
||||||
|
|
||||||
|
arg_dstr_cat(ds, "Usage:\n"); |
||||||
|
arg_dstr_catf(ds, " %s", module_name()); |
||||||
|
|
||||||
|
arg_print_syntaxv_ds(ds, argtable, "\n \nAvailable options:\n"); |
||||||
|
arg_print_glossary_ds(ds, argtable, " %-23s %s\n"); |
||||||
|
|
||||||
|
arg_dstr_cat(ds, "\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void arg_make_syntax_err_msg(arg_dstr_t ds, void** argtable, struct arg_end* end) { |
||||||
|
arg_print_errors_ds(ds, end, module_name()); |
||||||
|
arg_dstr_cat(ds, "Usage: \n"); |
||||||
|
arg_dstr_catf(ds, " %s", module_name()); |
||||||
|
arg_print_syntaxv_ds(ds, argtable, "\n"); |
||||||
|
arg_dstr_cat(ds, "\n"); |
||||||
|
} |
||||||
|
|
||||||
|
int arg_make_syntax_err_help_msg(arg_dstr_t ds, char* name, int help, int nerrors, void** argtable, struct arg_end* end, int* exitcode) { |
||||||
|
/* help handling
|
||||||
|
* note: '-h|--help' takes precedence over error reporting |
||||||
|
*/ |
||||||
|
if (help > 0) { |
||||||
|
arg_make_help_msg(ds, name, argtable); |
||||||
|
*exitcode = EXIT_SUCCESS; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
/* syntax error handling */ |
||||||
|
if (nerrors > 0) { |
||||||
|
arg_make_syntax_err_msg(ds, argtable, end); |
||||||
|
*exitcode = EXIT_FAILURE; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,575 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_date: Implements the date command-line option |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* <sheitmann@users.sourceforge.net> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#include "argtable3.h" |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
char* arg_strptime(const char* buf, const char* fmt, struct tm* tm); |
||||||
|
|
||||||
|
static void arg_date_resetfn(struct arg_date* parent) { |
||||||
|
ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); |
||||||
|
parent->count = 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_date_scanfn(struct arg_date* parent, const char* argval) { |
||||||
|
int errorcode = 0; |
||||||
|
|
||||||
|
if (parent->count == parent->hdr.maxcount) { |
||||||
|
errorcode = ARG_ERR_MAXCOUNT; |
||||||
|
} else if (!argval) { |
||||||
|
/* no argument value was given, leave parent->tmval[] unaltered but still count it */ |
||||||
|
parent->count++; |
||||||
|
} else { |
||||||
|
const char* pend; |
||||||
|
struct tm tm = parent->tmval[parent->count]; |
||||||
|
|
||||||
|
/* parse the given argument value, store result in parent->tmval[] */ |
||||||
|
pend = arg_strptime(argval, parent->format, &tm); |
||||||
|
if (pend && pend[0] == '\0') |
||||||
|
parent->tmval[parent->count++] = tm; |
||||||
|
else |
||||||
|
errorcode = ARG_ERR_BADDATE; |
||||||
|
} |
||||||
|
|
||||||
|
ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_date_checkfn(struct arg_date* parent) { |
||||||
|
int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; |
||||||
|
|
||||||
|
ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static void arg_date_errorfn(struct arg_date* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { |
||||||
|
const char* shortopts = parent->hdr.shortopts; |
||||||
|
const char* longopts = parent->hdr.longopts; |
||||||
|
const char* datatype = parent->hdr.datatype; |
||||||
|
|
||||||
|
/* make argval NULL safe */ |
||||||
|
argval = argval ? argval : ""; |
||||||
|
|
||||||
|
arg_dstr_catf(ds, "%s: ", progname); |
||||||
|
switch (errorcode) { |
||||||
|
case ARG_ERR_MINCOUNT: |
||||||
|
arg_dstr_cat(ds, "missing option "); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARG_ERR_MAXCOUNT: |
||||||
|
arg_dstr_cat(ds, "excess option "); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARG_ERR_BADDATE: { |
||||||
|
struct tm tm; |
||||||
|
char buff[200]; |
||||||
|
|
||||||
|
arg_dstr_catf(ds, "illegal timestamp format \"%s\"\n", argval); |
||||||
|
memset(&tm, 0, sizeof(tm)); |
||||||
|
arg_strptime("1999-12-31 23:59:59", "%F %H:%M:%S", &tm); |
||||||
|
strftime(buff, sizeof(buff), parent->format, &tm); |
||||||
|
arg_dstr_catf(ds, "correct format is \"%s\"\n", buff); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary) { |
||||||
|
return arg_daten(shortopts, longopts, format, datatype, 0, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary) { |
||||||
|
return arg_daten(shortopts, longopts, format, datatype, 1, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_date* |
||||||
|
arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary) { |
||||||
|
size_t nbytes; |
||||||
|
struct arg_date* result; |
||||||
|
|
||||||
|
/* foolproof things by ensuring maxcount is not less than mincount */ |
||||||
|
maxcount = (maxcount < mincount) ? mincount : maxcount; |
||||||
|
|
||||||
|
/* default time format is the national date format for the locale */ |
||||||
|
if (!format) |
||||||
|
format = "%x"; |
||||||
|
|
||||||
|
nbytes = sizeof(struct arg_date) /* storage for struct arg_date */ |
||||||
|
+ (size_t)maxcount * sizeof(struct tm); /* storage for tmval[maxcount] array */ |
||||||
|
|
||||||
|
/* allocate storage for the arg_date struct + tmval[] array. */ |
||||||
|
/* we use calloc because we want the tmval[] array zero filled. */ |
||||||
|
result = (struct arg_date*)xcalloc(1, nbytes); |
||||||
|
|
||||||
|
/* init the arg_hdr struct */ |
||||||
|
result->hdr.flag = ARG_HASVALUE; |
||||||
|
result->hdr.shortopts = shortopts; |
||||||
|
result->hdr.longopts = longopts; |
||||||
|
result->hdr.datatype = datatype ? datatype : format; |
||||||
|
result->hdr.glossary = glossary; |
||||||
|
result->hdr.mincount = mincount; |
||||||
|
result->hdr.maxcount = maxcount; |
||||||
|
result->hdr.parent = result; |
||||||
|
result->hdr.resetfn = (arg_resetfn*)arg_date_resetfn; |
||||||
|
result->hdr.scanfn = (arg_scanfn*)arg_date_scanfn; |
||||||
|
result->hdr.checkfn = (arg_checkfn*)arg_date_checkfn; |
||||||
|
result->hdr.errorfn = (arg_errorfn*)arg_date_errorfn; |
||||||
|
|
||||||
|
/* store the tmval[maxcount] array immediately after the arg_date struct */ |
||||||
|
result->tmval = (struct tm*)(result + 1); |
||||||
|
|
||||||
|
/* init the remaining arg_date member variables */ |
||||||
|
result->count = 0; |
||||||
|
result->format = format; |
||||||
|
|
||||||
|
ARG_TRACE(("arg_daten() returns %p\n", result)); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* This code was contributed to The NetBSD Foundation by Klaus Klein. |
||||||
|
* Heavily optimised by David Laight |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions |
||||||
|
* are met: |
||||||
|
* 1. Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||||
|
* POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <ctype.h> |
||||||
|
#include <string.h> |
||||||
|
#include <time.h> |
||||||
|
|
||||||
|
/*
|
||||||
|
* We do not implement alternate representations. However, we always |
||||||
|
* check whether a given modifier is allowed for a certain conversion. |
||||||
|
*/ |
||||||
|
#define ALT_E 0x01 |
||||||
|
#define ALT_O 0x02 |
||||||
|
#define LEGAL_ALT(x) \ |
||||||
|
{ \
|
||||||
|
if (alt_format & ~(x)) \
|
||||||
|
return (0); \
|
||||||
|
} |
||||||
|
#define TM_YEAR_BASE (1900) |
||||||
|
|
||||||
|
static int conv_num(const char**, int*, int, int); |
||||||
|
|
||||||
|
static const char* day[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; |
||||||
|
|
||||||
|
static const char* abday[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; |
||||||
|
|
||||||
|
static const char* mon[12] = {"January", "February", "March", "April", "May", "June", |
||||||
|
"July", "August", "September", "October", "November", "December"}; |
||||||
|
|
||||||
|
static const char* abmon[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; |
||||||
|
|
||||||
|
static const char* am_pm[2] = {"AM", "PM"}; |
||||||
|
|
||||||
|
static int arg_strcasecmp(const char* s1, const char* s2) { |
||||||
|
const unsigned char* us1 = (const unsigned char*)s1; |
||||||
|
const unsigned char* us2 = (const unsigned char*)s2; |
||||||
|
while (tolower(*us1) == tolower(*us2++)) |
||||||
|
if (*us1++ == '\0') |
||||||
|
return 0; |
||||||
|
|
||||||
|
return tolower(*us1) - tolower(*--us2); |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_strncasecmp(const char* s1, const char* s2, size_t n) { |
||||||
|
if (n != 0) { |
||||||
|
const unsigned char* us1 = (const unsigned char*)s1; |
||||||
|
const unsigned char* us2 = (const unsigned char*)s2; |
||||||
|
do { |
||||||
|
if (tolower(*us1) != tolower(*us2++)) |
||||||
|
return tolower(*us1) - tolower(*--us2); |
||||||
|
|
||||||
|
if (*us1++ == '\0') |
||||||
|
break; |
||||||
|
} while (--n != 0); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
char* arg_strptime(const char* buf, const char* fmt, struct tm* tm) { |
||||||
|
char c; |
||||||
|
const char* bp; |
||||||
|
size_t len = 0; |
||||||
|
int alt_format, i, split_year = 0; |
||||||
|
|
||||||
|
bp = buf; |
||||||
|
|
||||||
|
while ((c = *fmt) != '\0') { |
||||||
|
/* Clear `alternate' modifier prior to new conversion. */ |
||||||
|
alt_format = 0; |
||||||
|
|
||||||
|
/* Eat up white-space. */ |
||||||
|
if (isspace(c)) { |
||||||
|
while (isspace((int)(*bp))) |
||||||
|
bp++; |
||||||
|
|
||||||
|
fmt++; |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if ((c = *fmt++) != '%') |
||||||
|
goto literal; |
||||||
|
|
||||||
|
again: |
||||||
|
switch (c = *fmt++) { |
||||||
|
case '%': /* "%%" is converted to "%". */ |
||||||
|
literal: |
||||||
|
if (c != *bp++) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
/*
|
||||||
|
* "Alternative" modifiers. Just set the appropriate flag |
||||||
|
* and start over again. |
||||||
|
*/ |
||||||
|
case 'E': /* "%E?" alternative conversion modifier. */ |
||||||
|
LEGAL_ALT(0); |
||||||
|
alt_format |= ALT_E; |
||||||
|
goto again; |
||||||
|
|
||||||
|
case 'O': /* "%O?" alternative conversion modifier. */ |
||||||
|
LEGAL_ALT(0); |
||||||
|
alt_format |= ALT_O; |
||||||
|
goto again; |
||||||
|
|
||||||
|
/*
|
||||||
|
* "Complex" conversion rules, implemented through recursion. |
||||||
|
*/ |
||||||
|
case 'c': /* Date and time, using the locale's format. */ |
||||||
|
LEGAL_ALT(ALT_E); |
||||||
|
bp = arg_strptime(bp, "%x %X", tm); |
||||||
|
if (!bp) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'D': /* The date as "%m/%d/%y". */ |
||||||
|
LEGAL_ALT(0); |
||||||
|
bp = arg_strptime(bp, "%m/%d/%y", tm); |
||||||
|
if (!bp) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'R': /* The time as "%H:%M". */ |
||||||
|
LEGAL_ALT(0); |
||||||
|
bp = arg_strptime(bp, "%H:%M", tm); |
||||||
|
if (!bp) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'r': /* The time in 12-hour clock representation. */ |
||||||
|
LEGAL_ALT(0); |
||||||
|
bp = arg_strptime(bp, "%I:%M:%S %p", tm); |
||||||
|
if (!bp) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'T': /* The time as "%H:%M:%S". */ |
||||||
|
LEGAL_ALT(0); |
||||||
|
bp = arg_strptime(bp, "%H:%M:%S", tm); |
||||||
|
if (!bp) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'X': /* The time, using the locale's format. */ |
||||||
|
LEGAL_ALT(ALT_E); |
||||||
|
bp = arg_strptime(bp, "%H:%M:%S", tm); |
||||||
|
if (!bp) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'x': /* The date, using the locale's format. */ |
||||||
|
LEGAL_ALT(ALT_E); |
||||||
|
bp = arg_strptime(bp, "%m/%d/%y", tm); |
||||||
|
if (!bp) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
/*
|
||||||
|
* "Elementary" conversion rules. |
||||||
|
*/ |
||||||
|
case 'A': /* The day of week, using the locale's form. */ |
||||||
|
case 'a': |
||||||
|
LEGAL_ALT(0); |
||||||
|
for (i = 0; i < 7; i++) { |
||||||
|
/* Full name. */ |
||||||
|
len = strlen(day[i]); |
||||||
|
if (arg_strncasecmp(day[i], bp, len) == 0) |
||||||
|
break; |
||||||
|
|
||||||
|
/* Abbreviated name. */ |
||||||
|
len = strlen(abday[i]); |
||||||
|
if (arg_strncasecmp(abday[i], bp, len) == 0) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
/* Nothing matched. */ |
||||||
|
if (i == 7) |
||||||
|
return (0); |
||||||
|
|
||||||
|
tm->tm_wday = i; |
||||||
|
bp += len; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'B': /* The month, using the locale's form. */ |
||||||
|
case 'b': |
||||||
|
case 'h': |
||||||
|
LEGAL_ALT(0); |
||||||
|
for (i = 0; i < 12; i++) { |
||||||
|
/* Full name. */ |
||||||
|
len = strlen(mon[i]); |
||||||
|
if (arg_strncasecmp(mon[i], bp, len) == 0) |
||||||
|
break; |
||||||
|
|
||||||
|
/* Abbreviated name. */ |
||||||
|
len = strlen(abmon[i]); |
||||||
|
if (arg_strncasecmp(abmon[i], bp, len) == 0) |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
/* Nothing matched. */ |
||||||
|
if (i == 12) |
||||||
|
return (0); |
||||||
|
|
||||||
|
tm->tm_mon = i; |
||||||
|
bp += len; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'C': /* The century number. */ |
||||||
|
LEGAL_ALT(ALT_E); |
||||||
|
if (!(conv_num(&bp, &i, 0, 99))) |
||||||
|
return (0); |
||||||
|
|
||||||
|
if (split_year) { |
||||||
|
tm->tm_year = (tm->tm_year % 100) + (i * 100); |
||||||
|
} else { |
||||||
|
tm->tm_year = i * 100; |
||||||
|
split_year = 1; |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case 'd': /* The day of month. */ |
||||||
|
case 'e': |
||||||
|
LEGAL_ALT(ALT_O); |
||||||
|
if (!(conv_num(&bp, &tm->tm_mday, 1, 31))) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'k': /* The hour (24-hour clock representation). */ |
||||||
|
LEGAL_ALT(0); |
||||||
|
/* FALLTHROUGH */ |
||||||
|
case 'H': |
||||||
|
LEGAL_ALT(ALT_O); |
||||||
|
if (!(conv_num(&bp, &tm->tm_hour, 0, 23))) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'l': /* The hour (12-hour clock representation). */ |
||||||
|
LEGAL_ALT(0); |
||||||
|
/* FALLTHROUGH */ |
||||||
|
case 'I': |
||||||
|
LEGAL_ALT(ALT_O); |
||||||
|
if (!(conv_num(&bp, &tm->tm_hour, 1, 12))) |
||||||
|
return (0); |
||||||
|
if (tm->tm_hour == 12) |
||||||
|
tm->tm_hour = 0; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'j': /* The day of year. */ |
||||||
|
LEGAL_ALT(0); |
||||||
|
if (!(conv_num(&bp, &i, 1, 366))) |
||||||
|
return (0); |
||||||
|
tm->tm_yday = i - 1; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'M': /* The minute. */ |
||||||
|
LEGAL_ALT(ALT_O); |
||||||
|
if (!(conv_num(&bp, &tm->tm_min, 0, 59))) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'm': /* The month. */ |
||||||
|
LEGAL_ALT(ALT_O); |
||||||
|
if (!(conv_num(&bp, &i, 1, 12))) |
||||||
|
return (0); |
||||||
|
tm->tm_mon = i - 1; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'p': /* The locale's equivalent of AM/PM. */ |
||||||
|
LEGAL_ALT(0); |
||||||
|
/* AM? */ |
||||||
|
if (arg_strcasecmp(am_pm[0], bp) == 0) { |
||||||
|
if (tm->tm_hour > 11) |
||||||
|
return (0); |
||||||
|
|
||||||
|
bp += strlen(am_pm[0]); |
||||||
|
break; |
||||||
|
} |
||||||
|
/* PM? */ |
||||||
|
else if (arg_strcasecmp(am_pm[1], bp) == 0) { |
||||||
|
if (tm->tm_hour > 11) |
||||||
|
return (0); |
||||||
|
|
||||||
|
tm->tm_hour += 12; |
||||||
|
bp += strlen(am_pm[1]); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
/* Nothing matched. */ |
||||||
|
return (0); |
||||||
|
|
||||||
|
case 'S': /* The seconds. */ |
||||||
|
LEGAL_ALT(ALT_O); |
||||||
|
if (!(conv_num(&bp, &tm->tm_sec, 0, 61))) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'U': /* The week of year, beginning on sunday. */ |
||||||
|
case 'W': /* The week of year, beginning on monday. */ |
||||||
|
LEGAL_ALT(ALT_O); |
||||||
|
/*
|
||||||
|
* XXX This is bogus, as we can not assume any valid |
||||||
|
* information present in the tm structure at this |
||||||
|
* point to calculate a real value, so just check the |
||||||
|
* range for now. |
||||||
|
*/ |
||||||
|
if (!(conv_num(&bp, &i, 0, 53))) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'w': /* The day of week, beginning on sunday. */ |
||||||
|
LEGAL_ALT(ALT_O); |
||||||
|
if (!(conv_num(&bp, &tm->tm_wday, 0, 6))) |
||||||
|
return (0); |
||||||
|
break; |
||||||
|
|
||||||
|
case 'Y': /* The year. */ |
||||||
|
LEGAL_ALT(ALT_E); |
||||||
|
if (!(conv_num(&bp, &i, 0, 9999))) |
||||||
|
return (0); |
||||||
|
|
||||||
|
tm->tm_year = i - TM_YEAR_BASE; |
||||||
|
break; |
||||||
|
|
||||||
|
case 'y': /* The year within 100 years of the epoch. */ |
||||||
|
LEGAL_ALT(ALT_E | ALT_O); |
||||||
|
if (!(conv_num(&bp, &i, 0, 99))) |
||||||
|
return (0); |
||||||
|
|
||||||
|
if (split_year) { |
||||||
|
tm->tm_year = ((tm->tm_year / 100) * 100) + i; |
||||||
|
break; |
||||||
|
} |
||||||
|
split_year = 1; |
||||||
|
if (i <= 68) |
||||||
|
tm->tm_year = i + 2000 - TM_YEAR_BASE; |
||||||
|
else |
||||||
|
tm->tm_year = i + 1900 - TM_YEAR_BASE; |
||||||
|
break; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Miscellaneous conversions. |
||||||
|
*/ |
||||||
|
case 'n': /* Any kind of white-space. */ |
||||||
|
case 't': |
||||||
|
LEGAL_ALT(0); |
||||||
|
while (isspace((int)(*bp))) |
||||||
|
bp++; |
||||||
|
break; |
||||||
|
|
||||||
|
default: /* Unknown/unsupported conversion. */ |
||||||
|
return (0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* LINTED functional specification */ |
||||||
|
return ((char*)bp); |
||||||
|
} |
||||||
|
|
||||||
|
static int conv_num(const char** buf, int* dest, int llim, int ulim) { |
||||||
|
int result = 0; |
||||||
|
|
||||||
|
/* The limit also determines the number of valid digits. */ |
||||||
|
int rulim = ulim; |
||||||
|
|
||||||
|
if (**buf < '0' || **buf > '9') |
||||||
|
return (0); |
||||||
|
|
||||||
|
do { |
||||||
|
result *= 10; |
||||||
|
result += *(*buf)++ - '0'; |
||||||
|
rulim /= 10; |
||||||
|
} while ((result * 10 <= ulim) && rulim && **buf >= '0' && **buf <= '9'); |
||||||
|
|
||||||
|
if (result < llim || result > ulim) |
||||||
|
return (0); |
||||||
|
|
||||||
|
*dest = result; |
||||||
|
return (1); |
||||||
|
} |
@ -0,0 +1,164 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_dbl: Implements the double command-line option |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* <sheitmann@users.sourceforge.net> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#include "argtable3.h" |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
static void arg_dbl_resetfn(struct arg_dbl* parent) { |
||||||
|
ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); |
||||||
|
parent->count = 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_dbl_scanfn(struct arg_dbl* parent, const char* argval) { |
||||||
|
int errorcode = 0; |
||||||
|
|
||||||
|
if (parent->count == parent->hdr.maxcount) { |
||||||
|
/* maximum number of arguments exceeded */ |
||||||
|
errorcode = ARG_ERR_MAXCOUNT; |
||||||
|
} else if (!argval) { |
||||||
|
/* a valid argument with no argument value was given. */ |
||||||
|
/* This happens when an optional argument value was invoked. */ |
||||||
|
/* leave parent argument value unaltered but still count the argument. */ |
||||||
|
parent->count++; |
||||||
|
} else { |
||||||
|
double val; |
||||||
|
char* end; |
||||||
|
|
||||||
|
/* extract double from argval into val */ |
||||||
|
val = strtod(argval, &end); |
||||||
|
|
||||||
|
/* if success then store result in parent->dval[] array otherwise return error*/ |
||||||
|
if (*end == 0) |
||||||
|
parent->dval[parent->count++] = val; |
||||||
|
else |
||||||
|
errorcode = ARG_ERR_BADDOUBLE; |
||||||
|
} |
||||||
|
|
||||||
|
ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_dbl_checkfn(struct arg_dbl* parent) { |
||||||
|
int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; |
||||||
|
|
||||||
|
ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static void arg_dbl_errorfn(struct arg_dbl* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { |
||||||
|
const char* shortopts = parent->hdr.shortopts; |
||||||
|
const char* longopts = parent->hdr.longopts; |
||||||
|
const char* datatype = parent->hdr.datatype; |
||||||
|
|
||||||
|
/* make argval NULL safe */ |
||||||
|
argval = argval ? argval : ""; |
||||||
|
|
||||||
|
arg_dstr_catf(ds, "%s: ", progname); |
||||||
|
switch (errorcode) { |
||||||
|
case ARG_ERR_MINCOUNT: |
||||||
|
arg_dstr_cat(ds, "missing option "); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARG_ERR_MAXCOUNT: |
||||||
|
arg_dstr_cat(ds, "excess option "); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARG_ERR_BADDOUBLE: |
||||||
|
arg_dstr_catf(ds, "invalid argument \"%s\" to option ", argval); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { |
||||||
|
return arg_dbln(shortopts, longopts, datatype, 0, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { |
||||||
|
return arg_dbln(shortopts, longopts, datatype, 1, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { |
||||||
|
size_t nbytes; |
||||||
|
struct arg_dbl* result; |
||||||
|
size_t addr; |
||||||
|
size_t rem; |
||||||
|
|
||||||
|
/* foolproof things by ensuring maxcount is not less than mincount */ |
||||||
|
maxcount = (maxcount < mincount) ? mincount : maxcount; |
||||||
|
|
||||||
|
nbytes = sizeof(struct arg_dbl) /* storage for struct arg_dbl */ |
||||||
|
+ (size_t)(maxcount + 1) * sizeof(double); /* storage for dval[maxcount] array plus one extra for padding to memory boundary */ |
||||||
|
|
||||||
|
result = (struct arg_dbl*)xmalloc(nbytes); |
||||||
|
|
||||||
|
/* init the arg_hdr struct */ |
||||||
|
result->hdr.flag = ARG_HASVALUE; |
||||||
|
result->hdr.shortopts = shortopts; |
||||||
|
result->hdr.longopts = longopts; |
||||||
|
result->hdr.datatype = datatype ? datatype : "<double>"; |
||||||
|
result->hdr.glossary = glossary; |
||||||
|
result->hdr.mincount = mincount; |
||||||
|
result->hdr.maxcount = maxcount; |
||||||
|
result->hdr.parent = result; |
||||||
|
result->hdr.resetfn = (arg_resetfn*)arg_dbl_resetfn; |
||||||
|
result->hdr.scanfn = (arg_scanfn*)arg_dbl_scanfn; |
||||||
|
result->hdr.checkfn = (arg_checkfn*)arg_dbl_checkfn; |
||||||
|
result->hdr.errorfn = (arg_errorfn*)arg_dbl_errorfn; |
||||||
|
|
||||||
|
/* Store the dval[maxcount] array on the first double boundary that
|
||||||
|
* immediately follows the arg_dbl struct. We do the memory alignment |
||||||
|
* purely for SPARC and Motorola systems. They require floats and |
||||||
|
* doubles to be aligned on natural boundaries. |
||||||
|
*/ |
||||||
|
addr = (size_t)(result + 1); |
||||||
|
rem = addr % sizeof(double); |
||||||
|
result->dval = (double*)(addr + sizeof(double) - rem); |
||||||
|
ARG_TRACE(("addr=%p, dval=%p, sizeof(double)=%d rem=%d\n", addr, result->dval, (int)sizeof(double), (int)rem)); |
||||||
|
|
||||||
|
result->count = 0; |
||||||
|
|
||||||
|
ARG_TRACE(("arg_dbln() returns %p\n", result)); |
||||||
|
return result; |
||||||
|
} |
@ -0,0 +1,344 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2013-2019 Tom G. Huang |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_dstr: Implements the dynamic string utilities |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 2013-2019 Tom G. Huang |
||||||
|
* <tomghuang@gmail.com> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#include "argtable3.h" |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdarg.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#if defined(_MSC_VER) |
||||||
|
#pragma warning(push) |
||||||
|
#pragma warning(disable : 4996) |
||||||
|
#endif |
||||||
|
|
||||||
|
#define START_VSNBUFF 16 |
||||||
|
|
||||||
|
/*
|
||||||
|
* This dynamic string module is adapted from TclResult.c in the Tcl library. |
||||||
|
* Here is the copyright notice from the library: |
||||||
|
* |
||||||
|
* This software is copyrighted by the Regents of the University of |
||||||
|
* California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState |
||||||
|
* Corporation and other parties. The following terms apply to all files |
||||||
|
* associated with the software unless explicitly disclaimed in |
||||||
|
* individual files. |
||||||
|
* |
||||||
|
* The authors hereby grant permission to use, copy, modify, distribute, |
||||||
|
* and license this software and its documentation for any purpose, provided |
||||||
|
* that existing copyright notices are retained in all copies and that this |
||||||
|
* notice is included verbatim in any distributions. No written agreement, |
||||||
|
* license, or royalty fee is required for any of the authorized uses. |
||||||
|
* Modifications to this software may be copyrighted by their authors |
||||||
|
* and need not follow the licensing terms described here, provided that |
||||||
|
* the new terms are clearly indicated on the first page of each file where |
||||||
|
* they apply. |
||||||
|
* |
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY |
||||||
|
* FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
||||||
|
* ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY |
||||||
|
* DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE |
||||||
|
* POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
* THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, |
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE |
||||||
|
* IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE |
||||||
|
* NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR |
||||||
|
* MODIFICATIONS. |
||||||
|
* |
||||||
|
* GOVERNMENT USE: If you are acquiring this software on behalf of the |
||||||
|
* U.S. government, the Government shall have only "Restricted Rights" |
||||||
|
* in the software and related documentation as defined in the Federal |
||||||
|
* Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you |
||||||
|
* are acquiring the software on behalf of the Department of Defense, the |
||||||
|
* software shall be classified as "Commercial Computer Software" and the |
||||||
|
* Government shall have only "Restricted Rights" as defined in Clause |
||||||
|
* 252.227-7014 (b) (3) of DFARs. Notwithstanding the foregoing, the |
||||||
|
* authors grant the U.S. Government and others acting in its behalf |
||||||
|
* permission to use and distribute the software in accordance with the |
||||||
|
* terms specified in this license. |
||||||
|
*/ |
||||||
|
|
||||||
|
typedef struct _internal_arg_dstr { |
||||||
|
char* data; |
||||||
|
arg_dstr_freefn* free_proc; |
||||||
|
char sbuf[ARG_DSTR_SIZE + 1]; |
||||||
|
char* append_data; |
||||||
|
int append_data_size; |
||||||
|
int append_used; |
||||||
|
} _internal_arg_dstr_t; |
||||||
|
|
||||||
|
static void setup_append_buf(arg_dstr_t res, int newSpace); |
||||||
|
|
||||||
|
arg_dstr_t arg_dstr_create(void) { |
||||||
|
_internal_arg_dstr_t* h = (_internal_arg_dstr_t*)xmalloc(sizeof(_internal_arg_dstr_t)); |
||||||
|
memset(h, 0, sizeof(_internal_arg_dstr_t)); |
||||||
|
h->sbuf[0] = 0; |
||||||
|
h->data = h->sbuf; |
||||||
|
h->free_proc = ARG_DSTR_STATIC; |
||||||
|
return h; |
||||||
|
} |
||||||
|
|
||||||
|
void arg_dstr_destroy(arg_dstr_t ds) { |
||||||
|
if (ds == NULL) |
||||||
|
return; |
||||||
|
|
||||||
|
arg_dstr_reset(ds); |
||||||
|
xfree(ds); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
void arg_dstr_set(arg_dstr_t ds, char* str, arg_dstr_freefn* free_proc) { |
||||||
|
int length; |
||||||
|
register arg_dstr_freefn* old_free_proc = ds->free_proc; |
||||||
|
char* old_result = ds->data; |
||||||
|
|
||||||
|
if (str == NULL) { |
||||||
|
ds->sbuf[0] = 0; |
||||||
|
ds->data = ds->sbuf; |
||||||
|
ds->free_proc = ARG_DSTR_STATIC; |
||||||
|
} else if (free_proc == ARG_DSTR_VOLATILE) { |
||||||
|
length = (int)strlen(str); |
||||||
|
if (length > ARG_DSTR_SIZE) { |
||||||
|
ds->data = (char*)xmalloc((unsigned)length + 1); |
||||||
|
ds->free_proc = ARG_DSTR_DYNAMIC; |
||||||
|
} else { |
||||||
|
ds->data = ds->sbuf; |
||||||
|
ds->free_proc = ARG_DSTR_STATIC; |
||||||
|
} |
||||||
|
strcpy(ds->data, str); |
||||||
|
} else { |
||||||
|
ds->data = str; |
||||||
|
ds->free_proc = free_proc; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* If the old result was dynamically-allocated, free it up. Do it here, |
||||||
|
* rather than at the beginning, in case the new result value was part of |
||||||
|
* the old result value. |
||||||
|
*/ |
||||||
|
|
||||||
|
if ((old_free_proc != 0) && (old_result != ds->data)) { |
||||||
|
if (old_free_proc == ARG_DSTR_DYNAMIC) { |
||||||
|
xfree(old_result); |
||||||
|
} else { |
||||||
|
(*old_free_proc)(old_result); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if ((ds->append_data != NULL) && (ds->append_data_size > 0)) { |
||||||
|
xfree(ds->append_data); |
||||||
|
ds->append_data = NULL; |
||||||
|
ds->append_data_size = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
char* arg_dstr_cstr(arg_dstr_t ds) /* Interpreter whose result to return. */ |
||||||
|
{ |
||||||
|
return ds->data; |
||||||
|
} |
||||||
|
|
||||||
|
void arg_dstr_cat(arg_dstr_t ds, const char* str) { |
||||||
|
setup_append_buf(ds, (int)strlen(str) + 1); |
||||||
|
memcpy(ds->data + strlen(ds->data), str, strlen(str)); |
||||||
|
} |
||||||
|
|
||||||
|
void arg_dstr_catc(arg_dstr_t ds, char c) { |
||||||
|
setup_append_buf(ds, 2); |
||||||
|
memcpy(ds->data + strlen(ds->data), &c, 1); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* The logic of the `arg_dstr_catf` function is adapted from the `bformat` |
||||||
|
* function in The Better String Library by Paul Hsieh. Here is the copyright |
||||||
|
* notice from the library: |
||||||
|
* |
||||||
|
* Copyright (c) 2014, Paul Hsieh |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright notice, this |
||||||
|
* list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice, |
||||||
|
* this list of conditions and the following disclaimer in the documentation |
||||||
|
* and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of bstrlib nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
void arg_dstr_catf(arg_dstr_t ds, const char* fmt, ...) { |
||||||
|
va_list arglist; |
||||||
|
char* buff; |
||||||
|
int n, r; |
||||||
|
size_t slen; |
||||||
|
|
||||||
|
if (fmt == NULL) |
||||||
|
return; |
||||||
|
|
||||||
|
/* Since the length is not determinable beforehand, a search is
|
||||||
|
performed using the truncating "vsnprintf" call (to avoid buffer |
||||||
|
overflows) on increasing potential sizes for the output result. */ |
||||||
|
|
||||||
|
if ((n = (int)(2 * strlen(fmt))) < START_VSNBUFF) |
||||||
|
n = START_VSNBUFF; |
||||||
|
|
||||||
|
buff = (char*)xmalloc((size_t)(n + 2)); |
||||||
|
memset(buff, 0, (size_t)(n + 2)); |
||||||
|
|
||||||
|
for (;;) { |
||||||
|
va_start(arglist, fmt); |
||||||
|
r = vsnprintf(buff, (size_t)(n + 1), fmt, arglist); |
||||||
|
va_end(arglist); |
||||||
|
|
||||||
|
slen = strlen(buff); |
||||||
|
if (slen < (size_t)n) |
||||||
|
break; |
||||||
|
|
||||||
|
if (r > n) |
||||||
|
n = r; |
||||||
|
else |
||||||
|
n += n; |
||||||
|
|
||||||
|
xfree(buff); |
||||||
|
buff = (char*)xmalloc((size_t)(n + 2)); |
||||||
|
memset(buff, 0, (size_t)(n + 2)); |
||||||
|
} |
||||||
|
|
||||||
|
arg_dstr_cat(ds, buff); |
||||||
|
xfree(buff); |
||||||
|
} |
||||||
|
|
||||||
|
static void setup_append_buf(arg_dstr_t ds, int new_space) { |
||||||
|
int total_space; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Make the append buffer larger, if that's necessary, then copy the |
||||||
|
* data into the append buffer and make the append buffer the official |
||||||
|
* data. |
||||||
|
*/ |
||||||
|
if (ds->data != ds->append_data) { |
||||||
|
/*
|
||||||
|
* If the buffer is too big, then free it up so we go back to a |
||||||
|
* smaller buffer. This avoids tying up memory forever after a large |
||||||
|
* operation. |
||||||
|
*/ |
||||||
|
if (ds->append_data_size > 500) { |
||||||
|
xfree(ds->append_data); |
||||||
|
ds->append_data = NULL; |
||||||
|
ds->append_data_size = 0; |
||||||
|
} |
||||||
|
ds->append_used = (int)strlen(ds->data); |
||||||
|
} else if (ds->data[ds->append_used] != 0) { |
||||||
|
/*
|
||||||
|
* Most likely someone has modified a result created by |
||||||
|
* arg_dstr_cat et al. so that it has a different size. Just |
||||||
|
* recompute the size. |
||||||
|
*/ |
||||||
|
ds->append_used = (int)strlen(ds->data); |
||||||
|
} |
||||||
|
|
||||||
|
total_space = new_space + ds->append_used; |
||||||
|
if (total_space >= ds->append_data_size) { |
||||||
|
char* newbuf; |
||||||
|
|
||||||
|
if (total_space < 100) { |
||||||
|
total_space = 200; |
||||||
|
} else { |
||||||
|
total_space *= 2; |
||||||
|
} |
||||||
|
newbuf = (char*)xmalloc((unsigned)total_space); |
||||||
|
memset(newbuf, 0, (size_t)total_space); |
||||||
|
strcpy(newbuf, ds->data); |
||||||
|
if (ds->append_data != NULL) { |
||||||
|
xfree(ds->append_data); |
||||||
|
} |
||||||
|
ds->append_data = newbuf; |
||||||
|
ds->append_data_size = total_space; |
||||||
|
} else if (ds->data != ds->append_data) { |
||||||
|
strcpy(ds->append_data, ds->data); |
||||||
|
} |
||||||
|
|
||||||
|
arg_dstr_free(ds); |
||||||
|
ds->data = ds->append_data; |
||||||
|
} |
||||||
|
|
||||||
|
void arg_dstr_free(arg_dstr_t ds) { |
||||||
|
if (ds->free_proc != NULL) { |
||||||
|
if (ds->free_proc == ARG_DSTR_DYNAMIC) { |
||||||
|
xfree(ds->data); |
||||||
|
} else { |
||||||
|
(*ds->free_proc)(ds->data); |
||||||
|
} |
||||||
|
ds->free_proc = NULL; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void arg_dstr_reset(arg_dstr_t ds) { |
||||||
|
arg_dstr_free(ds); |
||||||
|
if ((ds->append_data != NULL) && (ds->append_data_size > 0)) { |
||||||
|
xfree(ds->append_data); |
||||||
|
ds->append_data = NULL; |
||||||
|
ds->append_data_size = 0; |
||||||
|
} |
||||||
|
|
||||||
|
ds->data = ds->sbuf; |
||||||
|
ds->sbuf[0] = 0; |
||||||
|
} |
||||||
|
|
||||||
|
#if defined(_MSC_VER) |
||||||
|
#pragma warning(pop) |
||||||
|
#endif |
@ -0,0 +1,135 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_end: Implements the error handling utilities |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* <sheitmann@users.sourceforge.net> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#include "argtable3.h" |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
static void arg_end_resetfn(struct arg_end* parent) { |
||||||
|
ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); |
||||||
|
parent->count = 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void arg_end_errorfn(void* parent, arg_dstr_t ds, int error, const char* argval, const char* progname) { |
||||||
|
/* suppress unreferenced formal parameter warning */ |
||||||
|
(void)parent; |
||||||
|
|
||||||
|
progname = progname ? progname : ""; |
||||||
|
argval = argval ? argval : ""; |
||||||
|
|
||||||
|
arg_dstr_catf(ds, "%s: ", progname); |
||||||
|
switch (error) { |
||||||
|
case ARG_ELIMIT: |
||||||
|
arg_dstr_cat(ds, "too many errors to display"); |
||||||
|
break; |
||||||
|
case ARG_EMALLOC: |
||||||
|
arg_dstr_cat(ds, "insufficient memory"); |
||||||
|
break; |
||||||
|
case ARG_ENOMATCH: |
||||||
|
arg_dstr_catf(ds, "unexpected argument \"%s\"", argval); |
||||||
|
break; |
||||||
|
case ARG_EMISSARG: |
||||||
|
arg_dstr_catf(ds, "option \"%s\" requires an argument", argval); |
||||||
|
break; |
||||||
|
case ARG_ELONGOPT: |
||||||
|
arg_dstr_catf(ds, "invalid option \"%s\"", argval); |
||||||
|
break; |
||||||
|
default: |
||||||
|
arg_dstr_catf(ds, "invalid option \"-%c\"", error); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
arg_dstr_cat(ds, "\n"); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_end* arg_end(int maxcount) { |
||||||
|
size_t nbytes; |
||||||
|
struct arg_end* result; |
||||||
|
|
||||||
|
nbytes = sizeof(struct arg_end) + (size_t)maxcount * sizeof(int) /* storage for int error[maxcount] array*/ |
||||||
|
+ (size_t)maxcount * sizeof(void*) /* storage for void* parent[maxcount] array */ |
||||||
|
+ (size_t)maxcount * sizeof(char*); /* storage for char* argval[maxcount] array */ |
||||||
|
|
||||||
|
result = (struct arg_end*)xmalloc(nbytes); |
||||||
|
|
||||||
|
/* init the arg_hdr struct */ |
||||||
|
result->hdr.flag = ARG_TERMINATOR; |
||||||
|
result->hdr.shortopts = NULL; |
||||||
|
result->hdr.longopts = NULL; |
||||||
|
result->hdr.datatype = NULL; |
||||||
|
result->hdr.glossary = NULL; |
||||||
|
result->hdr.mincount = 1; |
||||||
|
result->hdr.maxcount = maxcount; |
||||||
|
result->hdr.parent = result; |
||||||
|
result->hdr.resetfn = (arg_resetfn*)arg_end_resetfn; |
||||||
|
result->hdr.scanfn = NULL; |
||||||
|
result->hdr.checkfn = NULL; |
||||||
|
result->hdr.errorfn = (arg_errorfn*)arg_end_errorfn; |
||||||
|
|
||||||
|
/* store error[maxcount] array immediately after struct arg_end */ |
||||||
|
result->error = (int*)(result + 1); |
||||||
|
|
||||||
|
/* store parent[maxcount] array immediately after error[] array */ |
||||||
|
result->parent = (void**)(result->error + maxcount); |
||||||
|
|
||||||
|
/* store argval[maxcount] array immediately after parent[] array */ |
||||||
|
result->argval = (const char**)(result->parent + maxcount); |
||||||
|
|
||||||
|
ARG_TRACE(("arg_end(%d) returns %p\n", maxcount, result)); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
void arg_print_errors_ds(arg_dstr_t ds, struct arg_end* end, const char* progname) { |
||||||
|
int i; |
||||||
|
ARG_TRACE(("arg_errors()\n")); |
||||||
|
for (i = 0; i < end->count; i++) { |
||||||
|
struct arg_hdr* errorparent = (struct arg_hdr*)(end->parent[i]); |
||||||
|
if (errorparent->errorfn) |
||||||
|
errorparent->errorfn(end->parent[i], ds, end->error[i], end->argval[i], progname); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname) { |
||||||
|
arg_dstr_t ds = arg_dstr_create(); |
||||||
|
arg_print_errors_ds(ds, end, progname); |
||||||
|
fputs(arg_dstr_cstr(ds), fp); |
||||||
|
arg_dstr_destroy(ds); |
||||||
|
} |
@ -0,0 +1,213 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_file: Implements the file command-line option |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* <sheitmann@users.sourceforge.net> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#include "argtable3.h" |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#ifdef WIN32 |
||||||
|
#define FILESEPARATOR1 '\\' |
||||||
|
#define FILESEPARATOR2 '/' |
||||||
|
#else |
||||||
|
#define FILESEPARATOR1 '/' |
||||||
|
#define FILESEPARATOR2 '/' |
||||||
|
#endif |
||||||
|
|
||||||
|
static void arg_file_resetfn(struct arg_file* parent) { |
||||||
|
ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); |
||||||
|
parent->count = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* Returns ptr to the base filename within *filename */ |
||||||
|
static const char* arg_basename(const char* filename) { |
||||||
|
const char *result = NULL, *result1, *result2; |
||||||
|
|
||||||
|
/* Find the last occurrence of eother file separator character. */ |
||||||
|
/* Two alternative file separator chars are supported as legal */ |
||||||
|
/* file separators but not both together in the same filename. */ |
||||||
|
result1 = (filename ? strrchr(filename, FILESEPARATOR1) : NULL); |
||||||
|
result2 = (filename ? strrchr(filename, FILESEPARATOR2) : NULL); |
||||||
|
|
||||||
|
if (result2) |
||||||
|
result = result2 + 1; /* using FILESEPARATOR2 (the alternative file separator) */ |
||||||
|
|
||||||
|
if (result1) |
||||||
|
result = result1 + 1; /* using FILESEPARATOR1 (the preferred file separator) */ |
||||||
|
|
||||||
|
if (!result) |
||||||
|
result = filename; /* neither file separator was found so basename is the whole filename */ |
||||||
|
|
||||||
|
/* special cases of "." and ".." are not considered basenames */ |
||||||
|
if (result && (strcmp(".", result) == 0 || strcmp("..", result) == 0)) |
||||||
|
result = filename + strlen(filename); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/* Returns ptr to the file extension within *basename */ |
||||||
|
static const char* arg_extension(const char* basename) { |
||||||
|
/* find the last occurrence of '.' in basename */ |
||||||
|
const char* result = (basename ? strrchr(basename, '.') : NULL); |
||||||
|
|
||||||
|
/* if no '.' was found then return pointer to end of basename */ |
||||||
|
if (basename && !result) |
||||||
|
result = basename + strlen(basename); |
||||||
|
|
||||||
|
/* special case: basenames with a single leading dot (eg ".foo") are not considered as true extensions */ |
||||||
|
if (basename && result == basename) |
||||||
|
result = basename + strlen(basename); |
||||||
|
|
||||||
|
/* special case: empty extensions (eg "foo.","foo..") are not considered as true extensions */ |
||||||
|
if (basename && result && strlen(result) == 1) |
||||||
|
result = basename + strlen(basename); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_file_scanfn(struct arg_file* parent, const char* argval) { |
||||||
|
int errorcode = 0; |
||||||
|
|
||||||
|
if (parent->count == parent->hdr.maxcount) { |
||||||
|
/* maximum number of arguments exceeded */ |
||||||
|
errorcode = ARG_ERR_MAXCOUNT; |
||||||
|
} else if (!argval) { |
||||||
|
/* a valid argument with no argument value was given. */ |
||||||
|
/* This happens when an optional argument value was invoked. */ |
||||||
|
/* leave parent arguiment value unaltered but still count the argument. */ |
||||||
|
parent->count++; |
||||||
|
} else { |
||||||
|
parent->filename[parent->count] = argval; |
||||||
|
parent->basename[parent->count] = arg_basename(argval); |
||||||
|
parent->extension[parent->count] = |
||||||
|
arg_extension(parent->basename[parent->count]); /* only seek extensions within the basename (not the file path)*/ |
||||||
|
parent->count++; |
||||||
|
} |
||||||
|
|
||||||
|
ARG_TRACE(("%s4:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_file_checkfn(struct arg_file* parent) { |
||||||
|
int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; |
||||||
|
|
||||||
|
ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static void arg_file_errorfn(struct arg_file* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { |
||||||
|
const char* shortopts = parent->hdr.shortopts; |
||||||
|
const char* longopts = parent->hdr.longopts; |
||||||
|
const char* datatype = parent->hdr.datatype; |
||||||
|
|
||||||
|
/* make argval NULL safe */ |
||||||
|
argval = argval ? argval : ""; |
||||||
|
|
||||||
|
arg_dstr_catf(ds, "%s: ", progname); |
||||||
|
switch (errorcode) { |
||||||
|
case ARG_ERR_MINCOUNT: |
||||||
|
arg_dstr_cat(ds, "missing option "); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARG_ERR_MAXCOUNT: |
||||||
|
arg_dstr_cat(ds, "excess option "); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
arg_dstr_catf(ds, "unknown error at \"%s\"\n", argval); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { |
||||||
|
return arg_filen(shortopts, longopts, datatype, 0, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { |
||||||
|
return arg_filen(shortopts, longopts, datatype, 1, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { |
||||||
|
size_t nbytes; |
||||||
|
struct arg_file* result; |
||||||
|
int i; |
||||||
|
|
||||||
|
/* foolproof things by ensuring maxcount is not less than mincount */ |
||||||
|
maxcount = (maxcount < mincount) ? mincount : maxcount; |
||||||
|
|
||||||
|
nbytes = sizeof(struct arg_file) /* storage for struct arg_file */ |
||||||
|
+ sizeof(char*) * (size_t)maxcount /* storage for filename[maxcount] array */ |
||||||
|
+ sizeof(char*) * (size_t)maxcount /* storage for basename[maxcount] array */ |
||||||
|
+ sizeof(char*) * (size_t)maxcount; /* storage for extension[maxcount] array */ |
||||||
|
|
||||||
|
result = (struct arg_file*)xmalloc(nbytes); |
||||||
|
|
||||||
|
/* init the arg_hdr struct */ |
||||||
|
result->hdr.flag = ARG_HASVALUE; |
||||||
|
result->hdr.shortopts = shortopts; |
||||||
|
result->hdr.longopts = longopts; |
||||||
|
result->hdr.glossary = glossary; |
||||||
|
result->hdr.datatype = datatype ? datatype : "<file>"; |
||||||
|
result->hdr.mincount = mincount; |
||||||
|
result->hdr.maxcount = maxcount; |
||||||
|
result->hdr.parent = result; |
||||||
|
result->hdr.resetfn = (arg_resetfn*)arg_file_resetfn; |
||||||
|
result->hdr.scanfn = (arg_scanfn*)arg_file_scanfn; |
||||||
|
result->hdr.checkfn = (arg_checkfn*)arg_file_checkfn; |
||||||
|
result->hdr.errorfn = (arg_errorfn*)arg_file_errorfn; |
||||||
|
|
||||||
|
/* store the filename,basename,extension arrays immediately after the arg_file struct */ |
||||||
|
result->filename = (const char**)(result + 1); |
||||||
|
result->basename = result->filename + maxcount; |
||||||
|
result->extension = result->basename + maxcount; |
||||||
|
result->count = 0; |
||||||
|
|
||||||
|
/* foolproof the string pointers by initialising them with empty strings */ |
||||||
|
for (i = 0; i < maxcount; i++) { |
||||||
|
result->filename[i] = ""; |
||||||
|
result->basename[i] = ""; |
||||||
|
result->extension[i] = ""; |
||||||
|
} |
||||||
|
|
||||||
|
ARG_TRACE(("arg_filen() returns %p\n", result)); |
||||||
|
return result; |
||||||
|
} |
@ -0,0 +1,428 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2013-2019 Tom G. Huang |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_hashtable: Implements the hash table utilities |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 2013-2019 Tom G. Huang |
||||||
|
* <tomghuang@gmail.com> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <math.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
/*
|
||||||
|
* This hash table module is adapted from the C hash table implementation by |
||||||
|
* Christopher Clark. Here is the copyright notice from the library: |
||||||
|
* |
||||||
|
* Copyright (c) 2002, Christopher Clark |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions |
||||||
|
* are met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of the original author; nor the names of any contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER |
||||||
|
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
/*
|
||||||
|
* Credit for primes table: Aaron Krowne |
||||||
|
* http://br.endernet.org/~akrowne/
|
||||||
|
* http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
|
||||||
|
*/ |
||||||
|
static const unsigned int primes[] = {53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, |
||||||
|
24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, |
||||||
|
12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741}; |
||||||
|
const unsigned int prime_table_length = sizeof(primes) / sizeof(primes[0]); |
||||||
|
const float max_load_factor = (float)0.65; |
||||||
|
|
||||||
|
static unsigned int enhanced_hash(arg_hashtable_t* h, const void* k) { |
||||||
|
/*
|
||||||
|
* Aim to protect against poor hash functions by adding logic here. |
||||||
|
* The logic is taken from Java 1.4 hash table source. |
||||||
|
*/ |
||||||
|
unsigned int i = h->hashfn(k); |
||||||
|
i += ~(i << 9); |
||||||
|
i ^= ((i >> 14) | (i << 18)); /* >>> */ |
||||||
|
i += (i << 4); |
||||||
|
i ^= ((i >> 10) | (i << 22)); /* >>> */ |
||||||
|
return i; |
||||||
|
} |
||||||
|
|
||||||
|
static unsigned int index_for(unsigned int tablelength, unsigned int hashvalue) { |
||||||
|
return (hashvalue % tablelength); |
||||||
|
} |
||||||
|
|
||||||
|
arg_hashtable_t* arg_hashtable_create(unsigned int minsize, unsigned int (*hashfn)(const void*), int (*eqfn)(const void*, const void*)) { |
||||||
|
arg_hashtable_t* h; |
||||||
|
unsigned int pindex; |
||||||
|
unsigned int size = primes[0]; |
||||||
|
|
||||||
|
/* Check requested hash table isn't too large */ |
||||||
|
if (minsize > (1u << 30)) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Enforce size as prime. The reason is to avoid clustering of values |
||||||
|
* into a small number of buckets (yes, distribution). A more even |
||||||
|
* distributed hash table will perform more consistently. |
||||||
|
*/ |
||||||
|
for (pindex = 0; pindex < prime_table_length; pindex++) { |
||||||
|
if (primes[pindex] > minsize) { |
||||||
|
size = primes[pindex]; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
h = (arg_hashtable_t*)xmalloc(sizeof(arg_hashtable_t)); |
||||||
|
h->table = (struct arg_hashtable_entry**)xmalloc(sizeof(struct arg_hashtable_entry*) * size); |
||||||
|
memset(h->table, 0, size * sizeof(struct arg_hashtable_entry*)); |
||||||
|
h->tablelength = size; |
||||||
|
h->primeindex = pindex; |
||||||
|
h->entrycount = 0; |
||||||
|
h->hashfn = hashfn; |
||||||
|
h->eqfn = eqfn; |
||||||
|
h->loadlimit = (unsigned int)ceil(size * (double)max_load_factor); |
||||||
|
return h; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_hashtable_expand(arg_hashtable_t* h) { |
||||||
|
/* Double the size of the table to accommodate more entries */ |
||||||
|
struct arg_hashtable_entry** newtable; |
||||||
|
struct arg_hashtable_entry* e; |
||||||
|
unsigned int newsize; |
||||||
|
unsigned int i; |
||||||
|
unsigned int index; |
||||||
|
|
||||||
|
/* Check we're not hitting max capacity */ |
||||||
|
if (h->primeindex == (prime_table_length - 1)) |
||||||
|
return 0; |
||||||
|
newsize = primes[++(h->primeindex)]; |
||||||
|
|
||||||
|
newtable = (struct arg_hashtable_entry**)xmalloc(sizeof(struct arg_hashtable_entry*) * newsize); |
||||||
|
memset(newtable, 0, newsize * sizeof(struct arg_hashtable_entry*)); |
||||||
|
/*
|
||||||
|
* This algorithm is not 'stable': it reverses the list |
||||||
|
* when it transfers entries between the tables |
||||||
|
*/ |
||||||
|
for (i = 0; i < h->tablelength; i++) { |
||||||
|
while (NULL != (e = h->table[i])) { |
||||||
|
h->table[i] = e->next; |
||||||
|
index = index_for(newsize, e->h); |
||||||
|
e->next = newtable[index]; |
||||||
|
newtable[index] = e; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
xfree(h->table); |
||||||
|
h->table = newtable; |
||||||
|
h->tablelength = newsize; |
||||||
|
h->loadlimit = (unsigned int)ceil(newsize * (double)max_load_factor); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
unsigned int arg_hashtable_count(arg_hashtable_t* h) { |
||||||
|
return h->entrycount; |
||||||
|
} |
||||||
|
|
||||||
|
void arg_hashtable_insert(arg_hashtable_t* h, void* k, void* v) { |
||||||
|
/* This method allows duplicate keys - but they shouldn't be used */ |
||||||
|
unsigned int index; |
||||||
|
struct arg_hashtable_entry* e; |
||||||
|
if ((h->entrycount + 1) > h->loadlimit) { |
||||||
|
/*
|
||||||
|
* Ignore the return value. If expand fails, we should |
||||||
|
* still try cramming just this value into the existing table |
||||||
|
* -- we may not have memory for a larger table, but one more |
||||||
|
* element may be ok. Next time we insert, we'll try expanding again. |
||||||
|
*/ |
||||||
|
arg_hashtable_expand(h); |
||||||
|
} |
||||||
|
e = (struct arg_hashtable_entry*)xmalloc(sizeof(struct arg_hashtable_entry)); |
||||||
|
e->h = enhanced_hash(h, k); |
||||||
|
index = index_for(h->tablelength, e->h); |
||||||
|
e->k = k; |
||||||
|
e->v = v; |
||||||
|
e->next = h->table[index]; |
||||||
|
h->table[index] = e; |
||||||
|
h->entrycount++; |
||||||
|
} |
||||||
|
|
||||||
|
void* arg_hashtable_search(arg_hashtable_t* h, const void* k) { |
||||||
|
struct arg_hashtable_entry* e; |
||||||
|
unsigned int hashvalue; |
||||||
|
unsigned int index; |
||||||
|
|
||||||
|
hashvalue = enhanced_hash(h, k); |
||||||
|
index = index_for(h->tablelength, hashvalue); |
||||||
|
e = h->table[index]; |
||||||
|
while (e != NULL) { |
||||||
|
/* Check hash value to short circuit heavier comparison */ |
||||||
|
if ((hashvalue == e->h) && (h->eqfn(k, e->k))) |
||||||
|
return e->v; |
||||||
|
e = e->next; |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void arg_hashtable_remove(arg_hashtable_t* h, const void* k) { |
||||||
|
/*
|
||||||
|
* TODO: consider compacting the table when the load factor drops enough, |
||||||
|
* or provide a 'compact' method. |
||||||
|
*/ |
||||||
|
|
||||||
|
struct arg_hashtable_entry* e; |
||||||
|
struct arg_hashtable_entry** pE; |
||||||
|
unsigned int hashvalue; |
||||||
|
unsigned int index; |
||||||
|
|
||||||
|
hashvalue = enhanced_hash(h, k); |
||||||
|
index = index_for(h->tablelength, hashvalue); |
||||||
|
pE = &(h->table[index]); |
||||||
|
e = *pE; |
||||||
|
while (NULL != e) { |
||||||
|
/* Check hash value to short circuit heavier comparison */ |
||||||
|
if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { |
||||||
|
*pE = e->next; |
||||||
|
h->entrycount--; |
||||||
|
xfree(e->k); |
||||||
|
xfree(e->v); |
||||||
|
xfree(e); |
||||||
|
return; |
||||||
|
} |
||||||
|
pE = &(e->next); |
||||||
|
e = e->next; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void arg_hashtable_destroy(arg_hashtable_t* h, int free_values) { |
||||||
|
unsigned int i; |
||||||
|
struct arg_hashtable_entry *e, *f; |
||||||
|
struct arg_hashtable_entry** table = h->table; |
||||||
|
if (free_values) { |
||||||
|
for (i = 0; i < h->tablelength; i++) { |
||||||
|
e = table[i]; |
||||||
|
while (NULL != e) { |
||||||
|
f = e; |
||||||
|
e = e->next; |
||||||
|
xfree(f->k); |
||||||
|
xfree(f->v); |
||||||
|
xfree(f); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
for (i = 0; i < h->tablelength; i++) { |
||||||
|
e = table[i]; |
||||||
|
while (NULL != e) { |
||||||
|
f = e; |
||||||
|
e = e->next; |
||||||
|
xfree(f->k); |
||||||
|
xfree(f); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
xfree(h->table); |
||||||
|
xfree(h); |
||||||
|
} |
||||||
|
|
||||||
|
arg_hashtable_itr_t* arg_hashtable_itr_create(arg_hashtable_t* h) { |
||||||
|
unsigned int i; |
||||||
|
unsigned int tablelength; |
||||||
|
|
||||||
|
arg_hashtable_itr_t* itr = (arg_hashtable_itr_t*)xmalloc(sizeof(arg_hashtable_itr_t)); |
||||||
|
itr->h = h; |
||||||
|
itr->e = NULL; |
||||||
|
itr->parent = NULL; |
||||||
|
tablelength = h->tablelength; |
||||||
|
itr->index = tablelength; |
||||||
|
if (0 == h->entrycount) |
||||||
|
return itr; |
||||||
|
|
||||||
|
for (i = 0; i < tablelength; i++) { |
||||||
|
if (h->table[i] != NULL) { |
||||||
|
itr->e = h->table[i]; |
||||||
|
itr->index = i; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return itr; |
||||||
|
} |
||||||
|
|
||||||
|
void arg_hashtable_itr_destroy(arg_hashtable_itr_t* itr) { |
||||||
|
xfree(itr); |
||||||
|
} |
||||||
|
|
||||||
|
void* arg_hashtable_itr_key(arg_hashtable_itr_t* i) { |
||||||
|
return i->e->k; |
||||||
|
} |
||||||
|
|
||||||
|
void* arg_hashtable_itr_value(arg_hashtable_itr_t* i) { |
||||||
|
return i->e->v; |
||||||
|
} |
||||||
|
|
||||||
|
int arg_hashtable_itr_advance(arg_hashtable_itr_t* itr) { |
||||||
|
unsigned int j; |
||||||
|
unsigned int tablelength; |
||||||
|
struct arg_hashtable_entry** table; |
||||||
|
struct arg_hashtable_entry* next; |
||||||
|
|
||||||
|
if (itr->e == NULL) |
||||||
|
return 0; /* stupidity check */ |
||||||
|
|
||||||
|
next = itr->e->next; |
||||||
|
if (NULL != next) { |
||||||
|
itr->parent = itr->e; |
||||||
|
itr->e = next; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
tablelength = itr->h->tablelength; |
||||||
|
itr->parent = NULL; |
||||||
|
if (tablelength <= (j = ++(itr->index))) { |
||||||
|
itr->e = NULL; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
table = itr->h->table; |
||||||
|
while (NULL == (next = table[j])) { |
||||||
|
if (++j >= tablelength) { |
||||||
|
itr->index = tablelength; |
||||||
|
itr->e = NULL; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
itr->index = j; |
||||||
|
itr->e = next; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
int arg_hashtable_itr_remove(arg_hashtable_itr_t* itr) { |
||||||
|
struct arg_hashtable_entry* remember_e; |
||||||
|
struct arg_hashtable_entry* remember_parent; |
||||||
|
int ret; |
||||||
|
|
||||||
|
/* Do the removal */ |
||||||
|
if ((itr->parent) == NULL) { |
||||||
|
/* element is head of a chain */ |
||||||
|
itr->h->table[itr->index] = itr->e->next; |
||||||
|
} else { |
||||||
|
/* element is mid-chain */ |
||||||
|
itr->parent->next = itr->e->next; |
||||||
|
} |
||||||
|
/* itr->e is now outside the hashtable */ |
||||||
|
remember_e = itr->e; |
||||||
|
itr->h->entrycount--; |
||||||
|
xfree(remember_e->k); |
||||||
|
xfree(remember_e->v); |
||||||
|
|
||||||
|
/* Advance the iterator, correcting the parent */ |
||||||
|
remember_parent = itr->parent; |
||||||
|
ret = arg_hashtable_itr_advance(itr); |
||||||
|
if (itr->parent == remember_e) { |
||||||
|
itr->parent = remember_parent; |
||||||
|
} |
||||||
|
xfree(remember_e); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
int arg_hashtable_itr_search(arg_hashtable_itr_t* itr, arg_hashtable_t* h, void* k) { |
||||||
|
struct arg_hashtable_entry* e; |
||||||
|
struct arg_hashtable_entry* parent; |
||||||
|
unsigned int hashvalue; |
||||||
|
unsigned int index; |
||||||
|
|
||||||
|
hashvalue = enhanced_hash(h, k); |
||||||
|
index = index_for(h->tablelength, hashvalue); |
||||||
|
|
||||||
|
e = h->table[index]; |
||||||
|
parent = NULL; |
||||||
|
while (e != NULL) { |
||||||
|
/* Check hash value to short circuit heavier comparison */ |
||||||
|
if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { |
||||||
|
itr->index = index; |
||||||
|
itr->e = e; |
||||||
|
itr->parent = parent; |
||||||
|
itr->h = h; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
parent = e; |
||||||
|
e = e->next; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int arg_hashtable_change(arg_hashtable_t* h, void* k, void* v) { |
||||||
|
struct arg_hashtable_entry* e; |
||||||
|
unsigned int hashvalue; |
||||||
|
unsigned int index; |
||||||
|
|
||||||
|
hashvalue = enhanced_hash(h, k); |
||||||
|
index = index_for(h->tablelength, hashvalue); |
||||||
|
e = h->table[index]; |
||||||
|
while (e != NULL) { |
||||||
|
/* Check hash value to short circuit heavier comparison */ |
||||||
|
if ((hashvalue == e->h) && (h->eqfn(k, e->k))) { |
||||||
|
xfree(e->v); |
||||||
|
e->v = v; |
||||||
|
return -1; |
||||||
|
} |
||||||
|
e = e->next; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,294 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_int: Implements the int command-line option |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* <sheitmann@users.sourceforge.net> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#include "argtable3.h" |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <ctype.h> |
||||||
|
#include <limits.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
static void arg_int_resetfn(struct arg_int* parent) { |
||||||
|
ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); |
||||||
|
parent->count = 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* strtol0x() is like strtol() except that the numeric string is */ |
||||||
|
/* expected to be prefixed by "0X" where X is a user supplied char. */ |
||||||
|
/* The string may optionally be prefixed by white space and + or - */ |
||||||
|
/* as in +0X123 or -0X123. */ |
||||||
|
/* Once the prefix has been scanned, the remainder of the numeric */ |
||||||
|
/* string is converted using strtol() with the given base. */ |
||||||
|
/* eg: to parse hex str="-0X12324", specify X='X' and base=16. */ |
||||||
|
/* eg: to parse oct str="+0o12324", specify X='O' and base=8. */ |
||||||
|
/* eg: to parse bin str="-0B01010", specify X='B' and base=2. */ |
||||||
|
/* Failure of conversion is indicated by result where *endptr==str. */ |
||||||
|
static long int strtol0X(const char* str, const char** endptr, char X, int base) { |
||||||
|
long int val; /* stores result */ |
||||||
|
int s = 1; /* sign is +1 or -1 */ |
||||||
|
const char* ptr = str; /* ptr to current position in str */ |
||||||
|
|
||||||
|
/* skip leading whitespace */ |
||||||
|
while (isspace((int)(*ptr))) |
||||||
|
ptr++; |
||||||
|
/* printf("1) %s\n",ptr); */ |
||||||
|
|
||||||
|
/* scan optional sign character */ |
||||||
|
switch (*ptr) { |
||||||
|
case '+': |
||||||
|
ptr++; |
||||||
|
s = 1; |
||||||
|
break; |
||||||
|
case '-': |
||||||
|
ptr++; |
||||||
|
s = -1; |
||||||
|
break; |
||||||
|
default: |
||||||
|
s = 1; |
||||||
|
break; |
||||||
|
} |
||||||
|
/* printf("2) %s\n",ptr); */ |
||||||
|
|
||||||
|
/* '0X' prefix */ |
||||||
|
if ((*ptr++) != '0') { |
||||||
|
/* printf("failed to detect '0'\n"); */ |
||||||
|
*endptr = str; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
/* printf("3) %s\n",ptr); */ |
||||||
|
if (toupper(*ptr++) != toupper(X)) { |
||||||
|
/* printf("failed to detect '%c'\n",X); */ |
||||||
|
*endptr = str; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
/* printf("4) %s\n",ptr); */ |
||||||
|
|
||||||
|
/* attempt conversion on remainder of string using strtol() */ |
||||||
|
val = strtol(ptr, (char**)endptr, base); |
||||||
|
if (*endptr == ptr) { |
||||||
|
/* conversion failed */ |
||||||
|
*endptr = str; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
/* success */ |
||||||
|
return s * val; |
||||||
|
} |
||||||
|
|
||||||
|
/* Returns 1 if str matches suffix (case insensitive). */ |
||||||
|
/* Str may contain trailing whitespace, but nothing else. */ |
||||||
|
static int detectsuffix(const char* str, const char* suffix) { |
||||||
|
/* scan pairwise through strings until mismatch detected */ |
||||||
|
while (toupper(*str) == toupper(*suffix)) { |
||||||
|
/* printf("'%c' '%c'\n", *str, *suffix); */ |
||||||
|
|
||||||
|
/* return 1 (success) if match persists until the string terminator */ |
||||||
|
if (*str == '\0') |
||||||
|
return 1; |
||||||
|
|
||||||
|
/* next chars */ |
||||||
|
str++; |
||||||
|
suffix++; |
||||||
|
} |
||||||
|
/* printf("'%c' '%c' mismatch\n", *str, *suffix); */ |
||||||
|
|
||||||
|
/* return 0 (fail) if the matching did not consume the entire suffix */ |
||||||
|
if (*suffix != 0) |
||||||
|
return 0; /* failed to consume entire suffix */ |
||||||
|
|
||||||
|
/* skip any remaining whitespace in str */ |
||||||
|
while (isspace((int)(*str))) |
||||||
|
str++; |
||||||
|
|
||||||
|
/* return 1 (success) if we have reached end of str else return 0 (fail) */ |
||||||
|
return (*str == '\0') ? 1 : 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_int_scanfn(struct arg_int* parent, const char* argval) { |
||||||
|
int errorcode = 0; |
||||||
|
|
||||||
|
if (parent->count == parent->hdr.maxcount) { |
||||||
|
/* maximum number of arguments exceeded */ |
||||||
|
errorcode = ARG_ERR_MAXCOUNT; |
||||||
|
} else if (!argval) { |
||||||
|
/* a valid argument with no argument value was given. */ |
||||||
|
/* This happens when an optional argument value was invoked. */ |
||||||
|
/* leave parent arguiment value unaltered but still count the argument. */ |
||||||
|
parent->count++; |
||||||
|
} else { |
||||||
|
long int val; |
||||||
|
const char* end; |
||||||
|
|
||||||
|
/* attempt to extract hex integer (eg: +0x123) from argval into val conversion */ |
||||||
|
val = strtol0X(argval, &end, 'X', 16); |
||||||
|
if (end == argval) { |
||||||
|
/* hex failed, attempt octal conversion (eg +0o123) */ |
||||||
|
val = strtol0X(argval, &end, 'O', 8); |
||||||
|
if (end == argval) { |
||||||
|
/* octal failed, attempt binary conversion (eg +0B101) */ |
||||||
|
val = strtol0X(argval, &end, 'B', 2); |
||||||
|
if (end == argval) { |
||||||
|
/* binary failed, attempt decimal conversion with no prefix (eg 1234) */ |
||||||
|
val = strtol(argval, (char**)&end, 10); |
||||||
|
if (end == argval) { |
||||||
|
/* all supported number formats failed */ |
||||||
|
return ARG_ERR_BADINT; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Safety check for integer overflow. WARNING: this check */ |
||||||
|
/* achieves nothing on machines where size(int)==size(long). */ |
||||||
|
if (val > INT_MAX || val < INT_MIN) |
||||||
|
errorcode = ARG_ERR_OVERFLOW; |
||||||
|
|
||||||
|
/* Detect any suffixes (KB,MB,GB) and multiply argument value appropriately. */ |
||||||
|
/* We need to be mindful of integer overflows when using such big numbers. */ |
||||||
|
if (detectsuffix(end, "KB")) /* kilobytes */ |
||||||
|
{ |
||||||
|
if (val > (INT_MAX / 1024) || val < (INT_MIN / 1024)) |
||||||
|
errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */ |
||||||
|
else |
||||||
|
val *= 1024; /* 1KB = 1024 */ |
||||||
|
} else if (detectsuffix(end, "MB")) /* megabytes */ |
||||||
|
{ |
||||||
|
if (val > (INT_MAX / 1048576) || val < (INT_MIN / 1048576)) |
||||||
|
errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */ |
||||||
|
else |
||||||
|
val *= 1048576; /* 1MB = 1024*1024 */ |
||||||
|
} else if (detectsuffix(end, "GB")) /* gigabytes */ |
||||||
|
{ |
||||||
|
if (val > (INT_MAX / 1073741824) || val < (INT_MIN / 1073741824)) |
||||||
|
errorcode = ARG_ERR_OVERFLOW; /* Overflow would occur if we proceed */ |
||||||
|
else |
||||||
|
val *= 1073741824; /* 1GB = 1024*1024*1024 */ |
||||||
|
} else if (!detectsuffix(end, "")) |
||||||
|
errorcode = ARG_ERR_BADINT; /* invalid suffix detected */ |
||||||
|
|
||||||
|
/* if success then store result in parent->ival[] array */ |
||||||
|
if (errorcode == 0) |
||||||
|
parent->ival[parent->count++] = (int)val; |
||||||
|
} |
||||||
|
|
||||||
|
/* printf("%s:scanfn(%p,%p) returns %d\n",__FILE__,parent,argval,errorcode); */ |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_int_checkfn(struct arg_int* parent) { |
||||||
|
int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; |
||||||
|
/*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static void arg_int_errorfn(struct arg_int* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { |
||||||
|
const char* shortopts = parent->hdr.shortopts; |
||||||
|
const char* longopts = parent->hdr.longopts; |
||||||
|
const char* datatype = parent->hdr.datatype; |
||||||
|
|
||||||
|
/* make argval NULL safe */ |
||||||
|
argval = argval ? argval : ""; |
||||||
|
|
||||||
|
arg_dstr_catf(ds, "%s: ", progname); |
||||||
|
switch (errorcode) { |
||||||
|
case ARG_ERR_MINCOUNT: |
||||||
|
arg_dstr_cat(ds, "missing option "); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARG_ERR_MAXCOUNT: |
||||||
|
arg_dstr_cat(ds, "excess option "); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARG_ERR_BADINT: |
||||||
|
arg_dstr_catf(ds, "invalid argument \"%s\" to option ", argval); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARG_ERR_OVERFLOW: |
||||||
|
arg_dstr_cat(ds, "integer overflow at option "); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, datatype, " "); |
||||||
|
arg_dstr_catf(ds, "(%s is too large)\n", argval); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { |
||||||
|
return arg_intn(shortopts, longopts, datatype, 0, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { |
||||||
|
return arg_intn(shortopts, longopts, datatype, 1, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_int* arg_intn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { |
||||||
|
size_t nbytes; |
||||||
|
struct arg_int* result; |
||||||
|
|
||||||
|
/* foolproof things by ensuring maxcount is not less than mincount */ |
||||||
|
maxcount = (maxcount < mincount) ? mincount : maxcount; |
||||||
|
|
||||||
|
nbytes = sizeof(struct arg_int) /* storage for struct arg_int */ |
||||||
|
+ (size_t)maxcount * sizeof(int); /* storage for ival[maxcount] array */ |
||||||
|
|
||||||
|
result = (struct arg_int*)xmalloc(nbytes); |
||||||
|
|
||||||
|
/* init the arg_hdr struct */ |
||||||
|
result->hdr.flag = ARG_HASVALUE; |
||||||
|
result->hdr.shortopts = shortopts; |
||||||
|
result->hdr.longopts = longopts; |
||||||
|
result->hdr.datatype = datatype ? datatype : "<int>"; |
||||||
|
result->hdr.glossary = glossary; |
||||||
|
result->hdr.mincount = mincount; |
||||||
|
result->hdr.maxcount = maxcount; |
||||||
|
result->hdr.parent = result; |
||||||
|
result->hdr.resetfn = (arg_resetfn*)arg_int_resetfn; |
||||||
|
result->hdr.scanfn = (arg_scanfn*)arg_int_scanfn; |
||||||
|
result->hdr.checkfn = (arg_checkfn*)arg_int_checkfn; |
||||||
|
result->hdr.errorfn = (arg_errorfn*)arg_int_errorfn; |
||||||
|
|
||||||
|
/* store the ival[maxcount] array immediately after the arg_int struct */ |
||||||
|
result->ival = (int*)(result + 1); |
||||||
|
result->count = 0; |
||||||
|
|
||||||
|
ARG_TRACE(("arg_intn() returns %p\n", result)); |
||||||
|
return result; |
||||||
|
} |
@ -0,0 +1,124 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_lit: Implements the literature command-line option |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* <sheitmann@users.sourceforge.net> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#include "argtable3.h" |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
static void arg_lit_resetfn(struct arg_lit* parent) { |
||||||
|
ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); |
||||||
|
parent->count = 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_lit_scanfn(struct arg_lit* parent, const char* argval) { |
||||||
|
int errorcode = 0; |
||||||
|
if (parent->count < parent->hdr.maxcount) |
||||||
|
parent->count++; |
||||||
|
else |
||||||
|
errorcode = ARG_ERR_MAXCOUNT; |
||||||
|
|
||||||
|
ARG_TRACE(("%s:scanfn(%p,%s) returns %d\n", __FILE__, parent, argval, errorcode)); |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_lit_checkfn(struct arg_lit* parent) { |
||||||
|
int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; |
||||||
|
ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static void arg_lit_errorfn(struct arg_lit* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { |
||||||
|
const char* shortopts = parent->hdr.shortopts; |
||||||
|
const char* longopts = parent->hdr.longopts; |
||||||
|
const char* datatype = parent->hdr.datatype; |
||||||
|
|
||||||
|
switch (errorcode) { |
||||||
|
case ARG_ERR_MINCOUNT: |
||||||
|
arg_dstr_catf(ds, "%s: missing option ", progname); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); |
||||||
|
arg_dstr_cat(ds, "\n"); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARG_ERR_MAXCOUNT: |
||||||
|
arg_dstr_catf(ds, "%s: extraneous option ", progname); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
ARG_TRACE(("%s:errorfn(%p, %p, %d, %s, %s)\n", __FILE__, parent, ds, errorcode, argval, progname)); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary) { |
||||||
|
return arg_litn(shortopts, longopts, 0, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary) { |
||||||
|
return arg_litn(shortopts, longopts, 1, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary) { |
||||||
|
struct arg_lit* result; |
||||||
|
|
||||||
|
/* foolproof things by ensuring maxcount is not less than mincount */ |
||||||
|
maxcount = (maxcount < mincount) ? mincount : maxcount; |
||||||
|
|
||||||
|
result = (struct arg_lit*)xmalloc(sizeof(struct arg_lit)); |
||||||
|
|
||||||
|
/* init the arg_hdr struct */ |
||||||
|
result->hdr.flag = 0; |
||||||
|
result->hdr.shortopts = shortopts; |
||||||
|
result->hdr.longopts = longopts; |
||||||
|
result->hdr.datatype = NULL; |
||||||
|
result->hdr.glossary = glossary; |
||||||
|
result->hdr.mincount = mincount; |
||||||
|
result->hdr.maxcount = maxcount; |
||||||
|
result->hdr.parent = result; |
||||||
|
result->hdr.resetfn = (arg_resetfn*)arg_lit_resetfn; |
||||||
|
result->hdr.scanfn = (arg_scanfn*)arg_lit_scanfn; |
||||||
|
result->hdr.checkfn = (arg_checkfn*)arg_lit_checkfn; |
||||||
|
result->hdr.errorfn = (arg_errorfn*)arg_lit_errorfn; |
||||||
|
|
||||||
|
/* init local variables */ |
||||||
|
result->count = 0; |
||||||
|
|
||||||
|
ARG_TRACE(("arg_litn() returns %p\n", result)); |
||||||
|
return result; |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_rem: Implements the rem command-line option |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* <sheitmann@users.sourceforge.net> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#include "argtable3.h" |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
struct arg_rem* arg_rem(const char* datatype, const char* glossary) { |
||||||
|
struct arg_rem* result = (struct arg_rem*)xmalloc(sizeof(struct arg_rem)); |
||||||
|
|
||||||
|
result->hdr.flag = 0; |
||||||
|
result->hdr.shortopts = NULL; |
||||||
|
result->hdr.longopts = NULL; |
||||||
|
result->hdr.datatype = datatype; |
||||||
|
result->hdr.glossary = glossary; |
||||||
|
result->hdr.mincount = 1; |
||||||
|
result->hdr.maxcount = 1; |
||||||
|
result->hdr.parent = result; |
||||||
|
result->hdr.resetfn = NULL; |
||||||
|
result->hdr.scanfn = NULL; |
||||||
|
result->hdr.checkfn = NULL; |
||||||
|
result->hdr.errorfn = NULL; |
||||||
|
|
||||||
|
ARG_TRACE(("arg_rem() returns %p\n", result)); |
||||||
|
return result; |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,151 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_str: Implements the str command-line option |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* <sheitmann@users.sourceforge.net> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#include "argtable3.h" |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
static void arg_str_resetfn(struct arg_str* parent) { |
||||||
|
int i; |
||||||
|
|
||||||
|
ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); |
||||||
|
for (i = 0; i < parent->count; i++) { |
||||||
|
parent->sval[i] = ""; |
||||||
|
} |
||||||
|
parent->count = 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_str_scanfn(struct arg_str* parent, const char* argval) { |
||||||
|
int errorcode = 0; |
||||||
|
|
||||||
|
if (parent->count == parent->hdr.maxcount) { |
||||||
|
/* maximum number of arguments exceeded */ |
||||||
|
errorcode = ARG_ERR_MAXCOUNT; |
||||||
|
} else if (!argval) { |
||||||
|
/* a valid argument with no argument value was given. */ |
||||||
|
/* This happens when an optional argument value was invoked. */ |
||||||
|
/* leave parent argument value unaltered but still count the argument. */ |
||||||
|
parent->count++; |
||||||
|
} else { |
||||||
|
parent->sval[parent->count++] = argval; |
||||||
|
} |
||||||
|
|
||||||
|
ARG_TRACE(("%s:scanfn(%p) returns %d\n", __FILE__, parent, errorcode)); |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static int arg_str_checkfn(struct arg_str* parent) { |
||||||
|
int errorcode = (parent->count < parent->hdr.mincount) ? ARG_ERR_MINCOUNT : 0; |
||||||
|
|
||||||
|
ARG_TRACE(("%s:checkfn(%p) returns %d\n", __FILE__, parent, errorcode)); |
||||||
|
return errorcode; |
||||||
|
} |
||||||
|
|
||||||
|
static void arg_str_errorfn(struct arg_str* parent, arg_dstr_t ds, int errorcode, const char* argval, const char* progname) { |
||||||
|
const char* shortopts = parent->hdr.shortopts; |
||||||
|
const char* longopts = parent->hdr.longopts; |
||||||
|
const char* datatype = parent->hdr.datatype; |
||||||
|
|
||||||
|
/* make argval NULL safe */ |
||||||
|
argval = argval ? argval : ""; |
||||||
|
|
||||||
|
arg_dstr_catf(ds, "%s: ", progname); |
||||||
|
switch (errorcode) { |
||||||
|
case ARG_ERR_MINCOUNT: |
||||||
|
arg_dstr_cat(ds, "missing option "); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, datatype, "\n"); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARG_ERR_MAXCOUNT: |
||||||
|
arg_dstr_cat(ds, "excess option "); |
||||||
|
arg_print_option_ds(ds, shortopts, longopts, argval, "\n"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { |
||||||
|
return arg_strn(shortopts, longopts, datatype, 0, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary) { |
||||||
|
return arg_strn(shortopts, longopts, datatype, 1, 1, glossary); |
||||||
|
} |
||||||
|
|
||||||
|
struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary) { |
||||||
|
size_t nbytes; |
||||||
|
struct arg_str* result; |
||||||
|
int i; |
||||||
|
|
||||||
|
/* should not allow this stupid error */ |
||||||
|
/* we should return an error code warning this logic error */ |
||||||
|
/* foolproof things by ensuring maxcount is not less than mincount */ |
||||||
|
maxcount = (maxcount < mincount) ? mincount : maxcount; |
||||||
|
|
||||||
|
nbytes = sizeof(struct arg_str) /* storage for struct arg_str */ |
||||||
|
+ (size_t)maxcount * sizeof(char*); /* storage for sval[maxcount] array */ |
||||||
|
|
||||||
|
result = (struct arg_str*)xmalloc(nbytes); |
||||||
|
|
||||||
|
/* init the arg_hdr struct */ |
||||||
|
result->hdr.flag = ARG_HASVALUE; |
||||||
|
result->hdr.shortopts = shortopts; |
||||||
|
result->hdr.longopts = longopts; |
||||||
|
result->hdr.datatype = datatype ? datatype : "<string>"; |
||||||
|
result->hdr.glossary = glossary; |
||||||
|
result->hdr.mincount = mincount; |
||||||
|
result->hdr.maxcount = maxcount; |
||||||
|
result->hdr.parent = result; |
||||||
|
result->hdr.resetfn = (arg_resetfn*)arg_str_resetfn; |
||||||
|
result->hdr.scanfn = (arg_scanfn*)arg_str_scanfn; |
||||||
|
result->hdr.checkfn = (arg_checkfn*)arg_str_checkfn; |
||||||
|
result->hdr.errorfn = (arg_errorfn*)arg_str_errorfn; |
||||||
|
|
||||||
|
/* store the sval[maxcount] array immediately after the arg_str struct */ |
||||||
|
result->sval = (const char**)(result + 1); |
||||||
|
result->count = 0; |
||||||
|
|
||||||
|
/* foolproof the string pointers by initializing them to reference empty strings */ |
||||||
|
for (i = 0; i < maxcount; i++) |
||||||
|
result->sval[i] = ""; |
||||||
|
|
||||||
|
ARG_TRACE(("arg_strn() returns %p\n", result)); |
||||||
|
return result; |
||||||
|
} |
@ -0,0 +1,183 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2013-2019 Tom G. Huang |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* arg_utils: Implements memory, panic, and other utility functions |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 2013-2019 Tom G. Huang |
||||||
|
* <tomghuang@gmail.com> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#include "argtable3.h" |
||||||
|
|
||||||
|
#ifndef ARG_AMALGAMATION |
||||||
|
#include "argtable3_private.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdarg.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
static void panic(const char* fmt, ...); |
||||||
|
static arg_panicfn* s_panic = panic; |
||||||
|
|
||||||
|
void dbg_printf(const char* fmt, ...) { |
||||||
|
va_list args; |
||||||
|
va_start(args, fmt); |
||||||
|
vfprintf(stderr, fmt, args); |
||||||
|
va_end(args); |
||||||
|
} |
||||||
|
|
||||||
|
static void panic(const char* fmt, ...) { |
||||||
|
va_list args; |
||||||
|
char* s; |
||||||
|
|
||||||
|
va_start(args, fmt); |
||||||
|
vfprintf(stderr, fmt, args); |
||||||
|
va_end(args); |
||||||
|
|
||||||
|
#if defined(_MSC_VER) |
||||||
|
#pragma warning(push) |
||||||
|
#pragma warning(disable : 4996) |
||||||
|
#endif |
||||||
|
s = getenv("EF_DUMPCORE"); |
||||||
|
#if defined(_MSC_VER) |
||||||
|
#pragma warning(pop) |
||||||
|
#endif |
||||||
|
|
||||||
|
if (s != NULL && *s != '\0') { |
||||||
|
abort(); |
||||||
|
} else { |
||||||
|
exit(EXIT_FAILURE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void arg_set_panic(arg_panicfn* proc) { |
||||||
|
s_panic = proc; |
||||||
|
} |
||||||
|
|
||||||
|
void* xmalloc(size_t size) { |
||||||
|
void* ret = malloc(size); |
||||||
|
if (!ret) { |
||||||
|
s_panic("Out of memory!\n"); |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
void* xcalloc(size_t count, size_t size) { |
||||||
|
size_t allocated_count = count && size ? count : 1; |
||||||
|
size_t allocated_size = count && size ? size : 1; |
||||||
|
void* ret = calloc(allocated_count, allocated_size); |
||||||
|
if (!ret) { |
||||||
|
s_panic("Out of memory!\n"); |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
void* xrealloc(void* ptr, size_t size) { |
||||||
|
size_t allocated_size = size ? size : 1; |
||||||
|
void* ret = realloc(ptr, allocated_size); |
||||||
|
if (!ret) { |
||||||
|
s_panic("Out of memory!\n"); |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
void xfree(void* ptr) { |
||||||
|
free(ptr); |
||||||
|
} |
||||||
|
|
||||||
|
static void merge(void* data, int esize, int i, int j, int k, arg_comparefn* comparefn) { |
||||||
|
char* a = (char*)data; |
||||||
|
char* m; |
||||||
|
int ipos, jpos, mpos; |
||||||
|
|
||||||
|
/* Initialize the counters used in merging. */ |
||||||
|
ipos = i; |
||||||
|
jpos = j + 1; |
||||||
|
mpos = 0; |
||||||
|
|
||||||
|
/* Allocate storage for the merged elements. */ |
||||||
|
m = (char*)xmalloc((size_t)(esize * ((k - i) + 1))); |
||||||
|
|
||||||
|
/* Continue while either division has elements to merge. */ |
||||||
|
while (ipos <= j || jpos <= k) { |
||||||
|
if (ipos > j) { |
||||||
|
/* The left division has no more elements to merge. */ |
||||||
|
while (jpos <= k) { |
||||||
|
memcpy(&m[mpos * esize], &a[jpos * esize], (size_t)esize); |
||||||
|
jpos++; |
||||||
|
mpos++; |
||||||
|
} |
||||||
|
|
||||||
|
continue; |
||||||
|
} else if (jpos > k) { |
||||||
|
/* The right division has no more elements to merge. */ |
||||||
|
while (ipos <= j) { |
||||||
|
memcpy(&m[mpos * esize], &a[ipos * esize], (size_t)esize); |
||||||
|
ipos++; |
||||||
|
mpos++; |
||||||
|
} |
||||||
|
|
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
/* Append the next ordered element to the merged elements. */ |
||||||
|
if (comparefn(&a[ipos * esize], &a[jpos * esize]) < 0) { |
||||||
|
memcpy(&m[mpos * esize], &a[ipos * esize], (size_t)esize); |
||||||
|
ipos++; |
||||||
|
mpos++; |
||||||
|
} else { |
||||||
|
memcpy(&m[mpos * esize], &a[jpos * esize], (size_t)esize); |
||||||
|
jpos++; |
||||||
|
mpos++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Prepare to pass back the merged data. */ |
||||||
|
memcpy(&a[i * esize], m, (size_t)(esize * ((k - i) + 1))); |
||||||
|
xfree(m); |
||||||
|
} |
||||||
|
|
||||||
|
void arg_mgsort(void* data, int size, int esize, int i, int k, arg_comparefn* comparefn) { |
||||||
|
int j; |
||||||
|
|
||||||
|
/* Stop the recursion when no more divisions can be made. */ |
||||||
|
if (i < k) { |
||||||
|
/* Determine where to divide the elements. */ |
||||||
|
j = (int)(((i + k - 1)) / 2); |
||||||
|
|
||||||
|
/* Recursively sort the two divisions. */ |
||||||
|
arg_mgsort(data, size, esize, i, j, comparefn); |
||||||
|
arg_mgsort(data, size, esize, j + 1, k, comparefn); |
||||||
|
merge(data, esize, i, j, k, comparefn); |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,279 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* argtable3: Declares the main interfaces of the library |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann |
||||||
|
* <sheitmann@users.sourceforge.net> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#ifndef ARGTABLE3 |
||||||
|
#define ARGTABLE3 |
||||||
|
|
||||||
|
#include <stdio.h> /* FILE */ |
||||||
|
#include <time.h> /* struct tm */ |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
#define ARG_REX_ICASE 1 |
||||||
|
#define ARG_DSTR_SIZE 200 |
||||||
|
#define ARG_CMD_NAME_LEN 100 |
||||||
|
#define ARG_CMD_DESCRIPTION_LEN 256 |
||||||
|
|
||||||
|
#ifndef ARG_REPLACE_GETOPT |
||||||
|
#define ARG_REPLACE_GETOPT 0 /* ESP-IDF-specific: use newlib-provided getopt instead of the embedded one */ |
||||||
|
#endif /* ARG_REPLACE_GETOPT */ |
||||||
|
|
||||||
|
/* bit masks for arg_hdr.flag */ |
||||||
|
enum { ARG_TERMINATOR = 0x1, ARG_HASVALUE = 0x2, ARG_HASOPTVALUE = 0x4 }; |
||||||
|
|
||||||
|
#if defined(_WIN32) |
||||||
|
#if defined(argtable3_EXPORTS) |
||||||
|
#define ARG_EXTERN __declspec(dllexport) |
||||||
|
#elif defined(argtable3_IMPORTS) |
||||||
|
#define ARG_EXTERN __declspec(dllimport) |
||||||
|
#else |
||||||
|
#define ARG_EXTERN |
||||||
|
#endif |
||||||
|
#else |
||||||
|
#define ARG_EXTERN |
||||||
|
#endif |
||||||
|
|
||||||
|
typedef struct _internal_arg_dstr* arg_dstr_t; |
||||||
|
typedef void* arg_cmd_itr_t; |
||||||
|
|
||||||
|
typedef void(arg_resetfn)(void* parent); |
||||||
|
typedef int(arg_scanfn)(void* parent, const char* argval); |
||||||
|
typedef int(arg_checkfn)(void* parent); |
||||||
|
typedef void(arg_errorfn)(void* parent, arg_dstr_t ds, int error, const char* argval, const char* progname); |
||||||
|
typedef void(arg_dstr_freefn)(char* buf); |
||||||
|
typedef int(arg_cmdfn)(int argc, char* argv[], arg_dstr_t res); |
||||||
|
typedef int(arg_comparefn)(const void* k1, const void* k2); |
||||||
|
|
||||||
|
/*
|
||||||
|
* The arg_hdr struct defines properties that are common to all arg_xxx structs. |
||||||
|
* The argtable library requires each arg_xxx struct to have an arg_hdr |
||||||
|
* struct as its first data member. |
||||||
|
* The argtable library functions then use this data to identify the |
||||||
|
* properties of the command line option, such as its option tags, |
||||||
|
* datatype string, and glossary strings, and so on. |
||||||
|
* Moreover, the arg_hdr struct contains pointers to custom functions that |
||||||
|
* are provided by each arg_xxx struct which perform the tasks of parsing |
||||||
|
* that particular arg_xxx arguments, performing post-parse checks, and |
||||||
|
* reporting errors. |
||||||
|
* These functions are private to the individual arg_xxx source code |
||||||
|
* and are the pointer to them are initiliased by that arg_xxx struct's |
||||||
|
* constructor function. The user could alter them after construction |
||||||
|
* if desired, but the original intention is for them to be set by the |
||||||
|
* constructor and left unaltered. |
||||||
|
*/ |
||||||
|
typedef struct arg_hdr { |
||||||
|
char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */ |
||||||
|
const char* shortopts; /* String defining the short options */ |
||||||
|
const char* longopts; /* String defiing the long options */ |
||||||
|
const char* datatype; /* Description of the argument data type */ |
||||||
|
const char* glossary; /* Description of the option as shown by arg_print_glossary function */ |
||||||
|
int mincount; /* Minimum number of occurences of this option accepted */ |
||||||
|
int maxcount; /* Maximum number of occurences if this option accepted */ |
||||||
|
void* parent; /* Pointer to parent arg_xxx struct */ |
||||||
|
arg_resetfn* resetfn; /* Pointer to parent arg_xxx reset function */ |
||||||
|
arg_scanfn* scanfn; /* Pointer to parent arg_xxx scan function */ |
||||||
|
arg_checkfn* checkfn; /* Pointer to parent arg_xxx check function */ |
||||||
|
arg_errorfn* errorfn; /* Pointer to parent arg_xxx error function */ |
||||||
|
void* priv; /* Pointer to private header data for use by arg_xxx functions */ |
||||||
|
} arg_hdr_t; |
||||||
|
|
||||||
|
typedef struct arg_rem { |
||||||
|
struct arg_hdr hdr; /* The mandatory argtable header struct */ |
||||||
|
} arg_rem_t; |
||||||
|
|
||||||
|
typedef struct arg_lit { |
||||||
|
struct arg_hdr hdr; /* The mandatory argtable header struct */ |
||||||
|
int count; /* Number of matching command line args */ |
||||||
|
} arg_lit_t; |
||||||
|
|
||||||
|
typedef struct arg_int { |
||||||
|
struct arg_hdr hdr; /* The mandatory argtable header struct */ |
||||||
|
int count; /* Number of matching command line args */ |
||||||
|
int* ival; /* Array of parsed argument values */ |
||||||
|
} arg_int_t; |
||||||
|
|
||||||
|
typedef struct arg_dbl { |
||||||
|
struct arg_hdr hdr; /* The mandatory argtable header struct */ |
||||||
|
int count; /* Number of matching command line args */ |
||||||
|
double* dval; /* Array of parsed argument values */ |
||||||
|
} arg_dbl_t; |
||||||
|
|
||||||
|
typedef struct arg_str { |
||||||
|
struct arg_hdr hdr; /* The mandatory argtable header struct */ |
||||||
|
int count; /* Number of matching command line args */ |
||||||
|
const char** sval; /* Array of parsed argument values */ |
||||||
|
} arg_str_t; |
||||||
|
|
||||||
|
typedef struct arg_rex { |
||||||
|
struct arg_hdr hdr; /* The mandatory argtable header struct */ |
||||||
|
int count; /* Number of matching command line args */ |
||||||
|
const char** sval; /* Array of parsed argument values */ |
||||||
|
} arg_rex_t; |
||||||
|
|
||||||
|
typedef struct arg_file { |
||||||
|
struct arg_hdr hdr; /* The mandatory argtable header struct */ |
||||||
|
int count; /* Number of matching command line args*/ |
||||||
|
const char** filename; /* Array of parsed filenames (eg: /home/foo.bar) */ |
||||||
|
const char** basename; /* Array of parsed basenames (eg: foo.bar) */ |
||||||
|
const char** extension; /* Array of parsed extensions (eg: .bar) */ |
||||||
|
} arg_file_t; |
||||||
|
|
||||||
|
typedef struct arg_date { |
||||||
|
struct arg_hdr hdr; /* The mandatory argtable header struct */ |
||||||
|
const char* format; /* strptime format string used to parse the date */ |
||||||
|
int count; /* Number of matching command line args */ |
||||||
|
struct tm* tmval; /* Array of parsed time values */ |
||||||
|
} arg_date_t; |
||||||
|
|
||||||
|
enum { ARG_ELIMIT = 1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG }; |
||||||
|
typedef struct arg_end { |
||||||
|
struct arg_hdr hdr; /* The mandatory argtable header struct */ |
||||||
|
int count; /* Number of errors encountered */ |
||||||
|
int* error; /* Array of error codes */ |
||||||
|
void** parent; /* Array of pointers to offending arg_xxx struct */ |
||||||
|
const char** argval; /* Array of pointers to offending argv[] string */ |
||||||
|
} arg_end_t; |
||||||
|
|
||||||
|
typedef struct arg_cmd_info { |
||||||
|
char name[ARG_CMD_NAME_LEN]; |
||||||
|
char description[ARG_CMD_DESCRIPTION_LEN]; |
||||||
|
arg_cmdfn* proc; |
||||||
|
} arg_cmd_info_t; |
||||||
|
|
||||||
|
/**** arg_xxx constructor functions *********************************/ |
||||||
|
|
||||||
|
ARG_EXTERN struct arg_rem* arg_rem(const char* datatype, const char* glossary); |
||||||
|
|
||||||
|
ARG_EXTERN struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary); |
||||||
|
|
||||||
|
ARG_EXTERN struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_int* arg_intn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); |
||||||
|
|
||||||
|
ARG_EXTERN struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); |
||||||
|
|
||||||
|
ARG_EXTERN struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); |
||||||
|
|
||||||
|
ARG_EXTERN struct arg_rex* arg_rex0(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_rex* arg_rex1(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_rex* arg_rexn(const char* shortopts, |
||||||
|
const char* longopts, |
||||||
|
const char* pattern, |
||||||
|
const char* datatype, |
||||||
|
int mincount, |
||||||
|
int maxcount, |
||||||
|
int flags, |
||||||
|
const char* glossary); |
||||||
|
|
||||||
|
ARG_EXTERN struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary); |
||||||
|
|
||||||
|
ARG_EXTERN struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary); |
||||||
|
ARG_EXTERN struct arg_date* arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary); |
||||||
|
|
||||||
|
ARG_EXTERN struct arg_end* arg_end(int maxcount); |
||||||
|
|
||||||
|
#define ARG_DSTR_STATIC ((arg_dstr_freefn*)0) |
||||||
|
#define ARG_DSTR_VOLATILE ((arg_dstr_freefn*)1) |
||||||
|
#define ARG_DSTR_DYNAMIC ((arg_dstr_freefn*)3) |
||||||
|
|
||||||
|
/**** other functions *******************************************/ |
||||||
|
ARG_EXTERN int arg_nullcheck(void** argtable); |
||||||
|
ARG_EXTERN int arg_parse(int argc, char** argv, void** argtable); |
||||||
|
ARG_EXTERN void arg_print_option(FILE* fp, const char* shortopts, const char* longopts, const char* datatype, const char* suffix); |
||||||
|
ARG_EXTERN void arg_print_syntax(FILE* fp, void** argtable, const char* suffix); |
||||||
|
ARG_EXTERN void arg_print_syntaxv(FILE* fp, void** argtable, const char* suffix); |
||||||
|
ARG_EXTERN void arg_print_glossary(FILE* fp, void** argtable, const char* format); |
||||||
|
ARG_EXTERN void arg_print_glossary_gnu(FILE* fp, void** argtable); |
||||||
|
ARG_EXTERN void arg_print_formatted(FILE *fp, const unsigned lmargin, const unsigned rmargin, const char *text); |
||||||
|
ARG_EXTERN void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname); |
||||||
|
ARG_EXTERN void arg_print_option_ds(arg_dstr_t ds, const char* shortopts, const char* longopts, const char* datatype, const char* suffix); |
||||||
|
ARG_EXTERN void arg_print_syntax_ds(arg_dstr_t ds, void** argtable, const char* suffix); |
||||||
|
ARG_EXTERN void arg_print_syntaxv_ds(arg_dstr_t ds, void** argtable, const char* suffix); |
||||||
|
ARG_EXTERN void arg_print_glossary_ds(arg_dstr_t ds, void** argtable, const char* format); |
||||||
|
ARG_EXTERN void arg_print_glossary_gnu_ds(arg_dstr_t ds, void** argtable); |
||||||
|
ARG_EXTERN void arg_print_errors_ds(arg_dstr_t ds, struct arg_end* end, const char* progname); |
||||||
|
ARG_EXTERN void arg_freetable(void** argtable, size_t n); |
||||||
|
|
||||||
|
ARG_EXTERN arg_dstr_t arg_dstr_create(void); |
||||||
|
ARG_EXTERN void arg_dstr_destroy(arg_dstr_t ds); |
||||||
|
ARG_EXTERN void arg_dstr_reset(arg_dstr_t ds); |
||||||
|
ARG_EXTERN void arg_dstr_free(arg_dstr_t ds); |
||||||
|
ARG_EXTERN void arg_dstr_set(arg_dstr_t ds, char* str, arg_dstr_freefn* free_proc); |
||||||
|
ARG_EXTERN void arg_dstr_cat(arg_dstr_t ds, const char* str); |
||||||
|
ARG_EXTERN void arg_dstr_catc(arg_dstr_t ds, char c); |
||||||
|
ARG_EXTERN void arg_dstr_catf(arg_dstr_t ds, const char* fmt, ...); |
||||||
|
ARG_EXTERN char* arg_dstr_cstr(arg_dstr_t ds); |
||||||
|
|
||||||
|
ARG_EXTERN void arg_cmd_init(void); |
||||||
|
ARG_EXTERN void arg_cmd_uninit(void); |
||||||
|
ARG_EXTERN void arg_cmd_register(const char* name, arg_cmdfn* proc, const char* description); |
||||||
|
ARG_EXTERN void arg_cmd_unregister(const char* name); |
||||||
|
ARG_EXTERN int arg_cmd_dispatch(const char* name, int argc, char* argv[], arg_dstr_t res); |
||||||
|
ARG_EXTERN unsigned int arg_cmd_count(void); |
||||||
|
ARG_EXTERN arg_cmd_info_t* arg_cmd_info(const char* name); |
||||||
|
ARG_EXTERN arg_cmd_itr_t arg_cmd_itr_create(void); |
||||||
|
ARG_EXTERN void arg_cmd_itr_destroy(arg_cmd_itr_t itr); |
||||||
|
ARG_EXTERN int arg_cmd_itr_advance(arg_cmd_itr_t itr); |
||||||
|
ARG_EXTERN char* arg_cmd_itr_key(arg_cmd_itr_t itr); |
||||||
|
ARG_EXTERN arg_cmd_info_t* arg_cmd_itr_value(arg_cmd_itr_t itr); |
||||||
|
ARG_EXTERN int arg_cmd_itr_search(arg_cmd_itr_t itr, void* k); |
||||||
|
ARG_EXTERN void arg_mgsort(void* data, int size, int esize, int i, int k, arg_comparefn* comparefn); |
||||||
|
ARG_EXTERN void arg_make_get_help_msg(arg_dstr_t res); |
||||||
|
ARG_EXTERN void arg_make_help_msg(arg_dstr_t ds, char* cmd_name, void** argtable); |
||||||
|
ARG_EXTERN void arg_make_syntax_err_msg(arg_dstr_t ds, void** argtable, struct arg_end* end); |
||||||
|
ARG_EXTERN int arg_make_syntax_err_help_msg(arg_dstr_t ds, char* name, int help, int nerrors, void** argtable, struct arg_end* end, int* exitcode); |
||||||
|
ARG_EXTERN void arg_set_module_name(const char* name); |
||||||
|
ARG_EXTERN void arg_set_module_version(int major, int minor, int patch, const char* tag); |
||||||
|
|
||||||
|
/**** deprecated functions, for back-compatibility only ********/ |
||||||
|
ARG_EXTERN void arg_free(void** argtable); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,245 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2013-2019 Tom G. Huang |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
/*******************************************************************************
|
||||||
|
* argtable3_private: Declares private types, constants, and interfaces |
||||||
|
* |
||||||
|
* This file is part of the argtable3 library. |
||||||
|
* |
||||||
|
* Copyright (C) 2013-2019 Tom G. Huang |
||||||
|
* <tomghuang@gmail.com> |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are met: |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* * Neither the name of STEWART HEITMANN nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT, |
||||||
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
#ifndef ARG_UTILS_H |
||||||
|
#define ARG_UTILS_H |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
#define ARG_ENABLE_TRACE 0 |
||||||
|
#define ARG_ENABLE_LOG 0 |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
enum { ARG_ERR_MINCOUNT = 1, ARG_ERR_MAXCOUNT, ARG_ERR_BADINT, ARG_ERR_OVERFLOW, ARG_ERR_BADDOUBLE, ARG_ERR_BADDATE, ARG_ERR_REGNOMATCH }; |
||||||
|
|
||||||
|
typedef void(arg_panicfn)(const char* fmt, ...); |
||||||
|
|
||||||
|
#if defined(_MSC_VER) |
||||||
|
#define ARG_TRACE(x) \ |
||||||
|
__pragma(warning(push)) __pragma(warning(disable : 4127)) do { \
|
||||||
|
if (ARG_ENABLE_TRACE) \
|
||||||
|
dbg_printf x; \
|
||||||
|
} \
|
||||||
|
while (0) \
|
||||||
|
__pragma(warning(pop)) |
||||||
|
|
||||||
|
#define ARG_LOG(x) \ |
||||||
|
__pragma(warning(push)) __pragma(warning(disable : 4127)) do { \
|
||||||
|
if (ARG_ENABLE_LOG) \
|
||||||
|
dbg_printf x; \
|
||||||
|
} \
|
||||||
|
while (0) \
|
||||||
|
__pragma(warning(pop)) |
||||||
|
#else |
||||||
|
#define ARG_TRACE(x) \ |
||||||
|
do { \
|
||||||
|
if (ARG_ENABLE_TRACE) \
|
||||||
|
dbg_printf x; \
|
||||||
|
} while (0) |
||||||
|
|
||||||
|
#define ARG_LOG(x) \ |
||||||
|
do { \
|
||||||
|
if (ARG_ENABLE_LOG) \
|
||||||
|
dbg_printf x; \
|
||||||
|
} while (0) |
||||||
|
#endif |
||||||
|
|
||||||
|
/*
|
||||||
|
* Rename a few generic names to unique names. |
||||||
|
* They can be a problem for the platforms like NuttX, where |
||||||
|
* the namespace is flat for everything including apps and libraries. |
||||||
|
*/ |
||||||
|
#define xmalloc argtable3_xmalloc |
||||||
|
#define xcalloc argtable3_xcalloc |
||||||
|
#define xrealloc argtable3_xrealloc |
||||||
|
#define xfree argtable3_xfree |
||||||
|
|
||||||
|
extern void dbg_printf(const char* fmt, ...); |
||||||
|
extern void arg_set_panic(arg_panicfn* proc); |
||||||
|
extern void* xmalloc(size_t size); |
||||||
|
extern void* xcalloc(size_t count, size_t size); |
||||||
|
extern void* xrealloc(void* ptr, size_t size); |
||||||
|
extern void xfree(void* ptr); |
||||||
|
|
||||||
|
struct arg_hashtable_entry { |
||||||
|
void *k, *v; |
||||||
|
unsigned int h; |
||||||
|
struct arg_hashtable_entry* next; |
||||||
|
}; |
||||||
|
|
||||||
|
typedef struct arg_hashtable { |
||||||
|
unsigned int tablelength; |
||||||
|
struct arg_hashtable_entry** table; |
||||||
|
unsigned int entrycount; |
||||||
|
unsigned int loadlimit; |
||||||
|
unsigned int primeindex; |
||||||
|
unsigned int (*hashfn)(const void* k); |
||||||
|
int (*eqfn)(const void* k1, const void* k2); |
||||||
|
} arg_hashtable_t; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create a hash table. |
||||||
|
* |
||||||
|
* @param minsize minimum initial size of hash table |
||||||
|
* @param hashfn function for hashing keys |
||||||
|
* @param eqfn function for determining key equality |
||||||
|
* @return newly created hash table or NULL on failure |
||||||
|
*/ |
||||||
|
arg_hashtable_t* arg_hashtable_create(unsigned int minsize, unsigned int (*hashfn)(const void*), int (*eqfn)(const void*, const void*)); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function will cause the table to expand if the insertion would take |
||||||
|
* the ratio of entries to table size over the maximum load factor. |
||||||
|
* |
||||||
|
* This function does not check for repeated insertions with a duplicate key. |
||||||
|
* The value returned when using a duplicate key is undefined -- when |
||||||
|
* the hash table changes size, the order of retrieval of duplicate key |
||||||
|
* entries is reversed. |
||||||
|
* If in doubt, remove before insert. |
||||||
|
* |
||||||
|
* @param h the hash table to insert into |
||||||
|
* @param k the key - hash table claims ownership and will free on removal |
||||||
|
* @param v the value - does not claim ownership |
||||||
|
* @return non-zero for successful insertion |
||||||
|
*/ |
||||||
|
void arg_hashtable_insert(arg_hashtable_t* h, void* k, void* v); |
||||||
|
|
||||||
|
#define ARG_DEFINE_HASHTABLE_INSERT(fnname, keytype, valuetype) \ |
||||||
|
int fnname(arg_hashtable_t* h, keytype* k, valuetype* v) { return arg_hashtable_insert(h, k, v); } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Search the specified key in the hash table. |
||||||
|
* |
||||||
|
* @param h the hash table to search |
||||||
|
* @param k the key to search for - does not claim ownership |
||||||
|
* @return the value associated with the key, or NULL if none found |
||||||
|
*/ |
||||||
|
void* arg_hashtable_search(arg_hashtable_t* h, const void* k); |
||||||
|
|
||||||
|
#define ARG_DEFINE_HASHTABLE_SEARCH(fnname, keytype, valuetype) \ |
||||||
|
valuetype* fnname(arg_hashtable_t* h, keytype* k) { return (valuetype*)(arg_hashtable_search(h, k)); } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove the specified key from the hash table. |
||||||
|
* |
||||||
|
* @param h the hash table to remove the item from |
||||||
|
* @param k the key to search for - does not claim ownership |
||||||
|
*/ |
||||||
|
void arg_hashtable_remove(arg_hashtable_t* h, const void* k); |
||||||
|
|
||||||
|
#define ARG_DEFINE_HASHTABLE_REMOVE(fnname, keytype, valuetype) \ |
||||||
|
void fnname(arg_hashtable_t* h, keytype* k) { arg_hashtable_remove(h, k); } |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the number of keys in the hash table. |
||||||
|
* |
||||||
|
* @param h the hash table |
||||||
|
* @return the number of items stored in the hash table |
||||||
|
*/ |
||||||
|
unsigned int arg_hashtable_count(arg_hashtable_t* h); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Change the value associated with the key. |
||||||
|
* |
||||||
|
* function to change the value associated with a key, where there already |
||||||
|
* exists a value bound to the key in the hash table. |
||||||
|
* Source due to Holger Schemel. |
||||||
|
* |
||||||
|
* @name hashtable_change |
||||||
|
* @param h the hash table |
||||||
|
* @param key |
||||||
|
* @param value |
||||||
|
*/ |
||||||
|
int arg_hashtable_change(arg_hashtable_t* h, void* k, void* v); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free the hash table and the memory allocated for each key-value pair. |
||||||
|
* |
||||||
|
* @param h the hash table |
||||||
|
* @param free_values whether to call 'free' on the remaining values |
||||||
|
*/ |
||||||
|
void arg_hashtable_destroy(arg_hashtable_t* h, int free_values); |
||||||
|
|
||||||
|
typedef struct arg_hashtable_itr { |
||||||
|
arg_hashtable_t* h; |
||||||
|
struct arg_hashtable_entry* e; |
||||||
|
struct arg_hashtable_entry* parent; |
||||||
|
unsigned int index; |
||||||
|
} arg_hashtable_itr_t; |
||||||
|
|
||||||
|
arg_hashtable_itr_t* arg_hashtable_itr_create(arg_hashtable_t* h); |
||||||
|
|
||||||
|
void arg_hashtable_itr_destroy(arg_hashtable_itr_t* itr); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the value of the (key,value) pair at the current position. |
||||||
|
*/ |
||||||
|
extern void* arg_hashtable_itr_key(arg_hashtable_itr_t* i); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the value of the (key,value) pair at the current position. |
||||||
|
*/ |
||||||
|
extern void* arg_hashtable_itr_value(arg_hashtable_itr_t* i); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Advance the iterator to the next element. Returns zero if advanced to end of table. |
||||||
|
*/ |
||||||
|
int arg_hashtable_itr_advance(arg_hashtable_itr_t* itr); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove current element and advance the iterator to the next element. |
||||||
|
*/ |
||||||
|
int arg_hashtable_itr_remove(arg_hashtable_itr_t* itr); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Search and overwrite the supplied iterator, to point to the entry matching the supplied key. |
||||||
|
* |
||||||
|
* @return Zero if not found. |
||||||
|
*/ |
||||||
|
int arg_hashtable_itr_search(arg_hashtable_itr_t* itr, arg_hashtable_t* h, void* k); |
||||||
|
|
||||||
|
#define ARG_DEFINE_HASHTABLE_ITERATOR_SEARCH(fnname, keytype) \ |
||||||
|
int fnname(arg_hashtable_itr_t* i, arg_hashtable_t* h, keytype* k) { return (arg_hashtable_iterator_search(i, h, k)); } |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,302 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include <string.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <sys/param.h> |
||||||
|
#include "esp_heap_caps.h" |
||||||
|
#include "esp_log.h" |
||||||
|
#include "esp_console.h" |
||||||
|
#include "esp_system.h" |
||||||
|
#include "linenoise/linenoise.h" |
||||||
|
#include "argtable3/argtable3.h" |
||||||
|
#include "sys/queue.h" |
||||||
|
|
||||||
|
#define ANSI_COLOR_DEFAULT 39 /** Default foreground color */ |
||||||
|
|
||||||
|
typedef struct cmd_item_ { |
||||||
|
/**
|
||||||
|
* Command name (statically allocated by application) |
||||||
|
*/ |
||||||
|
const char *command; |
||||||
|
/**
|
||||||
|
* Help text (statically allocated by application), may be NULL. |
||||||
|
*/ |
||||||
|
const char *help; |
||||||
|
/**
|
||||||
|
* Hint text, usually lists possible arguments, dynamically allocated. |
||||||
|
* May be NULL. |
||||||
|
*/ |
||||||
|
char *hint; |
||||||
|
esp_console_cmd_func_t func; //!< pointer to the command handler
|
||||||
|
void *argtable; //!< optional pointer to arg table
|
||||||
|
SLIST_ENTRY(cmd_item_) next; //!< next command in the list
|
||||||
|
} cmd_item_t; |
||||||
|
|
||||||
|
/** linked list of command structures */ |
||||||
|
static SLIST_HEAD(cmd_list_, cmd_item_) s_cmd_list; |
||||||
|
|
||||||
|
/** run-time configuration options */ |
||||||
|
static esp_console_config_t s_config = { |
||||||
|
.heap_alloc_caps = MALLOC_CAP_DEFAULT |
||||||
|
}; |
||||||
|
|
||||||
|
/** temporary buffer used for command line parsing */ |
||||||
|
static char *s_tmp_line_buf; |
||||||
|
|
||||||
|
static const cmd_item_t *find_command_by_name(const char *name); |
||||||
|
|
||||||
|
esp_err_t esp_console_init(const esp_console_config_t *config) |
||||||
|
{ |
||||||
|
if (!config) { |
||||||
|
return ESP_ERR_INVALID_ARG; |
||||||
|
} |
||||||
|
if (s_tmp_line_buf) { |
||||||
|
return ESP_ERR_INVALID_STATE; |
||||||
|
} |
||||||
|
memcpy(&s_config, config, sizeof(s_config)); |
||||||
|
if (s_config.hint_color == 0) { |
||||||
|
s_config.hint_color = ANSI_COLOR_DEFAULT; |
||||||
|
} |
||||||
|
if (s_config.heap_alloc_caps == 0) { |
||||||
|
s_config.heap_alloc_caps = MALLOC_CAP_DEFAULT; |
||||||
|
} |
||||||
|
s_tmp_line_buf = heap_caps_calloc(1, config->max_cmdline_length, s_config.heap_alloc_caps); |
||||||
|
if (s_tmp_line_buf == NULL) { |
||||||
|
return ESP_ERR_NO_MEM; |
||||||
|
} |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
esp_err_t esp_console_deinit(void) |
||||||
|
{ |
||||||
|
if (!s_tmp_line_buf) { |
||||||
|
return ESP_ERR_INVALID_STATE; |
||||||
|
} |
||||||
|
free(s_tmp_line_buf); |
||||||
|
s_tmp_line_buf = NULL; |
||||||
|
cmd_item_t *it, *tmp; |
||||||
|
SLIST_FOREACH_SAFE(it, &s_cmd_list, next, tmp) { |
||||||
|
SLIST_REMOVE(&s_cmd_list, it, cmd_item_, next); |
||||||
|
free(it->hint); |
||||||
|
free(it); |
||||||
|
} |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd) |
||||||
|
{ |
||||||
|
cmd_item_t *item = NULL; |
||||||
|
if (!cmd || cmd->command == NULL) { |
||||||
|
return ESP_ERR_INVALID_ARG; |
||||||
|
} |
||||||
|
if (strchr(cmd->command, ' ') != NULL) { |
||||||
|
return ESP_ERR_INVALID_ARG; |
||||||
|
} |
||||||
|
item = (cmd_item_t *)find_command_by_name(cmd->command); |
||||||
|
if (!item) { |
||||||
|
// not registered before
|
||||||
|
item = heap_caps_calloc(1, sizeof(*item), s_config.heap_alloc_caps); |
||||||
|
if (item == NULL) { |
||||||
|
return ESP_ERR_NO_MEM; |
||||||
|
} |
||||||
|
} else { |
||||||
|
// remove from list and free the old hint, because we will alloc new hint for the command
|
||||||
|
SLIST_REMOVE(&s_cmd_list, item, cmd_item_, next); |
||||||
|
free(item->hint); |
||||||
|
} |
||||||
|
item->command = cmd->command; |
||||||
|
item->help = cmd->help; |
||||||
|
if (cmd->hint) { |
||||||
|
/* Prepend a space before the hint. It separates command name and
|
||||||
|
* the hint. arg_print_syntax below adds this space as well. |
||||||
|
*/ |
||||||
|
int unused __attribute__((unused)); |
||||||
|
unused = asprintf(&item->hint, " %s", cmd->hint); |
||||||
|
} else if (cmd->argtable) { |
||||||
|
/* Generate hint based on cmd->argtable */ |
||||||
|
char *buf = NULL; |
||||||
|
size_t buf_size = 0; |
||||||
|
FILE *f = open_memstream(&buf, &buf_size); |
||||||
|
if (f != NULL) { |
||||||
|
arg_print_syntax(f, cmd->argtable, NULL); |
||||||
|
fclose(f); |
||||||
|
} |
||||||
|
item->hint = buf; |
||||||
|
} |
||||||
|
item->argtable = cmd->argtable; |
||||||
|
item->func = cmd->func; |
||||||
|
cmd_item_t *last = SLIST_FIRST(&s_cmd_list); |
||||||
|
if (last == NULL) { |
||||||
|
SLIST_INSERT_HEAD(&s_cmd_list, item, next); |
||||||
|
} else { |
||||||
|
cmd_item_t *it; |
||||||
|
while ((it = SLIST_NEXT(last, next)) != NULL) { |
||||||
|
last = it; |
||||||
|
} |
||||||
|
SLIST_INSERT_AFTER(last, item, next); |
||||||
|
} |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
void esp_console_get_completion(const char *buf, linenoiseCompletions *lc) |
||||||
|
{ |
||||||
|
size_t len = strlen(buf); |
||||||
|
if (len == 0) { |
||||||
|
return; |
||||||
|
} |
||||||
|
cmd_item_t *it; |
||||||
|
SLIST_FOREACH(it, &s_cmd_list, next) { |
||||||
|
/* Check if command starts with buf */ |
||||||
|
if (strncmp(buf, it->command, len) == 0) { |
||||||
|
linenoiseAddCompletion(lc, it->command); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const char *esp_console_get_hint(const char *buf, int *color, int *bold) |
||||||
|
{ |
||||||
|
size_t len = strlen(buf); |
||||||
|
cmd_item_t *it; |
||||||
|
SLIST_FOREACH(it, &s_cmd_list, next) { |
||||||
|
if (strlen(it->command) == len && |
||||||
|
strncmp(buf, it->command, len) == 0) { |
||||||
|
*color = s_config.hint_color; |
||||||
|
*bold = s_config.hint_bold; |
||||||
|
return it->hint; |
||||||
|
} |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
static const cmd_item_t *find_command_by_name(const char *name) |
||||||
|
{ |
||||||
|
const cmd_item_t *cmd = NULL; |
||||||
|
cmd_item_t *it; |
||||||
|
size_t len = strlen(name); |
||||||
|
SLIST_FOREACH(it, &s_cmd_list, next) { |
||||||
|
if (strlen(it->command) == len && |
||||||
|
strcmp(name, it->command) == 0) { |
||||||
|
cmd = it; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return cmd; |
||||||
|
} |
||||||
|
|
||||||
|
esp_err_t esp_console_run(const char *cmdline, int *cmd_ret) |
||||||
|
{ |
||||||
|
if (s_tmp_line_buf == NULL) { |
||||||
|
return ESP_ERR_INVALID_STATE; |
||||||
|
} |
||||||
|
char **argv = (char **) heap_caps_calloc(s_config.max_cmdline_args, sizeof(char *), s_config.heap_alloc_caps); |
||||||
|
if (argv == NULL) { |
||||||
|
return ESP_ERR_NO_MEM; |
||||||
|
} |
||||||
|
strlcpy(s_tmp_line_buf, cmdline, s_config.max_cmdline_length); |
||||||
|
|
||||||
|
size_t argc = esp_console_split_argv(s_tmp_line_buf, argv, |
||||||
|
s_config.max_cmdline_args); |
||||||
|
if (argc == 0) { |
||||||
|
free(argv); |
||||||
|
return ESP_ERR_INVALID_ARG; |
||||||
|
} |
||||||
|
const cmd_item_t *cmd = find_command_by_name(argv[0]); |
||||||
|
if (cmd == NULL) { |
||||||
|
free(argv); |
||||||
|
return ESP_ERR_NOT_FOUND; |
||||||
|
} |
||||||
|
*cmd_ret = (*cmd->func)(argc, argv); |
||||||
|
free(argv); |
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
static struct { |
||||||
|
struct arg_str *help_cmd; |
||||||
|
struct arg_end *end; |
||||||
|
} help_args; |
||||||
|
|
||||||
|
static void print_arg_help(cmd_item_t *it) |
||||||
|
{ |
||||||
|
/* First line: command name and hint
|
||||||
|
* Pad all the hints to the same column |
||||||
|
*/ |
||||||
|
const char *hint = (it->hint) ? it->hint : ""; |
||||||
|
printf("%-s %s\n", it->command, hint); |
||||||
|
/* Second line: print help.
|
||||||
|
* Argtable has a nice helper function for this which does line |
||||||
|
* wrapping. |
||||||
|
*/ |
||||||
|
printf(" "); // arg_print_formatted does not indent the first line
|
||||||
|
arg_print_formatted(stdout, 2, 78, it->help); |
||||||
|
/* Finally, print the list of arguments */ |
||||||
|
if (it->argtable) { |
||||||
|
arg_print_glossary(stdout, (void **) it->argtable, " %12s %s\n"); |
||||||
|
} |
||||||
|
printf("\n"); |
||||||
|
} |
||||||
|
|
||||||
|
static int help_command(int argc, char **argv) |
||||||
|
{ |
||||||
|
int nerrors = arg_parse(argc, argv, (void **) &help_args); |
||||||
|
|
||||||
|
if (nerrors != 0) { |
||||||
|
arg_print_errors(stderr, help_args.end, argv[0]); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
cmd_item_t *it; |
||||||
|
int ret_value = 1; |
||||||
|
|
||||||
|
if (help_args.help_cmd->count == 0) { |
||||||
|
/* Print summary of each command */ |
||||||
|
SLIST_FOREACH(it, &s_cmd_list, next) { |
||||||
|
if (it->help == NULL) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
print_arg_help(it); |
||||||
|
} |
||||||
|
ret_value = 0; |
||||||
|
} else { |
||||||
|
/* Print summary of given command */ |
||||||
|
bool found_command = false; |
||||||
|
SLIST_FOREACH(it, &s_cmd_list, next) { |
||||||
|
if (it->help == NULL) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (strcmp(help_args.help_cmd->sval[0], it->command) == 0) { |
||||||
|
print_arg_help(it); |
||||||
|
found_command = true; |
||||||
|
ret_value = 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* If given command has not been found, print error message*/ |
||||||
|
if (!found_command) { |
||||||
|
printf("help: Unrecognized option '%s'. Please use correct command as argument " |
||||||
|
"or type 'help' only to print help for all commands\n", help_args.help_cmd->sval[0]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ret_value; |
||||||
|
} |
||||||
|
|
||||||
|
esp_err_t esp_console_register_help_command(void) |
||||||
|
{ |
||||||
|
help_args.help_cmd = arg_str0(NULL, NULL, "<string>", "Name of command"); |
||||||
|
help_args.end = arg_end(1); |
||||||
|
|
||||||
|
esp_console_cmd_t command = { |
||||||
|
.command = "help", |
||||||
|
.help = "Print the summary of all registered commands if no arguments " |
||||||
|
"are given, otherwise print summary of given command.", |
||||||
|
.func = &help_command, |
||||||
|
.argtable = &help_args |
||||||
|
}; |
||||||
|
return esp_console_cmd_register(&command); |
||||||
|
} |
@ -0,0 +1,397 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
#pragma once |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include "sdkconfig.h" |
||||||
|
#include "esp_heap_caps.h" |
||||||
|
#include "esp_err.h" |
||||||
|
|
||||||
|
// Forward declaration. Definition in linenoise/linenoise.h.
|
||||||
|
typedef struct linenoiseCompletions linenoiseCompletions; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parameters for console initialization |
||||||
|
*/ |
||||||
|
typedef struct { |
||||||
|
size_t max_cmdline_length; //!< length of command line buffer, in bytes
|
||||||
|
size_t max_cmdline_args; //!< maximum number of command line arguments to parse
|
||||||
|
uint32_t heap_alloc_caps; //!< where to (e.g. MALLOC_CAP_SPIRAM) allocate heap objects such as cmds used by esp_console
|
||||||
|
int hint_color; //!< ASCII color code of hint text
|
||||||
|
int hint_bold; //!< Set to 1 to print hint text in bold
|
||||||
|
} esp_console_config_t; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default console configuration value |
||||||
|
* |
||||||
|
*/ |
||||||
|
#define ESP_CONSOLE_CONFIG_DEFAULT() \ |
||||||
|
{ \
|
||||||
|
.max_cmdline_length = 256, \
|
||||||
|
.max_cmdline_args = 32, \
|
||||||
|
.heap_alloc_caps = MALLOC_CAP_DEFAULT, \
|
||||||
|
.hint_color = 39, \
|
||||||
|
.hint_bold = 0 \
|
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parameters for console REPL (Read Eval Print Loop) |
||||||
|
* |
||||||
|
*/ |
||||||
|
typedef struct { |
||||||
|
uint32_t max_history_len; //!< maximum length for the history
|
||||||
|
const char *history_save_path; //!< file path used to save history commands, set to NULL won't save to file system
|
||||||
|
uint32_t task_stack_size; //!< repl task stack size
|
||||||
|
uint32_t task_priority; //!< repl task priority
|
||||||
|
const char *prompt; //!< prompt (NULL represents default: "esp> ")
|
||||||
|
size_t max_cmdline_length; //!< maximum length of a command line. If 0, default value will be used
|
||||||
|
void (*prerun_cb)(void); |
||||||
|
} esp_console_repl_config_t; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default console repl configuration value |
||||||
|
* |
||||||
|
*/ |
||||||
|
#define ESP_CONSOLE_REPL_CONFIG_DEFAULT() \ |
||||||
|
{ \
|
||||||
|
.max_history_len = 32, \
|
||||||
|
.history_save_path = NULL, \
|
||||||
|
.task_stack_size = 4096, \
|
||||||
|
.task_priority = 2, \
|
||||||
|
.prompt = NULL, \
|
||||||
|
.max_cmdline_length = 0, \
|
||||||
|
} |
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM |
||||||
|
/**
|
||||||
|
* @brief Parameters for console device: UART |
||||||
|
* |
||||||
|
*/ |
||||||
|
typedef struct { |
||||||
|
int channel; //!< UART channel number (count from zero)
|
||||||
|
int baud_rate; //!< Comunication baud rate
|
||||||
|
int tx_gpio_num; //!< GPIO number for TX path, -1 means using default one
|
||||||
|
int rx_gpio_num; //!< GPIO number for RX path, -1 means using default one
|
||||||
|
} esp_console_dev_uart_config_t; |
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_UART_CUSTOM |
||||||
|
#define ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT() \ |
||||||
|
{ \
|
||||||
|
.channel = CONFIG_ESP_CONSOLE_UART_NUM, \
|
||||||
|
.baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, \
|
||||||
|
.tx_gpio_num = CONFIG_ESP_CONSOLE_UART_TX_GPIO, \
|
||||||
|
.rx_gpio_num = CONFIG_ESP_CONSOLE_UART_RX_GPIO, \
|
||||||
|
} |
||||||
|
#else |
||||||
|
#define ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT() \ |
||||||
|
{ \
|
||||||
|
.channel = CONFIG_ESP_CONSOLE_UART_NUM, \
|
||||||
|
.baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, \
|
||||||
|
.tx_gpio_num = -1, \
|
||||||
|
.rx_gpio_num = -1, \
|
||||||
|
} |
||||||
|
#endif // CONFIG_ESP_CONSOLE_UART_CUSTOM
|
||||||
|
#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED) |
||||||
|
/**
|
||||||
|
* @brief Parameters for console device: USB CDC |
||||||
|
* |
||||||
|
* @note It's an empty structure for now, reserved for future |
||||||
|
* |
||||||
|
*/ |
||||||
|
typedef struct { |
||||||
|
|
||||||
|
} esp_console_dev_usb_cdc_config_t; |
||||||
|
|
||||||
|
#define ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT() {} |
||||||
|
#endif // CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED)
|
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED) |
||||||
|
/**
|
||||||
|
* @brief Parameters for console device: USB-SERIAL-JTAG |
||||||
|
* |
||||||
|
* @note It's an empty structure for now, reserved for future |
||||||
|
* |
||||||
|
*/ |
||||||
|
typedef struct { |
||||||
|
|
||||||
|
} esp_console_dev_usb_serial_jtag_config_t; |
||||||
|
|
||||||
|
#define ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT() {} |
||||||
|
#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief initialize console module |
||||||
|
* @param config console configuration |
||||||
|
* @note Call this once before using other console module features |
||||||
|
* @return |
||||||
|
* - ESP_OK on success |
||||||
|
* - ESP_ERR_NO_MEM if out of memory |
||||||
|
* - ESP_ERR_INVALID_STATE if already initialized |
||||||
|
* - ESP_ERR_INVALID_ARG if the configuration is invalid |
||||||
|
*/ |
||||||
|
esp_err_t esp_console_init(const esp_console_config_t *config); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief de-initialize console module |
||||||
|
* @note Call this once when done using console module functions |
||||||
|
* @return |
||||||
|
* - ESP_OK on success |
||||||
|
* - ESP_ERR_INVALID_STATE if not initialized yet |
||||||
|
*/ |
||||||
|
esp_err_t esp_console_deinit(void); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Console command main function |
||||||
|
* @param argc number of arguments |
||||||
|
* @param argv array with argc entries, each pointing to a zero-terminated string argument |
||||||
|
* @return console command return code, 0 indicates "success" |
||||||
|
*/ |
||||||
|
typedef int (*esp_console_cmd_func_t)(int argc, char **argv); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Console command description |
||||||
|
*/ |
||||||
|
typedef struct { |
||||||
|
/**
|
||||||
|
* Command name. Must not be NULL, must not contain spaces. |
||||||
|
* The pointer must be valid until the call to esp_console_deinit. |
||||||
|
*/ |
||||||
|
const char *command; |
||||||
|
/**
|
||||||
|
* Help text for the command, shown by help command. |
||||||
|
* If set, the pointer must be valid until the call to esp_console_deinit. |
||||||
|
* If not set, the command will not be listed in 'help' output. |
||||||
|
*/ |
||||||
|
const char *help; |
||||||
|
/**
|
||||||
|
* Hint text, usually lists possible arguments. |
||||||
|
* If set to NULL, and 'argtable' field is non-NULL, hint will be generated |
||||||
|
* automatically |
||||||
|
*/ |
||||||
|
const char *hint; |
||||||
|
/**
|
||||||
|
* Pointer to a function which implements the command. |
||||||
|
*/ |
||||||
|
esp_console_cmd_func_t func; |
||||||
|
/**
|
||||||
|
* Array or structure of pointers to arg_xxx structures, may be NULL. |
||||||
|
* Used to generate hint text if 'hint' is set to NULL. |
||||||
|
* Array/structure which this field points to must end with an arg_end. |
||||||
|
* Only used for the duration of esp_console_cmd_register call. |
||||||
|
*/ |
||||||
|
void *argtable; |
||||||
|
} esp_console_cmd_t; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register console command |
||||||
|
* @param cmd pointer to the command description; can point to a temporary value |
||||||
|
* @return |
||||||
|
* - ESP_OK on success |
||||||
|
* - ESP_ERR_NO_MEM if out of memory |
||||||
|
* - ESP_ERR_INVALID_ARG if command description includes invalid arguments |
||||||
|
*/ |
||||||
|
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run command line |
||||||
|
* @param cmdline command line (command name followed by a number of arguments) |
||||||
|
* @param[out] cmd_ret return code from the command (set if command was run) |
||||||
|
* @return |
||||||
|
* - ESP_OK, if command was run |
||||||
|
* - ESP_ERR_INVALID_ARG, if the command line is empty, or only contained |
||||||
|
* whitespace |
||||||
|
* - ESP_ERR_NOT_FOUND, if command with given name wasn't registered |
||||||
|
* - ESP_ERR_INVALID_STATE, if esp_console_init wasn't called |
||||||
|
*/ |
||||||
|
esp_err_t esp_console_run(const char *cmdline, int *cmd_ret); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Split command line into arguments in place |
||||||
|
* @verbatim |
||||||
|
* - This function finds whitespace-separated arguments in the given input line. |
||||||
|
* |
||||||
|
* 'abc def 1 20 .3' -> [ 'abc', 'def', '1', '20', '.3' ] |
||||||
|
* |
||||||
|
* - Argument which include spaces may be surrounded with quotes. In this case |
||||||
|
* spaces are preserved and quotes are stripped. |
||||||
|
* |
||||||
|
* 'abc "123 456" def' -> [ 'abc', '123 456', 'def' ] |
||||||
|
* |
||||||
|
* - Escape sequences may be used to produce backslash, double quote, and space: |
||||||
|
* |
||||||
|
* 'a\ b\\c\"' -> [ 'a b\c"' ] |
||||||
|
* @endverbatim |
||||||
|
* @note Pointers to at most argv_size - 1 arguments are returned in argv array. |
||||||
|
* The pointer after the last one (i.e. argv[argc]) is set to NULL. |
||||||
|
* |
||||||
|
* @param line pointer to buffer to parse; it is modified in place |
||||||
|
* @param argv array where the pointers to arguments are written |
||||||
|
* @param argv_size number of elements in argv_array (max. number of arguments) |
||||||
|
* @return number of arguments found (argc) |
||||||
|
*/ |
||||||
|
size_t esp_console_split_argv(char *line, char **argv, size_t argv_size); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback which provides command completion for linenoise library |
||||||
|
* |
||||||
|
* When using linenoise for line editing, command completion support |
||||||
|
* can be enabled like this: |
||||||
|
* |
||||||
|
* linenoiseSetCompletionCallback(&esp_console_get_completion); |
||||||
|
* |
||||||
|
* @param buf the string typed by the user |
||||||
|
* @param lc linenoiseCompletions to be filled in |
||||||
|
*/ |
||||||
|
void esp_console_get_completion(const char *buf, linenoiseCompletions *lc); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback which provides command hints for linenoise library |
||||||
|
* |
||||||
|
* When using linenoise for line editing, hints support can be enabled as |
||||||
|
* follows: |
||||||
|
* |
||||||
|
* linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint); |
||||||
|
* |
||||||
|
* The extra cast is needed because linenoiseHintsCallback is defined as |
||||||
|
* returning a char* instead of const char*. |
||||||
|
* |
||||||
|
* @param buf line typed by the user |
||||||
|
* @param[out] color ANSI color code to be used when displaying the hint |
||||||
|
* @param[out] bold set to 1 if hint has to be displayed in bold |
||||||
|
* @return string containing the hint text. This string is persistent and should |
||||||
|
* not be freed (i.e. linenoiseSetFreeHintsCallback should not be used). |
||||||
|
*/ |
||||||
|
const char *esp_console_get_hint(const char *buf, int *color, int *bold); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register a 'help' command |
||||||
|
* |
||||||
|
* Default 'help' command prints the list of registered commands along with |
||||||
|
* hints and help strings if no additional argument is given. If an additional |
||||||
|
* argument is given, the help command will look for a command with the same |
||||||
|
* name and only print the hints and help strings of that command. |
||||||
|
* |
||||||
|
* @return |
||||||
|
* - ESP_OK on success |
||||||
|
* - ESP_ERR_INVALID_STATE, if esp_console_init wasn't called |
||||||
|
*/ |
||||||
|
esp_err_t esp_console_register_help_command(void); |
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* Console REPL |
||||||
|
******************************************************************************/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Type defined for console REPL |
||||||
|
* |
||||||
|
*/ |
||||||
|
typedef struct esp_console_repl_s esp_console_repl_t; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Console REPL base structure |
||||||
|
* |
||||||
|
*/ |
||||||
|
struct esp_console_repl_s { |
||||||
|
/**
|
||||||
|
* @brief Delete console REPL environment |
||||||
|
* @param[in] repl REPL handle returned from esp_console_new_repl_xxx |
||||||
|
* @return |
||||||
|
* - ESP_OK on success |
||||||
|
* - ESP_FAIL on errors |
||||||
|
*/ |
||||||
|
esp_err_t (*del)(esp_console_repl_t *repl); |
||||||
|
}; |
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM |
||||||
|
/**
|
||||||
|
* @brief Establish a console REPL environment over UART driver |
||||||
|
* |
||||||
|
* @param[in] dev_config UART device configuration |
||||||
|
* @param[in] repl_config REPL configuration |
||||||
|
* @param[out] ret_repl return REPL handle after initialization succeed, return NULL otherwise |
||||||
|
* |
||||||
|
* @note This is an all-in-one function to establish the environment needed for REPL, includes: |
||||||
|
* - Install the UART driver on the console UART (8n1, 115200, REF_TICK clock source) |
||||||
|
* - Configures the stdin/stdout to go through the UART driver |
||||||
|
* - Initializes linenoise |
||||||
|
* - Spawn new thread to run REPL in the background |
||||||
|
* |
||||||
|
* @attention This function is meant to be used in the examples to make the code more compact. |
||||||
|
* Applications which use console functionality should be based on |
||||||
|
* the underlying linenoise and esp_console functions. |
||||||
|
* |
||||||
|
* @return |
||||||
|
* - ESP_OK on success |
||||||
|
* - ESP_FAIL Parameter error |
||||||
|
*/ |
||||||
|
esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl); |
||||||
|
#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED) |
||||||
|
/**
|
||||||
|
* @brief Establish a console REPL environment over USB CDC |
||||||
|
* |
||||||
|
* @param[in] dev_config USB CDC configuration |
||||||
|
* @param[in] repl_config REPL configuration |
||||||
|
* @param[out] ret_repl return REPL handle after initialization succeed, return NULL otherwise |
||||||
|
* |
||||||
|
* @note This is a all-in-one function to establish the environment needed for REPL, includes: |
||||||
|
* - Initializes linenoise |
||||||
|
* - Spawn new thread to run REPL in the background |
||||||
|
* |
||||||
|
* @attention This function is meant to be used in the examples to make the code more compact. |
||||||
|
* Applications which use console functionality should be based on |
||||||
|
* the underlying linenoise and esp_console functions. |
||||||
|
* |
||||||
|
* @return |
||||||
|
* - ESP_OK on success |
||||||
|
* - ESP_FAIL Parameter error |
||||||
|
*/ |
||||||
|
esp_err_t esp_console_new_repl_usb_cdc(const esp_console_dev_usb_cdc_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl); |
||||||
|
#endif // CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED)
|
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED) |
||||||
|
/**
|
||||||
|
* @brief Establish a console REPL (Read-eval-print loop) environment over USB-SERIAL-JTAG |
||||||
|
* |
||||||
|
* @param[in] dev_config USB-SERIAL-JTAG configuration |
||||||
|
* @param[in] repl_config REPL configuration |
||||||
|
* @param[out] ret_repl return REPL handle after initialization succeed, return NULL otherwise |
||||||
|
* |
||||||
|
* @note This is an all-in-one function to establish the environment needed for REPL, includes: |
||||||
|
* - Initializes linenoise |
||||||
|
* - Spawn new thread to run REPL in the background |
||||||
|
* |
||||||
|
* @attention This function is meant to be used in the examples to make the code more compact. |
||||||
|
* Applications which use console functionality should be based on |
||||||
|
* the underlying linenoise and esp_console functions. |
||||||
|
* |
||||||
|
* @return |
||||||
|
* - ESP_OK on success |
||||||
|
* - ESP_FAIL Parameter error |
||||||
|
*/ |
||||||
|
esp_err_t esp_console_new_repl_usb_serial_jtag(const esp_console_dev_usb_serial_jtag_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl); |
||||||
|
#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start REPL environment |
||||||
|
* @param[in] repl REPL handle returned from esp_console_new_repl_xxx |
||||||
|
* @note Once the REPL gets started, it won't be stopped until the user calls repl->del(repl) to destroy the REPL environment. |
||||||
|
* @return |
||||||
|
* - ESP_OK on success |
||||||
|
* - ESP_ERR_INVALID_STATE, if repl has started already |
||||||
|
*/ |
||||||
|
esp_err_t esp_console_start_repl(esp_console_repl_t *repl); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,550 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <sys/cdefs.h> |
||||||
|
#include "sdkconfig.h" |
||||||
|
#include "esp_err.h" |
||||||
|
#include "esp_log.h" |
||||||
|
#include "esp_console.h" |
||||||
|
#include "esp_vfs_dev.h" |
||||||
|
#include "esp_vfs_cdcacm.h" |
||||||
|
#include "esp_vfs_usb_serial_jtag.h" |
||||||
|
#include "freertos/FreeRTOS.h" |
||||||
|
#include "freertos/task.h" |
||||||
|
#include "driver/uart.h" |
||||||
|
#include "driver/usb_serial_jtag.h" |
||||||
|
#include "linenoise/linenoise.h" |
||||||
|
|
||||||
|
static const char *TAG = "console.repl"; |
||||||
|
|
||||||
|
#define CONSOLE_PROMPT_MAX_LEN (32) |
||||||
|
#define CONSOLE_PATH_MAX_LEN (ESP_VFS_PATH_MAX) |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
CONSOLE_REPL_STATE_DEINIT, |
||||||
|
CONSOLE_REPL_STATE_INIT, |
||||||
|
CONSOLE_REPL_STATE_START, |
||||||
|
} repl_state_t; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
esp_console_repl_t repl_core; // base class
|
||||||
|
char prompt[CONSOLE_PROMPT_MAX_LEN]; // Prompt to be printed before each line
|
||||||
|
repl_state_t state; |
||||||
|
const char *history_save_path; |
||||||
|
TaskHandle_t task_hdl; // REPL task handle
|
||||||
|
size_t max_cmdline_length; // Maximum length of a command line. If 0, default value will be used.
|
||||||
|
void (*prerun_cb)(void); |
||||||
|
} esp_console_repl_com_t; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
esp_console_repl_com_t repl_com; // base class
|
||||||
|
int uart_channel; // uart channel number
|
||||||
|
} esp_console_repl_universal_t; |
||||||
|
|
||||||
|
static void esp_console_repl_task(void *args); |
||||||
|
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM |
||||||
|
static esp_err_t esp_console_repl_uart_delete(esp_console_repl_t *repl); |
||||||
|
#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
||||||
|
#if CONFIG_ESP_CONSOLE_USB_CDC |
||||||
|
static esp_err_t esp_console_repl_usb_cdc_delete(esp_console_repl_t *repl); |
||||||
|
#endif // CONFIG_ESP_CONSOLE_USB_CDC
|
||||||
|
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG |
||||||
|
static esp_err_t esp_console_repl_usb_serial_jtag_delete(esp_console_repl_t *repl); |
||||||
|
#endif //CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||||
|
static esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_com_t *repl_com); |
||||||
|
static esp_err_t esp_console_setup_prompt(const char *prompt, esp_console_repl_com_t *repl_com); |
||||||
|
static esp_err_t esp_console_setup_history(const char *history_path, uint32_t max_history_len, esp_console_repl_com_t *repl_com); |
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_USB_CDC |
||||||
|
esp_err_t esp_console_new_repl_usb_cdc(const esp_console_dev_usb_cdc_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) |
||||||
|
{ |
||||||
|
esp_err_t ret = ESP_OK; |
||||||
|
esp_console_repl_universal_t *cdc_repl = NULL; |
||||||
|
if (!repl_config | !dev_config | !ret_repl) { |
||||||
|
ret = ESP_ERR_INVALID_ARG; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
// allocate memory for console REPL context
|
||||||
|
cdc_repl = calloc(1, sizeof(esp_console_repl_universal_t)); |
||||||
|
if (!cdc_repl) { |
||||||
|
ret = ESP_ERR_NO_MEM; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ |
||||||
|
esp_vfs_dev_cdcacm_set_rx_line_endings(ESP_LINE_ENDINGS_CR); |
||||||
|
/* Move the caret to the beginning of the next line on '\n' */ |
||||||
|
esp_vfs_dev_cdcacm_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); |
||||||
|
|
||||||
|
/* Enable blocking mode on stdin and stdout */ |
||||||
|
fcntl(fileno(stdout), F_SETFL, 0); |
||||||
|
fcntl(fileno(stdin), F_SETFL, 0); |
||||||
|
|
||||||
|
// initialize console, common part
|
||||||
|
ret = esp_console_common_init(repl_config->max_cmdline_length, &cdc_repl->repl_com); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
// setup history
|
||||||
|
ret = esp_console_setup_history(repl_config->history_save_path, repl_config->max_history_len, &cdc_repl->repl_com); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
// setup prompt
|
||||||
|
esp_console_setup_prompt(repl_config->prompt, &cdc_repl->repl_com); |
||||||
|
|
||||||
|
/* Fill the structure here as it will be used directly by the created task. */ |
||||||
|
cdc_repl->uart_channel = CONFIG_ESP_CONSOLE_UART_NUM; |
||||||
|
cdc_repl->repl_com.state = CONSOLE_REPL_STATE_INIT; |
||||||
|
cdc_repl->repl_com.repl_core.del = esp_console_repl_usb_cdc_delete; |
||||||
|
|
||||||
|
/* spawn a single thread to run REPL */ |
||||||
|
if (xTaskCreate(esp_console_repl_task, "console_repl", repl_config->task_stack_size, |
||||||
|
cdc_repl, repl_config->task_priority, &cdc_repl->repl_com.task_hdl) != pdTRUE) { |
||||||
|
ret = ESP_FAIL; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
*ret_repl = &cdc_repl->repl_com.repl_core; |
||||||
|
return ESP_OK; |
||||||
|
_exit: |
||||||
|
if (cdc_repl) { |
||||||
|
esp_console_deinit(); |
||||||
|
free(cdc_repl); |
||||||
|
} |
||||||
|
if (ret_repl) { |
||||||
|
*ret_repl = NULL; |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
#endif // CONFIG_ESP_CONSOLE_USB_CDC
|
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG |
||||||
|
esp_err_t esp_console_new_repl_usb_serial_jtag(const esp_console_dev_usb_serial_jtag_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) |
||||||
|
{ |
||||||
|
esp_console_repl_universal_t *usb_serial_jtag_repl = NULL; |
||||||
|
if (!repl_config | !dev_config | !ret_repl) { |
||||||
|
return ESP_ERR_INVALID_ARG; |
||||||
|
} |
||||||
|
|
||||||
|
esp_err_t ret = ESP_OK; |
||||||
|
// allocate memory for console REPL context
|
||||||
|
usb_serial_jtag_repl = calloc(1, sizeof(esp_console_repl_universal_t)); |
||||||
|
if (!usb_serial_jtag_repl) { |
||||||
|
ret = ESP_ERR_NO_MEM; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ |
||||||
|
esp_vfs_dev_usb_serial_jtag_set_rx_line_endings(ESP_LINE_ENDINGS_CR); |
||||||
|
/* Move the caret to the beginning of the next line on '\n' */ |
||||||
|
esp_vfs_dev_usb_serial_jtag_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); |
||||||
|
|
||||||
|
/* Enable blocking mode on stdin and stdout */ |
||||||
|
fcntl(fileno(stdout), F_SETFL, 0); |
||||||
|
fcntl(fileno(stdin), F_SETFL, 0); |
||||||
|
|
||||||
|
usb_serial_jtag_driver_config_t usb_serial_jtag_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(); |
||||||
|
|
||||||
|
/* Install USB-SERIAL-JTAG driver for interrupt-driven reads and writes */ |
||||||
|
ret = usb_serial_jtag_driver_install(&usb_serial_jtag_config); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
// initialize console, common part
|
||||||
|
ret = esp_console_common_init(repl_config->max_cmdline_length, &usb_serial_jtag_repl->repl_com); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
/* Tell vfs to use usb-serial-jtag driver */ |
||||||
|
esp_vfs_usb_serial_jtag_use_driver(); |
||||||
|
|
||||||
|
// setup history
|
||||||
|
ret = esp_console_setup_history(repl_config->history_save_path, repl_config->max_history_len, &usb_serial_jtag_repl->repl_com); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
// setup prompt
|
||||||
|
esp_console_setup_prompt(repl_config->prompt, &usb_serial_jtag_repl->repl_com); |
||||||
|
|
||||||
|
/* Fill the structure here as it will be used directly by the created task. */ |
||||||
|
usb_serial_jtag_repl->uart_channel = CONFIG_ESP_CONSOLE_UART_NUM; |
||||||
|
usb_serial_jtag_repl->repl_com.state = CONSOLE_REPL_STATE_INIT; |
||||||
|
usb_serial_jtag_repl->repl_com.repl_core.del = esp_console_repl_usb_serial_jtag_delete; |
||||||
|
|
||||||
|
/* spawn a single thread to run REPL */ |
||||||
|
if (xTaskCreate(esp_console_repl_task, "console_repl", repl_config->task_stack_size, |
||||||
|
usb_serial_jtag_repl, repl_config->task_priority, &usb_serial_jtag_repl->repl_com.task_hdl) != pdTRUE) { |
||||||
|
ret = ESP_FAIL; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
*ret_repl = &usb_serial_jtag_repl->repl_com.repl_core; |
||||||
|
return ESP_OK; |
||||||
|
_exit: |
||||||
|
if (usb_serial_jtag_repl) { |
||||||
|
esp_console_deinit(); |
||||||
|
free(usb_serial_jtag_repl); |
||||||
|
} |
||||||
|
if (ret_repl) { |
||||||
|
*ret_repl = NULL; |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM |
||||||
|
esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) |
||||||
|
{ |
||||||
|
esp_err_t ret = ESP_OK; |
||||||
|
esp_console_repl_universal_t *uart_repl = NULL; |
||||||
|
if (!repl_config | !dev_config | !ret_repl) { |
||||||
|
ret = ESP_ERR_INVALID_ARG; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
// allocate memory for console REPL context
|
||||||
|
uart_repl = calloc(1, sizeof(esp_console_repl_universal_t)); |
||||||
|
if (!uart_repl) { |
||||||
|
ret = ESP_ERR_NO_MEM; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
/* Drain stdout before reconfiguring it */ |
||||||
|
fflush(stdout); |
||||||
|
fsync(fileno(stdout)); |
||||||
|
|
||||||
|
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ |
||||||
|
esp_vfs_dev_uart_port_set_rx_line_endings(dev_config->channel, ESP_LINE_ENDINGS_CR); |
||||||
|
/* Move the caret to the beginning of the next line on '\n' */ |
||||||
|
esp_vfs_dev_uart_port_set_tx_line_endings(dev_config->channel, ESP_LINE_ENDINGS_CRLF); |
||||||
|
|
||||||
|
/* Configure UART. Note that REF_TICK/XTAL is used so that the baud rate remains
|
||||||
|
* correct while APB frequency is changing in light sleep mode. |
||||||
|
*/ |
||||||
|
#if SOC_UART_SUPPORT_REF_TICK |
||||||
|
uart_sclk_t clk_source = UART_SCLK_REF_TICK; |
||||||
|
// REF_TICK clock can't provide a high baudrate
|
||||||
|
if (dev_config->baud_rate > 1 * 1000 * 1000) { |
||||||
|
clk_source = UART_SCLK_DEFAULT; |
||||||
|
ESP_LOGW(TAG, "light sleep UART wakeup might not work at the configured baud rate"); |
||||||
|
} |
||||||
|
#elif SOC_UART_SUPPORT_XTAL_CLK |
||||||
|
uart_sclk_t clk_source = UART_SCLK_XTAL; |
||||||
|
#else |
||||||
|
#error "No UART clock source is aware of DFS" |
||||||
|
#endif // SOC_UART_SUPPORT_xxx
|
||||||
|
const uart_config_t uart_config = { |
||||||
|
.baud_rate = dev_config->baud_rate, |
||||||
|
.data_bits = UART_DATA_8_BITS, |
||||||
|
.parity = UART_PARITY_DISABLE, |
||||||
|
.stop_bits = UART_STOP_BITS_1, |
||||||
|
.source_clk = clk_source, |
||||||
|
}; |
||||||
|
|
||||||
|
uart_param_config(dev_config->channel, &uart_config); |
||||||
|
uart_set_pin(dev_config->channel, dev_config->tx_gpio_num, dev_config->rx_gpio_num, -1, -1); |
||||||
|
|
||||||
|
/* Install UART driver for interrupt-driven reads and writes */ |
||||||
|
ret = uart_driver_install(dev_config->channel, 256, 0, 0, NULL, 0); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
/* Tell VFS to use UART driver */ |
||||||
|
esp_vfs_dev_uart_use_driver(dev_config->channel); |
||||||
|
|
||||||
|
// initialize console, common part
|
||||||
|
ret = esp_console_common_init(repl_config->max_cmdline_length, &uart_repl->repl_com); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
// setup history
|
||||||
|
ret = esp_console_setup_history(repl_config->history_save_path, repl_config->max_history_len, &uart_repl->repl_com); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
// setup prompt
|
||||||
|
esp_console_setup_prompt(repl_config->prompt, &uart_repl->repl_com); |
||||||
|
|
||||||
|
uart_repl->repl_com.prerun_cb = repl_config->prerun_cb; |
||||||
|
|
||||||
|
/* Fill the structure here as it will be used directly by the created task. */ |
||||||
|
uart_repl->uart_channel = dev_config->channel; |
||||||
|
uart_repl->repl_com.state = CONSOLE_REPL_STATE_INIT; |
||||||
|
uart_repl->repl_com.repl_core.del = esp_console_repl_uart_delete; |
||||||
|
|
||||||
|
/* Spawn a single thread to run REPL, we need to pass `uart_repl` to it as
|
||||||
|
* it also requires the uart channel. */ |
||||||
|
if (xTaskCreate(esp_console_repl_task, "console_repl", repl_config->task_stack_size, |
||||||
|
uart_repl, repl_config->task_priority, &uart_repl->repl_com.task_hdl) != pdTRUE) { |
||||||
|
ret = ESP_FAIL; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
*ret_repl = &uart_repl->repl_com.repl_core; |
||||||
|
return ESP_OK; |
||||||
|
_exit: |
||||||
|
if (uart_repl) { |
||||||
|
esp_console_deinit(); |
||||||
|
uart_driver_delete(dev_config->channel); |
||||||
|
free(uart_repl); |
||||||
|
} |
||||||
|
if (ret_repl) { |
||||||
|
*ret_repl = NULL; |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
||||||
|
|
||||||
|
esp_err_t esp_console_start_repl(esp_console_repl_t *repl) |
||||||
|
{ |
||||||
|
esp_err_t ret = ESP_OK; |
||||||
|
esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core); |
||||||
|
// check if already initialized
|
||||||
|
if (repl_com->state != CONSOLE_REPL_STATE_INIT) { |
||||||
|
ret = ESP_ERR_INVALID_STATE; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
repl_com->state = CONSOLE_REPL_STATE_START; |
||||||
|
xTaskNotifyGive(repl_com->task_hdl); |
||||||
|
return ESP_OK; |
||||||
|
_exit: |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
static esp_err_t esp_console_setup_prompt(const char *prompt, esp_console_repl_com_t *repl_com) |
||||||
|
{ |
||||||
|
/* set command line prompt */ |
||||||
|
const char *prompt_temp = "esp>"; |
||||||
|
if (prompt) { |
||||||
|
prompt_temp = prompt; |
||||||
|
} |
||||||
|
snprintf(repl_com->prompt, CONSOLE_PROMPT_MAX_LEN - 1, LOG_COLOR_I "%s " LOG_RESET_COLOR, prompt_temp); |
||||||
|
|
||||||
|
return ESP_OK; |
||||||
|
} |
||||||
|
|
||||||
|
static esp_err_t esp_console_setup_history(const char *history_path, uint32_t max_history_len, esp_console_repl_com_t *repl_com) |
||||||
|
{ |
||||||
|
esp_err_t ret = ESP_OK; |
||||||
|
|
||||||
|
repl_com->history_save_path = history_path; |
||||||
|
if (history_path) { |
||||||
|
/* Load command history from filesystem */ |
||||||
|
linenoiseHistoryLoad(history_path); |
||||||
|
} |
||||||
|
|
||||||
|
/* Set command history size */ |
||||||
|
if (linenoiseHistorySetMaxLen(max_history_len) != 1) { |
||||||
|
ESP_LOGE(TAG, "set max history length to %"PRIu32" failed", max_history_len); |
||||||
|
ret = ESP_FAIL; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
return ESP_OK; |
||||||
|
_exit: |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
static esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_com_t *repl_com) |
||||||
|
{ |
||||||
|
esp_err_t ret = ESP_OK; |
||||||
|
/* Initialize the console */ |
||||||
|
esp_console_config_t console_config = ESP_CONSOLE_CONFIG_DEFAULT(); |
||||||
|
repl_com->max_cmdline_length = console_config.max_cmdline_length; |
||||||
|
/* Replace the default command line length if passed as a parameter */ |
||||||
|
if (max_cmdline_length != 0) { |
||||||
|
console_config.max_cmdline_length = max_cmdline_length; |
||||||
|
repl_com->max_cmdline_length = max_cmdline_length; |
||||||
|
} |
||||||
|
|
||||||
|
#if CONFIG_LOG_COLORS |
||||||
|
console_config.hint_color = atoi(LOG_COLOR_CYAN); |
||||||
|
#else |
||||||
|
console_config.hint_color = -1; |
||||||
|
#endif |
||||||
|
ret = esp_console_init(&console_config); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
ret = esp_console_register_help_command(); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
|
||||||
|
/* Configure linenoise line completion library */ |
||||||
|
/* Enable multiline editing. If not set, long commands will scroll within single line */ |
||||||
|
linenoiseSetMultiLine(1); |
||||||
|
|
||||||
|
/* Tell linenoise where to get command completions and hints */ |
||||||
|
linenoiseSetCompletionCallback(&esp_console_get_completion); |
||||||
|
linenoiseSetHintsCallback((linenoiseHintsCallback *)&esp_console_get_hint); |
||||||
|
|
||||||
|
return ESP_OK; |
||||||
|
_exit: |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM |
||||||
|
static esp_err_t esp_console_repl_uart_delete(esp_console_repl_t *repl) |
||||||
|
{ |
||||||
|
esp_err_t ret = ESP_OK; |
||||||
|
esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core); |
||||||
|
esp_console_repl_universal_t *uart_repl = __containerof(repl_com, esp_console_repl_universal_t, repl_com); |
||||||
|
// check if already de-initialized
|
||||||
|
if (repl_com->state == CONSOLE_REPL_STATE_DEINIT) { |
||||||
|
ESP_LOGE(TAG, "already de-initialized"); |
||||||
|
ret = ESP_ERR_INVALID_STATE; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
repl_com->state = CONSOLE_REPL_STATE_DEINIT; |
||||||
|
esp_console_deinit(); |
||||||
|
esp_vfs_dev_uart_use_nonblocking(uart_repl->uart_channel); |
||||||
|
uart_driver_delete(uart_repl->uart_channel); |
||||||
|
free(uart_repl); |
||||||
|
_exit: |
||||||
|
return ret; |
||||||
|
} |
||||||
|
#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_USB_CDC |
||||||
|
static esp_err_t esp_console_repl_usb_cdc_delete(esp_console_repl_t *repl) |
||||||
|
{ |
||||||
|
esp_err_t ret = ESP_OK; |
||||||
|
esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core); |
||||||
|
esp_console_repl_universal_t *cdc_repl = __containerof(repl_com, esp_console_repl_universal_t, repl_com); |
||||||
|
// check if already de-initialized
|
||||||
|
if (repl_com->state == CONSOLE_REPL_STATE_DEINIT) { |
||||||
|
ESP_LOGE(TAG, "already de-initialized"); |
||||||
|
ret = ESP_ERR_INVALID_STATE; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
repl_com->state = CONSOLE_REPL_STATE_DEINIT; |
||||||
|
esp_console_deinit(); |
||||||
|
free(cdc_repl); |
||||||
|
_exit: |
||||||
|
return ret; |
||||||
|
} |
||||||
|
#endif // CONFIG_ESP_CONSOLE_USB_CDC
|
||||||
|
|
||||||
|
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG |
||||||
|
static esp_err_t esp_console_repl_usb_serial_jtag_delete(esp_console_repl_t *repl) |
||||||
|
{ |
||||||
|
esp_err_t ret = ESP_OK; |
||||||
|
esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core); |
||||||
|
esp_console_repl_universal_t *usb_serial_jtag_repl = __containerof(repl_com, esp_console_repl_universal_t, repl_com); |
||||||
|
// check if already de-initialized
|
||||||
|
if (repl_com->state == CONSOLE_REPL_STATE_DEINIT) { |
||||||
|
ESP_LOGE(TAG, "already de-initialized"); |
||||||
|
ret = ESP_ERR_INVALID_STATE; |
||||||
|
goto _exit; |
||||||
|
} |
||||||
|
repl_com->state = CONSOLE_REPL_STATE_DEINIT; |
||||||
|
esp_console_deinit(); |
||||||
|
esp_vfs_usb_serial_jtag_use_nonblocking(); |
||||||
|
usb_serial_jtag_driver_uninstall(); |
||||||
|
free(usb_serial_jtag_repl); |
||||||
|
_exit: |
||||||
|
return ret; |
||||||
|
} |
||||||
|
#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||||
|
|
||||||
|
static void esp_console_repl_task(void *args) |
||||||
|
{ |
||||||
|
esp_console_repl_universal_t *repl_conf = (esp_console_repl_universal_t *) args; |
||||||
|
esp_console_repl_com_t *repl_com = &repl_conf->repl_com; |
||||||
|
const int uart_channel = repl_conf->uart_channel; |
||||||
|
|
||||||
|
/* Waiting for task notify. This happens when `esp_console_start_repl()`
|
||||||
|
* function is called. */ |
||||||
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); |
||||||
|
|
||||||
|
if (repl_conf->repl_com.prerun_cb) { |
||||||
|
repl_conf->repl_com.prerun_cb(); |
||||||
|
} |
||||||
|
|
||||||
|
/* Figure out if the terminal supports escape sequences */ |
||||||
|
int probe_status = linenoiseProbe(); |
||||||
|
if (probe_status) { |
||||||
|
/* zero indicates success */ |
||||||
|
linenoiseSetDumbMode(1); |
||||||
|
} |
||||||
|
|
||||||
|
/* Change standard input and output of the task if the requested UART is
|
||||||
|
* NOT the default one. This block will replace stdin, stdout and stderr. |
||||||
|
*/ |
||||||
|
if (uart_channel != CONFIG_ESP_CONSOLE_UART_NUM) { |
||||||
|
char path[CONSOLE_PATH_MAX_LEN] = { 0 }; |
||||||
|
snprintf(path, CONSOLE_PATH_MAX_LEN, "/dev/uart/%d", uart_channel); |
||||||
|
|
||||||
|
stdin = fopen(path, "r"); |
||||||
|
stdout = fopen(path, "w"); |
||||||
|
stderr = stdout; |
||||||
|
} |
||||||
|
|
||||||
|
/* Disable buffering on stdin of the current task.
|
||||||
|
* If the console is ran on a different UART than the default one, |
||||||
|
* buffering shall only be disabled for the current one. */ |
||||||
|
setvbuf(stdin, NULL, _IONBF, 0); |
||||||
|
|
||||||
|
/* This message shall be printed here and not earlier as the stdout
|
||||||
|
* has just been set above. */ |
||||||
|
printf("\r\n" |
||||||
|
"Type 'help' to get the list of commands.\r\n" |
||||||
|
"Use UP/DOWN arrows to navigate through command history.\r\n" |
||||||
|
"Press TAB when typing command name to auto-complete.\r\n"); |
||||||
|
|
||||||
|
if (linenoiseIsDumbMode()) { |
||||||
|
printf("\r\n" |
||||||
|
"Your terminal application does not support escape sequences.\n\n" |
||||||
|
"Line editing and history features are disabled.\n\n" |
||||||
|
"On Windows, try using Putty instead.\r\n"); |
||||||
|
} |
||||||
|
|
||||||
|
linenoiseSetMaxLineLen(repl_com->max_cmdline_length); |
||||||
|
while (repl_com->state == CONSOLE_REPL_STATE_START) { |
||||||
|
char *line = linenoise(repl_com->prompt); |
||||||
|
if (line == NULL) { |
||||||
|
ESP_LOGD(TAG, "empty line"); |
||||||
|
/* Ignore empty lines */ |
||||||
|
continue; |
||||||
|
} |
||||||
|
/* Add the command to the history */ |
||||||
|
linenoiseHistoryAdd(line); |
||||||
|
/* Save command history to filesystem */ |
||||||
|
if (repl_com->history_save_path) { |
||||||
|
linenoiseHistorySave(repl_com->history_save_path); |
||||||
|
} |
||||||
|
|
||||||
|
/* Try to run the command */ |
||||||
|
int ret; |
||||||
|
esp_err_t err = esp_console_run(line, &ret); |
||||||
|
if (err == ESP_ERR_NOT_FOUND) { |
||||||
|
printf("Unrecognized command\n"); |
||||||
|
} else if (err == ESP_ERR_INVALID_ARG) { |
||||||
|
// command was empty
|
||||||
|
} else if (err == ESP_OK && ret != ESP_OK) { |
||||||
|
printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret)); |
||||||
|
} else if (err != ESP_OK) { |
||||||
|
printf("Internal error: %s\n", esp_err_to_name(err)); |
||||||
|
} |
||||||
|
/* linenoise allocates line buffer on the heap, so need to free it */ |
||||||
|
linenoiseFree(line); |
||||||
|
} |
||||||
|
ESP_LOGD(TAG, "The End"); |
||||||
|
vTaskDelete(NULL); |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com> |
||||||
|
Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> |
||||||
|
|
||||||
|
All rights reserved. |
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without |
||||||
|
modification, are permitted provided that the following conditions are met: |
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, |
||||||
|
this list of conditions and the following disclaimer. |
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, |
||||||
|
this list of conditions and the following disclaimer in the documentation |
||||||
|
and/or other materials provided with the distribution. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,82 @@ |
|||||||
|
/* linenoise.h -- VERSION 1.0
|
||||||
|
* |
||||||
|
* Guerrilla line editing library against the idea that a line editing lib |
||||||
|
* needs to be 20,000 lines of C code. |
||||||
|
* |
||||||
|
* See linenoise.c for more information. |
||||||
|
* |
||||||
|
* ------------------------------------------------------------------------ |
||||||
|
* |
||||||
|
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com> |
||||||
|
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> |
||||||
|
* |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __LINENOISE_H |
||||||
|
#define __LINENOISE_H |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdbool.h> |
||||||
|
#include <stddef.h> |
||||||
|
|
||||||
|
typedef struct linenoiseCompletions { |
||||||
|
size_t len; |
||||||
|
char **cvec; |
||||||
|
} linenoiseCompletions; |
||||||
|
|
||||||
|
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); |
||||||
|
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold); |
||||||
|
typedef void(linenoiseFreeHintsCallback)(void *); |
||||||
|
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); |
||||||
|
void linenoiseSetHintsCallback(linenoiseHintsCallback *); |
||||||
|
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); |
||||||
|
void linenoiseAddCompletion(linenoiseCompletions *, const char *); |
||||||
|
|
||||||
|
int linenoiseProbe(void); |
||||||
|
char *linenoise(const char *prompt); |
||||||
|
void linenoiseFree(void *ptr); |
||||||
|
int linenoiseHistoryAdd(const char *line); |
||||||
|
int linenoiseHistorySetMaxLen(int len); |
||||||
|
int linenoiseHistorySave(const char *filename); |
||||||
|
int linenoiseHistoryLoad(const char *filename); |
||||||
|
void linenoiseHistoryFree(void); |
||||||
|
void linenoiseClearScreen(void); |
||||||
|
void linenoiseSetMultiLine(int ml); |
||||||
|
void linenoiseSetDumbMode(int set); |
||||||
|
bool linenoiseIsDumbMode(void); |
||||||
|
void linenoisePrintKeyCodes(void); |
||||||
|
void linenoiseAllowEmpty(bool); |
||||||
|
int linenoiseSetMaxLineLen(size_t len); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* __LINENOISE_H */ |
@ -0,0 +1,112 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include <ctype.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#define SS_FLAG_ESCAPE 0x8 |
||||||
|
|
||||||
|
typedef enum { |
||||||
|
/* parsing the space between arguments */ |
||||||
|
SS_SPACE = 0x0, |
||||||
|
/* parsing an argument which isn't quoted */ |
||||||
|
SS_ARG = 0x1, |
||||||
|
/* parsing a quoted argument */ |
||||||
|
SS_QUOTED_ARG = 0x2, |
||||||
|
/* parsing an escape sequence within unquoted argument */ |
||||||
|
SS_ARG_ESCAPED = SS_ARG | SS_FLAG_ESCAPE, |
||||||
|
/* parsing an escape sequence within a quoted argument */ |
||||||
|
SS_QUOTED_ARG_ESCAPED = SS_QUOTED_ARG | SS_FLAG_ESCAPE, |
||||||
|
} split_state_t; |
||||||
|
|
||||||
|
/* helper macro, called when done with an argument */ |
||||||
|
#define END_ARG() do { \ |
||||||
|
char_out = 0; \
|
||||||
|
argv[argc++] = next_arg_start; \
|
||||||
|
state = SS_SPACE; \
|
||||||
|
} while(0) |
||||||
|
|
||||||
|
size_t esp_console_split_argv(char *line, char **argv, size_t argv_size) |
||||||
|
{ |
||||||
|
const int QUOTE = '"'; |
||||||
|
const int ESCAPE = '\\'; |
||||||
|
const int SPACE = ' '; |
||||||
|
split_state_t state = SS_SPACE; |
||||||
|
size_t argc = 0; |
||||||
|
char *next_arg_start = line; |
||||||
|
char *out_ptr = line; |
||||||
|
for (char *in_ptr = line; argc < argv_size - 1; ++in_ptr) { |
||||||
|
int char_in = (unsigned char) *in_ptr; |
||||||
|
if (char_in == 0) { |
||||||
|
break; |
||||||
|
} |
||||||
|
int char_out = -1; |
||||||
|
|
||||||
|
switch (state) { |
||||||
|
case SS_SPACE: |
||||||
|
if (char_in == SPACE) { |
||||||
|
/* skip space */ |
||||||
|
} else if (char_in == QUOTE) { |
||||||
|
next_arg_start = out_ptr; |
||||||
|
state = SS_QUOTED_ARG; |
||||||
|
} else if (char_in == ESCAPE) { |
||||||
|
next_arg_start = out_ptr; |
||||||
|
state = SS_ARG_ESCAPED; |
||||||
|
} else { |
||||||
|
next_arg_start = out_ptr; |
||||||
|
state = SS_ARG; |
||||||
|
char_out = char_in; |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case SS_QUOTED_ARG: |
||||||
|
if (char_in == QUOTE) { |
||||||
|
END_ARG(); |
||||||
|
} else if (char_in == ESCAPE) { |
||||||
|
state = SS_QUOTED_ARG_ESCAPED; |
||||||
|
} else { |
||||||
|
char_out = char_in; |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
case SS_ARG_ESCAPED: |
||||||
|
case SS_QUOTED_ARG_ESCAPED: |
||||||
|
if (char_in == ESCAPE || char_in == QUOTE || char_in == SPACE) { |
||||||
|
char_out = char_in; |
||||||
|
} else { |
||||||
|
/* unrecognized escape character, skip */ |
||||||
|
} |
||||||
|
state = (split_state_t) (state & (~SS_FLAG_ESCAPE)); |
||||||
|
break; |
||||||
|
|
||||||
|
case SS_ARG: |
||||||
|
if (char_in == SPACE) { |
||||||
|
END_ARG(); |
||||||
|
} else if (char_in == ESCAPE) { |
||||||
|
state = SS_ARG_ESCAPED; |
||||||
|
} else { |
||||||
|
char_out = char_in; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
/* need to output anything? */ |
||||||
|
if (char_out >= 0) { |
||||||
|
*out_ptr = char_out; |
||||||
|
++out_ptr; |
||||||
|
} |
||||||
|
} |
||||||
|
/* make sure the final argument is terminated */ |
||||||
|
*out_ptr = 0; |
||||||
|
/* finalize the last argument */ |
||||||
|
if (state != SS_SPACE && argc < argv_size - 1) { |
||||||
|
argv[argc++] = next_arg_start; |
||||||
|
} |
||||||
|
/* add a NULL at the end of argv */ |
||||||
|
argv[argc] = NULL; |
||||||
|
|
||||||
|
return argc; |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
# The following lines of boilerplate have to be in your project's |
||||||
|
# CMakeLists in this exact order for cmake to work correctly |
||||||
|
cmake_minimum_required(VERSION 3.16) |
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake) |
||||||
|
|
||||||
|
list(PREPEND SDKCONFIG_DEFAULTS "$ENV{IDF_PATH}/tools/test_apps/configs/sdkconfig.debug_helpers" "sdkconfig.defaults") |
||||||
|
|
||||||
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on. |
||||||
|
set(COMPONENTS main) |
||||||
|
|
||||||
|
project(test_console) |
@ -0,0 +1,2 @@ |
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | |
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | |
@ -0,0 +1,4 @@ |
|||||||
|
idf_component_register(SRCS "test_app_main.c" "test_console.c" |
||||||
|
INCLUDE_DIRS "." |
||||||
|
PRIV_REQUIRES unity console |
||||||
|
WHOLE_ARCHIVE) |
@ -0,0 +1,51 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "unity.h" |
||||||
|
#include "unity_test_runner.h" |
||||||
|
#include "esp_heap_caps.h" |
||||||
|
#include <sys/time.h> |
||||||
|
|
||||||
|
// Some resources are lazy allocated (newlib locks) in the console code, the threshold is left for that case
|
||||||
|
#define TEST_MEMORY_LEAK_THRESHOLD (-150) |
||||||
|
|
||||||
|
static size_t before_free_8bit; |
||||||
|
static size_t before_free_32bit; |
||||||
|
|
||||||
|
static void check_leak(size_t before_free, size_t after_free, const char *type) |
||||||
|
{ |
||||||
|
ssize_t delta = after_free - before_free; |
||||||
|
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); |
||||||
|
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); |
||||||
|
} |
||||||
|
|
||||||
|
void setUp(void) |
||||||
|
{ |
||||||
|
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); |
||||||
|
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void tearDown(void) |
||||||
|
{ |
||||||
|
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); |
||||||
|
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); |
||||||
|
check_leak(before_free_8bit, after_free_8bit, "8BIT"); |
||||||
|
check_leak(before_free_32bit, after_free_32bit, "32BIT"); |
||||||
|
} |
||||||
|
|
||||||
|
void app_main(void) |
||||||
|
{ |
||||||
|
/* Preallocate some newlib locks to avoid it from
|
||||||
|
registering as memory leaks */ |
||||||
|
|
||||||
|
struct timeval tv = { 0 }; |
||||||
|
gettimeofday(&tv, NULL); |
||||||
|
|
||||||
|
printf("Running console component tests\n"); |
||||||
|
unity_run_menu(); |
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
#include <stdio.h> |
||||||
|
#include <string.h> |
||||||
|
#include "sdkconfig.h" |
||||||
|
#include "unity.h" |
||||||
|
#include "esp_console.h" |
||||||
|
#include "argtable3/argtable3.h" |
||||||
|
#include "linenoise/linenoise.h" |
||||||
|
#include "freertos/FreeRTOS.h" |
||||||
|
#include "freertos/task.h" |
||||||
|
|
||||||
|
static int do_hello_cmd(int argc, char **argv) |
||||||
|
{ |
||||||
|
printf("Hello World\n"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
TEST_CASE("esp console init/deinit test", "[console]") |
||||||
|
{ |
||||||
|
esp_console_config_t console_config = ESP_CONSOLE_CONFIG_DEFAULT(); |
||||||
|
TEST_ESP_OK(esp_console_init(&console_config)); |
||||||
|
const esp_console_cmd_t cmd = { |
||||||
|
.command = "hello", |
||||||
|
.help = "Print Hello World", |
||||||
|
.hint = NULL, |
||||||
|
.func = do_hello_cmd, |
||||||
|
}; |
||||||
|
TEST_ESP_OK(esp_console_cmd_register(&cmd)); |
||||||
|
// re-register the same command, just for test
|
||||||
|
TEST_ESP_OK(esp_console_cmd_register(&cmd)); |
||||||
|
TEST_ESP_OK(esp_console_deinit()); |
||||||
|
} |
||||||
|
|
||||||
|
static esp_console_repl_t *s_repl = NULL; |
||||||
|
|
||||||
|
/* handle 'quit' command */ |
||||||
|
static int do_cmd_quit(int argc, char **argv) |
||||||
|
{ |
||||||
|
printf("ByeBye\r\n"); |
||||||
|
s_repl->del(s_repl); |
||||||
|
|
||||||
|
linenoiseHistoryFree(); // Free up memory
|
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static esp_console_cmd_t s_quit_cmd = { |
||||||
|
.command = "quit", |
||||||
|
.help = "Quit REPL environment", |
||||||
|
.func = &do_cmd_quit |
||||||
|
}; |
||||||
|
|
||||||
|
// Enter "quit" to exit REPL environment
|
||||||
|
/* Marked as ignore since it cannot run as a normal unity test case
|
||||||
|
ran separately in test_console_repl */ |
||||||
|
TEST_CASE("esp console repl test", "[console][ignore]") |
||||||
|
{ |
||||||
|
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); |
||||||
|
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); |
||||||
|
TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); |
||||||
|
|
||||||
|
TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd)); |
||||||
|
|
||||||
|
TEST_ESP_OK(esp_console_start_repl(s_repl)); |
||||||
|
vTaskDelay(pdMS_TO_TICKS(2000)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_CASE("esp console help command", "[console][ignore]") |
||||||
|
{ |
||||||
|
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); |
||||||
|
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); |
||||||
|
TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); |
||||||
|
|
||||||
|
TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd)); |
||||||
|
TEST_ESP_OK(esp_console_register_help_command()); |
||||||
|
|
||||||
|
TEST_ESP_OK(esp_console_start_repl(s_repl)); |
||||||
|
vTaskDelay(pdMS_TO_TICKS(5000)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_CASE("esp console init/deinit test, minimal config", "[console]") |
||||||
|
{ |
||||||
|
/* Test with minimal init config */ |
||||||
|
esp_console_config_t console_config = { |
||||||
|
.max_cmdline_length = 100, |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_ESP_OK(esp_console_init(&console_config)); |
||||||
|
const esp_console_cmd_t cmd = { |
||||||
|
.command = "hello", |
||||||
|
.help = "Print Hello World", |
||||||
|
.hint = NULL, |
||||||
|
.func = do_hello_cmd, |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_ESP_OK(esp_console_cmd_register(&cmd)); |
||||||
|
// re-register the same command, just for test
|
||||||
|
TEST_ESP_OK(esp_console_cmd_register(&cmd)); |
||||||
|
TEST_ESP_OK(esp_console_deinit()); |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD |
||||||
|
# SPDX-License-Identifier: CC0-1.0 |
||||||
|
|
||||||
|
import pytest |
||||||
|
from pytest_embedded import Dut |
||||||
|
|
||||||
|
|
||||||
|
def do_test_quit(dut: Dut) -> None: |
||||||
|
dut.expect_exact('Press ENTER to see the list of tests') |
||||||
|
dut.write('"esp console repl test"') |
||||||
|
|
||||||
|
dut.expect_exact('esp>', timeout=5) |
||||||
|
dut.write('quit') |
||||||
|
|
||||||
|
dut.expect_exact('ByeBye', timeout=5) |
||||||
|
|
||||||
|
|
||||||
|
def do_test_help_generic(dut: Dut) -> None: |
||||||
|
dut.expect_exact('Press ENTER to see the list of tests') |
||||||
|
dut.write('"esp console help command"') |
||||||
|
|
||||||
|
dut.expect_exact('esp>', timeout=5) |
||||||
|
dut.write('help') |
||||||
|
|
||||||
|
dut.expect_exact('quit', timeout=5) |
||||||
|
dut.expect_exact('Quit REPL environment', timeout=5) |
||||||
|
|
||||||
|
dut.expect(r'help\s+\[<string>\]', timeout=5) |
||||||
|
|
||||||
|
# Note: repl seems to do the line breaks by itself, this needs to be adjusted if repl changes its line width |
||||||
|
dut.expect_exact('Print the summary of all registered commands if no arguments are given,', timeout=5) |
||||||
|
dut.expect_exact('otherwise print summary of given command.', timeout=5) |
||||||
|
dut.expect(r'<string>\s+Name of command\s+esp>', timeout=5) |
||||||
|
|
||||||
|
|
||||||
|
def do_test_help_quit(dut: Dut) -> None: |
||||||
|
dut.expect_exact('Press ENTER to see the list of tests') |
||||||
|
dut.write('"esp console help command"') |
||||||
|
|
||||||
|
dut.expect_exact('esp>', timeout=5) |
||||||
|
dut.write('help quit') |
||||||
|
|
||||||
|
dut.expect(r'quit\s+Quit REPL environment\s+esp>', timeout=5) |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic |
||||||
|
@pytest.mark.supported_targets |
||||||
|
def test_console(dut: Dut) -> None: |
||||||
|
dut.run_all_single_board_cases() |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic |
||||||
|
@pytest.mark.supported_targets |
||||||
|
def test_console_repl(dut: Dut) -> None: |
||||||
|
do_test_quit(dut) |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic |
||||||
|
@pytest.mark.supported_targets |
||||||
|
def test_console_help_generic(dut: Dut) -> None: |
||||||
|
do_test_help_generic(dut) |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic |
||||||
|
@pytest.mark.supported_targets |
||||||
|
def test_console_help_quit(dut: Dut) -> None: |
||||||
|
do_test_help_quit(dut) |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.host_test |
||||||
|
@pytest.mark.qemu |
||||||
|
@pytest.mark.esp32 |
||||||
|
@pytest.mark.esp32c3 |
||||||
|
def test_console_qemu(dut: Dut) -> None: |
||||||
|
dut.run_all_single_board_cases() |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.host_test |
||||||
|
@pytest.mark.qemu |
||||||
|
@pytest.mark.esp32 |
||||||
|
def test_console_repl_qemu(dut: Dut) -> None: |
||||||
|
do_test_quit(dut) |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.host_test |
||||||
|
@pytest.mark.qemu |
||||||
|
@pytest.mark.esp32 |
||||||
|
def test_console_help_generic_qemu(dut: Dut) -> None: |
||||||
|
do_test_help_generic(dut) |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.host_test |
||||||
|
@pytest.mark.qemu |
||||||
|
@pytest.mark.esp32 |
||||||
|
def test_console_help_quit_qemu(dut: Dut) -> None: |
||||||
|
do_test_help_quit(dut) |
@ -0,0 +1,3 @@ |
|||||||
|
# This "default" configuration is appended to all other configurations |
||||||
|
# The contents of "sdkconfig.debug_helpers" is also appended to all other configurations (see CMakeLists.txt) |
||||||
|
CONFIG_ESP_TASK_WDT_INIT=n |
Loading…
Reference in new issue