forked from test34/can_wizard
remove console component
This commit is contained in:
parent
98b406b571
commit
df0790bb7d
32 changed files with 0 additions and 8979 deletions
|
@ -1,31 +0,0 @@
|
||||||
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)
|
|
|
@ -1,167 +0,0 @@
|
||||||
Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
|
|
||||||
<sheitmann@users.sourceforge.net>
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
* Neither the name of STEWART HEITMANN nor the names of its contributors
|
|
||||||
may be used to endorse or promote products derived from this software
|
|
||||||
without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
|
|
||||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
|
|
||||||
FreeBSD getopt library
|
|
||||||
======================
|
|
||||||
|
|
||||||
Copyright (c) 2000 The NetBSD Foundation, Inc.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
This code is derived from software contributed to The NetBSD Foundation
|
|
||||||
by Dieter Baron and Thomas Klausner.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
1. Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
||||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
||||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
|
|
||||||
Tcl library
|
|
||||||
===========
|
|
||||||
|
|
||||||
This software is copyrighted by the Regents of the University of
|
|
||||||
California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState
|
|
||||||
Corporation and other parties. The following terms apply to all files
|
|
||||||
associated with the software unless explicitly disclaimed in
|
|
||||||
individual files.
|
|
||||||
|
|
||||||
The authors hereby grant permission to use, copy, modify, distribute,
|
|
||||||
and license this software and its documentation for any purpose, provided
|
|
||||||
that existing copyright notices are retained in all copies and that this
|
|
||||||
notice is included verbatim in any distributions. No written agreement,
|
|
||||||
license, or royalty fee is required for any of the authorized uses.
|
|
||||||
Modifications to this software may be copyrighted by their authors
|
|
||||||
and need not follow the licensing terms described here, provided that
|
|
||||||
the new terms are clearly indicated on the first page of each file where
|
|
||||||
they apply.
|
|
||||||
|
|
||||||
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
|
|
||||||
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
|
||||||
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
|
|
||||||
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
|
||||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
|
|
||||||
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
|
|
||||||
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
|
||||||
MODIFICATIONS.
|
|
||||||
|
|
||||||
GOVERNMENT USE: If you are acquiring this software on behalf of the
|
|
||||||
U.S. government, the Government shall have only "Restricted Rights"
|
|
||||||
in the software and related documentation as defined in the Federal
|
|
||||||
Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
|
|
||||||
are acquiring the software on behalf of the Department of Defense, the
|
|
||||||
software shall be classified as "Commercial Computer Software" and the
|
|
||||||
Government shall have only "Restricted Rights" as defined in Clause
|
|
||||||
252.227-7014 (b) (3) of DFARs. Notwithstanding the foregoing, the
|
|
||||||
authors grant the U.S. Government and others acting in its behalf
|
|
||||||
permission to use and distribute the software in accordance with the
|
|
||||||
terms specified in this license.
|
|
||||||
|
|
||||||
|
|
||||||
C Hash Table library
|
|
||||||
====================
|
|
||||||
|
|
||||||
Copyright (c) 2002, Christopher Clark
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the original author; nor the names of any contributors
|
|
||||||
may be used to endorse or promote products derived from this software
|
|
||||||
without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
||||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
|
|
||||||
The Better String library
|
|
||||||
=========================
|
|
||||||
|
|
||||||
Copyright (c) 2014, Paul Hsieh
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of bstrlib nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -1,285 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
|
@ -1,575 +0,0 @@
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
|
@ -1,164 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
|
@ -1,344 +0,0 @@
|
||||||
/*
|
|
||||||
* 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
|
|
|
@ -1,135 +0,0 @@
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
|
@ -1,213 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
|
@ -1,428 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
|
@ -1,294 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
|
@ -1,124 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
*/
|
|
||||||
/*******************************************************************************
|
|
||||||
* arg_rem: Implements the rem command-line option
|
|
||||||
*
|
|
||||||
* This file is part of the argtable3 library.
|
|
||||||
*
|
|
||||||
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
|
|
||||||
* <sheitmann@users.sourceforge.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of STEWART HEITMANN nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
|
|
||||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
#include "argtable3.h"
|
|
||||||
|
|
||||||
#ifndef ARG_AMALGAMATION
|
|
||||||
#include "argtable3_private.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
struct arg_rem* arg_rem(const char* datatype, const char* glossary) {
|
|
||||||
struct arg_rem* result = (struct arg_rem*)xmalloc(sizeof(struct arg_rem));
|
|
||||||
|
|
||||||
result->hdr.flag = 0;
|
|
||||||
result->hdr.shortopts = NULL;
|
|
||||||
result->hdr.longopts = NULL;
|
|
||||||
result->hdr.datatype = datatype;
|
|
||||||
result->hdr.glossary = glossary;
|
|
||||||
result->hdr.mincount = 1;
|
|
||||||
result->hdr.maxcount = 1;
|
|
||||||
result->hdr.parent = result;
|
|
||||||
result->hdr.resetfn = NULL;
|
|
||||||
result->hdr.scanfn = NULL;
|
|
||||||
result->hdr.checkfn = NULL;
|
|
||||||
result->hdr.errorfn = NULL;
|
|
||||||
|
|
||||||
ARG_TRACE(("arg_rem() returns %p\n", result));
|
|
||||||
return result;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,151 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
|
@ -1,183 +0,0 @@
|
||||||
/*
|
|
||||||
* 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,279 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 1998-2001,2003-2011,2013 Stewart Heitmann
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
*/
|
|
||||||
/*******************************************************************************
|
|
||||||
* argtable3: Declares the main interfaces of the library
|
|
||||||
*
|
|
||||||
* This file is part of the argtable3 library.
|
|
||||||
*
|
|
||||||
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
|
|
||||||
* <sheitmann@users.sourceforge.net>
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of STEWART HEITMANN nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
|
|
||||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
#ifndef ARGTABLE3
|
|
||||||
#define ARGTABLE3
|
|
||||||
|
|
||||||
#include <stdio.h> /* FILE */
|
|
||||||
#include <time.h> /* struct tm */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ARG_REX_ICASE 1
|
|
||||||
#define ARG_DSTR_SIZE 200
|
|
||||||
#define ARG_CMD_NAME_LEN 100
|
|
||||||
#define ARG_CMD_DESCRIPTION_LEN 256
|
|
||||||
|
|
||||||
#ifndef ARG_REPLACE_GETOPT
|
|
||||||
#define ARG_REPLACE_GETOPT 0 /* ESP-IDF-specific: use newlib-provided getopt instead of the embedded one */
|
|
||||||
#endif /* ARG_REPLACE_GETOPT */
|
|
||||||
|
|
||||||
/* bit masks for arg_hdr.flag */
|
|
||||||
enum { ARG_TERMINATOR = 0x1, ARG_HASVALUE = 0x2, ARG_HASOPTVALUE = 0x4 };
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#if defined(argtable3_EXPORTS)
|
|
||||||
#define ARG_EXTERN __declspec(dllexport)
|
|
||||||
#elif defined(argtable3_IMPORTS)
|
|
||||||
#define ARG_EXTERN __declspec(dllimport)
|
|
||||||
#else
|
|
||||||
#define ARG_EXTERN
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define ARG_EXTERN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct _internal_arg_dstr* arg_dstr_t;
|
|
||||||
typedef void* arg_cmd_itr_t;
|
|
||||||
|
|
||||||
typedef void(arg_resetfn)(void* parent);
|
|
||||||
typedef int(arg_scanfn)(void* parent, const char* argval);
|
|
||||||
typedef int(arg_checkfn)(void* parent);
|
|
||||||
typedef void(arg_errorfn)(void* parent, arg_dstr_t ds, int error, const char* argval, const char* progname);
|
|
||||||
typedef void(arg_dstr_freefn)(char* buf);
|
|
||||||
typedef int(arg_cmdfn)(int argc, char* argv[], arg_dstr_t res);
|
|
||||||
typedef int(arg_comparefn)(const void* k1, const void* k2);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The arg_hdr struct defines properties that are common to all arg_xxx structs.
|
|
||||||
* The argtable library requires each arg_xxx struct to have an arg_hdr
|
|
||||||
* struct as its first data member.
|
|
||||||
* The argtable library functions then use this data to identify the
|
|
||||||
* properties of the command line option, such as its option tags,
|
|
||||||
* datatype string, and glossary strings, and so on.
|
|
||||||
* Moreover, the arg_hdr struct contains pointers to custom functions that
|
|
||||||
* are provided by each arg_xxx struct which perform the tasks of parsing
|
|
||||||
* that particular arg_xxx arguments, performing post-parse checks, and
|
|
||||||
* reporting errors.
|
|
||||||
* These functions are private to the individual arg_xxx source code
|
|
||||||
* and are the pointer to them are initiliased by that arg_xxx struct's
|
|
||||||
* constructor function. The user could alter them after construction
|
|
||||||
* if desired, but the original intention is for them to be set by the
|
|
||||||
* constructor and left unaltered.
|
|
||||||
*/
|
|
||||||
typedef struct arg_hdr {
|
|
||||||
char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */
|
|
||||||
const char* shortopts; /* String defining the short options */
|
|
||||||
const char* longopts; /* String defiing the long options */
|
|
||||||
const char* datatype; /* Description of the argument data type */
|
|
||||||
const char* glossary; /* Description of the option as shown by arg_print_glossary function */
|
|
||||||
int mincount; /* Minimum number of occurences of this option accepted */
|
|
||||||
int maxcount; /* Maximum number of occurences if this option accepted */
|
|
||||||
void* parent; /* Pointer to parent arg_xxx struct */
|
|
||||||
arg_resetfn* resetfn; /* Pointer to parent arg_xxx reset function */
|
|
||||||
arg_scanfn* scanfn; /* Pointer to parent arg_xxx scan function */
|
|
||||||
arg_checkfn* checkfn; /* Pointer to parent arg_xxx check function */
|
|
||||||
arg_errorfn* errorfn; /* Pointer to parent arg_xxx error function */
|
|
||||||
void* priv; /* Pointer to private header data for use by arg_xxx functions */
|
|
||||||
} arg_hdr_t;
|
|
||||||
|
|
||||||
typedef struct arg_rem {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
} arg_rem_t;
|
|
||||||
|
|
||||||
typedef struct arg_lit {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
} arg_lit_t;
|
|
||||||
|
|
||||||
typedef struct arg_int {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
int* ival; /* Array of parsed argument values */
|
|
||||||
} arg_int_t;
|
|
||||||
|
|
||||||
typedef struct arg_dbl {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
double* dval; /* Array of parsed argument values */
|
|
||||||
} arg_dbl_t;
|
|
||||||
|
|
||||||
typedef struct arg_str {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
const char** sval; /* Array of parsed argument values */
|
|
||||||
} arg_str_t;
|
|
||||||
|
|
||||||
typedef struct arg_rex {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
const char** sval; /* Array of parsed argument values */
|
|
||||||
} arg_rex_t;
|
|
||||||
|
|
||||||
typedef struct arg_file {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of matching command line args*/
|
|
||||||
const char** filename; /* Array of parsed filenames (eg: /home/foo.bar) */
|
|
||||||
const char** basename; /* Array of parsed basenames (eg: foo.bar) */
|
|
||||||
const char** extension; /* Array of parsed extensions (eg: .bar) */
|
|
||||||
} arg_file_t;
|
|
||||||
|
|
||||||
typedef struct arg_date {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
const char* format; /* strptime format string used to parse the date */
|
|
||||||
int count; /* Number of matching command line args */
|
|
||||||
struct tm* tmval; /* Array of parsed time values */
|
|
||||||
} arg_date_t;
|
|
||||||
|
|
||||||
enum { ARG_ELIMIT = 1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG };
|
|
||||||
typedef struct arg_end {
|
|
||||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
|
||||||
int count; /* Number of errors encountered */
|
|
||||||
int* error; /* Array of error codes */
|
|
||||||
void** parent; /* Array of pointers to offending arg_xxx struct */
|
|
||||||
const char** argval; /* Array of pointers to offending argv[] string */
|
|
||||||
} arg_end_t;
|
|
||||||
|
|
||||||
typedef struct arg_cmd_info {
|
|
||||||
char name[ARG_CMD_NAME_LEN];
|
|
||||||
char description[ARG_CMD_DESCRIPTION_LEN];
|
|
||||||
arg_cmdfn* proc;
|
|
||||||
} arg_cmd_info_t;
|
|
||||||
|
|
||||||
/**** arg_xxx constructor functions *********************************/
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_rem* arg_rem(const char* datatype, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_int* arg_intn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_rex* arg_rex0(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_rex* arg_rex1(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_rex* arg_rexn(const char* shortopts,
|
|
||||||
const char* longopts,
|
|
||||||
const char* pattern,
|
|
||||||
const char* datatype,
|
|
||||||
int mincount,
|
|
||||||
int maxcount,
|
|
||||||
int flags,
|
|
||||||
const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary);
|
|
||||||
ARG_EXTERN struct arg_date* arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary);
|
|
||||||
|
|
||||||
ARG_EXTERN struct arg_end* arg_end(int maxcount);
|
|
||||||
|
|
||||||
#define ARG_DSTR_STATIC ((arg_dstr_freefn*)0)
|
|
||||||
#define ARG_DSTR_VOLATILE ((arg_dstr_freefn*)1)
|
|
||||||
#define ARG_DSTR_DYNAMIC ((arg_dstr_freefn*)3)
|
|
||||||
|
|
||||||
/**** other functions *******************************************/
|
|
||||||
ARG_EXTERN int arg_nullcheck(void** argtable);
|
|
||||||
ARG_EXTERN int arg_parse(int argc, char** argv, void** argtable);
|
|
||||||
ARG_EXTERN void arg_print_option(FILE* fp, const char* shortopts, const char* longopts, const char* datatype, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_syntax(FILE* fp, void** argtable, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_syntaxv(FILE* fp, void** argtable, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_glossary(FILE* fp, void** argtable, const char* format);
|
|
||||||
ARG_EXTERN void arg_print_glossary_gnu(FILE* fp, void** argtable);
|
|
||||||
ARG_EXTERN void arg_print_formatted(FILE *fp, const unsigned lmargin, const unsigned rmargin, const char *text);
|
|
||||||
ARG_EXTERN void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname);
|
|
||||||
ARG_EXTERN void arg_print_option_ds(arg_dstr_t ds, const char* shortopts, const char* longopts, const char* datatype, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_syntax_ds(arg_dstr_t ds, void** argtable, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_syntaxv_ds(arg_dstr_t ds, void** argtable, const char* suffix);
|
|
||||||
ARG_EXTERN void arg_print_glossary_ds(arg_dstr_t ds, void** argtable, const char* format);
|
|
||||||
ARG_EXTERN void arg_print_glossary_gnu_ds(arg_dstr_t ds, void** argtable);
|
|
||||||
ARG_EXTERN void arg_print_errors_ds(arg_dstr_t ds, struct arg_end* end, const char* progname);
|
|
||||||
ARG_EXTERN void arg_freetable(void** argtable, size_t n);
|
|
||||||
|
|
||||||
ARG_EXTERN arg_dstr_t arg_dstr_create(void);
|
|
||||||
ARG_EXTERN void arg_dstr_destroy(arg_dstr_t ds);
|
|
||||||
ARG_EXTERN void arg_dstr_reset(arg_dstr_t ds);
|
|
||||||
ARG_EXTERN void arg_dstr_free(arg_dstr_t ds);
|
|
||||||
ARG_EXTERN void arg_dstr_set(arg_dstr_t ds, char* str, arg_dstr_freefn* free_proc);
|
|
||||||
ARG_EXTERN void arg_dstr_cat(arg_dstr_t ds, const char* str);
|
|
||||||
ARG_EXTERN void arg_dstr_catc(arg_dstr_t ds, char c);
|
|
||||||
ARG_EXTERN void arg_dstr_catf(arg_dstr_t ds, const char* fmt, ...);
|
|
||||||
ARG_EXTERN char* arg_dstr_cstr(arg_dstr_t ds);
|
|
||||||
|
|
||||||
ARG_EXTERN void arg_cmd_init(void);
|
|
||||||
ARG_EXTERN void arg_cmd_uninit(void);
|
|
||||||
ARG_EXTERN void arg_cmd_register(const char* name, arg_cmdfn* proc, const char* description);
|
|
||||||
ARG_EXTERN void arg_cmd_unregister(const char* name);
|
|
||||||
ARG_EXTERN int arg_cmd_dispatch(const char* name, int argc, char* argv[], arg_dstr_t res);
|
|
||||||
ARG_EXTERN unsigned int arg_cmd_count(void);
|
|
||||||
ARG_EXTERN arg_cmd_info_t* arg_cmd_info(const char* name);
|
|
||||||
ARG_EXTERN arg_cmd_itr_t arg_cmd_itr_create(void);
|
|
||||||
ARG_EXTERN void arg_cmd_itr_destroy(arg_cmd_itr_t itr);
|
|
||||||
ARG_EXTERN int arg_cmd_itr_advance(arg_cmd_itr_t itr);
|
|
||||||
ARG_EXTERN char* arg_cmd_itr_key(arg_cmd_itr_t itr);
|
|
||||||
ARG_EXTERN arg_cmd_info_t* arg_cmd_itr_value(arg_cmd_itr_t itr);
|
|
||||||
ARG_EXTERN int arg_cmd_itr_search(arg_cmd_itr_t itr, void* k);
|
|
||||||
ARG_EXTERN void arg_mgsort(void* data, int size, int esize, int i, int k, arg_comparefn* comparefn);
|
|
||||||
ARG_EXTERN void arg_make_get_help_msg(arg_dstr_t res);
|
|
||||||
ARG_EXTERN void arg_make_help_msg(arg_dstr_t ds, char* cmd_name, void** argtable);
|
|
||||||
ARG_EXTERN void arg_make_syntax_err_msg(arg_dstr_t ds, void** argtable, struct arg_end* end);
|
|
||||||
ARG_EXTERN int arg_make_syntax_err_help_msg(arg_dstr_t ds, char* name, int help, int nerrors, void** argtable, struct arg_end* end, int* exitcode);
|
|
||||||
ARG_EXTERN void arg_set_module_name(const char* name);
|
|
||||||
ARG_EXTERN void arg_set_module_version(int major, int minor, int patch, const char* tag);
|
|
||||||
|
|
||||||
/**** deprecated functions, for back-compatibility only ********/
|
|
||||||
ARG_EXTERN void arg_free(void** argtable);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
|
@ -1,245 +0,0 @@
|
||||||
/*
|
|
||||||
* 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,302 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include "esp_heap_caps.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
#include "esp_console.h"
|
|
||||||
#include "esp_system.h"
|
|
||||||
#include "linenoise/linenoise.h"
|
|
||||||
#include "argtable3/argtable3.h"
|
|
||||||
#include "sys/queue.h"
|
|
||||||
|
|
||||||
#define ANSI_COLOR_DEFAULT 39 /** Default foreground color */
|
|
||||||
|
|
||||||
typedef struct cmd_item_ {
|
|
||||||
/**
|
|
||||||
* Command name (statically allocated by application)
|
|
||||||
*/
|
|
||||||
const char *command;
|
|
||||||
/**
|
|
||||||
* Help text (statically allocated by application), may be NULL.
|
|
||||||
*/
|
|
||||||
const char *help;
|
|
||||||
/**
|
|
||||||
* Hint text, usually lists possible arguments, dynamically allocated.
|
|
||||||
* May be NULL.
|
|
||||||
*/
|
|
||||||
char *hint;
|
|
||||||
esp_console_cmd_func_t func; //!< pointer to the command handler
|
|
||||||
void *argtable; //!< optional pointer to arg table
|
|
||||||
SLIST_ENTRY(cmd_item_) next; //!< next command in the list
|
|
||||||
} cmd_item_t;
|
|
||||||
|
|
||||||
/** linked list of command structures */
|
|
||||||
static SLIST_HEAD(cmd_list_, cmd_item_) s_cmd_list;
|
|
||||||
|
|
||||||
/** run-time configuration options */
|
|
||||||
static esp_console_config_t s_config = {
|
|
||||||
.heap_alloc_caps = MALLOC_CAP_DEFAULT
|
|
||||||
};
|
|
||||||
|
|
||||||
/** temporary buffer used for command line parsing */
|
|
||||||
static char *s_tmp_line_buf;
|
|
||||||
|
|
||||||
static const cmd_item_t *find_command_by_name(const char *name);
|
|
||||||
|
|
||||||
esp_err_t esp_console_init(const esp_console_config_t *config)
|
|
||||||
{
|
|
||||||
if (!config) {
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
if (s_tmp_line_buf) {
|
|
||||||
return ESP_ERR_INVALID_STATE;
|
|
||||||
}
|
|
||||||
memcpy(&s_config, config, sizeof(s_config));
|
|
||||||
if (s_config.hint_color == 0) {
|
|
||||||
s_config.hint_color = ANSI_COLOR_DEFAULT;
|
|
||||||
}
|
|
||||||
if (s_config.heap_alloc_caps == 0) {
|
|
||||||
s_config.heap_alloc_caps = MALLOC_CAP_DEFAULT;
|
|
||||||
}
|
|
||||||
s_tmp_line_buf = heap_caps_calloc(1, config->max_cmdline_length, s_config.heap_alloc_caps);
|
|
||||||
if (s_tmp_line_buf == NULL) {
|
|
||||||
return ESP_ERR_NO_MEM;
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t esp_console_deinit(void)
|
|
||||||
{
|
|
||||||
if (!s_tmp_line_buf) {
|
|
||||||
return ESP_ERR_INVALID_STATE;
|
|
||||||
}
|
|
||||||
free(s_tmp_line_buf);
|
|
||||||
s_tmp_line_buf = NULL;
|
|
||||||
cmd_item_t *it, *tmp;
|
|
||||||
SLIST_FOREACH_SAFE(it, &s_cmd_list, next, tmp) {
|
|
||||||
SLIST_REMOVE(&s_cmd_list, it, cmd_item_, next);
|
|
||||||
free(it->hint);
|
|
||||||
free(it);
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd)
|
|
||||||
{
|
|
||||||
cmd_item_t *item = NULL;
|
|
||||||
if (!cmd || cmd->command == NULL) {
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
if (strchr(cmd->command, ' ') != NULL) {
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
item = (cmd_item_t *)find_command_by_name(cmd->command);
|
|
||||||
if (!item) {
|
|
||||||
// not registered before
|
|
||||||
item = heap_caps_calloc(1, sizeof(*item), s_config.heap_alloc_caps);
|
|
||||||
if (item == NULL) {
|
|
||||||
return ESP_ERR_NO_MEM;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// remove from list and free the old hint, because we will alloc new hint for the command
|
|
||||||
SLIST_REMOVE(&s_cmd_list, item, cmd_item_, next);
|
|
||||||
free(item->hint);
|
|
||||||
}
|
|
||||||
item->command = cmd->command;
|
|
||||||
item->help = cmd->help;
|
|
||||||
if (cmd->hint) {
|
|
||||||
/* Prepend a space before the hint. It separates command name and
|
|
||||||
* the hint. arg_print_syntax below adds this space as well.
|
|
||||||
*/
|
|
||||||
int unused __attribute__((unused));
|
|
||||||
unused = asprintf(&item->hint, " %s", cmd->hint);
|
|
||||||
} else if (cmd->argtable) {
|
|
||||||
/* Generate hint based on cmd->argtable */
|
|
||||||
char *buf = NULL;
|
|
||||||
size_t buf_size = 0;
|
|
||||||
FILE *f = open_memstream(&buf, &buf_size);
|
|
||||||
if (f != NULL) {
|
|
||||||
arg_print_syntax(f, cmd->argtable, NULL);
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
item->hint = buf;
|
|
||||||
}
|
|
||||||
item->argtable = cmd->argtable;
|
|
||||||
item->func = cmd->func;
|
|
||||||
cmd_item_t *last = SLIST_FIRST(&s_cmd_list);
|
|
||||||
if (last == NULL) {
|
|
||||||
SLIST_INSERT_HEAD(&s_cmd_list, item, next);
|
|
||||||
} else {
|
|
||||||
cmd_item_t *it;
|
|
||||||
while ((it = SLIST_NEXT(last, next)) != NULL) {
|
|
||||||
last = it;
|
|
||||||
}
|
|
||||||
SLIST_INSERT_AFTER(last, item, next);
|
|
||||||
}
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void esp_console_get_completion(const char *buf, linenoiseCompletions *lc)
|
|
||||||
{
|
|
||||||
size_t len = strlen(buf);
|
|
||||||
if (len == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cmd_item_t *it;
|
|
||||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
|
||||||
/* Check if command starts with buf */
|
|
||||||
if (strncmp(buf, it->command, len) == 0) {
|
|
||||||
linenoiseAddCompletion(lc, it->command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *esp_console_get_hint(const char *buf, int *color, int *bold)
|
|
||||||
{
|
|
||||||
size_t len = strlen(buf);
|
|
||||||
cmd_item_t *it;
|
|
||||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
|
||||||
if (strlen(it->command) == len &&
|
|
||||||
strncmp(buf, it->command, len) == 0) {
|
|
||||||
*color = s_config.hint_color;
|
|
||||||
*bold = s_config.hint_bold;
|
|
||||||
return it->hint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const cmd_item_t *find_command_by_name(const char *name)
|
|
||||||
{
|
|
||||||
const cmd_item_t *cmd = NULL;
|
|
||||||
cmd_item_t *it;
|
|
||||||
size_t len = strlen(name);
|
|
||||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
|
||||||
if (strlen(it->command) == len &&
|
|
||||||
strcmp(name, it->command) == 0) {
|
|
||||||
cmd = it;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t esp_console_run(const char *cmdline, int *cmd_ret)
|
|
||||||
{
|
|
||||||
if (s_tmp_line_buf == NULL) {
|
|
||||||
return ESP_ERR_INVALID_STATE;
|
|
||||||
}
|
|
||||||
char **argv = (char **) heap_caps_calloc(s_config.max_cmdline_args, sizeof(char *), s_config.heap_alloc_caps);
|
|
||||||
if (argv == NULL) {
|
|
||||||
return ESP_ERR_NO_MEM;
|
|
||||||
}
|
|
||||||
strlcpy(s_tmp_line_buf, cmdline, s_config.max_cmdline_length);
|
|
||||||
|
|
||||||
size_t argc = esp_console_split_argv(s_tmp_line_buf, argv,
|
|
||||||
s_config.max_cmdline_args);
|
|
||||||
if (argc == 0) {
|
|
||||||
free(argv);
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
const cmd_item_t *cmd = find_command_by_name(argv[0]);
|
|
||||||
if (cmd == NULL) {
|
|
||||||
free(argv);
|
|
||||||
return ESP_ERR_NOT_FOUND;
|
|
||||||
}
|
|
||||||
*cmd_ret = (*cmd->func)(argc, argv);
|
|
||||||
free(argv);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
struct arg_str *help_cmd;
|
|
||||||
struct arg_end *end;
|
|
||||||
} help_args;
|
|
||||||
|
|
||||||
static void print_arg_help(cmd_item_t *it)
|
|
||||||
{
|
|
||||||
/* First line: command name and hint
|
|
||||||
* Pad all the hints to the same column
|
|
||||||
*/
|
|
||||||
const char *hint = (it->hint) ? it->hint : "";
|
|
||||||
printf("%-s %s\n", it->command, hint);
|
|
||||||
/* Second line: print help.
|
|
||||||
* Argtable has a nice helper function for this which does line
|
|
||||||
* wrapping.
|
|
||||||
*/
|
|
||||||
printf(" "); // arg_print_formatted does not indent the first line
|
|
||||||
arg_print_formatted(stdout, 2, 78, it->help);
|
|
||||||
/* Finally, print the list of arguments */
|
|
||||||
if (it->argtable) {
|
|
||||||
arg_print_glossary(stdout, (void **) it->argtable, " %12s %s\n");
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static int help_command(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int nerrors = arg_parse(argc, argv, (void **) &help_args);
|
|
||||||
|
|
||||||
if (nerrors != 0) {
|
|
||||||
arg_print_errors(stderr, help_args.end, argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd_item_t *it;
|
|
||||||
int ret_value = 1;
|
|
||||||
|
|
||||||
if (help_args.help_cmd->count == 0) {
|
|
||||||
/* Print summary of each command */
|
|
||||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
|
||||||
if (it->help == NULL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
print_arg_help(it);
|
|
||||||
}
|
|
||||||
ret_value = 0;
|
|
||||||
} else {
|
|
||||||
/* Print summary of given command */
|
|
||||||
bool found_command = false;
|
|
||||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
|
||||||
if (it->help == NULL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (strcmp(help_args.help_cmd->sval[0], it->command) == 0) {
|
|
||||||
print_arg_help(it);
|
|
||||||
found_command = true;
|
|
||||||
ret_value = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If given command has not been found, print error message*/
|
|
||||||
if (!found_command) {
|
|
||||||
printf("help: Unrecognized option '%s'. Please use correct command as argument "
|
|
||||||
"or type 'help' only to print help for all commands\n", help_args.help_cmd->sval[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t esp_console_register_help_command(void)
|
|
||||||
{
|
|
||||||
help_args.help_cmd = arg_str0(NULL, NULL, "<string>", "Name of command");
|
|
||||||
help_args.end = arg_end(1);
|
|
||||||
|
|
||||||
esp_console_cmd_t command = {
|
|
||||||
.command = "help",
|
|
||||||
.help = "Print the summary of all registered commands if no arguments "
|
|
||||||
"are given, otherwise print summary of given command.",
|
|
||||||
.func = &help_command,
|
|
||||||
.argtable = &help_args
|
|
||||||
};
|
|
||||||
return esp_console_cmd_register(&command);
|
|
||||||
}
|
|
|
@ -1,396 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include "sdkconfig.h"
|
|
||||||
#include "esp_heap_caps.h"
|
|
||||||
#include "esp_err.h"
|
|
||||||
|
|
||||||
// Forward declaration. Definition in linenoise/linenoise.h.
|
|
||||||
typedef struct linenoiseCompletions linenoiseCompletions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Parameters for console initialization
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
size_t max_cmdline_length; //!< length of command line buffer, in bytes
|
|
||||||
size_t max_cmdline_args; //!< maximum number of command line arguments to parse
|
|
||||||
uint32_t heap_alloc_caps; //!< where to (e.g. MALLOC_CAP_SPIRAM) allocate heap objects such as cmds used by esp_console
|
|
||||||
int hint_color; //!< ASCII color code of hint text
|
|
||||||
int hint_bold; //!< Set to 1 to print hint text in bold
|
|
||||||
} esp_console_config_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Default console configuration value
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define ESP_CONSOLE_CONFIG_DEFAULT() \
|
|
||||||
{ \
|
|
||||||
.max_cmdline_length = 256, \
|
|
||||||
.max_cmdline_args = 32, \
|
|
||||||
.heap_alloc_caps = MALLOC_CAP_DEFAULT, \
|
|
||||||
.hint_color = 39, \
|
|
||||||
.hint_bold = 0 \
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Parameters for console REPL (Read Eval Print Loop)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
uint32_t max_history_len; //!< maximum length for the history
|
|
||||||
const char *history_save_path; //!< file path used to save history commands, set to NULL won't save to file system
|
|
||||||
uint32_t task_stack_size; //!< repl task stack size
|
|
||||||
uint32_t task_priority; //!< repl task priority
|
|
||||||
const char *prompt; //!< prompt (NULL represents default: "esp> ")
|
|
||||||
size_t max_cmdline_length; //!< maximum length of a command line. If 0, default value will be used
|
|
||||||
} esp_console_repl_config_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Default console repl configuration value
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define ESP_CONSOLE_REPL_CONFIG_DEFAULT() \
|
|
||||||
{ \
|
|
||||||
.max_history_len = 32, \
|
|
||||||
.history_save_path = NULL, \
|
|
||||||
.task_stack_size = 4096, \
|
|
||||||
.task_priority = 2, \
|
|
||||||
.prompt = NULL, \
|
|
||||||
.max_cmdline_length = 0, \
|
|
||||||
}
|
|
||||||
|
|
||||||
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
|
||||||
/**
|
|
||||||
* @brief Parameters for console device: UART
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
int channel; //!< UART channel number (count from zero)
|
|
||||||
int baud_rate; //!< Comunication baud rate
|
|
||||||
int tx_gpio_num; //!< GPIO number for TX path, -1 means using default one
|
|
||||||
int rx_gpio_num; //!< GPIO number for RX path, -1 means using default one
|
|
||||||
} esp_console_dev_uart_config_t;
|
|
||||||
|
|
||||||
#if CONFIG_ESP_CONSOLE_UART_CUSTOM
|
|
||||||
#define ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT() \
|
|
||||||
{ \
|
|
||||||
.channel = CONFIG_ESP_CONSOLE_UART_NUM, \
|
|
||||||
.baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, \
|
|
||||||
.tx_gpio_num = CONFIG_ESP_CONSOLE_UART_TX_GPIO, \
|
|
||||||
.rx_gpio_num = CONFIG_ESP_CONSOLE_UART_RX_GPIO, \
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT() \
|
|
||||||
{ \
|
|
||||||
.channel = CONFIG_ESP_CONSOLE_UART_NUM, \
|
|
||||||
.baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, \
|
|
||||||
.tx_gpio_num = -1, \
|
|
||||||
.rx_gpio_num = -1, \
|
|
||||||
}
|
|
||||||
#endif // CONFIG_ESP_CONSOLE_UART_CUSTOM
|
|
||||||
#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
|
||||||
|
|
||||||
#if CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED)
|
|
||||||
/**
|
|
||||||
* @brief Parameters for console device: USB CDC
|
|
||||||
*
|
|
||||||
* @note It's an empty structure for now, reserved for future
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
|
|
||||||
} esp_console_dev_usb_cdc_config_t;
|
|
||||||
|
|
||||||
#define ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT() {}
|
|
||||||
#endif // CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED)
|
|
||||||
|
|
||||||
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED)
|
|
||||||
/**
|
|
||||||
* @brief Parameters for console device: USB-SERIAL-JTAG
|
|
||||||
*
|
|
||||||
* @note It's an empty structure for now, reserved for future
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
|
|
||||||
} esp_console_dev_usb_serial_jtag_config_t;
|
|
||||||
|
|
||||||
#define ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT() {}
|
|
||||||
#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief initialize console module
|
|
||||||
* @param config console configuration
|
|
||||||
* @note Call this once before using other console module features
|
|
||||||
* @return
|
|
||||||
* - ESP_OK on success
|
|
||||||
* - ESP_ERR_NO_MEM if out of memory
|
|
||||||
* - ESP_ERR_INVALID_STATE if already initialized
|
|
||||||
* - ESP_ERR_INVALID_ARG if the configuration is invalid
|
|
||||||
*/
|
|
||||||
esp_err_t esp_console_init(const esp_console_config_t *config);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief de-initialize console module
|
|
||||||
* @note Call this once when done using console module functions
|
|
||||||
* @return
|
|
||||||
* - ESP_OK on success
|
|
||||||
* - ESP_ERR_INVALID_STATE if not initialized yet
|
|
||||||
*/
|
|
||||||
esp_err_t esp_console_deinit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Console command main function
|
|
||||||
* @param argc number of arguments
|
|
||||||
* @param argv array with argc entries, each pointing to a zero-terminated string argument
|
|
||||||
* @return console command return code, 0 indicates "success"
|
|
||||||
*/
|
|
||||||
typedef int (*esp_console_cmd_func_t)(int argc, char **argv);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Console command description
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
/**
|
|
||||||
* Command name. Must not be NULL, must not contain spaces.
|
|
||||||
* The pointer must be valid until the call to esp_console_deinit.
|
|
||||||
*/
|
|
||||||
const char *command;
|
|
||||||
/**
|
|
||||||
* Help text for the command, shown by help command.
|
|
||||||
* If set, the pointer must be valid until the call to esp_console_deinit.
|
|
||||||
* If not set, the command will not be listed in 'help' output.
|
|
||||||
*/
|
|
||||||
const char *help;
|
|
||||||
/**
|
|
||||||
* Hint text, usually lists possible arguments.
|
|
||||||
* If set to NULL, and 'argtable' field is non-NULL, hint will be generated
|
|
||||||
* automatically
|
|
||||||
*/
|
|
||||||
const char *hint;
|
|
||||||
/**
|
|
||||||
* Pointer to a function which implements the command.
|
|
||||||
*/
|
|
||||||
esp_console_cmd_func_t func;
|
|
||||||
/**
|
|
||||||
* Array or structure of pointers to arg_xxx structures, may be NULL.
|
|
||||||
* Used to generate hint text if 'hint' is set to NULL.
|
|
||||||
* Array/structure which this field points to must end with an arg_end.
|
|
||||||
* Only used for the duration of esp_console_cmd_register call.
|
|
||||||
*/
|
|
||||||
void *argtable;
|
|
||||||
} esp_console_cmd_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Register console command
|
|
||||||
* @param cmd pointer to the command description; can point to a temporary value
|
|
||||||
* @return
|
|
||||||
* - ESP_OK on success
|
|
||||||
* - ESP_ERR_NO_MEM if out of memory
|
|
||||||
* - ESP_ERR_INVALID_ARG if command description includes invalid arguments
|
|
||||||
*/
|
|
||||||
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Run command line
|
|
||||||
* @param cmdline command line (command name followed by a number of arguments)
|
|
||||||
* @param[out] cmd_ret return code from the command (set if command was run)
|
|
||||||
* @return
|
|
||||||
* - ESP_OK, if command was run
|
|
||||||
* - ESP_ERR_INVALID_ARG, if the command line is empty, or only contained
|
|
||||||
* whitespace
|
|
||||||
* - ESP_ERR_NOT_FOUND, if command with given name wasn't registered
|
|
||||||
* - ESP_ERR_INVALID_STATE, if esp_console_init wasn't called
|
|
||||||
*/
|
|
||||||
esp_err_t esp_console_run(const char *cmdline, int *cmd_ret);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Split command line into arguments in place
|
|
||||||
* @verbatim
|
|
||||||
* - This function finds whitespace-separated arguments in the given input line.
|
|
||||||
*
|
|
||||||
* 'abc def 1 20 .3' -> [ 'abc', 'def', '1', '20', '.3' ]
|
|
||||||
*
|
|
||||||
* - Argument which include spaces may be surrounded with quotes. In this case
|
|
||||||
* spaces are preserved and quotes are stripped.
|
|
||||||
*
|
|
||||||
* 'abc "123 456" def' -> [ 'abc', '123 456', 'def' ]
|
|
||||||
*
|
|
||||||
* - Escape sequences may be used to produce backslash, double quote, and space:
|
|
||||||
*
|
|
||||||
* 'a\ b\\c\"' -> [ 'a b\c"' ]
|
|
||||||
* @endverbatim
|
|
||||||
* @note Pointers to at most argv_size - 1 arguments are returned in argv array.
|
|
||||||
* The pointer after the last one (i.e. argv[argc]) is set to NULL.
|
|
||||||
*
|
|
||||||
* @param line pointer to buffer to parse; it is modified in place
|
|
||||||
* @param argv array where the pointers to arguments are written
|
|
||||||
* @param argv_size number of elements in argv_array (max. number of arguments)
|
|
||||||
* @return number of arguments found (argc)
|
|
||||||
*/
|
|
||||||
size_t esp_console_split_argv(char *line, char **argv, size_t argv_size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Callback which provides command completion for linenoise library
|
|
||||||
*
|
|
||||||
* When using linenoise for line editing, command completion support
|
|
||||||
* can be enabled like this:
|
|
||||||
*
|
|
||||||
* linenoiseSetCompletionCallback(&esp_console_get_completion);
|
|
||||||
*
|
|
||||||
* @param buf the string typed by the user
|
|
||||||
* @param lc linenoiseCompletions to be filled in
|
|
||||||
*/
|
|
||||||
void esp_console_get_completion(const char *buf, linenoiseCompletions *lc);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Callback which provides command hints for linenoise library
|
|
||||||
*
|
|
||||||
* When using linenoise for line editing, hints support can be enabled as
|
|
||||||
* follows:
|
|
||||||
*
|
|
||||||
* linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);
|
|
||||||
*
|
|
||||||
* The extra cast is needed because linenoiseHintsCallback is defined as
|
|
||||||
* returning a char* instead of const char*.
|
|
||||||
*
|
|
||||||
* @param buf line typed by the user
|
|
||||||
* @param[out] color ANSI color code to be used when displaying the hint
|
|
||||||
* @param[out] bold set to 1 if hint has to be displayed in bold
|
|
||||||
* @return string containing the hint text. This string is persistent and should
|
|
||||||
* not be freed (i.e. linenoiseSetFreeHintsCallback should not be used).
|
|
||||||
*/
|
|
||||||
const char *esp_console_get_hint(const char *buf, int *color, int *bold);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Register a 'help' command
|
|
||||||
*
|
|
||||||
* Default 'help' command prints the list of registered commands along with
|
|
||||||
* hints and help strings if no additional argument is given. If an additional
|
|
||||||
* argument is given, the help command will look for a command with the same
|
|
||||||
* name and only print the hints and help strings of that command.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK on success
|
|
||||||
* - ESP_ERR_INVALID_STATE, if esp_console_init wasn't called
|
|
||||||
*/
|
|
||||||
esp_err_t esp_console_register_help_command(void);
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* Console REPL
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Type defined for console REPL
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
typedef struct esp_console_repl_s esp_console_repl_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Console REPL base structure
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
struct esp_console_repl_s {
|
|
||||||
/**
|
|
||||||
* @brief Delete console REPL environment
|
|
||||||
* @param[in] repl REPL handle returned from esp_console_new_repl_xxx
|
|
||||||
* @return
|
|
||||||
* - ESP_OK on success
|
|
||||||
* - ESP_FAIL on errors
|
|
||||||
*/
|
|
||||||
esp_err_t (*del)(esp_console_repl_t *repl);
|
|
||||||
};
|
|
||||||
|
|
||||||
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
|
||||||
/**
|
|
||||||
* @brief Establish a console REPL environment over UART driver
|
|
||||||
*
|
|
||||||
* @param[in] dev_config UART device configuration
|
|
||||||
* @param[in] repl_config REPL configuration
|
|
||||||
* @param[out] ret_repl return REPL handle after initialization succeed, return NULL otherwise
|
|
||||||
*
|
|
||||||
* @note This is an all-in-one function to establish the environment needed for REPL, includes:
|
|
||||||
* - Install the UART driver on the console UART (8n1, 115200, REF_TICK clock source)
|
|
||||||
* - Configures the stdin/stdout to go through the UART driver
|
|
||||||
* - Initializes linenoise
|
|
||||||
* - Spawn new thread to run REPL in the background
|
|
||||||
*
|
|
||||||
* @attention This function is meant to be used in the examples to make the code more compact.
|
|
||||||
* Applications which use console functionality should be based on
|
|
||||||
* the underlying linenoise and esp_console functions.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK on success
|
|
||||||
* - ESP_FAIL Parameter error
|
|
||||||
*/
|
|
||||||
esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl);
|
|
||||||
#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
|
||||||
|
|
||||||
#if CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED)
|
|
||||||
/**
|
|
||||||
* @brief Establish a console REPL environment over USB CDC
|
|
||||||
*
|
|
||||||
* @param[in] dev_config USB CDC configuration
|
|
||||||
* @param[in] repl_config REPL configuration
|
|
||||||
* @param[out] ret_repl return REPL handle after initialization succeed, return NULL otherwise
|
|
||||||
*
|
|
||||||
* @note This is a all-in-one function to establish the environment needed for REPL, includes:
|
|
||||||
* - Initializes linenoise
|
|
||||||
* - Spawn new thread to run REPL in the background
|
|
||||||
*
|
|
||||||
* @attention This function is meant to be used in the examples to make the code more compact.
|
|
||||||
* Applications which use console functionality should be based on
|
|
||||||
* the underlying linenoise and esp_console functions.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK on success
|
|
||||||
* - ESP_FAIL Parameter error
|
|
||||||
*/
|
|
||||||
esp_err_t esp_console_new_repl_usb_cdc(const esp_console_dev_usb_cdc_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl);
|
|
||||||
#endif // CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED)
|
|
||||||
|
|
||||||
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED)
|
|
||||||
/**
|
|
||||||
* @brief Establish a console REPL (Read-eval-print loop) environment over USB-SERIAL-JTAG
|
|
||||||
*
|
|
||||||
* @param[in] dev_config USB-SERIAL-JTAG configuration
|
|
||||||
* @param[in] repl_config REPL configuration
|
|
||||||
* @param[out] ret_repl return REPL handle after initialization succeed, return NULL otherwise
|
|
||||||
*
|
|
||||||
* @note This is an all-in-one function to establish the environment needed for REPL, includes:
|
|
||||||
* - Initializes linenoise
|
|
||||||
* - Spawn new thread to run REPL in the background
|
|
||||||
*
|
|
||||||
* @attention This function is meant to be used in the examples to make the code more compact.
|
|
||||||
* Applications which use console functionality should be based on
|
|
||||||
* the underlying linenoise and esp_console functions.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
* - ESP_OK on success
|
|
||||||
* - ESP_FAIL Parameter error
|
|
||||||
*/
|
|
||||||
esp_err_t esp_console_new_repl_usb_serial_jtag(const esp_console_dev_usb_serial_jtag_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl);
|
|
||||||
#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Start REPL environment
|
|
||||||
* @param[in] repl REPL handle returned from esp_console_new_repl_xxx
|
|
||||||
* @note Once the REPL gets started, it won't be stopped until the user calls repl->del(repl) to destroy the REPL environment.
|
|
||||||
* @return
|
|
||||||
* - ESP_OK on success
|
|
||||||
* - ESP_ERR_INVALID_STATE, if repl has started already
|
|
||||||
*/
|
|
||||||
esp_err_t esp_console_start_repl(esp_console_repl_t *repl);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,549 +0,0 @@
|
||||||
/*
|
|
||||||
* 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, NULL);
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,126 +0,0 @@
|
||||||
/* linenoise.h -- VERSION 1.0
|
|
||||||
*
|
|
||||||
* Guerrilla line editing library against the idea that a line editing lib
|
|
||||||
* needs to be 20,000 lines of C code.
|
|
||||||
*
|
|
||||||
* See linenoise.c for more information.
|
|
||||||
*
|
|
||||||
* ------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* Copyright (c) 2023-2023, Danila Gornushko <d3g3v3 at gmail dot com>
|
|
||||||
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
|
|
||||||
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
*
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __LINENOISE_H
|
|
||||||
#define __LINENOISE_H
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stddef.h> /* For size_t. */
|
|
||||||
#include <stdbool.h>
|
|
||||||
// for semaphore
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/semphr.h"
|
|
||||||
|
|
||||||
extern char *linenoiseEditMore;
|
|
||||||
extern SemaphoreHandle_t stdout_taken_sem;
|
|
||||||
|
|
||||||
/* The linenoiseState structure represents the state during line editing.
|
|
||||||
* We pass this state to functions implementing specific editing
|
|
||||||
* functionalities. */
|
|
||||||
struct linenoiseState {
|
|
||||||
int in_completion; /* The user pressed TAB and we are now in completion
|
|
||||||
* mode, so input is handled by completeLine(). */
|
|
||||||
size_t completion_idx; /* Index of next completion to propose. */
|
|
||||||
char *buf; /* Edited line buffer. */
|
|
||||||
size_t buflen; /* Edited line buffer size. */
|
|
||||||
const char *prompt; /* Prompt to display. */
|
|
||||||
size_t plen; /* Prompt length. */
|
|
||||||
size_t pos; /* Current cursor position. */
|
|
||||||
size_t oldpos; /* Previous refresh cursor position. */
|
|
||||||
size_t len; /* Current edited line length. */
|
|
||||||
size_t cols; /* Number of columns in terminal. */
|
|
||||||
size_t oldrows; /* Rows used by last refrehsed line (multiline mode) */
|
|
||||||
int history_index; /* The history index we are currently editing. */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct linenoiseCompletions {
|
|
||||||
size_t len;
|
|
||||||
char **cvec;
|
|
||||||
} linenoiseCompletions;
|
|
||||||
|
|
||||||
/* Non blocking API. */
|
|
||||||
int linenoiseEditStart(struct linenoiseState *l);
|
|
||||||
char *linenoiseEditFeed(struct linenoiseState *l);
|
|
||||||
void linenoiseEditStop(struct linenoiseState *l);
|
|
||||||
void linenoiseHide(struct linenoiseState *l);
|
|
||||||
void linenoiseShow(struct linenoiseState *l);
|
|
||||||
|
|
||||||
/* Blocking API. */
|
|
||||||
char *linenoise(const char *prompt, struct linenoiseState **ls_to_pass);
|
|
||||||
void linenoiseFree(void *ptr);
|
|
||||||
|
|
||||||
/* Completion API. */
|
|
||||||
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
|
|
||||||
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
|
|
||||||
typedef void(linenoiseFreeHintsCallback)(void *);
|
|
||||||
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
|
|
||||||
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
|
|
||||||
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
|
|
||||||
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
|
||||||
|
|
||||||
/* History API. */
|
|
||||||
int linenoiseHistoryAdd(const char *line);
|
|
||||||
int linenoiseHistorySetMaxLen(int len);
|
|
||||||
int linenoiseHistorySave(const char *filename);
|
|
||||||
int linenoiseHistoryLoad(const char *filename);
|
|
||||||
void linenoiseHistoryFree();
|
|
||||||
|
|
||||||
|
|
||||||
/* Other utilities. */
|
|
||||||
void linenoiseClearScreen(void);
|
|
||||||
void linenoiseSetMultiLine(int ml);
|
|
||||||
void linenoiseSetDumbMode(int set);
|
|
||||||
bool linenoiseIsDumbMode(void);
|
|
||||||
int linenoiseProbe();
|
|
||||||
void linenoiseMaskModeEnable(void);
|
|
||||||
void linenoiseMaskModeDisable(void);
|
|
||||||
int linenoiseSetMaxLineLen(size_t len);
|
|
||||||
void flushWrite(void);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __LINENOISE_H */
|
|
|
@ -1,112 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define SS_FLAG_ESCAPE 0x8
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
/* parsing the space between arguments */
|
|
||||||
SS_SPACE = 0x0,
|
|
||||||
/* parsing an argument which isn't quoted */
|
|
||||||
SS_ARG = 0x1,
|
|
||||||
/* parsing a quoted argument */
|
|
||||||
SS_QUOTED_ARG = 0x2,
|
|
||||||
/* parsing an escape sequence within unquoted argument */
|
|
||||||
SS_ARG_ESCAPED = SS_ARG | SS_FLAG_ESCAPE,
|
|
||||||
/* parsing an escape sequence within a quoted argument */
|
|
||||||
SS_QUOTED_ARG_ESCAPED = SS_QUOTED_ARG | SS_FLAG_ESCAPE,
|
|
||||||
} split_state_t;
|
|
||||||
|
|
||||||
/* helper macro, called when done with an argument */
|
|
||||||
#define END_ARG() do { \
|
|
||||||
char_out = 0; \
|
|
||||||
argv[argc++] = next_arg_start; \
|
|
||||||
state = SS_SPACE; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
size_t esp_console_split_argv(char *line, char **argv, size_t argv_size)
|
|
||||||
{
|
|
||||||
const int QUOTE = '"';
|
|
||||||
const int ESCAPE = '\\';
|
|
||||||
const int SPACE = ' ';
|
|
||||||
split_state_t state = SS_SPACE;
|
|
||||||
size_t argc = 0;
|
|
||||||
char *next_arg_start = line;
|
|
||||||
char *out_ptr = line;
|
|
||||||
for (char *in_ptr = line; argc < argv_size - 1; ++in_ptr) {
|
|
||||||
int char_in = (unsigned char) *in_ptr;
|
|
||||||
if (char_in == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int char_out = -1;
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case SS_SPACE:
|
|
||||||
if (char_in == SPACE) {
|
|
||||||
/* skip space */
|
|
||||||
} else if (char_in == QUOTE) {
|
|
||||||
next_arg_start = out_ptr;
|
|
||||||
state = SS_QUOTED_ARG;
|
|
||||||
} else if (char_in == ESCAPE) {
|
|
||||||
next_arg_start = out_ptr;
|
|
||||||
state = SS_ARG_ESCAPED;
|
|
||||||
} else {
|
|
||||||
next_arg_start = out_ptr;
|
|
||||||
state = SS_ARG;
|
|
||||||
char_out = char_in;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SS_QUOTED_ARG:
|
|
||||||
if (char_in == QUOTE) {
|
|
||||||
END_ARG();
|
|
||||||
} else if (char_in == ESCAPE) {
|
|
||||||
state = SS_QUOTED_ARG_ESCAPED;
|
|
||||||
} else {
|
|
||||||
char_out = char_in;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SS_ARG_ESCAPED:
|
|
||||||
case SS_QUOTED_ARG_ESCAPED:
|
|
||||||
if (char_in == ESCAPE || char_in == QUOTE || char_in == SPACE) {
|
|
||||||
char_out = char_in;
|
|
||||||
} else {
|
|
||||||
/* unrecognized escape character, skip */
|
|
||||||
}
|
|
||||||
state = (split_state_t) (state & (~SS_FLAG_ESCAPE));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SS_ARG:
|
|
||||||
if (char_in == SPACE) {
|
|
||||||
END_ARG();
|
|
||||||
} else if (char_in == ESCAPE) {
|
|
||||||
state = SS_ARG_ESCAPED;
|
|
||||||
} else {
|
|
||||||
char_out = char_in;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* need to output anything? */
|
|
||||||
if (char_out >= 0) {
|
|
||||||
*out_ptr = char_out;
|
|
||||||
++out_ptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* make sure the final argument is terminated */
|
|
||||||
*out_ptr = 0;
|
|
||||||
/* finalize the last argument */
|
|
||||||
if (state != SS_SPACE && argc < argv_size - 1) {
|
|
||||||
argv[argc++] = next_arg_start;
|
|
||||||
}
|
|
||||||
/* add a NULL at the end of argv */
|
|
||||||
argv[argc] = NULL;
|
|
||||||
|
|
||||||
return argc;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
# 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)
|
|
|
@ -1,2 +0,0 @@
|
||||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
|
||||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
|
|
@ -1,4 +0,0 @@
|
||||||
idf_component_register(SRCS "test_app_main.c" "test_console.c"
|
|
||||||
INCLUDE_DIRS "."
|
|
||||||
PRIV_REQUIRES unity console
|
|
||||||
WHOLE_ARCHIVE)
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
* 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();
|
|
||||||
}
|
|
|
@ -1,104 +0,0 @@
|
||||||
/*
|
|
||||||
* 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());
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
# 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)
|
|
|
@ -1,3 +0,0 @@
|
||||||
# This "default" configuration is appended to all other configurations
|
|
||||||
# The contents of "sdkconfig.debug_helpers" is also appended to all other configurations (see CMakeLists.txt)
|
|
||||||
CONFIG_ESP_TASK_WDT_INIT=n
|
|
Loading…
Reference in a new issue