forked from test34/can_wizard
temporary use old version of console component with old linenoise
This commit is contained in:
parent
3b9c168a3b
commit
e17520f632
34 changed files with 4729 additions and 6455 deletions
|
@ -1,24 +0,0 @@
|
|||
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 "."
|
||||
REQUIRES vfs
|
||||
PRIV_REQUIRES driver)
|
|
@ -24,144 +24,3 @@ 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,11 +1,4 @@
|
|||
/*
|
||||
* 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
|
||||
|
@ -38,240 +31,273 @@
|
|||
#ifndef ARGTABLE3
|
||||
#define ARGTABLE3
|
||||
|
||||
#include <stdio.h> /* FILE */
|
||||
#include <time.h> /* struct tm */
|
||||
#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 };
|
||||
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 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, FILE *fp, int error, const char *argval, const char *progname);
|
||||
|
||||
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;
|
||||
* 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.
|
||||
*/
|
||||
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 */
|
||||
};
|
||||
|
||||
typedef struct arg_rem {
|
||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||
} arg_rem_t;
|
||||
struct arg_rem
|
||||
{
|
||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||
};
|
||||
|
||||
typedef struct arg_lit {
|
||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||
int count; /* Number of matching command line args */
|
||||
} arg_lit_t;
|
||||
struct arg_lit
|
||||
{
|
||||
struct arg_hdr hdr; /* The mandatory argtable header struct */
|
||||
int count; /* Number of matching command line args */
|
||||
};
|
||||
|
||||
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;
|
||||
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 */
|
||||
};
|
||||
|
||||
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;
|
||||
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 */
|
||||
};
|
||||
|
||||
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;
|
||||
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 */
|
||||
};
|
||||
|
||||
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;
|
||||
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 */
|
||||
};
|
||||
|
||||
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;
|
||||
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) */
|
||||
};
|
||||
|
||||
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;
|
||||
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 */
|
||||
};
|
||||
|
||||
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;
|
||||
enum {ARG_ELIMIT=1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG};
|
||||
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 */
|
||||
};
|
||||
|
||||
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);
|
||||
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);
|
||||
struct arg_lit* arg_lit0(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* glossary);
|
||||
struct arg_lit* arg_lit1(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char *glossary);
|
||||
struct arg_lit* arg_litn(const char* shortopts,
|
||||
const char* longopts,
|
||||
int mincount,
|
||||
int maxcount,
|
||||
const char *glossary);
|
||||
|
||||
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);
|
||||
struct arg_key* arg_key0(const char* keyword,
|
||||
int flags,
|
||||
const char* glossary);
|
||||
struct arg_key* arg_key1(const char* keyword,
|
||||
int flags,
|
||||
const char* glossary);
|
||||
struct arg_key* arg_keyn(const char* keyword,
|
||||
int flags,
|
||||
int mincount,
|
||||
int maxcount,
|
||||
const char* glossary);
|
||||
|
||||
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);
|
||||
struct arg_int* arg_int0(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* datatype,
|
||||
const char* glossary);
|
||||
struct arg_int* arg_int1(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* datatype,
|
||||
const char *glossary);
|
||||
struct arg_int* arg_intn(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char *datatype,
|
||||
int mincount,
|
||||
int maxcount,
|
||||
const char *glossary);
|
||||
|
||||
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);
|
||||
struct arg_dbl* arg_dbl0(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* datatype,
|
||||
const char* glossary);
|
||||
struct arg_dbl* arg_dbl1(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* datatype,
|
||||
const char *glossary);
|
||||
struct arg_dbl* arg_dbln(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char *datatype,
|
||||
int mincount,
|
||||
int maxcount,
|
||||
const char *glossary);
|
||||
|
||||
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);
|
||||
struct arg_str* arg_str0(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* datatype,
|
||||
const char* glossary);
|
||||
struct arg_str* arg_str1(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* datatype,
|
||||
const char *glossary);
|
||||
struct arg_str* arg_strn(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* datatype,
|
||||
int mincount,
|
||||
int maxcount,
|
||||
const char *glossary);
|
||||
|
||||
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);
|
||||
struct arg_rex* arg_rex0(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* pattern,
|
||||
const char* datatype,
|
||||
int flags,
|
||||
const char* glossary);
|
||||
struct arg_rex* arg_rex1(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* pattern,
|
||||
const char* datatype,
|
||||
int flags,
|
||||
const char *glossary);
|
||||
struct arg_rex* arg_rexn(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* pattern,
|
||||
const char* datatype,
|
||||
int mincount,
|
||||
int maxcount,
|
||||
int flags,
|
||||
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);
|
||||
struct arg_file* arg_file0(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* datatype,
|
||||
const char* glossary);
|
||||
struct arg_file* arg_file1(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* datatype,
|
||||
const char *glossary);
|
||||
struct arg_file* arg_filen(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* datatype,
|
||||
int mincount,
|
||||
int maxcount,
|
||||
const char *glossary);
|
||||
|
||||
ARG_EXTERN struct arg_end* arg_end(int maxcount);
|
||||
struct arg_date* arg_date0(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* format,
|
||||
const char* datatype,
|
||||
const char* glossary);
|
||||
struct arg_date* arg_date1(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* format,
|
||||
const char* datatype,
|
||||
const char *glossary);
|
||||
struct arg_date* arg_daten(const char* shortopts,
|
||||
const char* longopts,
|
||||
const char* format,
|
||||
const char* datatype,
|
||||
int mincount,
|
||||
int maxcount,
|
||||
const char *glossary);
|
||||
|
||||
struct arg_end* arg_end(int maxerrors);
|
||||
|
||||
#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);
|
||||
int arg_nullcheck(void **argtable);
|
||||
int arg_parse(int argc, char **argv, void **argtable);
|
||||
void arg_print_option(FILE *fp, const char *shortopts, const char *longopts, const char *datatype, const char *suffix);
|
||||
void arg_print_syntax(FILE *fp, void **argtable, const char *suffix);
|
||||
void arg_print_syntaxv(FILE *fp, void **argtable, const char *suffix);
|
||||
void arg_print_glossary(FILE *fp, void **argtable, const char *format);
|
||||
void arg_print_glossary_gnu(FILE *fp, void **argtable);
|
||||
void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname);
|
||||
void arg_freetable(void **argtable, size_t n);
|
||||
|
||||
/**** deprecated functions, for back-compatibility only ********/
|
||||
ARG_EXTERN void arg_free(void** argtable);
|
||||
void arg_free(void **argtable);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -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,8 +1,16 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
@ -10,10 +18,9 @@
|
|||
#include <sys/param.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"
|
||||
#include "rom/queue.h"
|
||||
|
||||
#define ANSI_COLOR_DEFAULT 39 /** Default foreground color */
|
||||
|
||||
|
@ -21,18 +28,18 @@ typedef struct cmd_item_ {
|
|||
/**
|
||||
* Command name (statically allocated by application)
|
||||
*/
|
||||
const char *command;
|
||||
const char* command;
|
||||
/**
|
||||
* Help text (statically allocated by application), may be NULL.
|
||||
*/
|
||||
const char *help;
|
||||
const char* help;
|
||||
/**
|
||||
* Hint text, usually lists possible arguments, dynamically allocated.
|
||||
* May be NULL.
|
||||
*/
|
||||
char *hint;
|
||||
char* hint;
|
||||
esp_console_cmd_func_t func; //!< pointer to the command handler
|
||||
void *argtable; //!< optional pointer to arg table
|
||||
void* argtable; //!< optional pointer to arg table
|
||||
SLIST_ENTRY(cmd_item_) next; //!< next command in the list
|
||||
} cmd_item_t;
|
||||
|
||||
|
@ -43,15 +50,12 @@ static SLIST_HEAD(cmd_list_, cmd_item_) s_cmd_list;
|
|||
static esp_console_config_t s_config;
|
||||
|
||||
/** temporary buffer used for command line parsing */
|
||||
static char *s_tmp_line_buf;
|
||||
static char* s_tmp_line_buf;
|
||||
|
||||
static const cmd_item_t *find_command_by_name(const char *name);
|
||||
static const cmd_item_t* find_command_by_name(const char* name);
|
||||
|
||||
esp_err_t esp_console_init(const esp_console_config_t *config)
|
||||
esp_err_t esp_console_init(const esp_console_config_t* config)
|
||||
{
|
||||
if (!config) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (s_tmp_line_buf) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
@ -66,16 +70,14 @@ esp_err_t esp_console_init(const esp_console_config_t *config)
|
|||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_console_deinit(void)
|
||||
esp_err_t esp_console_deinit()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -84,51 +86,39 @@ esp_err_t esp_console_deinit(void)
|
|||
|
||||
esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd)
|
||||
{
|
||||
cmd_item_t *item = NULL;
|
||||
if (!cmd || cmd->command == NULL) {
|
||||
cmd_item_t *item = (cmd_item_t *) calloc(1, sizeof(*item));
|
||||
if (item == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (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 = calloc(1, sizeof(*item));
|
||||
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);
|
||||
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);
|
||||
}
|
||||
char* buf;
|
||||
size_t buf_size;
|
||||
FILE* f = open_memstream(&buf, &buf_size);
|
||||
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);
|
||||
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;
|
||||
cmd_item_t* it;
|
||||
while ((it = SLIST_NEXT(last, next)) != NULL) {
|
||||
last = it;
|
||||
}
|
||||
|
@ -143,7 +133,7 @@ void esp_console_get_completion(const char *buf, linenoiseCompletions *lc)
|
|||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
cmd_item_t *it;
|
||||
cmd_item_t* it;
|
||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
||||
/* Check if command starts with buf */
|
||||
if (strncmp(buf, it->command, len) == 0) {
|
||||
|
@ -152,10 +142,10 @@ void esp_console_get_completion(const char *buf, linenoiseCompletions *lc)
|
|||
}
|
||||
}
|
||||
|
||||
const char *esp_console_get_hint(const char *buf, int *color, int *bold)
|
||||
const char* esp_console_get_hint(const char *buf, int *color, int *bold)
|
||||
{
|
||||
size_t len = strlen(buf);
|
||||
cmd_item_t *it;
|
||||
int 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) {
|
||||
|
@ -167,14 +157,12 @@ const char *esp_console_get_hint(const char *buf, int *color, int *bold)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static const cmd_item_t *find_command_by_name(const char *name)
|
||||
static const cmd_item_t* find_command_by_name(const char* name)
|
||||
{
|
||||
const cmd_item_t *cmd = NULL;
|
||||
cmd_item_t *it;
|
||||
size_t len = strlen(name);
|
||||
const cmd_item_t* cmd = NULL;
|
||||
cmd_item_t* it;
|
||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
||||
if (strlen(it->command) == len &&
|
||||
strcmp(name, it->command) == 0) {
|
||||
if (strcmp(name, it->command) == 0) {
|
||||
cmd = it;
|
||||
break;
|
||||
}
|
||||
|
@ -182,26 +170,22 @@ static const cmd_item_t *find_command_by_name(const char *name)
|
|||
return cmd;
|
||||
}
|
||||
|
||||
esp_err_t esp_console_run(const char *cmdline, int *cmd_ret)
|
||||
esp_err_t esp_console_run(const char* cmdline, int* cmd_ret)
|
||||
{
|
||||
if (s_tmp_line_buf == NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
char **argv = (char **) calloc(s_config.max_cmdline_args, sizeof(char *));
|
||||
char** argv = (char**) calloc(s_config.max_cmdline_args, sizeof(char*));
|
||||
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]);
|
||||
s_config.max_cmdline_args);
|
||||
|
||||
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);
|
||||
|
@ -209,9 +193,9 @@ esp_err_t esp_console_run(const char *cmdline, int *cmd_ret)
|
|||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int help_command(int argc, char **argv)
|
||||
static int help_command(int argc, char** argv)
|
||||
{
|
||||
cmd_item_t *it;
|
||||
cmd_item_t* it;
|
||||
|
||||
/* Print summary of each command */
|
||||
SLIST_FOREACH(it, &s_cmd_list, next) {
|
||||
|
@ -221,7 +205,7 @@ static int help_command(int argc, char **argv)
|
|||
/* First line: command name and hint
|
||||
* Pad all the hints to the same column
|
||||
*/
|
||||
const char *hint = (it->hint) ? it->hint : "";
|
||||
const char* hint = (it->hint) ? it->hint : "";
|
||||
printf("%-s %s\n", it->command, hint);
|
||||
/* Second line: print help.
|
||||
* Argtable has a nice helper function for this which does line
|
||||
|
@ -231,19 +215,20 @@ static int help_command(int argc, char **argv)
|
|||
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");
|
||||
arg_print_glossary(stdout, (void**) it->argtable, " %12s %s\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
esp_err_t esp_console_register_help_command(void)
|
||||
|
||||
esp_err_t esp_console_register_help_command()
|
||||
{
|
||||
esp_console_cmd_t command = {
|
||||
.command = "help",
|
||||
.help = "Print the list of registered commands",
|
||||
.func = &help_command
|
||||
.command = "help",
|
||||
.help = "Print the list of registered commands",
|
||||
.func = &help_command
|
||||
};
|
||||
return esp_console_cmd_register(&command);
|
||||
}
|
||||
|
|
2
components/console/component.mk
Normal file
2
components/console/component.mk
Normal file
|
@ -0,0 +1,2 @@
|
|||
COMPONENT_ADD_INCLUDEDIRS := .
|
||||
COMPONENT_SRCDIRS := linenoise argtable3 .
|
|
@ -1,16 +1,19 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
// Forward declaration. Definition in linenoise/linenoise.h.
|
||||
|
@ -22,128 +25,30 @@ typedef struct linenoiseCompletions linenoiseCompletions;
|
|||
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
|
||||
int hint_color; //!< ASCII color code of hint text
|
||||
int hint_bold; //!< Set to 1 to print hint text in bold
|
||||
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, \
|
||||
.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
|
||||
* 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);
|
||||
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
|
||||
* 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);
|
||||
esp_err_t esp_console_deinit();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Console command main function
|
||||
|
@ -151,7 +56,7 @@ esp_err_t esp_console_deinit(void);
|
|||
* @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);
|
||||
typedef int (*esp_console_cmd_func_t)(int argc, char** argv);
|
||||
|
||||
/**
|
||||
* @brief Console command description
|
||||
|
@ -161,19 +66,19 @@ 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;
|
||||
const char* command; //!< command name
|
||||
/**
|
||||
* 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;
|
||||
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;
|
||||
const char* hint;
|
||||
/**
|
||||
* Pointer to a function which implements the command.
|
||||
*/
|
||||
|
@ -184,7 +89,7 @@ typedef struct {
|
|||
* 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;
|
||||
void* argtable;
|
||||
} esp_console_cmd_t;
|
||||
|
||||
/**
|
||||
|
@ -193,7 +98,6 @@ typedef struct {
|
|||
* @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);
|
||||
|
||||
|
@ -203,16 +107,14 @@ esp_err_t esp_console_cmd_register(const esp_console_cmd_t *cmd);
|
|||
* @param[out] cmd_ret return code from the command (set if command was run)
|
||||
* @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);
|
||||
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' ]
|
||||
|
@ -225,9 +127,9 @@ esp_err_t esp_console_run(const char *cmdline, int *cmd_ret);
|
|||
* - 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.
|
||||
*
|
||||
* 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
|
||||
|
@ -270,122 +172,10 @@ 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.
|
||||
*
|
||||
* @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
|
||||
esp_err_t esp_console_register_help_command();
|
||||
|
|
|
@ -1,552 +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 non-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;
|
||||
}
|
||||
|
||||
/* Disable buffering on stdin */
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
|
||||
/* 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 non-blocking mode on stdin and stdout */
|
||||
fcntl(fileno(stdout), F_SETFL, 0);
|
||||
fcntl(fileno(stdin), F_SETFL, 0);
|
||||
|
||||
usb_serial_jtag_driver_config_t usb_serial_jtag_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
|
||||
|
||||
/* Install USB-SERIAL-JTAG driver for interrupt-driven reads and writes */
|
||||
ret = usb_serial_jtag_driver_install(&usb_serial_jtag_config);
|
||||
if (ret != ESP_OK) {
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
// initialize console, common part
|
||||
ret = esp_console_common_init(repl_config->max_cmdline_length, &usb_serial_jtag_repl->repl_com);
|
||||
if (ret != ESP_OK) {
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* Tell vfs to use usb-serial-jtag driver */
|
||||
esp_vfs_usb_serial_jtag_use_driver();
|
||||
|
||||
// setup history
|
||||
ret = esp_console_setup_history(repl_config->history_save_path, repl_config->max_history_len, &usb_serial_jtag_repl->repl_com);
|
||||
if (ret != ESP_OK) {
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
// setup prompt
|
||||
esp_console_setup_prompt(repl_config->prompt, &usb_serial_jtag_repl->repl_com);
|
||||
|
||||
/* Fill the structure here as it will be used directly by the created task. */
|
||||
usb_serial_jtag_repl->uart_channel = CONFIG_ESP_CONSOLE_UART_NUM;
|
||||
usb_serial_jtag_repl->repl_com.state = CONSOLE_REPL_STATE_INIT;
|
||||
usb_serial_jtag_repl->repl_com.repl_core.del = esp_console_repl_usb_serial_jtag_delete;
|
||||
|
||||
/* spawn a single thread to run REPL */
|
||||
if (xTaskCreate(esp_console_repl_task, "console_repl", repl_config->task_stack_size,
|
||||
usb_serial_jtag_repl, repl_config->task_priority, &usb_serial_jtag_repl->repl_com.task_hdl) != pdTRUE) {
|
||||
ret = ESP_FAIL;
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
*ret_repl = &usb_serial_jtag_repl->repl_com.repl_core;
|
||||
return ESP_OK;
|
||||
_exit:
|
||||
if (usb_serial_jtag_repl) {
|
||||
esp_console_deinit();
|
||||
free(usb_serial_jtag_repl);
|
||||
}
|
||||
if (ret_repl) {
|
||||
*ret_repl = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||
|
||||
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
||||
esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_console_repl_universal_t *uart_repl = NULL;
|
||||
if (!repl_config | !dev_config | !ret_repl) {
|
||||
ret = ESP_ERR_INVALID_ARG;
|
||||
goto _exit;
|
||||
}
|
||||
// allocate memory for console REPL context
|
||||
uart_repl = calloc(1, sizeof(esp_console_repl_universal_t));
|
||||
if (!uart_repl) {
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* Drain stdout before reconfiguring it */
|
||||
fflush(stdout);
|
||||
fsync(fileno(stdout));
|
||||
|
||||
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
|
||||
esp_vfs_dev_uart_port_set_rx_line_endings(dev_config->channel, ESP_LINE_ENDINGS_CR);
|
||||
/* Move the caret to the beginning of the next line on '\n' */
|
||||
esp_vfs_dev_uart_port_set_tx_line_endings(dev_config->channel, ESP_LINE_ENDINGS_CRLF);
|
||||
|
||||
/* Configure UART. Note that REF_TICK/XTAL is used so that the baud rate remains
|
||||
* correct while APB frequency is changing in light sleep mode.
|
||||
*/
|
||||
#if SOC_UART_SUPPORT_REF_TICK
|
||||
uart_sclk_t clk_source = UART_SCLK_REF_TICK;
|
||||
// REF_TICK clock can't provide a high baudrate
|
||||
if (dev_config->baud_rate > 1 * 1000 * 1000) {
|
||||
clk_source = UART_SCLK_DEFAULT;
|
||||
ESP_LOGW(TAG, "light sleep UART wakeup might not work at the configured baud rate");
|
||||
}
|
||||
#elif SOC_UART_SUPPORT_XTAL_CLK
|
||||
uart_sclk_t clk_source = UART_SCLK_XTAL;
|
||||
#else
|
||||
#error "No UART clock source is aware of DFS"
|
||||
#endif // SOC_UART_SUPPORT_xxx
|
||||
const uart_config_t uart_config = {
|
||||
.baud_rate = dev_config->baud_rate,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.source_clk = clk_source,
|
||||
};
|
||||
|
||||
uart_param_config(dev_config->channel, &uart_config);
|
||||
uart_set_pin(dev_config->channel, dev_config->tx_gpio_num, dev_config->rx_gpio_num, -1, -1);
|
||||
|
||||
/* Install UART driver for interrupt-driven reads and writes */
|
||||
ret = uart_driver_install(dev_config->channel, 256, 0, 0, NULL, 0);
|
||||
if (ret != ESP_OK) {
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* Tell VFS to use UART driver */
|
||||
esp_vfs_dev_uart_use_driver(dev_config->channel);
|
||||
|
||||
// initialize console, common part
|
||||
ret = esp_console_common_init(repl_config->max_cmdline_length, &uart_repl->repl_com);
|
||||
if (ret != ESP_OK) {
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
// setup history
|
||||
ret = esp_console_setup_history(repl_config->history_save_path, repl_config->max_history_len, &uart_repl->repl_com);
|
||||
if (ret != ESP_OK) {
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
// setup prompt
|
||||
esp_console_setup_prompt(repl_config->prompt, &uart_repl->repl_com);
|
||||
|
||||
/* Fill the structure here as it will be used directly by the created task. */
|
||||
uart_repl->uart_channel = dev_config->channel;
|
||||
uart_repl->repl_com.state = CONSOLE_REPL_STATE_INIT;
|
||||
uart_repl->repl_com.repl_core.del = esp_console_repl_uart_delete;
|
||||
|
||||
/* Spawn a single thread to run REPL, we need to pass `uart_repl` to it as
|
||||
* it also requires the uart channel. */
|
||||
if (xTaskCreate(esp_console_repl_task, "console_repl", repl_config->task_stack_size,
|
||||
uart_repl, repl_config->task_priority, &uart_repl->repl_com.task_hdl) != pdTRUE) {
|
||||
ret = ESP_FAIL;
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
*ret_repl = &uart_repl->repl_com.repl_core;
|
||||
return ESP_OK;
|
||||
_exit:
|
||||
if (uart_repl) {
|
||||
esp_console_deinit();
|
||||
uart_driver_delete(dev_config->channel);
|
||||
free(uart_repl);
|
||||
}
|
||||
if (ret_repl) {
|
||||
*ret_repl = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
||||
|
||||
esp_err_t esp_console_start_repl(esp_console_repl_t *repl)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core);
|
||||
// check if already initialized
|
||||
if (repl_com->state != CONSOLE_REPL_STATE_INIT) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
repl_com->state = CONSOLE_REPL_STATE_START;
|
||||
xTaskNotifyGive(repl_com->task_hdl);
|
||||
return ESP_OK;
|
||||
_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t esp_console_setup_prompt(const char *prompt, esp_console_repl_com_t *repl_com)
|
||||
{
|
||||
/* set command line prompt */
|
||||
const char *prompt_temp = "esp>";
|
||||
if (prompt) {
|
||||
prompt_temp = prompt;
|
||||
}
|
||||
snprintf(repl_com->prompt, CONSOLE_PROMPT_MAX_LEN - 1, LOG_COLOR_I "%s " LOG_RESET_COLOR, prompt_temp);
|
||||
|
||||
/* Figure out if the terminal supports escape sequences */
|
||||
int probe_status = linenoiseProbe();
|
||||
if (probe_status) {
|
||||
/* zero indicates success */
|
||||
linenoiseSetDumbMode(1);
|
||||
#if CONFIG_LOG_COLORS
|
||||
/* Since the terminal doesn't support escape sequences,
|
||||
* don't use color codes in the s_prompt.
|
||||
*/
|
||||
snprintf(repl_com->prompt, CONSOLE_PROMPT_MAX_LEN - 1, "%s ", prompt_temp);
|
||||
#endif //CONFIG_LOG_COLORS
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_console_setup_history(const char *history_path, uint32_t max_history_len, esp_console_repl_com_t *repl_com)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
repl_com->history_save_path = history_path;
|
||||
if (history_path) {
|
||||
/* Load command history from filesystem */
|
||||
linenoiseHistoryLoad(history_path);
|
||||
}
|
||||
|
||||
/* Set command history size */
|
||||
if (linenoiseHistorySetMaxLen(max_history_len) != 1) {
|
||||
ESP_LOGE(TAG, "set max history length to %"PRIu32" failed", max_history_len);
|
||||
ret = ESP_FAIL;
|
||||
goto _exit;
|
||||
}
|
||||
return ESP_OK;
|
||||
_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_com_t *repl_com)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
/* Initialize the console */
|
||||
esp_console_config_t console_config = ESP_CONSOLE_CONFIG_DEFAULT();
|
||||
repl_com->max_cmdline_length = console_config.max_cmdline_length;
|
||||
/* Replace the default command line length if passed as a parameter */
|
||||
if (max_cmdline_length != 0) {
|
||||
console_config.max_cmdline_length = max_cmdline_length;
|
||||
repl_com->max_cmdline_length = max_cmdline_length;
|
||||
}
|
||||
|
||||
#if CONFIG_LOG_COLORS
|
||||
console_config.hint_color = atoi(LOG_COLOR_CYAN);
|
||||
#else
|
||||
console_config.hint_color = -1;
|
||||
#endif
|
||||
ret = esp_console_init(&console_config);
|
||||
if (ret != ESP_OK) {
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
ret = esp_console_register_help_command();
|
||||
if (ret != ESP_OK) {
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* Configure linenoise line completion library */
|
||||
/* Enable multiline editing. If not set, long commands will scroll within single line */
|
||||
linenoiseSetMultiLine(1);
|
||||
|
||||
/* Tell linenoise where to get command completions and hints */
|
||||
linenoiseSetCompletionCallback(&esp_console_get_completion);
|
||||
linenoiseSetHintsCallback((linenoiseHintsCallback *)&esp_console_get_hint);
|
||||
|
||||
return ESP_OK;
|
||||
_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
||||
static esp_err_t esp_console_repl_uart_delete(esp_console_repl_t *repl)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core);
|
||||
esp_console_repl_universal_t *uart_repl = __containerof(repl_com, esp_console_repl_universal_t, repl_com);
|
||||
// check if already de-initialized
|
||||
if (repl_com->state == CONSOLE_REPL_STATE_DEINIT) {
|
||||
ESP_LOGE(TAG, "already de-initialized");
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto _exit;
|
||||
}
|
||||
repl_com->state = CONSOLE_REPL_STATE_DEINIT;
|
||||
esp_console_deinit();
|
||||
esp_vfs_dev_uart_use_nonblocking(uart_repl->uart_channel);
|
||||
uart_driver_delete(uart_repl->uart_channel);
|
||||
free(uart_repl);
|
||||
_exit:
|
||||
return ret;
|
||||
}
|
||||
#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
|
||||
|
||||
#if CONFIG_ESP_CONSOLE_USB_CDC
|
||||
static esp_err_t esp_console_repl_usb_cdc_delete(esp_console_repl_t *repl)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core);
|
||||
esp_console_repl_universal_t *cdc_repl = __containerof(repl_com, esp_console_repl_universal_t, repl_com);
|
||||
// check if already de-initialized
|
||||
if (repl_com->state == CONSOLE_REPL_STATE_DEINIT) {
|
||||
ESP_LOGE(TAG, "already de-initialized");
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto _exit;
|
||||
}
|
||||
repl_com->state = CONSOLE_REPL_STATE_DEINIT;
|
||||
esp_console_deinit();
|
||||
free(cdc_repl);
|
||||
_exit:
|
||||
return ret;
|
||||
}
|
||||
#endif // CONFIG_ESP_CONSOLE_USB_CDC
|
||||
|
||||
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||
static esp_err_t esp_console_repl_usb_serial_jtag_delete(esp_console_repl_t *repl)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core);
|
||||
esp_console_repl_universal_t *usb_serial_jtag_repl = __containerof(repl_com, esp_console_repl_universal_t, repl_com);
|
||||
// check if already de-initialized
|
||||
if (repl_com->state == CONSOLE_REPL_STATE_DEINIT) {
|
||||
ESP_LOGE(TAG, "already de-initialized");
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto _exit;
|
||||
}
|
||||
repl_com->state = CONSOLE_REPL_STATE_DEINIT;
|
||||
esp_console_deinit();
|
||||
esp_vfs_usb_serial_jtag_use_nonblocking();
|
||||
usb_serial_jtag_driver_uninstall();
|
||||
free(usb_serial_jtag_repl);
|
||||
_exit:
|
||||
return ret;
|
||||
}
|
||||
#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||
|
||||
static void esp_console_repl_task(void *args)
|
||||
{
|
||||
esp_console_repl_universal_t *repl_conf = (esp_console_repl_universal_t *) args;
|
||||
esp_console_repl_com_t *repl_com = &repl_conf->repl_com;
|
||||
const int uart_channel = repl_conf->uart_channel;
|
||||
|
||||
/* Waiting for task notify. This happens when `esp_console_start_repl()`
|
||||
* function is called. */
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
/* Change standard input and output of the task if the requested UART is
|
||||
* NOT the default one. This block will replace stdin, stdout and stderr.
|
||||
*/
|
||||
if (uart_channel != CONFIG_ESP_CONSOLE_UART_NUM) {
|
||||
char path[CONSOLE_PATH_MAX_LEN] = { 0 };
|
||||
snprintf(path, CONSOLE_PATH_MAX_LEN, "/dev/uart/%d", uart_channel);
|
||||
|
||||
stdin = fopen(path, "r");
|
||||
stdout = fopen(path, "w");
|
||||
stderr = stdout;
|
||||
}
|
||||
|
||||
/* Disable buffering on stdin of the current task.
|
||||
* If the console is ran on a different UART than the default one,
|
||||
* buffering shall only be disabled for the current one. */
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
|
||||
/* This message shall be printed here and not earlier as the stdout
|
||||
* has just been set above. */
|
||||
printf("\r\n"
|
||||
"Type 'help' to get the list of commands.\r\n"
|
||||
"Use UP/DOWN arrows to navigate through command history.\r\n"
|
||||
"Press TAB when typing command name to auto-complete.\r\n");
|
||||
|
||||
if (linenoiseIsDumbMode()) {
|
||||
printf("\r\n"
|
||||
"Your terminal application does not support escape sequences.\n\n"
|
||||
"Line editing and history features are disabled.\n\n"
|
||||
"On Windows, try using Putty instead.\r\n");
|
||||
}
|
||||
|
||||
linenoiseSetMaxLineLen(repl_com->max_cmdline_length);
|
||||
while (repl_com->state == CONSOLE_REPL_STATE_START) {
|
||||
char *line = linenoise(repl_com->prompt);
|
||||
if (line == NULL) {
|
||||
ESP_LOGD(TAG, "empty line");
|
||||
/* Ignore empty lines */
|
||||
continue;
|
||||
}
|
||||
/* Add the command to the history */
|
||||
linenoiseHistoryAdd(line);
|
||||
/* Save command history to filesystem */
|
||||
if (repl_com->history_save_path) {
|
||||
linenoiseHistorySave(repl_com->history_save_path);
|
||||
}
|
||||
|
||||
/* Try to run the command */
|
||||
int ret;
|
||||
esp_err_t err = esp_console_run(line, &ret);
|
||||
if (err == ESP_ERR_NOT_FOUND) {
|
||||
printf("Unrecognized command\n");
|
||||
} else if (err == ESP_ERR_INVALID_ARG) {
|
||||
// command was empty
|
||||
} else if (err == ESP_OK && ret != ESP_OK) {
|
||||
printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret));
|
||||
} else if (err != ESP_OK) {
|
||||
printf("Internal error: %s\n", esp_err_to_name(err));
|
||||
}
|
||||
/* linenoise allocates line buffer on the heap, so need to free it */
|
||||
linenoiseFree(line);
|
||||
}
|
||||
ESP_LOGD(TAG, "The End");
|
||||
vTaskDelete(NULL);
|
||||
}
|
|
@ -106,35 +106,26 @@
|
|||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdio_ext.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include "linenoise.h"
|
||||
|
||||
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
|
||||
#define LINENOISE_DEFAULT_MAX_LINE 4096
|
||||
#define LINENOISE_MINIMAL_MAX_LINE 64
|
||||
#define LINENOISE_COMMAND_MAX_LEN 32
|
||||
#define LINENOISE_PASTE_KEY_DELAY 30 /* Delay, in milliseconds, between two characters being pasted from clipboard */
|
||||
#define LINENOISE_MAX_LINE 4096
|
||||
|
||||
static linenoiseCompletionCallback *completionCallback = NULL;
|
||||
static linenoiseHintsCallback *hintsCallback = NULL;
|
||||
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
|
||||
|
||||
static size_t max_cmdline_length = LINENOISE_DEFAULT_MAX_LINE;
|
||||
static int mlmode = 0; /* Multi line mode. Default is single line. */
|
||||
static int dumbmode = 0; /* Dumb mode where line editing is disabled. Off by default */
|
||||
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
|
||||
static int history_len = 0;
|
||||
static char **history = NULL;
|
||||
static bool allow_empty = true;
|
||||
|
||||
/* The linenoiseState structure represents the state during line editing.
|
||||
* We pass this state to functions implementing specific editing
|
||||
|
@ -203,130 +194,50 @@ void linenoiseSetMultiLine(int ml) {
|
|||
mlmode = ml;
|
||||
}
|
||||
|
||||
/* Set if terminal does not recognize escape sequences */
|
||||
void linenoiseSetDumbMode(int set) {
|
||||
dumbmode = set;
|
||||
}
|
||||
|
||||
/* Returns whether the current mode is dumbmode or not. */
|
||||
bool linenoiseIsDumbMode(void) {
|
||||
return dumbmode;
|
||||
}
|
||||
|
||||
static void flushWrite(void) {
|
||||
if (__fbufsize(stdout) > 0) {
|
||||
fflush(stdout);
|
||||
}
|
||||
fsync(fileno(stdout));
|
||||
}
|
||||
|
||||
/* Use the ESC [6n escape sequence to query the horizontal cursor position
|
||||
* and return it. On error -1 is returned, on success the position of the
|
||||
* cursor. */
|
||||
static int getCursorPosition(void) {
|
||||
char buf[LINENOISE_COMMAND_MAX_LEN] = { 0 };
|
||||
int cols = 0;
|
||||
int rows = 0;
|
||||
int i = 0;
|
||||
const int out_fd = fileno(stdout);
|
||||
const int in_fd = fileno(stdin);
|
||||
/* The following ANSI escape sequence is used to get from the TTY the
|
||||
* cursor position. */
|
||||
const char get_cursor_cmd[] = "\x1b[6n";
|
||||
static int getCursorPosition() {
|
||||
char buf[32];
|
||||
int cols, rows;
|
||||
unsigned int i = 0;
|
||||
|
||||
/* Send the command to the TTY on the other end of the UART.
|
||||
* Let's use unistd's write function. Thus, data sent through it are raw
|
||||
* reducing the overhead compared to using fputs, fprintf, etc... */
|
||||
write(out_fd, get_cursor_cmd, sizeof(get_cursor_cmd));
|
||||
|
||||
/* For USB CDC, it is required to flush the output. */
|
||||
flushWrite();
|
||||
|
||||
/* The other end will send its response which format is ESC [ rows ; cols R
|
||||
* We don't know exactly how many bytes we have to read, thus, perform a
|
||||
* read for each byte.
|
||||
* Stop right before the last character of the buffer, to be able to NULL
|
||||
* terminate it. */
|
||||
/* Report cursor location */
|
||||
fprintf(stdout, "\x1b[6n");
|
||||
/* Read the response: ESC [ rows ; cols R */
|
||||
while (i < sizeof(buf)-1) {
|
||||
/* Keep using unistd's functions. Here, using `read` instead of `fgets`
|
||||
* or `fgets` guarantees us that we we can read a byte regardless on
|
||||
* whether the sender sent end of line character(s) (CR, CRLF, LF). */
|
||||
if (read(in_fd, buf + i, 1) != 1 || buf[i] == 'R') {
|
||||
/* If we couldn't read a byte from STDIN or if 'R' was received,
|
||||
* the transmission is finished. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* For some reasons, it is possible that we receive new line character
|
||||
* after querying the cursor position on some UART. Let's ignore them,
|
||||
* this will not affect the rest of the program. */
|
||||
if (buf[i] != '\n') {
|
||||
i++;
|
||||
}
|
||||
if (fread(buf+i, 1, 1, stdin) != 1) break;
|
||||
if (buf[i] == 'R') break;
|
||||
i++;
|
||||
}
|
||||
|
||||
/* NULL-terminate the buffer, this is required by `sscanf`. */
|
||||
buf[i] = '\0';
|
||||
|
||||
/* Parse the received data to get the position of the cursor. */
|
||||
if (buf[0] != ESC || buf[1] != '[' || sscanf(buf+2,"%d;%d",&rows,&cols) != 2) {
|
||||
return -1;
|
||||
}
|
||||
/* Parse it. */
|
||||
if (buf[0] != ESC || buf[1] != '[') return -1;
|
||||
if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
|
||||
return cols;
|
||||
}
|
||||
|
||||
/* Try to get the number of columns in the current terminal, or assume 80
|
||||
* if it fails. */
|
||||
static int getColumns(void) {
|
||||
int start = 0;
|
||||
int cols = 0;
|
||||
int written = 0;
|
||||
char seq[LINENOISE_COMMAND_MAX_LEN] = { 0 };
|
||||
const int fd = fileno(stdout);
|
||||
|
||||
/* The following ANSI escape sequence is used to tell the TTY to move
|
||||
* the cursor to the most-right position. */
|
||||
const char move_cursor_right[] = "\x1b[999C";
|
||||
const size_t cmd_len = sizeof(move_cursor_right);
|
||||
|
||||
/* This one is used to set the cursor position. */
|
||||
const char set_cursor_pos[] = "\x1b[%dD";
|
||||
static int getColumns() {
|
||||
int start, cols;
|
||||
|
||||
/* Get the initial position so we can restore it later. */
|
||||
start = getCursorPosition();
|
||||
if (start == -1) {
|
||||
goto failed;
|
||||
}
|
||||
if (start == -1) goto failed;
|
||||
|
||||
/* Send the command to go to right margin. Use `write` function instead of
|
||||
* `fwrite` for the same reasons explained in `getCursorPosition()` */
|
||||
if (write(fd, move_cursor_right, cmd_len) != cmd_len) {
|
||||
goto failed;
|
||||
}
|
||||
flushWrite();
|
||||
|
||||
/* After sending this command, we can get the new position of the cursor,
|
||||
* we'd get the size, in columns, of the opened TTY. */
|
||||
/* Go to right margin and get position. */
|
||||
if (fwrite("\x1b[999C", 1, 6, stdout) != 6) goto failed;
|
||||
cols = getCursorPosition();
|
||||
if (cols == -1) {
|
||||
goto failed;
|
||||
}
|
||||
if (cols == -1) goto failed;
|
||||
|
||||
/* Restore the position of the cursor back. */
|
||||
/* Restore position. */
|
||||
if (cols > start) {
|
||||
/* Generate the move cursor command. */
|
||||
written = snprintf(seq, LINENOISE_COMMAND_MAX_LEN, set_cursor_pos, cols-start);
|
||||
|
||||
/* If `written` is equal or bigger than LINENOISE_COMMAND_MAX_LEN, it
|
||||
* means that the output has been truncated because the size provided
|
||||
* is too small. */
|
||||
assert (written < LINENOISE_COMMAND_MAX_LEN);
|
||||
|
||||
/* Send the command with `write`, which is not buffered. */
|
||||
if (write(fd, seq, written) == -1) {
|
||||
char seq[32];
|
||||
snprintf(seq,32,"\x1b[%dD",cols-start);
|
||||
if (fwrite(seq, 1, strlen(seq), stdout) == -1) {
|
||||
/* Can't recover... */
|
||||
}
|
||||
flushWrite();
|
||||
}
|
||||
return cols;
|
||||
|
||||
|
@ -337,14 +248,12 @@ failed:
|
|||
/* Clear the screen. Used to handle ctrl+l */
|
||||
void linenoiseClearScreen(void) {
|
||||
fprintf(stdout,"\x1b[H\x1b[2J");
|
||||
flushWrite();
|
||||
}
|
||||
|
||||
/* Beep, used for completion when there is nothing to complete or when all
|
||||
* the choices were already shown. */
|
||||
static void linenoiseBeep(void) {
|
||||
fprintf(stdout, "\x7");
|
||||
flushWrite();
|
||||
}
|
||||
|
||||
/* ============================== Completion ================================ */
|
||||
|
@ -368,7 +277,6 @@ static int completeLine(struct linenoiseState *ls) {
|
|||
linenoiseCompletions lc = { 0, NULL };
|
||||
int nread, nwritten;
|
||||
char c = 0;
|
||||
int in_fd = fileno(stdin);
|
||||
|
||||
completionCallback(ls->buf,&lc);
|
||||
if (lc.len == 0) {
|
||||
|
@ -391,7 +299,7 @@ static int completeLine(struct linenoiseState *ls) {
|
|||
refreshLine(ls);
|
||||
}
|
||||
|
||||
nread = read(in_fd, &c, 1);
|
||||
nread = fread(&c, 1, 1, stdin);
|
||||
if (nread <= 0) {
|
||||
freeCompletions(&lc);
|
||||
return -1;
|
||||
|
@ -501,10 +409,9 @@ void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
|
|||
int hintmaxlen = l->cols-(plen+l->len);
|
||||
if (hintlen > hintmaxlen) hintlen = hintmaxlen;
|
||||
if (bold == 1 && color == -1) color = 37;
|
||||
if (color != -1 || bold != 0) {
|
||||
if (color != -1 || bold != 0)
|
||||
snprintf(seq,64,"\033[%d;%d;49m",bold,color);
|
||||
abAppend(ab,seq,strlen(seq));
|
||||
}
|
||||
abAppend(ab,seq,strlen(seq));
|
||||
abAppend(ab,hint,hintlen);
|
||||
if (color != -1 || bold != 0)
|
||||
abAppend(ab,"\033[0m",4);
|
||||
|
@ -521,7 +428,6 @@ void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
|
|||
static void refreshSingleLine(struct linenoiseState *l) {
|
||||
char seq[64];
|
||||
size_t plen = l->plen;
|
||||
int fd = fileno(stdout);
|
||||
char *buf = l->buf;
|
||||
size_t len = l->len;
|
||||
size_t pos = l->pos;
|
||||
|
@ -551,8 +457,7 @@ static void refreshSingleLine(struct linenoiseState *l) {
|
|||
/* Move cursor to original position. */
|
||||
snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));
|
||||
abAppend(&ab,seq,strlen(seq));
|
||||
if (write(fd, ab.b, ab.len) == -1) {} /* Can't recover from write error. */
|
||||
flushWrite();
|
||||
if (fwrite(ab.b, ab.len, 1, stdout) == -1) {} /* Can't recover from write error. */
|
||||
abFree(&ab);
|
||||
}
|
||||
|
||||
|
@ -569,7 +474,6 @@ static void refreshMultiLine(struct linenoiseState *l) {
|
|||
int col; /* colum position, zero-based. */
|
||||
int old_rows = l->maxrows;
|
||||
int j;
|
||||
int fd = fileno(stdout);
|
||||
struct abuf ab;
|
||||
|
||||
/* Update maxrows if needed. */
|
||||
|
@ -640,8 +544,7 @@ static void refreshMultiLine(struct linenoiseState *l) {
|
|||
lndebug("\n");
|
||||
l->oldpos = l->pos;
|
||||
|
||||
if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
|
||||
flushWrite();
|
||||
if (fwrite(ab.b,ab.len,1,stdout) == -1) {} /* Can't recover from write error. */
|
||||
abFree(&ab);
|
||||
}
|
||||
|
||||
|
@ -658,7 +561,6 @@ static void refreshLine(struct linenoiseState *l) {
|
|||
*
|
||||
* On error writing to the terminal -1 is returned, otherwise 0. */
|
||||
int linenoiseEditInsert(struct linenoiseState *l, char c) {
|
||||
int fd = fileno(stdout);
|
||||
if (l->len < l->buflen) {
|
||||
if (l->len == l->pos) {
|
||||
l->buf[l->pos] = c;
|
||||
|
@ -668,10 +570,7 @@ int linenoiseEditInsert(struct linenoiseState *l, char c) {
|
|||
if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
|
||||
/* Avoid a full update of the line in the
|
||||
* trivial case. */
|
||||
if (write(fd, &c,1) == -1) {
|
||||
return -1;
|
||||
}
|
||||
flushWrite();
|
||||
if (fwrite(&c,1,1,stdout) == -1) return -1;
|
||||
} else {
|
||||
refreshLine(l);
|
||||
}
|
||||
|
@ -687,21 +586,6 @@ int linenoiseEditInsert(struct linenoiseState *l, char c) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int linenoiseInsertPastedChar(struct linenoiseState *l, char c) {
|
||||
int fd = fileno(stdout);
|
||||
if (l->len < l->buflen && l->len == l->pos) {
|
||||
l->buf[l->pos] = c;
|
||||
l->pos++;
|
||||
l->len++;
|
||||
l->buf[l->len] = '\0';
|
||||
if (write(fd, &c,1) == -1) {
|
||||
return -1;
|
||||
}
|
||||
flushWrite();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Move cursor on the left. */
|
||||
void linenoiseEditMoveLeft(struct linenoiseState *l) {
|
||||
if (l->pos > 0) {
|
||||
|
@ -798,12 +682,6 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
|
|||
refreshLine(l);
|
||||
}
|
||||
|
||||
uint32_t getMillis(void) {
|
||||
struct timeval tv = { 0 };
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
}
|
||||
|
||||
/* This function is the core of the line editing capability of linenoise.
|
||||
* It expects 'fd' to be already in "raw mode" so that every key pressed
|
||||
* will be returned ASAP to read().
|
||||
|
@ -814,10 +692,7 @@ uint32_t getMillis(void) {
|
|||
* The function returns the length of the current buffer. */
|
||||
static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
|
||||
{
|
||||
uint32_t t1 = 0;
|
||||
struct linenoiseState l;
|
||||
int out_fd = fileno(stdout);
|
||||
int in_fd = fileno(stdin);
|
||||
|
||||
/* Populate the linenoise state that we pass to functions implementing
|
||||
* specific editing functionalities. */
|
||||
|
@ -840,10 +715,7 @@ static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
|
|||
linenoiseHistoryAdd("");
|
||||
|
||||
int pos1 = getCursorPosition();
|
||||
if (write(out_fd, prompt,l.plen) == -1) {
|
||||
return -1;
|
||||
}
|
||||
flushWrite();
|
||||
if (fwrite(prompt,l.plen,1,stdout) == -1) return -1;
|
||||
int pos2 = getCursorPosition();
|
||||
if (pos1 >= 0 && pos2 >= 0) {
|
||||
l.plen = pos2 - pos1;
|
||||
|
@ -853,29 +725,9 @@ static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
|
|||
int nread;
|
||||
char seq[3];
|
||||
|
||||
/**
|
||||
* To determine whether the user is pasting data or typing itself, we
|
||||
* need to calculate how many milliseconds elapsed between two key
|
||||
* presses. Indeed, if there is less than LINENOISE_PASTE_KEY_DELAY
|
||||
* (typically 30-40ms), then a paste is being performed, else, the
|
||||
* user is typing.
|
||||
* NOTE: pressing a key down without releasing it will also spend
|
||||
* about 40ms (or even more)
|
||||
*/
|
||||
t1 = getMillis();
|
||||
nread = read(in_fd, &c, 1);
|
||||
nread = fread(&c, 1, 1, stdin);
|
||||
if (nread <= 0) return l.len;
|
||||
|
||||
if ( (getMillis() - t1) < LINENOISE_PASTE_KEY_DELAY && c != ENTER) {
|
||||
/* Pasting data, insert characters without formatting.
|
||||
* This can only be performed when the cursor is at the end of the
|
||||
* line. */
|
||||
if (linenoiseInsertPastedChar(&l,c)) {
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Only autocomplete when the callback is set. It returns < 0 when
|
||||
* there was an error reading from fd. Otherwise it will return the
|
||||
* character that should be handled next. */
|
||||
|
@ -942,17 +794,13 @@ static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
|
|||
break;
|
||||
case ESC: /* escape sequence */
|
||||
/* Read the next two bytes representing the escape sequence. */
|
||||
if (read(in_fd, seq, 2) < 2) {
|
||||
break;
|
||||
}
|
||||
if (fread(seq, 1, 2, stdin) < 2) break;
|
||||
|
||||
/* ESC [ sequences. */
|
||||
if (seq[0] == '[') {
|
||||
if (seq[1] >= '0' && seq[1] <= '9') {
|
||||
/* Extended escape, read additional byte. */
|
||||
if (read(in_fd, seq+2, 1) == -1) {
|
||||
break;
|
||||
}
|
||||
if (fread(seq+2, 1, 1, stdin) == -1) break;
|
||||
if (seq[2] == '~') {
|
||||
switch(seq[1]) {
|
||||
case '3': /* Delete key. */
|
||||
|
@ -1023,57 +871,10 @@ static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
|
|||
linenoiseEditDeletePrevWord(&l);
|
||||
break;
|
||||
}
|
||||
flushWrite();
|
||||
}
|
||||
return l.len;
|
||||
}
|
||||
|
||||
void linenoiseAllowEmpty(bool val) {
|
||||
allow_empty = val;
|
||||
}
|
||||
|
||||
int linenoiseProbe(void) {
|
||||
/* Switch to non-blocking mode */
|
||||
int stdin_fileno = fileno(stdin);
|
||||
int flags = fcntl(stdin_fileno, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
int res = fcntl(stdin_fileno, F_SETFL, flags);
|
||||
if (res != 0) {
|
||||
return -1;
|
||||
}
|
||||
/* Device status request */
|
||||
fprintf(stdout, "\x1b[5n");
|
||||
flushWrite();
|
||||
|
||||
/* Try to read response */
|
||||
int timeout_ms = 500;
|
||||
const int retry_ms = 10;
|
||||
size_t read_bytes = 0;
|
||||
while (timeout_ms > 0 && read_bytes < 4) { // response is ESC[0n or ESC[3n
|
||||
usleep(retry_ms * 1000);
|
||||
timeout_ms -= retry_ms;
|
||||
char c;
|
||||
int cb = read(stdin_fileno, &c, 1);
|
||||
if (cb < 0) {
|
||||
continue;
|
||||
}
|
||||
if (read_bytes == 0 && c != '\x1b') {
|
||||
/* invalid response */
|
||||
break;
|
||||
}
|
||||
read_bytes += cb;
|
||||
}
|
||||
/* Restore old mode */
|
||||
flags &= ~O_NONBLOCK;
|
||||
res = fcntl(stdin_fileno, F_SETFL, flags);
|
||||
if (res != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (read_bytes < 4) {
|
||||
return -2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
|
||||
int count;
|
||||
|
@ -1084,71 +885,18 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
|
|||
}
|
||||
|
||||
count = linenoiseEdit(buf, buflen, prompt);
|
||||
fputc('\n', stdout);
|
||||
flushWrite();
|
||||
printf("\n");
|
||||
return count;
|
||||
}
|
||||
|
||||
static int linenoiseDumb(char* buf, size_t buflen, const char* prompt) {
|
||||
/* dumb terminal, fall back to fgets */
|
||||
fputs(prompt, stdout);
|
||||
size_t count = 0;
|
||||
while (count < buflen) {
|
||||
int c = fgetc(stdin);
|
||||
if (c == '\n') {
|
||||
break;
|
||||
} else if (c >= 0x1c && c <= 0x1f){
|
||||
continue; /* consume arrow keys */
|
||||
} else if (c == BACKSPACE || c == 0x8) {
|
||||
if (count > 0) {
|
||||
buf[count - 1] = 0;
|
||||
count --;
|
||||
}
|
||||
fputs("\x08 ", stdout); /* Windows CMD: erase symbol under cursor */
|
||||
} else {
|
||||
buf[count] = c;
|
||||
++count;
|
||||
}
|
||||
fputc(c, stdout); /* echo */
|
||||
}
|
||||
fputc('\n', stdout);
|
||||
flushWrite();
|
||||
return count;
|
||||
}
|
||||
|
||||
static void sanitize(char* src) {
|
||||
char* dst = src;
|
||||
for (int c = *src; c != 0; src++, c = *src) {
|
||||
if (isprint(c)) {
|
||||
*dst = c;
|
||||
++dst;
|
||||
}
|
||||
}
|
||||
*dst = 0;
|
||||
}
|
||||
|
||||
/* The high level function that is the main API of the linenoise library. */
|
||||
char *linenoise(const char *prompt) {
|
||||
char *buf = calloc(1, max_cmdline_length);
|
||||
int count = 0;
|
||||
if (buf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (!dumbmode) {
|
||||
count = linenoiseRaw(buf, max_cmdline_length, prompt);
|
||||
} else {
|
||||
count = linenoiseDumb(buf, max_cmdline_length, prompt);
|
||||
}
|
||||
if (count > 0) {
|
||||
sanitize(buf);
|
||||
count = strlen(buf);
|
||||
} else if (count == 0 && allow_empty) {
|
||||
/* will return an empty (0-length) string */
|
||||
} else {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
return buf;
|
||||
char buf[LINENOISE_MAX_LINE];
|
||||
int count;
|
||||
|
||||
count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
|
||||
if (count == -1) return NULL;
|
||||
return strdup(buf);
|
||||
}
|
||||
|
||||
/* This is just a wrapper the user may want to call in order to make sure
|
||||
|
@ -1161,7 +909,7 @@ void linenoiseFree(void *ptr) {
|
|||
|
||||
/* ================================ History ================================= */
|
||||
|
||||
void linenoiseHistoryFree(void) {
|
||||
void linenoiseHistoryFree() {
|
||||
if (history) {
|
||||
for (int j = 0; j < history_len; j++) {
|
||||
free(history[j]);
|
||||
|
@ -1259,18 +1007,12 @@ int linenoiseHistorySave(const char *filename) {
|
|||
* If the file exists and the operation succeeded 0 is returned, otherwise
|
||||
* on error -1 is returned. */
|
||||
int linenoiseHistoryLoad(const char *filename) {
|
||||
FILE *fp = fopen(filename, "r");
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
FILE *fp = fopen(filename,"r");
|
||||
char buf[LINENOISE_MAX_LINE];
|
||||
|
||||
char *buf = calloc(1, max_cmdline_length);
|
||||
if (buf == NULL) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
if (fp == NULL) return -1;
|
||||
|
||||
while (fgets(buf, max_cmdline_length, fp) != NULL) {
|
||||
while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
|
||||
char *p;
|
||||
|
||||
p = strchr(buf,'\r');
|
||||
|
@ -1278,20 +1020,6 @@ int linenoiseHistoryLoad(const char *filename) {
|
|||
if (p) *p = '\0';
|
||||
linenoiseHistoryAdd(buf);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set line maximum length. If len parameter is smaller than
|
||||
* LINENOISE_MINIMAL_MAX_LINE, -1 is returned
|
||||
* otherwise 0 is returned. */
|
||||
int linenoiseSetMaxLineLen(size_t len) {
|
||||
if (len < LINENOISE_MINIMAL_MAX_LINE) {
|
||||
return -1;
|
||||
}
|
||||
max_cmdline_length = len;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -43,9 +43,6 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct linenoiseCompletions {
|
||||
size_t len;
|
||||
char **cvec;
|
||||
|
@ -59,21 +56,16 @@ void linenoiseSetHintsCallback(linenoiseHintsCallback *);
|
|||
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
|
||||
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||
|
||||
int linenoiseProbe(void);
|
||||
char *linenoise(const char *prompt);
|
||||
void linenoiseFree(void *ptr);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
int linenoiseHistorySetMaxLen(int len);
|
||||
int linenoiseHistorySave(const char *filename);
|
||||
int linenoiseHistoryLoad(const char *filename);
|
||||
void linenoiseHistoryFree(void);
|
||||
void linenoiseHistoryFree();
|
||||
void linenoiseClearScreen(void);
|
||||
void linenoiseSetMultiLine(int ml);
|
||||
void linenoiseSetDumbMode(int set);
|
||||
bool linenoiseIsDumbMode(void);
|
||||
void linenoisePrintKeyCodes(void);
|
||||
void linenoiseAllowEmpty(bool);
|
||||
int linenoiseSetMaxLineLen(size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
@ -23,20 +31,13 @@ typedef enum {
|
|||
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;
|
||||
int argc = 0;
|
||||
char *next_arg_start = line;
|
||||
char *out_ptr = line;
|
||||
for (char *in_ptr = line; argc < argv_size - 1; ++in_ptr) {
|
||||
|
@ -46,6 +47,13 @@ size_t esp_console_split_argv(char *line, char **argv, size_t argv_size)
|
|||
}
|
||||
int char_out = -1;
|
||||
|
||||
/* helper function, called when done with an argument */
|
||||
void end_arg() {
|
||||
char_out = 0;
|
||||
argv[argc++] = next_arg_start;
|
||||
state = SS_SPACE;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case SS_SPACE:
|
||||
if (char_in == SPACE) {
|
||||
|
@ -65,7 +73,7 @@ size_t esp_console_split_argv(char *line, char **argv, size_t argv_size)
|
|||
|
||||
case SS_QUOTED_ARG:
|
||||
if (char_in == QUOTE) {
|
||||
END_ARG();
|
||||
end_arg();
|
||||
} else if (char_in == ESCAPE) {
|
||||
state = SS_QUOTED_ARG_ESCAPED;
|
||||
} else {
|
||||
|
@ -85,7 +93,7 @@ size_t esp_console_split_argv(char *line, char **argv, size_t argv_size)
|
|||
|
||||
case SS_ARG:
|
||||
if (char_in == SPACE) {
|
||||
END_ARG();
|
||||
end_arg();
|
||||
} else if (char_in == ESCAPE) {
|
||||
state = SS_ARG_ESCAPED;
|
||||
} else {
|
||||
|
|
|
@ -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-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,45 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
|
||||
// Some resources are lazy allocated (newlib locks) in the console code, the threshold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-100)
|
||||
|
||||
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)
|
||||
{
|
||||
printf("Running console component tests\n");
|
||||
unity_run_menu();
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 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 "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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Enter "quit" to exit REPL environment
|
||||
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));
|
||||
|
||||
esp_console_cmd_t cmd = {
|
||||
.command = "quit",
|
||||
.help = "Quit REPL environment",
|
||||
.func = &do_cmd_quit
|
||||
};
|
||||
TEST_ESP_OK(esp_console_cmd_register(&cmd));
|
||||
|
||||
TEST_ESP_OK(esp_console_start_repl(s_repl));
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.supported_targets
|
||||
def test_console(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
|
@ -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
|
|
@ -25,23 +25,6 @@ void console_task(void* arg) {
|
|||
"Use UP/DOWN arrows to navigate through command history.\n"
|
||||
"Press TAB when typing command name to auto-complete.\n"
|
||||
"Ctrl+C will terminate the console environment.\n");
|
||||
|
||||
/* Figure out if the terminal supports escape sequences */
|
||||
int probe_status = linenoiseProbe();
|
||||
if (probe_status) { /* zero indicates success */
|
||||
printf("\n"
|
||||
"Your terminal application does not support escape sequences.\n"
|
||||
"Line editing and history features are disabled.\n"
|
||||
"On Windows, try using Putty instead.\n");
|
||||
linenoiseSetDumbMode(1);
|
||||
#if CONFIG_LOG_COLORS
|
||||
/* Since the terminal doesn't support escape sequences,
|
||||
* don't use color codes in the prompt.
|
||||
*/
|
||||
prompt = PROMPT_STR;
|
||||
#endif // CONFIG_LOG_COLORS
|
||||
}
|
||||
|
||||
|
||||
/* Main loop */
|
||||
while (true) {
|
||||
|
|
|
@ -58,12 +58,6 @@ static void initialize_console(void) {
|
|||
/* Set command history size */
|
||||
linenoiseHistorySetMaxLen(100);
|
||||
|
||||
/* Set command maximum length */
|
||||
linenoiseSetMaxLineLen(console_config.max_cmdline_length);
|
||||
|
||||
/* Allow empty lines */
|
||||
linenoiseAllowEmpty(true);
|
||||
|
||||
/* Load command history from filesystem */
|
||||
linenoiseHistoryLoad(HISTORY_PATH);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue