forked from test34/can_wizard
My linenoise + console component from esp-idf master + a few fixes
This commit is contained in:
parent
046062fcb7
commit
a583c32ba3
34 changed files with 6357 additions and 4709 deletions
31
components/console/CMakeLists.txt
Normal file
31
components/console/CMakeLists.txt
Normal file
|
@ -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)
|
|
@ -24,3 +24,144 @@ 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
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
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.
|
||||||
|
|
285
components/console/argtable3/arg_cmd.c
Normal file
285
components/console/argtable3/arg_cmd.c
Normal file
|
@ -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;
|
||||||
|
}
|
575
components/console/argtable3/arg_date.c
Normal file
575
components/console/argtable3/arg_date.c
Normal file
|
@ -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);
|
||||||
|
}
|
164
components/console/argtable3/arg_dbl.c
Normal file
164
components/console/argtable3/arg_dbl.c
Normal file
|
@ -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;
|
||||||
|
}
|
344
components/console/argtable3/arg_dstr.c
Normal file
344
components/console/argtable3/arg_dstr.c
Normal file
|
@ -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
|
135
components/console/argtable3/arg_end.c
Normal file
135
components/console/argtable3/arg_end.c
Normal file
|
@ -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);
|
||||||
|
}
|
213
components/console/argtable3/arg_file.c
Normal file
213
components/console/argtable3/arg_file.c
Normal file
|
@ -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;
|
||||||
|
}
|
428
components/console/argtable3/arg_hashtable.c
Normal file
428
components/console/argtable3/arg_hashtable.c
Normal file
|
@ -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;
|
||||||
|
}
|
294
components/console/argtable3/arg_int.c
Normal file
294
components/console/argtable3/arg_int.c
Normal file
|
@ -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;
|
||||||
|
}
|
124
components/console/argtable3/arg_lit.c
Normal file
124
components/console/argtable3/arg_lit.c
Normal file
|
@ -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;
|
||||||
|
}
|
64
components/console/argtable3/arg_rem.c
Normal file
64
components/console/argtable3/arg_rem.c
Normal file
|
@ -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;
|
||||||
|
}
|
1014
components/console/argtable3/arg_rex.c
Normal file
1014
components/console/argtable3/arg_rex.c
Normal file
File diff suppressed because it is too large
Load diff
151
components/console/argtable3/arg_str.c
Normal file
151
components/console/argtable3/arg_str.c
Normal file
|
@ -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;
|
||||||
|
}
|
183
components/console/argtable3/arg_utils.c
Normal file
183
components/console/argtable3/arg_utils.c
Normal file
|
@ -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
|
@ -1,4 +1,11 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
* This file is part of the argtable3 library.
|
||||||
*
|
*
|
||||||
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
|
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
|
||||||
|
@ -39,265 +46,232 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ARG_REX_ICASE 1
|
#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 */
|
/* bit masks for arg_hdr.flag */
|
||||||
enum
|
enum { ARG_TERMINATOR = 0x1, ARG_HASVALUE = 0x2, ARG_HASOPTVALUE = 0x4 };
|
||||||
{
|
|
||||||
ARG_TERMINATOR=0x1,
|
|
||||||
ARG_HASVALUE=0x2,
|
|
||||||
ARG_HASOPTVALUE=0x4
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void (arg_resetfn)(void *parent);
|
#if defined(_WIN32)
|
||||||
typedef int (arg_scanfn)(void *parent, const char *argval);
|
#if defined(argtable3_EXPORTS)
|
||||||
typedef int (arg_checkfn)(void *parent);
|
#define ARG_EXTERN __declspec(dllexport)
|
||||||
typedef void (arg_errorfn)(void *parent, FILE *fp, int error, const char *argval, const char *progname);
|
#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 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
|
* The argtable library requires each arg_xxx struct to have an arg_hdr
|
||||||
* struct as its first data member.
|
* struct as its first data member.
|
||||||
* The argtable library functions then use this data to identify the
|
* The argtable library functions then use this data to identify the
|
||||||
* properties of the command line option, such as its option tags,
|
* properties of the command line option, such as its option tags,
|
||||||
* datatype string, and glossary strings, and so on.
|
* datatype string, and glossary strings, and so on.
|
||||||
* Moreover, the arg_hdr struct contains pointers to custom functions that
|
* Moreover, the arg_hdr struct contains pointers to custom functions that
|
||||||
* are provided by each arg_xxx struct which perform the tasks of parsing
|
* are provided by each arg_xxx struct which perform the tasks of parsing
|
||||||
* that particular arg_xxx arguments, performing post-parse checks, and
|
* that particular arg_xxx arguments, performing post-parse checks, and
|
||||||
* reporting errors.
|
* reporting errors.
|
||||||
* These functions are private to the individual arg_xxx source code
|
* 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
|
* and are the pointer to them are initiliased by that arg_xxx struct's
|
||||||
* constructor function. The user could alter them after construction
|
* constructor function. The user could alter them after construction
|
||||||
* if desired, but the original intention is for them to be set by the
|
* if desired, but the original intention is for them to be set by the
|
||||||
* constructor and left unaltered.
|
* constructor and left unaltered.
|
||||||
*/
|
*/
|
||||||
struct arg_hdr
|
typedef struct arg_hdr {
|
||||||
{
|
|
||||||
char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */
|
char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */
|
||||||
const char *shortopts; /* String defining the short options */
|
const char* shortopts; /* String defining the short options */
|
||||||
const char *longopts; /* String defiing the long options */
|
const char* longopts; /* String defiing the long options */
|
||||||
const char *datatype; /* Description of the argument data type */
|
const char* datatype; /* Description of the argument data type */
|
||||||
const char *glossary; /* Description of the option as shown by arg_print_glossary function */
|
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 mincount; /* Minimum number of occurences of this option accepted */
|
||||||
int maxcount; /* Maximum number of occurences if this option accepted */
|
int maxcount; /* Maximum number of occurences if this option accepted */
|
||||||
void *parent; /* Pointer to parent arg_xxx struct */
|
void* parent; /* Pointer to parent arg_xxx struct */
|
||||||
arg_resetfn *resetfn; /* Pointer to parent arg_xxx reset function */
|
arg_resetfn* resetfn; /* Pointer to parent arg_xxx reset function */
|
||||||
arg_scanfn *scanfn; /* Pointer to parent arg_xxx scan function */
|
arg_scanfn* scanfn; /* Pointer to parent arg_xxx scan function */
|
||||||
arg_checkfn *checkfn; /* Pointer to parent arg_xxx check function */
|
arg_checkfn* checkfn; /* Pointer to parent arg_xxx check function */
|
||||||
arg_errorfn *errorfn; /* Pointer to parent arg_xxx error function */
|
arg_errorfn* errorfn; /* Pointer to parent arg_xxx error function */
|
||||||
void *priv; /* Pointer to private header data for use by arg_xxx functions */
|
void* priv; /* Pointer to private header data for use by arg_xxx functions */
|
||||||
};
|
} arg_hdr_t;
|
||||||
|
|
||||||
struct arg_rem
|
typedef struct arg_rem {
|
||||||
{
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||||
};
|
} arg_rem_t;
|
||||||
|
|
||||||
struct arg_lit
|
typedef struct arg_lit {
|
||||||
{
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||||
int count; /* Number of matching command line args */
|
int count; /* Number of matching command line args */
|
||||||
};
|
} arg_lit_t;
|
||||||
|
|
||||||
struct arg_int
|
typedef struct arg_int {
|
||||||
{
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||||
int count; /* Number of matching command line args */
|
int count; /* Number of matching command line args */
|
||||||
int *ival; /* Array of parsed argument values */
|
int* ival; /* Array of parsed argument values */
|
||||||
};
|
} arg_int_t;
|
||||||
|
|
||||||
struct arg_dbl
|
typedef struct arg_dbl {
|
||||||
{
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||||
int count; /* Number of matching command line args */
|
int count; /* Number of matching command line args */
|
||||||
double *dval; /* Array of parsed argument values */
|
double* dval; /* Array of parsed argument values */
|
||||||
};
|
} arg_dbl_t;
|
||||||
|
|
||||||
struct arg_str
|
typedef struct arg_str {
|
||||||
{
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||||
int count; /* Number of matching command line args */
|
int count; /* Number of matching command line args */
|
||||||
const char **sval; /* Array of parsed argument values */
|
const char** sval; /* Array of parsed argument values */
|
||||||
};
|
} arg_str_t;
|
||||||
|
|
||||||
struct arg_rex
|
typedef struct arg_rex {
|
||||||
{
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||||
int count; /* Number of matching command line args */
|
int count; /* Number of matching command line args */
|
||||||
const char **sval; /* Array of parsed argument values */
|
const char** sval; /* Array of parsed argument values */
|
||||||
};
|
} arg_rex_t;
|
||||||
|
|
||||||
struct arg_file
|
typedef struct arg_file {
|
||||||
{
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||||
int count; /* Number of matching command line args*/
|
int count; /* Number of matching command line args*/
|
||||||
const char **filename; /* Array of parsed filenames (eg: /home/foo.bar) */
|
const char** filename; /* Array of parsed filenames (eg: /home/foo.bar) */
|
||||||
const char **basename; /* Array of parsed basenames (eg: foo.bar) */
|
const char** basename; /* Array of parsed basenames (eg: foo.bar) */
|
||||||
const char **extension; /* Array of parsed extensions (eg: .bar) */
|
const char** extension; /* Array of parsed extensions (eg: .bar) */
|
||||||
};
|
} arg_file_t;
|
||||||
|
|
||||||
struct arg_date
|
typedef struct arg_date {
|
||||||
{
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||||
const char *format; /* strptime format string used to parse the date */
|
const char* format; /* strptime format string used to parse the date */
|
||||||
int count; /* Number of matching command line args */
|
int count; /* Number of matching command line args */
|
||||||
struct tm *tmval; /* Array of parsed time values */
|
struct tm* tmval; /* Array of parsed time values */
|
||||||
};
|
} arg_date_t;
|
||||||
|
|
||||||
enum {ARG_ELIMIT=1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG};
|
enum { ARG_ELIMIT = 1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG };
|
||||||
struct arg_end
|
typedef struct arg_end {
|
||||||
{
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||||
int count; /* Number of errors encountered */
|
int count; /* Number of errors encountered */
|
||||||
int *error; /* Array of error codes */
|
int* error; /* Array of error codes */
|
||||||
void **parent; /* Array of pointers to offending arg_xxx struct */
|
void** parent; /* Array of pointers to offending arg_xxx struct */
|
||||||
const char **argval; /* Array of pointers to offending argv[] string */
|
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_xxx constructor functions *********************************/
|
||||||
|
|
||||||
struct arg_rem* arg_rem(const char* datatype, const char* glossary);
|
ARG_EXTERN struct arg_rem* arg_rem(const char* datatype, const char* glossary);
|
||||||
|
|
||||||
struct arg_lit* arg_lit0(const char* shortopts,
|
ARG_EXTERN struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary);
|
||||||
const char* longopts,
|
ARG_EXTERN struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary);
|
||||||
const char* glossary);
|
ARG_EXTERN struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary);
|
||||||
struct arg_lit* arg_lit1(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char *glossary);
|
|
||||||
struct arg_lit* arg_litn(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
int mincount,
|
|
||||||
int maxcount,
|
|
||||||
const char *glossary);
|
|
||||||
|
|
||||||
struct arg_key* arg_key0(const char* keyword,
|
ARG_EXTERN struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
||||||
int flags,
|
ARG_EXTERN struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
||||||
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);
|
||||||
struct arg_key* arg_key1(const char* keyword,
|
|
||||||
int flags,
|
|
||||||
const char* glossary);
|
|
||||||
struct arg_key* arg_keyn(const char* keyword,
|
|
||||||
int flags,
|
|
||||||
int mincount,
|
|
||||||
int maxcount,
|
|
||||||
const char* glossary);
|
|
||||||
|
|
||||||
struct arg_int* arg_int0(const char* shortopts,
|
ARG_EXTERN struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
||||||
const char* longopts,
|
ARG_EXTERN struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
||||||
const char* datatype,
|
ARG_EXTERN struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
|
||||||
const char* glossary);
|
|
||||||
struct arg_int* arg_int1(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* datatype,
|
|
||||||
const char *glossary);
|
|
||||||
struct arg_int* arg_intn(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char *datatype,
|
|
||||||
int mincount,
|
|
||||||
int maxcount,
|
|
||||||
const char *glossary);
|
|
||||||
|
|
||||||
struct arg_dbl* arg_dbl0(const char* shortopts,
|
ARG_EXTERN struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
||||||
const char* longopts,
|
ARG_EXTERN struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
||||||
const char* datatype,
|
ARG_EXTERN struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
|
||||||
const char* glossary);
|
|
||||||
struct arg_dbl* arg_dbl1(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* datatype,
|
|
||||||
const char *glossary);
|
|
||||||
struct arg_dbl* arg_dbln(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char *datatype,
|
|
||||||
int mincount,
|
|
||||||
int maxcount,
|
|
||||||
const char *glossary);
|
|
||||||
|
|
||||||
struct arg_str* arg_str0(const char* shortopts,
|
ARG_EXTERN struct arg_rex* arg_rex0(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary);
|
||||||
const char* longopts,
|
ARG_EXTERN struct arg_rex* arg_rex1(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary);
|
||||||
const char* datatype,
|
ARG_EXTERN struct arg_rex* arg_rexn(const char* shortopts,
|
||||||
const char* glossary);
|
|
||||||
struct arg_str* arg_str1(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* datatype,
|
|
||||||
const char *glossary);
|
|
||||||
struct arg_str* arg_strn(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* datatype,
|
|
||||||
int mincount,
|
|
||||||
int maxcount,
|
|
||||||
const char *glossary);
|
|
||||||
|
|
||||||
struct arg_rex* arg_rex0(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* pattern,
|
|
||||||
const char* datatype,
|
|
||||||
int flags,
|
|
||||||
const char* glossary);
|
|
||||||
struct arg_rex* arg_rex1(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* pattern,
|
|
||||||
const char* datatype,
|
|
||||||
int flags,
|
|
||||||
const char *glossary);
|
|
||||||
struct arg_rex* arg_rexn(const char* shortopts,
|
|
||||||
const char* longopts,
|
const char* longopts,
|
||||||
const char* pattern,
|
const char* pattern,
|
||||||
const char* datatype,
|
const char* datatype,
|
||||||
int mincount,
|
int mincount,
|
||||||
int maxcount,
|
int maxcount,
|
||||||
int flags,
|
int flags,
|
||||||
const char *glossary);
|
|
||||||
|
|
||||||
struct arg_file* arg_file0(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* datatype,
|
|
||||||
const char* glossary);
|
const char* glossary);
|
||||||
struct arg_file* arg_file1(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* datatype,
|
|
||||||
const char *glossary);
|
|
||||||
struct arg_file* arg_filen(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* datatype,
|
|
||||||
int mincount,
|
|
||||||
int maxcount,
|
|
||||||
const char *glossary);
|
|
||||||
|
|
||||||
struct arg_date* arg_date0(const char* shortopts,
|
ARG_EXTERN struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
||||||
const char* longopts,
|
ARG_EXTERN struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
||||||
const char* format,
|
ARG_EXTERN struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
|
||||||
const char* datatype,
|
|
||||||
const char* glossary);
|
|
||||||
struct arg_date* arg_date1(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* format,
|
|
||||||
const char* datatype,
|
|
||||||
const char *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);
|
|
||||||
|
|
||||||
struct arg_end* arg_end(int maxerrors);
|
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 *******************************************/
|
/**** other functions *******************************************/
|
||||||
int arg_nullcheck(void **argtable);
|
ARG_EXTERN int arg_nullcheck(void** argtable);
|
||||||
int arg_parse(int argc, char **argv, void **argtable);
|
ARG_EXTERN int arg_parse(int argc, char** argv, void** argtable);
|
||||||
void arg_print_option(FILE *fp, const char *shortopts, const char *longopts, const char *datatype, const char *suffix);
|
ARG_EXTERN void arg_print_option(FILE* fp, const char* shortopts, const char* longopts, const char* datatype, const char* suffix);
|
||||||
void arg_print_syntax(FILE *fp, void **argtable, const char *suffix);
|
ARG_EXTERN void arg_print_syntax(FILE* fp, void** argtable, const char* suffix);
|
||||||
void arg_print_syntaxv(FILE *fp, void **argtable, const char *suffix);
|
ARG_EXTERN void arg_print_syntaxv(FILE* fp, void** argtable, const char* suffix);
|
||||||
void arg_print_glossary(FILE *fp, void **argtable, const char *format);
|
ARG_EXTERN void arg_print_glossary(FILE* fp, void** argtable, const char* format);
|
||||||
void arg_print_glossary_gnu(FILE *fp, void **argtable);
|
ARG_EXTERN void arg_print_glossary_gnu(FILE* fp, void** argtable);
|
||||||
void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname);
|
ARG_EXTERN void arg_print_formatted(FILE *fp, const unsigned lmargin, const unsigned rmargin, const char *text);
|
||||||
void arg_freetable(void **argtable, size_t n);
|
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 ********/
|
/**** deprecated functions, for back-compatibility only ********/
|
||||||
void arg_free(void **argtable);
|
ARG_EXTERN void arg_free(void** argtable);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
245
components/console/argtable3/argtable3_private.h
Normal file
245
components/console/argtable3/argtable3_private.h
Normal file
|
@ -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
|
|
@ -1,26 +1,20 @@
|
||||||
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
// you may not use this file except in compliance with the License.
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
// You may obtain a copy of the License at
|
*/
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_console.h"
|
#include "esp_console.h"
|
||||||
|
#include "esp_system.h"
|
||||||
#include "linenoise/linenoise.h"
|
#include "linenoise/linenoise.h"
|
||||||
#include "argtable3/argtable3.h"
|
#include "argtable3/argtable3.h"
|
||||||
#include "rom/queue.h"
|
#include "sys/queue.h"
|
||||||
|
|
||||||
#define ANSI_COLOR_DEFAULT 39 /** Default foreground color */
|
#define ANSI_COLOR_DEFAULT 39 /** Default foreground color */
|
||||||
|
|
||||||
|
@ -28,18 +22,18 @@ typedef struct cmd_item_ {
|
||||||
/**
|
/**
|
||||||
* Command name (statically allocated by application)
|
* Command name (statically allocated by application)
|
||||||
*/
|
*/
|
||||||
const char* command;
|
const char *command;
|
||||||
/**
|
/**
|
||||||
* Help text (statically allocated by application), may be NULL.
|
* Help text (statically allocated by application), may be NULL.
|
||||||
*/
|
*/
|
||||||
const char* help;
|
const char *help;
|
||||||
/**
|
/**
|
||||||
* Hint text, usually lists possible arguments, dynamically allocated.
|
* Hint text, usually lists possible arguments, dynamically allocated.
|
||||||
* May be NULL.
|
* May be NULL.
|
||||||
*/
|
*/
|
||||||
char* hint;
|
char *hint;
|
||||||
esp_console_cmd_func_t func; //!< pointer to the command handler
|
esp_console_cmd_func_t func; //!< pointer to the command handler
|
||||||
void* argtable; //!< optional pointer to arg table
|
void *argtable; //!< optional pointer to arg table
|
||||||
SLIST_ENTRY(cmd_item_) next; //!< next command in the list
|
SLIST_ENTRY(cmd_item_) next; //!< next command in the list
|
||||||
} cmd_item_t;
|
} cmd_item_t;
|
||||||
|
|
||||||
|
@ -47,15 +41,20 @@ typedef struct cmd_item_ {
|
||||||
static SLIST_HEAD(cmd_list_, cmd_item_) s_cmd_list;
|
static SLIST_HEAD(cmd_list_, cmd_item_) s_cmd_list;
|
||||||
|
|
||||||
/** run-time configuration options */
|
/** run-time configuration options */
|
||||||
static esp_console_config_t s_config;
|
static esp_console_config_t s_config = {
|
||||||
|
.heap_alloc_caps = MALLOC_CAP_DEFAULT
|
||||||
|
};
|
||||||
|
|
||||||
/** temporary buffer used for command line parsing */
|
/** temporary buffer used for command line parsing */
|
||||||
static char* s_tmp_line_buf;
|
static char *s_tmp_line_buf;
|
||||||
|
|
||||||
static const cmd_item_t* find_command_by_name(const char* name);
|
static const cmd_item_t *find_command_by_name(const char *name);
|
||||||
|
|
||||||
esp_err_t esp_console_init(const esp_console_config_t* config)
|
esp_err_t esp_console_init(const esp_console_config_t *config)
|
||||||
{
|
{
|
||||||
|
if (!config) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
if (s_tmp_line_buf) {
|
if (s_tmp_line_buf) {
|
||||||
return ESP_ERR_INVALID_STATE;
|
return ESP_ERR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
@ -63,21 +62,26 @@ esp_err_t esp_console_init(const esp_console_config_t* config)
|
||||||
if (s_config.hint_color == 0) {
|
if (s_config.hint_color == 0) {
|
||||||
s_config.hint_color = ANSI_COLOR_DEFAULT;
|
s_config.hint_color = ANSI_COLOR_DEFAULT;
|
||||||
}
|
}
|
||||||
s_tmp_line_buf = calloc(config->max_cmdline_length, 1);
|
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) {
|
if (s_tmp_line_buf == NULL) {
|
||||||
return ESP_ERR_NO_MEM;
|
return ESP_ERR_NO_MEM;
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t esp_console_deinit()
|
esp_err_t esp_console_deinit(void)
|
||||||
{
|
{
|
||||||
if (!s_tmp_line_buf) {
|
if (!s_tmp_line_buf) {
|
||||||
return ESP_ERR_INVALID_STATE;
|
return ESP_ERR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
free(s_tmp_line_buf);
|
free(s_tmp_line_buf);
|
||||||
|
s_tmp_line_buf = NULL;
|
||||||
cmd_item_t *it, *tmp;
|
cmd_item_t *it, *tmp;
|
||||||
SLIST_FOREACH_SAFE(it, &s_cmd_list, next, tmp) {
|
SLIST_FOREACH_SAFE(it, &s_cmd_list, next, tmp) {
|
||||||
|
SLIST_REMOVE(&s_cmd_list, it, cmd_item_, next);
|
||||||
free(it->hint);
|
free(it->hint);
|
||||||
free(it);
|
free(it);
|
||||||
}
|
}
|
||||||
|
@ -86,39 +90,51 @@ esp_err_t esp_console_deinit()
|
||||||
|
|
||||||
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd)
|
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd)
|
||||||
{
|
{
|
||||||
cmd_item_t *item = (cmd_item_t *) calloc(1, sizeof(*item));
|
cmd_item_t *item = NULL;
|
||||||
if (item == NULL) {
|
if (!cmd || cmd->command == NULL) {
|
||||||
return ESP_ERR_NO_MEM;
|
|
||||||
}
|
|
||||||
if (cmd->command == NULL) {
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
if (strchr(cmd->command, ' ') != NULL) {
|
if (strchr(cmd->command, ' ') != NULL) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
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->command = cmd->command;
|
||||||
item->help = cmd->help;
|
item->help = cmd->help;
|
||||||
if (cmd->hint) {
|
if (cmd->hint) {
|
||||||
/* Prepend a space before the hint. It separates command name and
|
/* Prepend a space before the hint. It separates command name and
|
||||||
* the hint. arg_print_syntax below adds this space as well.
|
* the hint. arg_print_syntax below adds this space as well.
|
||||||
*/
|
*/
|
||||||
asprintf(&item->hint, " %s", cmd->hint);
|
int unused __attribute__((unused));
|
||||||
|
unused = asprintf(&item->hint, " %s", cmd->hint);
|
||||||
} else if (cmd->argtable) {
|
} else if (cmd->argtable) {
|
||||||
/* Generate hint based on cmd->argtable */
|
/* Generate hint based on cmd->argtable */
|
||||||
char* buf;
|
char *buf = NULL;
|
||||||
size_t buf_size;
|
size_t buf_size = 0;
|
||||||
FILE* f = open_memstream(&buf, &buf_size);
|
FILE *f = open_memstream(&buf, &buf_size);
|
||||||
|
if (f != NULL) {
|
||||||
arg_print_syntax(f, cmd->argtable, NULL);
|
arg_print_syntax(f, cmd->argtable, NULL);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
}
|
||||||
item->hint = buf;
|
item->hint = buf;
|
||||||
}
|
}
|
||||||
item->argtable = cmd->argtable;
|
item->argtable = cmd->argtable;
|
||||||
item->func = cmd->func;
|
item->func = cmd->func;
|
||||||
cmd_item_t* last = SLIST_FIRST(&s_cmd_list);
|
cmd_item_t *last = SLIST_FIRST(&s_cmd_list);
|
||||||
if (last == NULL) {
|
if (last == NULL) {
|
||||||
SLIST_INSERT_HEAD(&s_cmd_list, item, next);
|
SLIST_INSERT_HEAD(&s_cmd_list, item, next);
|
||||||
} else {
|
} else {
|
||||||
cmd_item_t* it;
|
cmd_item_t *it;
|
||||||
while ((it = SLIST_NEXT(last, next)) != NULL) {
|
while ((it = SLIST_NEXT(last, next)) != NULL) {
|
||||||
last = it;
|
last = it;
|
||||||
}
|
}
|
||||||
|
@ -133,7 +149,7 @@ void esp_console_get_completion(const char *buf, linenoiseCompletions *lc)
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cmd_item_t* it;
|
cmd_item_t *it;
|
||||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
SLIST_FOREACH(it, &s_cmd_list, next) {
|
||||||
/* Check if command starts with buf */
|
/* Check if command starts with buf */
|
||||||
if (strncmp(buf, it->command, len) == 0) {
|
if (strncmp(buf, it->command, len) == 0) {
|
||||||
|
@ -142,10 +158,10 @@ void esp_console_get_completion(const char *buf, linenoiseCompletions *lc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* esp_console_get_hint(const char *buf, int *color, int *bold)
|
const char *esp_console_get_hint(const char *buf, int *color, int *bold)
|
||||||
{
|
{
|
||||||
int len = strlen(buf);
|
size_t len = strlen(buf);
|
||||||
cmd_item_t* it;
|
cmd_item_t *it;
|
||||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
SLIST_FOREACH(it, &s_cmd_list, next) {
|
||||||
if (strlen(it->command) == len &&
|
if (strlen(it->command) == len &&
|
||||||
strncmp(buf, it->command, len) == 0) {
|
strncmp(buf, it->command, len) == 0) {
|
||||||
|
@ -157,12 +173,14 @@ const char* esp_console_get_hint(const char *buf, int *color, int *bold)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const cmd_item_t* find_command_by_name(const char* name)
|
static const cmd_item_t *find_command_by_name(const char *name)
|
||||||
{
|
{
|
||||||
const cmd_item_t* cmd = NULL;
|
const cmd_item_t *cmd = NULL;
|
||||||
cmd_item_t* it;
|
cmd_item_t *it;
|
||||||
|
size_t len = strlen(name);
|
||||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
SLIST_FOREACH(it, &s_cmd_list, next) {
|
||||||
if (strcmp(name, it->command) == 0) {
|
if (strlen(it->command) == len &&
|
||||||
|
strcmp(name, it->command) == 0) {
|
||||||
cmd = it;
|
cmd = it;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -170,12 +188,12 @@ static const cmd_item_t* find_command_by_name(const char* name)
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t esp_console_run(const char* cmdline, int* cmd_ret)
|
esp_err_t esp_console_run(const char *cmdline, int *cmd_ret)
|
||||||
{
|
{
|
||||||
if (s_tmp_line_buf == NULL) {
|
if (s_tmp_line_buf == NULL) {
|
||||||
return ESP_ERR_INVALID_STATE;
|
return ESP_ERR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
char** argv = (char**) calloc(s_config.max_cmdline_args, sizeof(char*));
|
char **argv = (char **) heap_caps_calloc(s_config.max_cmdline_args, sizeof(char *), s_config.heap_alloc_caps);
|
||||||
if (argv == NULL) {
|
if (argv == NULL) {
|
||||||
return ESP_ERR_NO_MEM;
|
return ESP_ERR_NO_MEM;
|
||||||
}
|
}
|
||||||
|
@ -183,9 +201,13 @@ esp_err_t esp_console_run(const char* cmdline, int* cmd_ret)
|
||||||
|
|
||||||
size_t argc = esp_console_split_argv(s_tmp_line_buf, argv,
|
size_t argc = esp_console_split_argv(s_tmp_line_buf, argv,
|
||||||
s_config.max_cmdline_args);
|
s_config.max_cmdline_args);
|
||||||
|
if (argc == 0) {
|
||||||
const cmd_item_t* cmd = find_command_by_name(argv[0]);
|
free(argv);
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
const cmd_item_t *cmd = find_command_by_name(argv[0]);
|
||||||
if (cmd == NULL) {
|
if (cmd == NULL) {
|
||||||
|
free(argv);
|
||||||
return ESP_ERR_NOT_FOUND;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
*cmd_ret = (*cmd->func)(argc, argv);
|
*cmd_ret = (*cmd->func)(argc, argv);
|
||||||
|
@ -193,19 +215,17 @@ esp_err_t esp_console_run(const char* cmdline, int* cmd_ret)
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int help_command(int argc, char** argv)
|
static struct {
|
||||||
{
|
struct arg_str *help_cmd;
|
||||||
cmd_item_t* it;
|
struct arg_end *end;
|
||||||
|
} help_args;
|
||||||
|
|
||||||
/* Print summary of each command */
|
static void print_arg_help(cmd_item_t *it)
|
||||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
{
|
||||||
if (it->help == NULL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* First line: command name and hint
|
/* First line: command name and hint
|
||||||
* Pad all the hints to the same column
|
* Pad all the hints to the same column
|
||||||
*/
|
*/
|
||||||
const char* hint = (it->hint) ? it->hint : "";
|
const char *hint = (it->hint) ? it->hint : "";
|
||||||
printf("%-s %s\n", it->command, hint);
|
printf("%-s %s\n", it->command, hint);
|
||||||
/* Second line: print help.
|
/* Second line: print help.
|
||||||
* Argtable has a nice helper function for this which does line
|
* Argtable has a nice helper function for this which does line
|
||||||
|
@ -215,20 +235,68 @@ static int help_command(int argc, char** argv)
|
||||||
arg_print_formatted(stdout, 2, 78, it->help);
|
arg_print_formatted(stdout, 2, 78, it->help);
|
||||||
/* Finally, print the list of arguments */
|
/* Finally, print the list of arguments */
|
||||||
if (it->argtable) {
|
if (it->argtable) {
|
||||||
arg_print_glossary(stdout, (void**) it->argtable, " %12s %s\n");
|
arg_print_glossary(stdout, (void **) it->argtable, " %12s %s\n");
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int help_command(int argc, char **argv)
|
||||||
esp_err_t esp_console_register_help_command()
|
|
||||||
{
|
{
|
||||||
|
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 = {
|
esp_console_cmd_t command = {
|
||||||
.command = "help",
|
.command = "help",
|
||||||
.help = "Print the list of registered commands",
|
.help = "Print the summary of all registered commands if no arguments "
|
||||||
.func = &help_command
|
"are given, otherwise print summary of given command.",
|
||||||
|
.func = &help_command,
|
||||||
|
.argtable = &help_args
|
||||||
};
|
};
|
||||||
return esp_console_cmd_register(&command);
|
return esp_console_cmd_register(&command);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
COMPONENT_ADD_INCLUDEDIRS := .
|
|
||||||
COMPONENT_SRCDIRS := linenoise argtable3 .
|
|
|
@ -1,19 +1,17 @@
|
||||||
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
// you may not use this file except in compliance with the License.
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
// You may obtain a copy of the License at
|
*/
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
|
||||||
// Forward declaration. Definition in linenoise/linenoise.h.
|
// Forward declaration. Definition in linenoise/linenoise.h.
|
||||||
|
@ -25,30 +23,130 @@ typedef struct linenoiseCompletions linenoiseCompletions;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t max_cmdline_length; //!< length of command line buffer, in bytes
|
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
|
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_color; //!< ASCII color code of hint text
|
||||||
int hint_bold; //!< Set to 1 to print hint text in bold
|
int hint_bold; //!< Set to 1 to print hint text in bold
|
||||||
} esp_console_config_t;
|
} 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
|
||||||
|
} 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
|
* @brief initialize console module
|
||||||
* Call this once before using other console module features
|
* @param config console configuration
|
||||||
|
* @note Call this once before using other console module features
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK on success
|
* - ESP_OK on success
|
||||||
* - ESP_ERR_NO_MEM if out of memory
|
* - ESP_ERR_NO_MEM if out of memory
|
||||||
* - ESP_ERR_INVALID_STATE if already initialized
|
* - 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);
|
esp_err_t esp_console_init(const esp_console_config_t *config);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief de-initialize console module
|
* @brief de-initialize console module
|
||||||
* Call this once when done using console module functions
|
* @note Call this once when done using console module functions
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK on success
|
* - ESP_OK on success
|
||||||
* - ESP_ERR_INVALID_STATE if not initialized yet
|
* - ESP_ERR_INVALID_STATE if not initialized yet
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_console_deinit();
|
esp_err_t esp_console_deinit(void);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Console command main function
|
* @brief Console command main function
|
||||||
|
@ -56,7 +154,7 @@ esp_err_t esp_console_deinit();
|
||||||
* @param argv array with argc entries, each pointing to a zero-terminated string argument
|
* @param argv array with argc entries, each pointing to a zero-terminated string argument
|
||||||
* @return console command return code, 0 indicates "success"
|
* @return console command return code, 0 indicates "success"
|
||||||
*/
|
*/
|
||||||
typedef int (*esp_console_cmd_func_t)(int argc, char** argv);
|
typedef int (*esp_console_cmd_func_t)(int argc, char **argv);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Console command description
|
* @brief Console command description
|
||||||
|
@ -66,19 +164,19 @@ typedef struct {
|
||||||
* Command name. Must not be NULL, must not contain spaces.
|
* Command name. Must not be NULL, must not contain spaces.
|
||||||
* The pointer must be valid until the call to esp_console_deinit.
|
* The pointer must be valid until the call to esp_console_deinit.
|
||||||
*/
|
*/
|
||||||
const char* command; //!< command name
|
const char *command;
|
||||||
/**
|
/**
|
||||||
* Help text for the command, shown by help 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 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.
|
* If not set, the command will not be listed in 'help' output.
|
||||||
*/
|
*/
|
||||||
const char* help;
|
const char *help;
|
||||||
/**
|
/**
|
||||||
* Hint text, usually lists possible arguments.
|
* Hint text, usually lists possible arguments.
|
||||||
* If set to NULL, and 'argtable' field is non-NULL, hint will be generated
|
* If set to NULL, and 'argtable' field is non-NULL, hint will be generated
|
||||||
* automatically
|
* automatically
|
||||||
*/
|
*/
|
||||||
const char* hint;
|
const char *hint;
|
||||||
/**
|
/**
|
||||||
* Pointer to a function which implements the command.
|
* Pointer to a function which implements the command.
|
||||||
*/
|
*/
|
||||||
|
@ -89,7 +187,7 @@ typedef struct {
|
||||||
* Array/structure which this field points to must end with an arg_end.
|
* Array/structure which this field points to must end with an arg_end.
|
||||||
* Only used for the duration of esp_console_cmd_register call.
|
* Only used for the duration of esp_console_cmd_register call.
|
||||||
*/
|
*/
|
||||||
void* argtable;
|
void *argtable;
|
||||||
} esp_console_cmd_t;
|
} esp_console_cmd_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,6 +196,7 @@ typedef struct {
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK on success
|
* - ESP_OK on success
|
||||||
* - ESP_ERR_NO_MEM if out of memory
|
* - 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);
|
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd);
|
||||||
|
|
||||||
|
@ -107,14 +206,16 @@ esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd);
|
||||||
* @param[out] cmd_ret return code from the command (set if command was run)
|
* @param[out] cmd_ret return code from the command (set if command was run)
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK, if command was run
|
* - 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_NOT_FOUND, if command with given name wasn't registered
|
||||||
* - ESP_ERR_INVALID_STATE, if esp_console_init wasn't called
|
* - ESP_ERR_INVALID_STATE, if esp_console_init wasn't called
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_console_run(const char* cmdline, int* cmd_ret);
|
esp_err_t esp_console_run(const char *cmdline, int *cmd_ret);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Split command line into arguments in place
|
* @brief Split command line into arguments in place
|
||||||
*
|
* @verbatim
|
||||||
* - This function finds whitespace-separated arguments in the given input line.
|
* - This function finds whitespace-separated arguments in the given input line.
|
||||||
*
|
*
|
||||||
* 'abc def 1 20 .3' -> [ 'abc', 'def', '1', '20', '.3' ]
|
* 'abc def 1 20 .3' -> [ 'abc', 'def', '1', '20', '.3' ]
|
||||||
|
@ -127,8 +228,8 @@ esp_err_t esp_console_run(const char* cmdline, int* cmd_ret);
|
||||||
* - Escape sequences may be used to produce backslash, double quote, and space:
|
* - Escape sequences may be used to produce backslash, double quote, and space:
|
||||||
*
|
*
|
||||||
* 'a\ b\\c\"' -> [ 'a b\c"' ]
|
* 'a\ b\\c\"' -> [ 'a b\c"' ]
|
||||||
*
|
* @endverbatim
|
||||||
* Pointers to at most argv_size - 1 arguments are returned in argv array.
|
* @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.
|
* 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 line pointer to buffer to parse; it is modified in place
|
||||||
|
@ -172,10 +273,124 @@ const char *esp_console_get_hint(const char *buf, int *color, int *bold);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Register a 'help' command
|
* @brief Register a 'help' command
|
||||||
|
*
|
||||||
* Default 'help' command prints the list of registered commands along with
|
* Default 'help' command prints the list of registered commands along with
|
||||||
* hints and help strings.
|
* 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
|
* @return
|
||||||
* - ESP_OK on success
|
* - ESP_OK on success
|
||||||
* - ESP_ERR_INVALID_STATE, if esp_console_init wasn't called
|
* - ESP_ERR_INVALID_STATE, if esp_console_init wasn't called
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_console_register_help_command();
|
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
|
||||||
|
|
549
components/console/esp_console_repl.c
Normal file
549
components/console/esp_console_repl.c
Normal file
|
@ -0,0 +1,549 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
} 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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* Figure out if the terminal supports escape sequences */
|
||||||
|
int probe_status = linenoiseProbe();
|
||||||
|
if (probe_status) {
|
||||||
|
/* zero indicates success */
|
||||||
|
linenoiseSetDumbMode(1);
|
||||||
|
#if CONFIG_LOG_COLORS
|
||||||
|
/* Since the terminal doesn't support escape sequences,
|
||||||
|
* don't use color codes in the s_prompt.
|
||||||
|
*/
|
||||||
|
snprintf(repl_com->prompt, CONSOLE_PROMPT_MAX_LEN - 1, "%s ", prompt_temp);
|
||||||
|
#endif //CONFIG_LOG_COLORS
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
|
@ -856,7 +856,8 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
|
||||||
// TODO: try to make a non-blocking dumb mode
|
// TODO: try to make a non-blocking dumb mode
|
||||||
static char *linenoiseDumb(struct linenoiseState *l) {
|
static char *linenoiseDumb(struct linenoiseState *l) {
|
||||||
/* dumb terminal, fall back to fgets */
|
/* dumb terminal, fall back to fgets */
|
||||||
fputs(l->prompt, stdout);
|
// Not needed anymore, prompt is now in linenoiseEditStart
|
||||||
|
// fputs(l->prompt, stdout);
|
||||||
flushWrite();
|
flushWrite();
|
||||||
l->len = 0; //needed?
|
l->len = 0; //needed?
|
||||||
while (l->len < l->buflen) {
|
while (l->len < l->buflen) {
|
||||||
|
@ -1008,7 +1009,8 @@ char *linenoiseEditFeed(struct linenoiseState *l) {
|
||||||
if ((l->in_completion || c == 9) && completionCallback != NULL) {
|
if ((l->in_completion || c == 9) && completionCallback != NULL) {
|
||||||
c = completeLine(l,c);
|
c = completeLine(l,c);
|
||||||
/* Return on errors */
|
/* Return on errors */
|
||||||
if (c < 0) return NULL;
|
// TODO: how was it supposed to work? c can't be less than 0
|
||||||
|
// if (c < 0) return NULL;
|
||||||
/* Read next character when 0 */
|
/* Read next character when 0 */
|
||||||
if (c == 0) return linenoiseEditMore;
|
if (c == 0) return linenoiseEditMore;
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,7 @@ void linenoiseClearScreen(void);
|
||||||
void linenoiseSetMultiLine(int ml);
|
void linenoiseSetMultiLine(int ml);
|
||||||
void linenoiseSetDumbMode(int set);
|
void linenoiseSetDumbMode(int set);
|
||||||
bool linenoiseIsDumbMode(void);
|
bool linenoiseIsDumbMode(void);
|
||||||
|
int linenoiseProbe();
|
||||||
void linenoiseMaskModeEnable(void);
|
void linenoiseMaskModeEnable(void);
|
||||||
void linenoiseMaskModeDisable(void);
|
void linenoiseMaskModeDisable(void);
|
||||||
int linenoiseSetMaxLineLen(size_t len);
|
int linenoiseSetMaxLineLen(size_t len);
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
|
/*
|
||||||
//
|
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
// you may not use this file except in compliance with the License.
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
// You may obtain a copy of the License at
|
*/
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
@ -31,13 +23,20 @@ typedef enum {
|
||||||
SS_QUOTED_ARG_ESCAPED = SS_QUOTED_ARG | SS_FLAG_ESCAPE,
|
SS_QUOTED_ARG_ESCAPED = SS_QUOTED_ARG | SS_FLAG_ESCAPE,
|
||||||
} split_state_t;
|
} 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)
|
size_t esp_console_split_argv(char *line, char **argv, size_t argv_size)
|
||||||
{
|
{
|
||||||
const int QUOTE = '"';
|
const int QUOTE = '"';
|
||||||
const int ESCAPE = '\\';
|
const int ESCAPE = '\\';
|
||||||
const int SPACE = ' ';
|
const int SPACE = ' ';
|
||||||
split_state_t state = SS_SPACE;
|
split_state_t state = SS_SPACE;
|
||||||
int argc = 0;
|
size_t argc = 0;
|
||||||
char *next_arg_start = line;
|
char *next_arg_start = line;
|
||||||
char *out_ptr = line;
|
char *out_ptr = line;
|
||||||
for (char *in_ptr = line; argc < argv_size - 1; ++in_ptr) {
|
for (char *in_ptr = line; argc < argv_size - 1; ++in_ptr) {
|
||||||
|
@ -47,13 +46,6 @@ size_t esp_console_split_argv(char *line, char **argv, size_t argv_size)
|
||||||
}
|
}
|
||||||
int char_out = -1;
|
int char_out = -1;
|
||||||
|
|
||||||
/* helper function, called when done with an argument */
|
|
||||||
void end_arg() {
|
|
||||||
char_out = 0;
|
|
||||||
argv[argc++] = next_arg_start;
|
|
||||||
state = SS_SPACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case SS_SPACE:
|
case SS_SPACE:
|
||||||
if (char_in == SPACE) {
|
if (char_in == SPACE) {
|
||||||
|
@ -73,7 +65,7 @@ size_t esp_console_split_argv(char *line, char **argv, size_t argv_size)
|
||||||
|
|
||||||
case SS_QUOTED_ARG:
|
case SS_QUOTED_ARG:
|
||||||
if (char_in == QUOTE) {
|
if (char_in == QUOTE) {
|
||||||
end_arg();
|
END_ARG();
|
||||||
} else if (char_in == ESCAPE) {
|
} else if (char_in == ESCAPE) {
|
||||||
state = SS_QUOTED_ARG_ESCAPED;
|
state = SS_QUOTED_ARG_ESCAPED;
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,7 +85,7 @@ size_t esp_console_split_argv(char *line, char **argv, size_t argv_size)
|
||||||
|
|
||||||
case SS_ARG:
|
case SS_ARG:
|
||||||
if (char_in == SPACE) {
|
if (char_in == SPACE) {
|
||||||
end_arg();
|
END_ARG();
|
||||||
} else if (char_in == ESCAPE) {
|
} else if (char_in == ESCAPE) {
|
||||||
state = SS_ARG_ESCAPED;
|
state = SS_ARG_ESCAPED;
|
||||||
} else {
|
} else {
|
||||||
|
|
12
components/console/test_apps/console/CMakeLists.txt
Normal file
12
components/console/test_apps/console/CMakeLists.txt
Normal file
|
@ -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)
|
2
components/console/test_apps/console/README.md
Normal file
2
components/console/test_apps/console/README.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
4
components/console/test_apps/console/main/CMakeLists.txt
Normal file
4
components/console/test_apps/console/main/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
idf_component_register(SRCS "test_app_main.c" "test_console.c"
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
PRIV_REQUIRES unity console
|
||||||
|
WHOLE_ARCHIVE)
|
51
components/console/test_apps/console/main/test_app_main.c
Normal file
51
components/console/test_apps/console/main/test_app_main.c
Normal file
|
@ -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();
|
||||||
|
}
|
104
components/console/test_apps/console/main/test_console.c
Normal file
104
components/console/test_apps/console/main/test_console.c
Normal file
|
@ -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());
|
||||||
|
}
|
96
components/console/test_apps/console/pytest_console.py
Normal file
96
components/console/test_apps/console/pytest_console.py
Normal file
|
@ -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)
|
3
components/console/test_apps/console/sdkconfig.defaults
Normal file
3
components/console/test_apps/console/sdkconfig.defaults
Normal file
|
@ -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
|
|
@ -3,6 +3,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "linenoise/linenoise.h"
|
#include "linenoise/linenoise.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_console.h"
|
#include "esp_console.h"
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
|
@ -19,6 +21,7 @@ void console_task(void* arg) {
|
||||||
* This can be customized, made dynamic, etc.
|
* This can be customized, made dynamic, etc.
|
||||||
*/
|
*/
|
||||||
const char* prompt = LOG_COLOR_E PROMPT_STR LOG_RESET_COLOR;
|
const char* prompt = LOG_COLOR_E PROMPT_STR LOG_RESET_COLOR;
|
||||||
|
// const char* prompt = "dumb_console >";
|
||||||
|
|
||||||
printf("\n"
|
printf("\n"
|
||||||
"Type 'help' to get the list of commands.\n"
|
"Type 'help' to get the list of commands.\n"
|
||||||
|
@ -26,6 +29,24 @@ void console_task(void* arg) {
|
||||||
"Press TAB when typing command name to auto-complete.\n"
|
"Press TAB when typing command name to auto-complete.\n"
|
||||||
"Ctrl+C will terminate the console environment.\n");
|
"Ctrl+C will terminate the console environment.\n");
|
||||||
|
|
||||||
|
/* Figure out if the terminal supports escape sequences */
|
||||||
|
int probe_status = linenoiseProbe();
|
||||||
|
// int probe_status = 1;
|
||||||
|
if (probe_status) { /* zero indicates success */
|
||||||
|
printf("\n"
|
||||||
|
"Your terminal application does not support escape sequences.\n"
|
||||||
|
"Line editing and history features are disabled.\n"
|
||||||
|
"On Windows, try using Putty instead.\n");
|
||||||
|
linenoiseSetDumbMode(1);
|
||||||
|
|
||||||
|
#if CONFIG_LOG_COLORS
|
||||||
|
/* Since the terminal doesn't support escape sequences,
|
||||||
|
* don't use color codes in the prompt.
|
||||||
|
*/
|
||||||
|
// prompt = "> ";
|
||||||
|
#endif //CONFIG_LOG_COLORS
|
||||||
|
}
|
||||||
|
|
||||||
/* Main loop */
|
/* Main loop */
|
||||||
while (true) {
|
while (true) {
|
||||||
/* Get a line using linenoise.
|
/* Get a line using linenoise.
|
||||||
|
@ -33,6 +54,7 @@ void console_task(void* arg) {
|
||||||
*/
|
*/
|
||||||
char* line = linenoise(prompt);
|
char* line = linenoise(prompt);
|
||||||
if (line == NULL) { /* Break on EOF or error */
|
if (line == NULL) { /* Break on EOF or error */
|
||||||
|
ESP_LOGE(TAG, "Ctrl+C???");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Add the command to the history if not empty*/
|
/* Add the command to the history if not empty*/
|
||||||
|
@ -60,4 +82,7 @@ void console_task(void* arg) {
|
||||||
|
|
||||||
ESP_LOGE(TAG, "Error or end-of-input, terminating console");
|
ESP_LOGE(TAG, "Error or end-of-input, terminating console");
|
||||||
esp_console_deinit();
|
esp_console_deinit();
|
||||||
|
while (true) {
|
||||||
|
vTaskDelay(100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,12 +52,15 @@ static void initialize_console(void) {
|
||||||
linenoiseSetMultiLine(1);
|
linenoiseSetMultiLine(1);
|
||||||
|
|
||||||
/* Tell linenoise where to get command completions and hints */
|
/* Tell linenoise where to get command completions and hints */
|
||||||
// linenoiseSetCompletionCallback(&esp_console_get_completion);
|
linenoiseSetCompletionCallback(&esp_console_get_completion);
|
||||||
linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);
|
linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);
|
||||||
|
|
||||||
/* Set command history size */
|
/* Set command history size */
|
||||||
linenoiseHistorySetMaxLen(100);
|
linenoiseHistorySetMaxLen(100);
|
||||||
|
|
||||||
|
/* Set command maximum length */
|
||||||
|
linenoiseSetMaxLineLen(console_config.max_cmdline_length);
|
||||||
|
|
||||||
/* Load command history from filesystem */
|
/* Load command history from filesystem */
|
||||||
linenoiseHistoryLoad(HISTORY_PATH);
|
linenoiseHistoryLoad(HISTORY_PATH);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue