Compare commits

...

12 Commits

Author SHA1 Message Date
prozessorkern
6c76dfc7ae fixed comments to get rid of doxygen warnings 2020-04-01 19:16:11 +02:00
prozessorkern
785d73306d fix #44 add busy mode 2020-03-28 12:12:56 +01:00
prozessorkern
96cf0c8d65 added busy command to the example + declared option parser options as const 2020-03-28 12:08:01 +01:00
prozessorkern
d7962a54dc added busy mode and test
a command can now return SHELLMATTA_BUSY
This will be passed back to the caller of processData
afterwards the instance has to be called with the same parameters
The shellmatta will then just call the busy command until it finishes
as soon as the command returns != SHELLMATTA_BUSY the instance will continue processing the rest of the input data
2020-03-28 11:26:50 +01:00
prozessorkern
3b99ad2a56 fix #43 added a config interface to change the newline character expected 2020-03-27 18:35:07 +01:00
prozessorkern
c2e4324236 bugfix - now stdin returns 0 if the heredoc body is empty 2020-03-26 05:55:38 +01:00
prozessorkern
5e84f1b022 added continuous mode support fix #3
If a command returns SHELLMATTA_CONTINUE all received data is passed to this command until it returns != SHELLMATTA_CONTINUE or a cancel is received
The data is passed stdin like and can be read byte by byte vie shellmatta_read
The stdin buffer is overwritten witch each new char
2020-03-22 21:27:18 +01:00
prozessorkern
17bb88d292 changed heredoc to exchange data via a stdin like interface with a read function fix #39 2020-03-22 20:37:08 +01:00
prozessorkern
5a00f22e31 added the help command variable to the instance structure to get independant commands for each instance fix #42 2020-03-22 20:07:30 +01:00
prozessorkern
2d130e2ee1 added long option parser fix #1 2020-03-22 18:57:31 +01:00
prozessorkern
ecc43307af added first working option parser for short options 2020-03-16 22:08:06 +01:00
prozessorkern
60c4c7dadd added basic interface of the shellmatta option parser + started adding a test module 2020-03-08 22:02:51 +01:00
20 changed files with 1793 additions and 184 deletions

2
.vscode/launch.json vendored
View File

@ -9,7 +9,7 @@
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/output/example/example",
"args": ["/dev/pts/3"],
"args": ["/dev/pts/4"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],

13
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,13 @@
{
"files.associations": {
"random": "cpp",
"*.tcc": "cpp",
"string": "cpp",
"vector": "cpp",
"fstream": "cpp",
"limits": "cpp",
"sstream": "cpp",
"utility": "cpp",
"algorithm": "cpp"
}
}

16
.vscode/tasks.json vendored
View File

@ -1,6 +1,4 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
@ -51,6 +49,20 @@
"kind": "test",
"isDefault": true
}
},
{
"type": "shell",
"label": "gcc build active file",
"command": "/usr/bin/gcc",
"args": [
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "/usr/bin"
}
}
]
}

View File

@ -39,7 +39,8 @@ typedef enum
SHELLMATTA_ERROR , /**< error occured */
SHELLMATTA_CONTINUE , /**< the function is not over */
SHELLMATTA_USE_FAULT , /**< parameter error - wrong usage */
SHELLMATTA_DUPLICATE /**< duplicate command */
SHELLMATTA_DUPLICATE , /**< duplicate command */
SHELLMATTA_BUSY /**< command is busy keep calling */
} shellmatta_retCode_t;
/**
@ -51,6 +52,36 @@ typedef enum
SHELLMATTA_MODE_OVERWRITE , /**< overwrite mode */
} shellmatta_mode_t;
/**
* @brief definition of shellmatta optionparser agument type
*/
typedef enum
{
SHELLMATTA_OPT_ARG_NONE = 0u, /**< no argument expected */
SHELLMATTA_OPT_ARG_REQUIRED, /**< argument is required */
SHELLMATTA_OPT_ARG_OPTIONAL, /**< argument is optional */
} shellmatta_opt_argtype_t;
/**
* @brief definition of shellmatta optionparser agument type
*/
typedef struct
{
const char *paramLong; /**< long parameter string */
const char paramShort; /**< short parameter char */
shellmatta_opt_argtype_t argtype; /**< argument type expected */
} shellmatta_opt_long_t;
/**
* @brief definition of shellmatta optionparser structure
*/
typedef struct
{
uint32_t offset; /**< current offset of the option parser */
uint32_t nextOffset; /**< offset of the next hunk */
uint32_t len; /**< length of the current hunk */
} shellmatta_opt_t;
/**
* @brief shellmatta command function definition
* @param[in] handle pointer to the instance which is calling the cmd
@ -91,8 +122,11 @@ typedef struct
char *buffer; /**< input buffer */
uint32_t bufferSize; /**< size of the input buffer */
uint32_t inputCount; /**< offset of the current write operation */
uint32_t byteCounter; /**< counter used to loop over input data */
uint32_t lastNewlineIdx; /**< index of the lest newline */
uint32_t cursor; /**< offset where the cursor is at */
uint32_t stdinIdx; /**< start index of stdin in buffer */
uint32_t stdinLength; /**< length of the stdin data */
char *historyBuffer; /**< buffer to store the last commands */
uint32_t historyBufferSize; /**< size of the history buffer */
uint32_t historyStart; /**< index of the oldest stored command */
@ -108,11 +142,16 @@ typedef struct
bool echoEnabled; /**< if true the input is printed */
bool dirty; /**< dirty flag to show changes */
const char *prompt; /**< prompt is printed after every command */
char delimiter; /**< delimiter (return) to terminate a cmd */
shellmatta_mode_t mode; /**< mode of the shell */
shellmatta_write_t write; /**< pointer to write function */
shellmatta_cmd_t helpCmd; /**< help command structure */
shellmatta_cmd_t *cmdList; /**< pointer to the first command */
shellmatta_cmd_t *continuousCmd; /**< command to be called continuously */
shellmatta_cmd_t *busyCmd; /**< command to be polled (busy mode) */
bool cmdListIsConst; /**< true if the #cmdList was passed during
initialization */
shellmatta_opt_t optionParser; /**< option parser sructure */
} shellmatta_instance_t;
@ -137,7 +176,8 @@ shellmatta_retCode_t shellmatta_removeCmd( shellmatta_handle_t handle,
shellmatta_retCode_t shellmatta_configure( shellmatta_handle_t handle,
shellmatta_mode_t mode,
bool echoEnabled);
bool echoEnabled,
char delimiter);
shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
char *data,
@ -146,6 +186,23 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
shellmatta_retCode_t shellmatta_write( shellmatta_handle_t handle,
char *data,
uint32_t length);
shellmatta_retCode_t shellmatta_read( shellmatta_handle_t handle,
char **data,
uint32_t *length);
shellmatta_retCode_t shellmatta_opt( shellmatta_handle_t handle,
const char *optionString,
char *option,
char **argument,
uint32_t *argLen);
shellmatta_retCode_t shellmatta_opt_long( shellmatta_handle_t handle,
const shellmatta_opt_long_t *longOptions,
char *option,
char **argument,
uint32_t *argLen);
#ifndef SHELLMATTA_STRIP_PRINTF
shellmatta_retCode_t shellmatta_printf( shellmatta_handle_t handle,
const char *fmt,

View File

@ -1,4 +1,4 @@
# Doxyfile 1.8.15
# Doxyfile 1.8.17
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@ -197,6 +197,16 @@ SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
# such as
# /***************
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
# Javadoc-style will behave just like regular comments and it will not be
# interpreted by doxygen.
# The default value is: NO.
JAVADOC_BANNER = NO
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
@ -299,7 +309,7 @@ OPTIMIZE_OUTPUT_SLICE = NO
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
@ -329,7 +339,7 @@ MARKDOWN_SUPPORT = YES
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 0.
# Minimum value: 0, maximum value: 99, default value: 5.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 0
@ -465,6 +475,12 @@ EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
# methods of a class will be included in the documentation.
# The default value is: NO.
EXTRACT_PRIV_VIRTUAL = NO
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
@ -519,8 +535,8 @@ HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
# (class|struct|union) declarations. If set to NO, these declarations will be
# included in the documentation.
# declarations. If set to NO, these declarations will be included in the
# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
@ -543,7 +559,7 @@ INTERNAL_DOCS = NO
# names in lower-case letters. If set to YES, upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO.
# (including Cygwin) ands Mac users are advised to set this option to NO.
# The default value is: system dependent.
CASE_SENSE_NAMES = YES
@ -813,7 +829,8 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = src api
INPUT = src \
api
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@ -835,8 +852,10 @@ INPUT_ENCODING = UTF-8
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.c \
*.cc \
@ -1250,9 +1269,9 @@ HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via Javascript. If disabled, the navigation index will
# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
# page. Disable this option to support browsers that do not have Javascript,
# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
@ -1403,7 +1422,7 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@ -1411,7 +1430,7 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
# folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@ -1420,7 +1439,7 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
@ -1428,7 +1447,7 @@ QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
@ -1436,7 +1455,7 @@ QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
@ -1540,8 +1559,14 @@ FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
FORMULA_MACROFILE =
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# https://www.mathjax.org) which uses client side Javascript for the rendering
# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
@ -1611,7 +1636,7 @@ MATHJAX_CODEFILE =
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using Javascript. There
# implemented using a web server instead of a web client using JavaScript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
@ -1715,10 +1740,11 @@ LATEX_CMD_NAME =
MAKEINDEX_CMD_NAME = makeindex
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
# generate index for LaTeX.
# generate index for LaTeX. In case there is no backslash (\) as first character
# it will be automatically added in the LaTeX code.
# Note: This tag is used in the generated output file (.tex).
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
# The default value is: \makeindex.
# The default value is: makeindex.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_MAKEINDEX_CMD = \makeindex
@ -2210,12 +2236,6 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of 'which perl').
# The default file (with absolute path) is: /usr/bin/perl.
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
@ -2229,15 +2249,6 @@ PERL_PATH = /usr/bin/perl
CLASS_DIAGRAMS = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. Doxygen will then run the mscgen tool (see:
# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
MSCGEN_PATH =
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.

View File

@ -1,19 +1,23 @@
/*
* main.c
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
*
* Created on: Jun 10, 2019
* Author: stefan
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/**
* @file main.c
* @brief main module to demonstrate use of the shellmatta.
* @author Stefan Strobel <stefan.strobel@shimatta.net>
*/
#include "shellmatta.h"
#include <stdint.h>
#include <stdio.h>
#include <ncurses.h>
#include <stdbool.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <errno.h>
@ -22,24 +26,6 @@ static bool exitRequest = false;
int f;
shellmatta_handle_t handle;
void set_blocking (int fd, int should_block)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
printf ("error %d from tggetattr", errno);
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
printf ("error %d setting term attributes", errno);
}
static shellmatta_retCode_t doSomething(shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
shellmatta_printf(handle, "%s - length: %u", arguments, length);
@ -52,7 +38,7 @@ static shellmatta_retCode_t doSome(shellmatta_handle_t handle, const char *argum
shellmatta_write(handle, "blubb\r\n", 7u);
shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false);
shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');
(void)arguments;
(void)length;
@ -93,7 +79,7 @@ static shellmatta_retCode_t empty(shellmatta_handle_t handle, const char *argume
(void)length;
shellmatta_printf(handle, "empty function called\r\n");
shellmatta_configure(handle, SHELLMATTA_MODE_OVERWRITE, true);
shellmatta_configure(handle, SHELLMATTA_MODE_OVERWRITE, true, '\r');
return SHELLMATTA_OK;
}
@ -101,23 +87,93 @@ shellmatta_cmd_t emptyCommand = {"empty", NULL, NULL, NULL, empty, NULL};
static shellmatta_retCode_t reset(shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
shellmatta_retCode_t ret;
(void)arguments;
(void)length;
char option;
char *argument;
uint32_t argLen;
bool printPrompt = false;
if(0 == strncmp(arguments, "prompt", length))
static const shellmatta_opt_long_t options[] =
{
shellmatta_resetShell(handle, true);
}
else
{"prompt", 'p', SHELLMATTA_OPT_ARG_REQUIRED},
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE}
};
ret = shellmatta_opt_long(handle, options, &option, &argument, &argLen);
while(SHELLMATTA_OK == ret)
{
shellmatta_resetShell(handle, false);
switch(option)
{
case 'p':
if(NULL != argument)
{
if(0 == strncmp("true", argument, 4u))
{
printPrompt = true;
}
}
break;
default:
shellmatta_printf(handle, "Unknown option: %c\r\n", option);
break;
}
ret = shellmatta_opt_long(handle, options, &option, &argument, &argLen);
}
shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, true);
shellmatta_resetShell(handle, printPrompt);
shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, true, '\r');
return SHELLMATTA_OK;
}
shellmatta_cmd_t resetCommand = {"reset", NULL, "resets the shellmatta instance", "reset [prompt]", reset, NULL};
shellmatta_cmd_t resetCommand = {"reset", NULL, "resets the shellmatta instance", "reset [--prompt true/false]", reset, NULL};
static shellmatta_retCode_t continuous(shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
(void)arguments;
(void)length;
shellmatta_retCode_t ret = SHELLMATTA_CONTINUE;
uint32_t stdinLength;
char *stdinData;
shellmatta_read(handle, &stdinData, &stdinLength);
if(NULL != stdinData)
{
if('x' == stdinData[0u])
{
ret = SHELLMATTA_OK;
}
stdinData[0u] ++;
shellmatta_write(handle, stdinData, stdinLength);
}
return ret;
}
shellmatta_cmd_t continuousCommand = {"continuous", "cont", "prints continously all input bytes", "continuous", continuous, NULL};
static shellmatta_retCode_t busy(shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
(void)arguments;
(void)length;
static uint32_t callCnt = 0u;
shellmatta_retCode_t ret = SHELLMATTA_BUSY;
if(callCnt < 10u)
{
callCnt ++;
shellmatta_printf(handle, "%s - length %u - callCnt %u\r\n", arguments, length, callCnt);
}
else
{
callCnt = 0u;
ret = SHELLMATTA_OK;
}
return ret;
}
shellmatta_cmd_t busyCommand = {"busy", NULL, NULL, NULL, busy, NULL};
shellmatta_retCode_t writeFct(const char* data, uint32_t length)
@ -147,8 +203,6 @@ int main(int argc, char **argv)
return f;
}
set_blocking (f, 1);
shellmatta_doInit( &instance,
&handle,
buffer,
@ -164,18 +218,27 @@ int main(int argc, char **argv)
shellmatta_addCmd(handle, &removeCommand);
shellmatta_addCmd(handle, &emptyCommand);
shellmatta_addCmd(handle, &resetCommand);
shellmatta_addCmd(handle, &continuousCommand);
shellmatta_addCmd(handle, &busyCommand);
while(exitRequest == false)
{
char c;
shellmatta_retCode_t ret;
int res = 0;
res = read (f, &c, 1);
fprintf(stdout, "0x%02x \n", c);
fflush(stdout);
shellmatta_processData(handle, &c, res);
do
{
ret = shellmatta_processData(handle, &c, res);
if(SHELLMATTA_BUSY == ret)
{
sleep(1);
}
} while(SHELLMATTA_BUSY == ret);
}
close(f);

View File

@ -16,11 +16,13 @@ SOURCES := src/shellmatta.c \
src/shellmatta_autocomplete.c \
src/shellmatta_history.c \
src/shellmatta_utils.c \
src/shellmatta_escape.c
src/shellmatta_escape.c \
src/shellmatta_opt.c
INCLUDES := api .
UNITTEST_SOURCES := test/unittest/test_main.cpp \
test/unittest/shellmatta_opt/test_opt_findNextHunk.cpp \
test/unittest/shellmatta_utils/test_utils_writeEcho.cpp \
test/unittest/shellmatta_utils/test_utils_shellItoa.cpp \
test/unittest/shellmatta_utils/test_utils_saveCursorPos.cpp \
@ -36,8 +38,11 @@ UNITTEST_SOURCES := test/unittest/test_main.cpp
test/unittest/shellmatta_history/test_appendHistoryByte.cpp \
test/unittest/shellmatta/test_shellmatta_doInit.cpp
INTEGRATIONTEST_SOURCES := test/integrationtest/test_main.cpp \
test/integrationtest/test_integration.cpp
INTEGRATIONTEST_SOURCES := test/integrationtest/test_main.cpp \
test/integrationtest/test_integration.cpp \
test/integrationtest/test_integration_opt.cpp \
test/integrationtest/test_integration_optLong.cpp \
test/integrationtest/test_integration_busy.cpp
UNITTEST_CPPOBJ := $(patsubst %.cpp,$(OBJ_DIR)%.o,$(UNITTEST_SOURCES))

View File

@ -22,6 +22,7 @@
#include "shellmatta_history.h"
#include "shellmatta_utils.h"
#include "shellmatta_escape.h"
#include "shellmatta_opt.h"
#include <stddef.h>
#include <string.h>
#include <stdarg.h>
@ -74,8 +75,11 @@ shellmatta_retCode_t shellmatta_doInit(
inst->buffer = buffer;
inst->bufferSize = bufferSize;
inst->inputCount = 0u;
inst->byteCounter = 0u;
inst->lastNewlineIdx = 0u;
inst->cursor = 0u;
inst->stdinIdx = 0u;
inst->stdinLength = 0u;
inst->historyBuffer = historyBuffer;
inst->historyBufferSize = historyBufferSize;
inst->historyStart = 0u;
@ -91,13 +95,19 @@ shellmatta_retCode_t shellmatta_doInit(
inst->hereStartIdx = 0u;
inst->hereDelimiterIdx = 0u;
inst->hereLength = 0u;
inst->delimiter = '\r';
inst->mode = SHELLMATTA_MODE_INSERT;
inst->cmdList = &helpCmd;
inst->cmdList = &(inst->helpCmd);
inst->continuousCmd = NULL;
inst->busyCmd = NULL;
inst->cmdListIsConst = false;
/** -# copy the help command structure to this instance */
memcpy(&(inst->helpCmd), &helpCmd, sizeof(shellmatta_cmd_t));
if(NULL != cmdList)
{
helpCmd.next = (shellmatta_cmd_t *) cmdList;
inst->helpCmd.next = (shellmatta_cmd_t *) cmdList;
inst->cmdListIsConst = true;
}
@ -126,13 +136,18 @@ shellmatta_retCode_t shellmatta_resetShell( shellmatta_handle_t handle, bool pri
shellmatta_instance_t *inst = (shellmatta_instance_t *)handle;
shellmatta_retCode_t ret = SHELLMATTA_OK;
/*! -# check if the instance is plausible */
/** -# check if the instance is plausible */
if( (NULL != handle)
&& (SHELLMATTA_MAGIC == inst->magic))
{
inst->inputCount = 0u;
inst->byteCounter = 0u;
inst->continuousCmd = NULL;
inst->busyCmd = NULL;
inst->lastNewlineIdx = 0u;
inst->cursor = 0u;
inst->stdinIdx = 0u;
inst->stdinLength = 0u;
inst->historyStart = 0u;
inst->historyEnd = 0u;
inst->historyRead = 0u;
@ -268,25 +283,25 @@ shellmatta_retCode_t shellmatta_removeCmd(shellmatta_handle_t handle, shellmatta
tempCmd = inst->cmdList;
prevCmd = NULL;
/*! -# loop through command list */
/** -# loop through command list */
while(NULL != tempCmd)
{
/*! -# compare command strings to find the command to delete */
/** -# compare command strings to find the command to delete */
if (0 == strcmp( tempCmd->cmd,
cmd->cmd)
&& (strlen(tempCmd->cmd) == strlen(cmd->cmd)))
{
/*! -# first command removed */
/** -# first command removed */
if(NULL == prevCmd)
{
inst->cmdList = tempCmd->next;
}
/*! -# last command removed */
/** -# last command removed */
else if(NULL == tempCmd->next)
{
prevCmd->next = NULL;
}
/*! -# command removed from the middle of the list */
/** -# command removed from the middle of the list */
else
{
prevCmd->next = tempCmd->next;
@ -312,10 +327,14 @@ shellmatta_retCode_t shellmatta_removeCmd(shellmatta_handle_t handle, shellmatta
* @param[in] handle shellmatta instance handle
* @param[in] mode insert mode of the shellmatta type #shellmatta_mode_t
* @param[in] echoEnabled true: echo received chars to the output
* @param[in] delimiter delimiter used to detect the end of a cmd (default "\r")
* @return errorcode #SHELLMATTA_OK
* #SHELLMATTA_USE_FAULT (param err)
*/
shellmatta_retCode_t shellmatta_configure(shellmatta_handle_t handle, shellmatta_mode_t mode, bool echoEnabled)
shellmatta_retCode_t shellmatta_configure( shellmatta_handle_t handle,
shellmatta_mode_t mode,
bool echoEnabled,
char delimiter)
{
shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
shellmatta_retCode_t ret = SHELLMATTA_OK;
@ -327,6 +346,7 @@ shellmatta_retCode_t shellmatta_configure(shellmatta_handle_t handle, shellmatta
{
inst->mode = mode;
inst->echoEnabled = echoEnabled;
inst->delimiter = delimiter;
}
else
{
@ -352,49 +372,84 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
uint8_t cmdExecuted = 0u;
uint32_t cmdLen;
char *tempString;
char *argumentString;
uint32_t argumentLength;
uint32_t byteCounter;
uint32_t idx;
shellmatta_retCode_t ret = SHELLMATTA_OK;
shellmatta_retCode_t cmdRet;
shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
/** -# check parameters for plausibility */
if( (NULL != inst)
&& (SHELLMATTA_MAGIC == inst->magic))
{
/** -# process byte wise */
for (byteCounter = 0u; byteCounter < size; byteCounter++)
/** -# in busy mode - keep calling this command */
if(NULL != inst->busyCmd)
{
/*! -# set default values for the command argument - can be overwritten by heredoc */
argumentString = inst->buffer;
argumentLength = inst->inputCount;
/** -# just call the function until it is not busy anymore */
cmdRet = inst->busyCmd->cmdFct(handle, inst->buffer, inst->inputCount);
if(SHELLMATTA_BUSY != cmdRet)
{
utils_terminateInput(inst);
}
else if(SHELLMATTA_CONTINUE == cmdRet)
{
inst->continuousCmd = inst->busyCmd;
inst->busyCmd = NULL;
}
else
{
ret = cmdRet;
}
}
/** -# process byte wise */
for (; (inst->byteCounter < size) && (NULL == inst->busyCmd); inst->byteCounter++)
{
/** -# in continuous mode - pass data directly to the command */
if(NULL != inst->continuousCmd)
{
/** -# copy data and call command function */
inst->buffer[inst->stdinIdx] = data[inst->byteCounter];
inst->stdinLength = 1u;
cmdRet = inst->continuousCmd->cmdFct(handle, inst->buffer, inst->inputCount);
/** -# check if continuous mode is canceled or interrupted by busy mode */
if(SHELLMATTA_BUSY == cmdRet)
{
inst->busyCmd = inst->busyCmd;
inst->continuousCmd = NULL;
}
else if(('\x03' == data[inst->byteCounter]))
{
utils_terminateInput(inst);
}
else if(SHELLMATTA_CONTINUE == cmdRet)
{
/** -# do nothing - continue */
}
else
{
utils_terminateInput(inst);
}
}
/** -# handle escape sequences */
if(inst->escapeCounter != 0u)
else if(inst->escapeCounter != 0u)
{
escape_handleSequence(inst, *data);
escape_handleSequence(inst, data[inst->byteCounter]);
}
/** -# ignore newline as first character (to be compatible to
* terminals sending newline after return */
else if((0u == inst->inputCount) && ('\n' == *data))
{
/* do nothing */
}
/** -# handle return as start of processing the command */
else if ('\r' == *data)
/** -# handle delimiter as start of processing the command */
else if (inst->delimiter == data[inst->byteCounter])
{
if(0u == inst->hereLength)
{
/**
* \dot
* @dot
* digraph heredocParser {
* start -> wait [ label="<< in first line - store delimiter" ];
* wait -> wait [ label="delimiter not detected" ];
* wait -> end [ label="delimiter found - remove all heredoc stuff and send the input to the command" ];
* }
* \enddot */
* @enddot */
/** -# check for heredoc - add string delimiter to stop strstr from searching too far */
inst->buffer[inst->inputCount] = '\0';
@ -416,7 +471,7 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
inst->hereLength = inst->inputCount - inst->hereDelimiterIdx;
inst->dirty = true;
utils_insertChars(inst, data, 1);
utils_insertChars(inst, &data[inst->byteCounter], 1u);
inst->lastNewlineIdx = inst->inputCount;
}
else
@ -454,45 +509,56 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
tempString,
inst->hereLength)))
{
argumentLength = inst->lastNewlineIdx;
/** -# store the current command and reset the history buffer */
inst->dirty = true;
history_storeCmd(inst);
history_reset(inst);
/* TODO it is difficult to store the complete command in the history buffer if it is restructured before...
* So this should be an extra function that can be called after parsing the command and before calling the command function */
for(idx = 1u; idx <= inst->hereStartIdx; idx++)
/** -# process heredoc as stdin like input */
/** -# find start of heredoc data */
inst->stdinIdx = inst->hereDelimiterIdx + inst->hereLength;
while( ('\n' == inst->buffer[inst->stdinIdx])
|| ('\r' == inst->buffer[inst->stdinIdx]))
{
inst->buffer[inst->hereDelimiterIdx + inst->hereLength - idx] = inst->buffer[inst->hereStartIdx - idx];
inst->stdinIdx ++;
}
/** -# calculate length and terminate stdin string */
if(inst->stdinIdx < inst->lastNewlineIdx)
{
inst->stdinLength = inst->lastNewlineIdx - inst->stdinIdx;
inst->buffer[inst->lastNewlineIdx] = '\0';
}
else
{
inst->stdinLength = 0u;
}
argumentString = &(inst->buffer[inst->hereDelimiterIdx + inst->hereLength - inst->hereStartIdx]);
argumentLength = inst->lastNewlineIdx - ((inst->hereDelimiterIdx + inst->hereLength) - inst->hereStartIdx);
/** -# calculate length and terminate argument string */
inst->inputCount = inst->hereStartIdx;
inst->buffer[inst->hereStartIdx] = '\0';
/** -# terminate heredoc session */
inst->hereLength = 0u;
}
else
{
/*! -# the party goes on - print the \r and add a \n to satisfy most terminals */
/** -# the party goes on - just print the delimiter and store the position */
inst->lastNewlineIdx = inst->inputCount;
utils_insertChars(inst, data, 1u);
utils_insertChars(inst, &data[inst->byteCounter], 1u);
}
}
if(0u == inst->hereLength)
{
cmd = inst->cmdList;
argumentString[argumentLength] = 0u;
cmd = inst->cmdList;
/** -# determine the cmd len (chars until first space or \0 is found */
cmdLen = 0u;
while( (cmdLen < argumentLength)
&& (' ' != argumentString[cmdLen])
&& ('\r' != argumentString[cmdLen])
&& ('\n' != argumentString[cmdLen])
&& ('\0' != argumentString[cmdLen]))
while( (cmdLen < inst->inputCount)
&& (' ' != inst->buffer[cmdLen])
&& ('\r' != inst->buffer[cmdLen])
&& ('\n' != inst->buffer[cmdLen])
&& ('\0' != inst->buffer[cmdLen]))
{
cmdLen ++;
}
@ -501,20 +567,39 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
while (NULL != cmd)
{
/** -# compare command and alias string and length */
if ( ((0 == strncmp( argumentString,
cmd->cmd,
cmdLen))
if ( ((0 == strncmp( inst->buffer,
cmd->cmd,
cmdLen))
&& (cmdLen == strlen(cmd->cmd)))
|| ((NULL != cmd->cmdAlias)
&& ((0 == strncmp( argumentString,
&& ((0 == strncmp( inst->buffer,
cmd->cmdAlias,
cmdLen))
&& (cmdLen == strlen(cmd->cmdAlias)))))
{
utils_writeEcho(inst, "\r\n", 2u);
shellmatta_opt_init(inst, cmdLen + 1u);
cmdExecuted = 1u;
cmd->cmdFct(inst, argumentString, argumentLength);
cmdRet = cmd->cmdFct(handle, inst->buffer, inst->inputCount);
switch(cmdRet)
{
case SHELLMATTA_CONTINUE:
/** -# initialize stdin buffer and continuous cmd */
inst->stdinIdx = inst->inputCount + 1u;
inst->stdinLength = 0u;
inst->continuousCmd = cmd;
break;
case SHELLMATTA_BUSY:
inst->busyCmd = cmd;
ret = cmdRet;
break;
default:
/* nothing to do - everything ok */
break;
}
cmd = NULL;
}
else
@ -526,52 +611,67 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
if ((cmdExecuted == 0u) && (inst->inputCount > 0))
{
inst->write("\r\nCommand: ", 11u);
inst->write(argumentString, argumentLength);
inst->write(inst->buffer, inst->inputCount);
inst->write(" not found", 10u);
}
utils_terminateInput(inst);
}
/** -# terminate this session if no continuous mode is requested */
if( (NULL == inst->continuousCmd)
&& (NULL == inst->busyCmd))
{
utils_terminateInput(inst);
}
}
}
/** -# ignore newline as first character (to be compatible to
* terminals sending newline after return */
else if((0u == inst->inputCount) && ('\n' == data[inst->byteCounter]))
{
/* do nothing */
}
/** -# check for tabulator key - auto complete */
else if('\t' == *data)
else if('\t' == data[inst->byteCounter])
{
inst->dirty = true;
autocomplete_run(inst);
}
/** -# check for cancel -
* terminate current input and print prompt again */
else if(3 == *data)
else if('\x03' == data[inst->byteCounter])
{
inst->dirty = false;
history_reset(inst);
utils_terminateInput(inst);
}
/** -# check for backspace */
else if( ('\b' == *data)
|| ('\x7f' == *data))
else if( ('\b' == data[inst->byteCounter])
|| ('\x7f' == data[inst->byteCounter]))
{
inst->dirty = true;
utils_removeChars(inst, 1u, true);
}
/** -# check for start of escape sequence */
else if('\x1b' == *data)
else if('\x1b' == data[inst->byteCounter])
{
inst->escapeCounter = 1u;
}
else
{
inst->dirty = true;
utils_insertChars(inst, data, 1);
utils_insertChars(inst, &data[inst->byteCounter], 1u);
}
/** -# reset tab counter on not a tab */
if ('\t' != *data)
if ('\t' != data[inst->byteCounter])
{
inst->tabCounter = 0u;
}
}
data ++;
/*! -# initialize the byte buffer if processing of the input is finished */
if(ret != SHELLMATTA_BUSY)
{
inst->byteCounter = 0u;
}
}
else
@ -606,6 +706,41 @@ shellmatta_retCode_t shellmatta_write( shellmatta_handle_t handle,
return ret;
}
/**
* @brief reads the stdin like buffer
* @param[in] handle shellmatta instance handle
* @param[out] data stdin data or NULL
* @param[out] length size of the stdin data
* @return
*/
shellmatta_retCode_t shellmatta_read( shellmatta_handle_t handle,
char **data,
uint32_t *length)
{
shellmatta_retCode_t ret = SHELLMATTA_USE_FAULT;
shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
/** -# check parameters for plausibility */
if( (NULL != inst)
&& (SHELLMATTA_MAGIC == inst->magic)
&& (NULL != data)
&& (NULL != length))
{
/** -# return a pointer to the data or NULL */
if(0u == inst->stdinLength)
{
*data = NULL;
}
else
{
*data = &(inst->buffer[inst->stdinIdx]);
}
*length = inst->stdinLength;
}
return ret;
}
#ifndef SHELLMATTA_STRIP_PRINTF
/**

View File

@ -44,11 +44,11 @@ shellmatta_retCode_t escape_processArrowKeys(shellmatta_instance_t *inst)
history_storeCmd(inst);
if(false == inst->historyReadUp)
{
history_navigate(inst, -1);
(void)history_navigate(inst, -1);
}
inst->historyReadUp = true;
history_restoreCmd(inst);
history_navigate(inst, -1);
(void)history_navigate(inst, -1);
break;
case 'B': /* arrow down */
@ -58,10 +58,10 @@ shellmatta_retCode_t escape_processArrowKeys(shellmatta_instance_t *inst)
history_storeCmd(inst);
if(true == inst->historyReadUp)
{
history_navigate(inst, 1);
(void)history_navigate(inst, 1);
}
inst->historyReadUp = false;
history_navigate(inst, 1);
(void)history_navigate(inst, 1);
history_restoreCmd(inst);
}
break;

View File

@ -59,7 +59,7 @@ static void appendHistoryByte(shellmatta_instance_t *inst, char byte)
* @brief reads a byte from the history buffer and decreases the read index
* @param[in] inst pointer to a shellmatta instance
* @param[out] byte pointer to a char where the read out byte will be stored
* @return
* @return false: no new byte to read
*/
static bool getHistoryByte(shellmatta_instance_t *inst, char *byte)
{
@ -84,6 +84,12 @@ static bool getHistoryByte(shellmatta_instance_t *inst, char *byte)
return ret;
}
/**
* @brief navigates in the history buffer by the given number of commands
* @param[in, out] inst pointer to a shellmatta instance
* @param[in] cnt direction and count to navigate
* @return false: end of buffer reached
*/
bool history_navigate(shellmatta_instance_t *inst, int32_t cnt)
{
bool ret = true;
@ -185,13 +191,6 @@ void history_storeCmd(shellmatta_instance_t *inst)
inst->dirty = false;
}
/**
* @brief navigates in the history buffer by the given number of commands
* @param[in] inst pointer to a shellmatta instance
* @param[in] cnt direction and count to navigate
* @return
*/
/**
* @brief restores the command from the history buffer where the read
* index points on
@ -205,7 +204,7 @@ void history_restoreCmd(shellmatta_instance_t *inst)
ret = getHistoryByte(inst, &byte);
/*! -# delete the input if there is data in the history buffer */
/** -# delete the input if there is data in the history buffer */
if(true == ret)
{
utils_clearInput(inst);
@ -224,7 +223,7 @@ void history_restoreCmd(shellmatta_instance_t *inst)
utils_writeEcho(inst, inst->buffer, inst->inputCount);
inst->dirty = false;
}
history_navigate(inst, 1);
(void)history_navigate(inst, 1);
}
/**

423
src/shellmatta_opt.c Normal file
View File

@ -0,0 +1,423 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/**
* @file shellmatta_opt.c
* @brief option parser implementation of the shellmatta
* @author Stefan Strobel <stefan.strobel@shimatta.net>
*/
/**
* @addtogroup shellmatta_opt
* @{
*/
#include "shellmatta_opt.h"
#include "shellmatta_utils.h"
#include "shellmatta.h"
#include <string.h>
/**
* @brief finds the next parsable hunk of data in the input
* @param[in] inst shellmatta instance
* @return errorcode #SHELLMATTA_OK - new hunk found
* #SHELLMATTA_ERROR - error parsing or end of input
*/
static shellmatta_retCode_t findNextHunk(shellmatta_instance_t *inst)
{
shellmatta_retCode_t ret = SHELLMATTA_ERROR;
uint32_t newOffset = inst->optionParser.nextOffset;
uint32_t exeptionOffset = 0u;
char quotation = '\0'; /* holds the current quotation mark if any */
/** -# find beginning of next hunk */
while( (newOffset < inst->inputCount)
&& ((' ' == inst->buffer[newOffset])
|| ('\0' == inst->buffer[newOffset])))
{
newOffset ++;
}
inst->optionParser.offset = newOffset;
/** -# determine length */
while((newOffset < inst->inputCount)
&& (((' ' != inst->buffer[newOffset]) && ('\0' != inst->buffer[newOffset])) || '\0' != quotation))
{
/** -# check for new quotation */
if((('\'' == inst->buffer[newOffset]) || ('"' == inst->buffer[newOffset])) && (quotation == '\0'))
{
quotation = inst->buffer[newOffset];
exeptionOffset ++;
}
/** -# check if quotation has ended */
else if(quotation == inst->buffer[newOffset])
{
exeptionOffset ++;
/** -# check if quotation is excaped */
if('\\' != inst->buffer[newOffset - 1u])
{
quotation = '\0';
}
else
{
inst->buffer[newOffset - exeptionOffset] = inst->buffer[newOffset];
}
}
else
{
/** -# shift back chars */
if(0u != exeptionOffset)
{
inst->buffer[newOffset - exeptionOffset] = inst->buffer[newOffset];
}
}
newOffset ++;
}
inst->optionParser.nextOffset = newOffset;
inst->optionParser.len = newOffset - inst->optionParser.offset - exeptionOffset;
/** -# add terminating 0 */
inst->buffer[inst->optionParser.offset + inst->optionParser.len] = '\0';
if((inst->optionParser.offset < inst->inputCount) && (0u != inst->optionParser.len) && ('\0' == quotation))
{
ret = SHELLMATTA_OK;
}
return ret;
}
/**
* @brief peeks the first char of the next hunk
* @param[in] inst shellmatta instance
* @return char first char of next hunk \0 if not existing
*/
static char peekNextHunk(shellmatta_instance_t *inst)
{
uint32_t newOffset = inst->optionParser.nextOffset;
/** -# find beginning of next hunk */
while( (newOffset < inst->inputCount)
&& ((' ' == inst->buffer[newOffset])
|| ('\0' == inst->buffer[newOffset])))
{
newOffset ++;
}
return inst->buffer[newOffset];
}
/**
* @brief tries to parse the current input hunk and check if this is a configured option
* @param[in] inst pointer to shellmatta instance
* @param[in] optionString option string e.g. "cd:e::"
* @param[out] option pointer to store the detected option to
* @param[out] argtype pointer to var of type #shellmatta_opt_argtype_t != NULL
* @return errorcode #SHELLMATTA_OK - option parsable and found in option String
* #SHELLMATTA_ERROR - format error or option unknown
*/
static shellmatta_retCode_t parseShortOpt( shellmatta_instance_t *inst,
const char *optionString,
char *option,
shellmatta_opt_argtype_t *argtype)
{
shellmatta_retCode_t ret = SHELLMATTA_ERROR;
char *buffer = &inst->buffer[inst->optionParser.offset];
uint32_t i;
/** -# check for correct syntax */
if((2u == inst->optionParser.len) && ('-' == buffer[0u]) && ('-' != buffer[1u]) && ('\0' != buffer[1u]))
{
*option = '\0';
/** -# search for option character in option string */
for(i = 0u; ('\0' != optionString[i]) && ('\0' == *option); i ++)
{
if(buffer[1u] == optionString[i])
{
ret = SHELLMATTA_OK;
/** -# return found option character */
*option = buffer[1u];
/** -# check if an argument is required or optional */
if(':' == optionString[i + 1u])
{
*argtype = SHELLMATTA_OPT_ARG_REQUIRED;
if(':' == optionString[i + 2u])
{
*argtype = SHELLMATTA_OPT_ARG_OPTIONAL;
}
}
else
{
*argtype = SHELLMATTA_OPT_ARG_NONE;
}
}
}
}
/** -# skip "--" */
else if((2u == inst->optionParser.len) && ('-' == buffer[0u]) && ('-' == buffer[1u]))
{
ret = SHELLMATTA_CONTINUE;
}
else
{
*option = '\0';
}
return ret;
}
/**
* @brief tries to parse the current input hunk and check if this is a configured option
* @param[in] inst pointer to shellmatta instance
* @param[in] longOptions option structure - pointer to array of type #shellmatta_opt_long_t
* @param[out] option pointer to store the detected option to
* @param[out] argtype pointer to var of type #shellmatta_opt_argtype_t != NULL
* @return errorcode #SHELLMATTA_OK - option parsable and found in option String
* #SHELLMATTA_ERROR - format error or option unknown
*/
static shellmatta_retCode_t parseLongOpt( shellmatta_instance_t *inst,
const shellmatta_opt_long_t *longOptions,
char *option,
shellmatta_opt_argtype_t *argtype)
{
shellmatta_retCode_t ret = SHELLMATTA_ERROR;
char *buffer = &inst->buffer[inst->optionParser.offset];
uint32_t i;
/** -# check for correct syntax for short options */
if((2u == inst->optionParser.len) && ('-' == buffer[0u]) && ('-' != buffer[1u]) && ('\0' != buffer[1u]))
{
/** -# search for option character in option list */
for(i = 0u; ('\0' != longOptions[i].paramShort) && ('\0' == *option); i ++)
{
if(buffer[1u] == longOptions[i].paramShort)
{
ret = SHELLMATTA_OK;
/** -# return found option character */
*option = longOptions[i].paramShort;
*argtype = longOptions[i].argtype;
}
}
}
/** -# check for correct syntax for long options */
else if((3u <= inst->optionParser.len) && ('-' == buffer[0u]) && ('-' == buffer[1u]))
{
/** -# search for long option in option list */
for(i = 0u; ('\0' != longOptions[i].paramShort) && ('\0' == *option); i ++)
{
if(0 == strcmp(&buffer[2u], longOptions[i].paramLong))
{
ret = SHELLMATTA_OK;
/** -# return found option character */
*option = longOptions[i].paramShort;
*argtype = longOptions[i].argtype;
}
}
}
/** -# ignore "--" */
else if((2u == inst->optionParser.len) && ('-' == buffer[0u]) && ('-' == buffer[1u]))
{
*option = '\0';
ret = SHELLMATTA_CONTINUE;
}
else
{
*option = '\0';
}
return ret;
}
/**
* @brief Scans the current input and parses options in getopt style - pass either optionString or longOptions
* This is an internal funtion to handle both getopt styles and remove duplicated code...
* The standard functions are just wrapper around this one.
* @param[in] handle shellmatta handle
* @param[in] optionString option string e.g. "cd:e::"
* @param[in] longOptions option structure - pointer to array of type #shellmatta_opt_long_t
* @param[out] option pointer to store the detected option to
* @param[out] argument pointer to store the argument string to (can be NULL)
* @param[out] argLen pointer to store the argument lengh to (can be NULL)
* @return errorcode #SHELLMATTA_OK - no error - keep on calling
* #SHELLMATTA_ERROR - error occured - e.g. argument missing
*/
static shellmatta_retCode_t shellmatta_opt_int( shellmatta_handle_t handle,
const char *optionString,
const shellmatta_opt_long_t *longOptions,
char *option,
char **argument,
uint32_t *argLen)
{
shellmatta_retCode_t ret = SHELLMATTA_USE_FAULT;
shellmatta_instance_t *inst = (shellmatta_instance_t*)handle;
shellmatta_opt_argtype_t argtype = SHELLMATTA_OPT_ARG_NONE;
/** -# check parameters for plausibility */
if( (NULL != inst)
&& (SHELLMATTA_MAGIC == inst->magic)
&& (NULL != option))
{
*option = '\0';
if(NULL != argument)
{
*argument = NULL;
}
if(NULL != argLen)
{
*argLen = 0u;
}
/** -# do this until we find a not skipable argument */
do
{
ret = findNextHunk(inst);
if(SHELLMATTA_OK == ret)
{
/** -# call the matching parse function */
if(NULL != optionString)
{
ret = parseShortOpt(inst, optionString, option, &argtype);
}
else if(NULL != longOptions)
{
ret = parseLongOpt(inst, longOptions, option, &argtype);
}
else
{
ret = SHELLMATTA_USE_FAULT;
}
/** -# when no option is found return this as raw argument */
if(SHELLMATTA_ERROR == ret)
{
if(NULL != argument)
{
*argument = &(inst->buffer[inst->optionParser.offset]);
}
if(NULL != argLen)
{
*argLen = inst->optionParser.len;
}
ret = SHELLMATTA_OK;
}
else if(SHELLMATTA_USE_FAULT == ret)
{
/** -# nothing to do - just return errorcode */
}
else
{
switch(argtype)
{
case SHELLMATTA_OPT_ARG_REQUIRED:
ret = findNextHunk(inst);
if((NULL == argument) || (NULL == argLen))
{
ret = SHELLMATTA_USE_FAULT;
}
if(SHELLMATTA_OK == ret)
{
*argument = &(inst->buffer[inst->optionParser.offset]);
*argLen = inst->optionParser.len;
}
break;
case SHELLMATTA_OPT_ARG_OPTIONAL:
/** -# treat anything not starting with '-' as argument */
if('-' != peekNextHunk(inst))
{
ret = findNextHunk(inst);
if((NULL == argument) || (NULL == argLen))
{
ret = SHELLMATTA_USE_FAULT;
}
if(SHELLMATTA_OK == ret)
{
*argument = &(inst->buffer[inst->optionParser.offset]);
*argLen = inst->optionParser.len;
}
}
break;
default:
/* nothing to do */
break;
}
}
}
} while(SHELLMATTA_CONTINUE == ret);
}
return ret;
}
/**
* @brief scans the current input and parses options in getopt style
* @param[in] handle shellmatta handle
* @param[in] optionString option string e.g. "cd:e::"
* @param[out] option pointer to store the detected option to
* @param[out] argument pointer to store the argument string to (can be NULL)
* @param[out] argLen pointer to store the argument lengh to (can be NULL)
* @return errorcode #SHELLMATTA_OK - no error - keep on calling
* #SHELLMATTA_ERROR - error occured - e.g. argument missing
*/
shellmatta_retCode_t shellmatta_opt( shellmatta_handle_t handle,
const char *optionString,
char *option,
char **argument,
uint32_t *argLen)
{
return shellmatta_opt_int( handle,
optionString,
NULL,
option,
argument,
argLen);
}
/**
* @brief scans the current input and parses options in getopt_long style
* @param[in] handle shellmatta handle
* @param[in] longOptions option structure - pointer to array of type #shellmatta_opt_long_t
* @param[out] option pointer to store the detected option to
* @param[out] argument pointer to store the argument string to (can be NULL)
* @param[out] argLen pointer to store the argument lengh to (can be NULL)
*/
shellmatta_retCode_t shellmatta_opt_long( shellmatta_handle_t handle,
const shellmatta_opt_long_t *longOptions,
char *option,
char **argument,
uint32_t *argLen)
{
return shellmatta_opt_int( handle,
NULL,
longOptions,
option,
argument,
argLen);
}
/**
* @brief initializes the option parser instance
* @param[in, out] inst pointer to a shellmatta instance
* @param[in] argStart start offset of the arguments (after command name/alias)
*/
shellmatta_retCode_t shellmatta_opt_init(shellmatta_instance_t *inst, uint32_t argStart)
{
/** -# initialize all relevant option parser variables */
inst->optionParser.nextOffset = argStart;
return SHELLMATTA_OK;
}
/**
* @}
*/

43
src/shellmatta_opt.h Normal file
View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/**
* @file shellmatta_opt.h
* @brief option parser implementation of the shellmatta
* @author Stefan Strobel <stefan.strobel@shimatta.net>
*/
/**
* @addtogroup shellmatta_opt
* @{
*/
#ifndef _SHELLMATTA_OPT_H_
#define _SHELLMATTA_OPT_H_
#include "shellmatta.h"
#include <stdint.h>
shellmatta_retCode_t shellmatta_opt( shellmatta_handle_t handle,
const char *optionString,
char *option,
char **argument,
uint32_t *argLen);
shellmatta_retCode_t shellmatta_opt_long( shellmatta_handle_t handle,
const shellmatta_opt_long_t *longOptions,
char *option,
char **argument,
uint32_t *argLen);
shellmatta_retCode_t shellmatta_opt_init( shellmatta_instance_t *inst,
uint32_t argStart);
#endif
/** @} */

View File

@ -294,7 +294,7 @@ static shellmatta_retCode_t helpCmdFct(shellmatta_handle_t handle, const char *a
/** -# loop through all commands to determine the lengths of each cmd */
while(NULL != cmd)
{
maxCmdLen = SHELLMATTA_MAX(maxCmdLen, strlen(cmd->cmd));
maxCmdLen = SHELLMATTA_MAX(maxCmdLen, strlen(cmd->cmd));
if(NULL != cmd->cmdAlias)
{
maxCmdAliasLen = SHELLMATTA_MAX(maxCmdAliasLen, strlen(cmd->cmdAlias));
@ -347,12 +347,12 @@ static shellmatta_retCode_t helpCmdFct(shellmatta_handle_t handle, const char *a
return ret;
}
shellmatta_cmd_t helpCmd = { SHELLMATTA_HELP_COMMAND
, SHELLMATTA_HELP_ALIAS
, SHELLMATTA_HELP_HELP_TEXT
, SHELLMATTA_HELP_USAGE_TEXT
, helpCmdFct
, NULL};
const shellmatta_cmd_t helpCmd = {SHELLMATTA_HELP_COMMAND
, SHELLMATTA_HELP_ALIAS
, SHELLMATTA_HELP_HELP_TEXT
, SHELLMATTA_HELP_USAGE_TEXT
, helpCmdFct
, NULL};
/**
* @brief terminates an input and prints the prompt again
@ -364,6 +364,10 @@ void utils_terminateInput(shellmatta_instance_t *inst)
inst->lastNewlineIdx = 0u;
inst->hereLength = 0u;
inst->cursor = 0u;
inst->stdinIdx = 0u;
inst->stdinLength = 0u;
inst->continuousCmd = NULL;
inst->busyCmd = NULL;
inst->write("\r\n", 2u);
inst->write(inst->prompt, strlen(inst->prompt));
}

View File

@ -22,9 +22,26 @@
#include "shellmatta.h"
#include <stdint.h>
/* some helper macros */
/**
* @brief returns the minimum of a and b
* @param[in] a parameter a
* @param[in] b parameter b
*/
#define SHELLMATTA_MIN(a,b) (((a) > (b)) ? (b) : (a))
/**
* @brief returns the maximum of a and b
* @param[in] a parameter a
* @param[in] b parameter b
*/
#define SHELLMATTA_MAX(a,b) (((a) < (b)) ? (b) : (a))
/**
* @brief calls fct with cnt bytes from buffer (to print cnt same bytes)
* @param[in] buffer buffer to send (shall contain the same char)
* @param[in] cnt count of bytes to send
* @param[in] fct write function
*/
#define SHELLMATTA_PRINT_BUFFER(buffer,cnt,fct) \
while((cnt) > sizeof((buffer))) \
{ \
@ -36,16 +53,18 @@
(fct)((buffer), (cnt)); \
}
extern shellmatta_cmd_t helpCmd;
/** @brief help command which prints all shellmatta commands as table */
extern const shellmatta_cmd_t helpCmd;
/** @brief magic used to check if a shellmatta instance is initiated */
#define SHELLMATTA_MAGIC 0x5101E110u
/*! \brief overwritable output buffer size */
/** @brief overwritable output buffer size */
#ifndef SHELLMATTA_OUTPUT_BUFFER_SIZE
#define SHELLMATTA_OUTPUT_BUFFER_SIZE 128u
#endif
/*! @defgroup Shellmatta Help command overwrites
/** @defgroup Shellmatta Help command overwrites
* @{
* overwritable help command parameters - the help command is built in and cannot be removed, but you can change
* the command, alias and help texts by defining them in your build process
@ -57,18 +76,18 @@ extern shellmatta_cmd_t helpCmd;
* e.g. use _-DSHELLMATTA_HELP_ALIAS=\"?\"_ as compile option to change the alias to ?
*/
#ifndef SHELLMATTA_HELP_COMMAND
#define SHELLMATTA_HELP_COMMAND (char*)"help" /*!< help command */
#define SHELLMATTA_HELP_COMMAND (char*)"help" /**< help command */
#endif
#ifndef SHELLMATTA_HELP_ALIAS
#define SHELLMATTA_HELP_ALIAS (char*)"h" /*!< help command alias */
#define SHELLMATTA_HELP_ALIAS (char*)"h" /**< help command alias */
#endif
#ifndef SHELLMATTA_HELP_HELP_TEXT
#define SHELLMATTA_HELP_HELP_TEXT (char*)"Print this help text" /*!< help command help text */
#define SHELLMATTA_HELP_HELP_TEXT (char*)"Print this help text" /**< help command help text */
#endif
#ifndef SHELLMATTA_HELP_USAGE_TEXT
#define SHELLMATTA_HELP_USAGE_TEXT (char*)"help" /*!< help command usage text */
#define SHELLMATTA_HELP_USAGE_TEXT (char*)"help" /**< help command usage text */
#endif
/*!
/**
* @}
*/

View File

@ -9,6 +9,8 @@ static char write_data[1024];
static uint32_t write_length;
static const char *doSomethingArguments;
static uint32_t doSomethingLength;
static char *doSomethingStdin;
static uint32_t doSomethingStdinLength;
static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
{
@ -28,6 +30,9 @@ static shellmatta_retCode_t doSomething(shellmatta_handle_t handle, const char *
{
doSomethingArguments = arguments;
doSomethingLength = length;
shellmatta_read(handle, &doSomethingStdin, &doSomethingStdinLength);
shellmatta_printf(handle, "%s - length: %u", arguments, length);
return SHELLMATTA_OK;
}
@ -140,8 +145,8 @@ TEST_CASE( "shellmatta heredoc test" ) {
shellmatta_handle_t handle;
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"do this \r\n"
"asdf\r\n"
char *dummyData = (char*)"do this ";
char *dummyStdin = (char*)"asdf\r\n"
"1234";
shellmatta_doInit( &inst,
@ -162,14 +167,45 @@ TEST_CASE( "shellmatta heredoc test" ) {
"asdf\r\n"
"1234\r\n"
"EOF\r\n"
, 34);
, 33);
CHECK( doSomethingLength == 20u);
CHECK( doSomethingStdinLength == 10u);
CHECK( strcmp(dummyStdin, doSomethingStdin) == 0);
CHECK( doSomethingLength == 8u);
REQUIRE( strcmp(dummyData, doSomethingArguments) == 0);
}
TEST_CASE( "shellmatta heredoc test empty" ) {
shellmatta_instance_t inst;
shellmatta_handle_t handle;
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"do this ";
shellmatta_doInit( &inst,
&handle,
buffer,
sizeof(buffer),
historyBuffer,
sizeof(historyBuffer),
"shellmatta->",
NULL,
writeFct);
shellmatta_addCmd(handle, &doSomethingCmd);
doSomethingArguments = NULL;
doSomethingLength = 0u;
shellmatta_processData(handle, (char*)"do this << EOF\r\n"
"EOF\r\n"
, 21);
CHECK( doSomethingStdinLength == 0u);
CHECK( NULL == doSomethingStdin );
CHECK( doSomethingLength == 8u);
REQUIRE( strcmp(dummyData, doSomethingArguments) == 0);
}
TEST_CASE( "shellmatta remove function" ) {
@ -423,7 +459,7 @@ TEST_CASE( "shellmatta configure disable echo" ) {
write_length = 0u;
/* turn off echo now */
ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false);
ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');
CHECK( ret == SHELLMATTA_OK );
/* check with echo disabled */
@ -454,7 +490,7 @@ TEST_CASE( "shellmatta configure mode" ) {
NULL,
writeFct);
shellmatta_addCmd(handle, &doSomethingCmd);
ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false);
ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
@ -471,7 +507,7 @@ TEST_CASE( "shellmatta configure mode" ) {
write_length = 0u;
/* check with overwrite mode */
ret = shellmatta_configure(handle, SHELLMATTA_MODE_OVERWRITE, false);
ret = shellmatta_configure(handle, SHELLMATTA_MODE_OVERWRITE, false, '\r');
CHECK( ret == SHELLMATTA_OK );
/* check with echo disabled */
@ -481,3 +517,49 @@ TEST_CASE( "shellmatta configure mode" ) {
REQUIRE( strcmp(dummyData2, write_data) == 0);
}
TEST_CASE( "shellmatta configure delimiter" ) {
shellmatta_instance_t inst;
shellmatta_handle_t handle;
shellmatta_retCode_t ret;
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"doSomething argument - length: 20\r\nshellmatta->";
shellmatta_doInit( &inst,
&handle,
buffer,
sizeof(buffer),
historyBuffer,
sizeof(historyBuffer),
"shellmatta->",
NULL,
writeFct);
shellmatta_addCmd(handle, &doSomethingCmd);
ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\r');
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_length = 0u;
/* check with insert mode */
shellmatta_processData(handle, (char*)"doSomething argument\n", 21u);
CHECK( write_length == 0u);
shellmatta_resetShell(handle, false);
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_length = 0u;
/* check with changed delimiter mode */
ret = shellmatta_configure(handle, SHELLMATTA_MODE_INSERT, false, '\n');
CHECK( ret == SHELLMATTA_OK );
/* check with echo disabled */
shellmatta_processData(handle, (char*)"doSomething argument\n", 21u);
CHECK( write_length == strlen(dummyData));
REQUIRE( strcmp(dummyData, write_data) == 0);
}

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/**
* @file test_integration_busy.cpp
* @brief integration test implementation for the cmd busy function
* @author Stefan Strobel <stefan.strobel@shimatta.net>
*/
#include "test/framework/catch.hpp"
extern "C" {
#include "shellmatta.h"
}
#include <string.h>
static uint32_t write_callCnt = 0u;
static char write_data[1024];
static uint32_t write_length;
static uint32_t busyCallCnt;
static uint32_t notBusyCallCnt;
static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
{
write_callCnt ++;
while((length > 0) && (write_length < sizeof(write_data)))
{
write_data[write_length] = *data;
data ++;
length --;
write_length ++;
}
return SHELLMATTA_OK;
}
static shellmatta_retCode_t busyCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
(void) handle;
(void) arguments;
(void) length;
shellmatta_retCode_t ret = SHELLMATTA_BUSY;
static const char *callArgs = NULL;
static uint32_t callLength = 0u;;
if(busyCallCnt < 10u)
{
if(NULL == callArgs)
{
callArgs = arguments;
callLength = length;
}
else
{
CHECK(callArgs == arguments);
CHECK(callLength == length);
}
busyCallCnt ++;
}
else
{
ret = SHELLMATTA_OK;
}
return ret;
}
shellmatta_cmd_t busyCmd = {(char*)"busy", (char*)"b", NULL, NULL, busyCmdFct, NULL};
static shellmatta_retCode_t notBusyCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
(void) handle;
(void) arguments;
(void) length;
notBusyCallCnt ++;
return SHELLMATTA_OK;
}
shellmatta_cmd_t notBusyCmd = {(char*)"notBusy", (char*)"n", NULL, NULL, notBusyCmdFct, NULL};
TEST_CASE( "shellmatta busy 1" ) {
shellmatta_retCode_t ret;
shellmatta_instance_t inst;
shellmatta_handle_t handle;
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*) "busy and some arguments\r\n"
"\r\nshellmatta->notBusy and some arguments\r\n"
"\r\nshellmatta->";
shellmatta_doInit( &inst,
&handle,
buffer,
sizeof(buffer),
historyBuffer,
sizeof(historyBuffer),
"shellmatta->",
NULL,
writeFct);
busyCallCnt = 0u;
notBusyCallCnt = 0u;
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_length = 0u;
shellmatta_addCmd(handle, &busyCmd);
shellmatta_addCmd(handle, &notBusyCmd);
do
{
ret = shellmatta_processData(handle, (char*)"busy and some arguments\r"
"notBusy and some arguments\r", 51);
} while (SHELLMATTA_BUSY == ret);
CHECK( 10u == busyCallCnt);
CHECK( 1u == notBusyCallCnt );
CHECK( write_length == strlen(dummyData));
REQUIRE( strcmp(dummyData, write_data) == 0);
}

View File

@ -0,0 +1,218 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/**
* @file test_integration_opt.cpp
* @brief integration test implementation for the option parser of the shellmatta
* @author Stefan Strobel <stefan.strobel@shimatta.net>
*/
#include "test/framework/catch.hpp"
extern "C" {
#include "shellmatta.h"
}
#include <string.h>
static uint32_t write_callCnt = 0u;
static char write_data[1024];
static uint32_t write_length;
static uint32_t cntA = 0u;
static uint32_t cntB = 0u;
static uint32_t cntC = 0u;
static uint32_t cntD = 0u;
static uint32_t cntE = 0u;
static uint32_t cntF = 0u;
static uint32_t cntDef = 0u;
static char *argA = NULL;
static char *argB = NULL;
static char *argC = NULL;
static char *argD = NULL;
static char *argE = NULL;
static char *argF = NULL;
static char *argDef = NULL;
static uint32_t lenA = 0u;
static uint32_t lenB = 0u;
static uint32_t lenC = 0u;
static uint32_t lenD = 0u;
static uint32_t lenE = 0u;
static uint32_t lenF = 0u;
static uint32_t lenDef = 0u;
static void initTestcase(void)
{
cntA = 0u;
cntB = 0u;
cntC = 0u;
cntD = 0u;
cntE = 0u;
cntF = 0u;
cntDef = 0u;
argA = NULL;
argB = NULL;
argC = NULL;
argD = NULL;
argE = NULL;
argF = NULL;
argDef = NULL;
lenA = 0u;
lenB = 0u;
lenC = 0u;
lenD = 0u;
lenE = 0u;
lenF = 0u;
lenDef = 0u;
}
static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
{
write_callCnt ++;
while((length > 0) && (write_length < sizeof(write_data)))
{
write_data[write_length] = *data;
data ++;
length --;
write_length ++;
}
return SHELLMATTA_OK;
}
static shellmatta_retCode_t parseOpts(shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
(void) arguments;
(void) length;
char option;
char *argumentString;
uint32_t argumentLength;
uint32_t optionCount = 0u;
while(SHELLMATTA_OK == shellmatta_opt(handle, (char*)"abcde:f::", &option, &argumentString, &argumentLength))
{
optionCount ++;
switch(option)
{
case 'a':
cntA ++;
argA = argumentString;
lenA = argumentLength;
break;
case 'b':
cntB ++;
argB = argumentString;
lenB = argumentLength;
break;
case 'c':
cntC ++;
argC = argumentString;
lenC = argumentLength;
break;
case 'd':
cntD ++;
argD = argumentString;
lenD = argumentLength;
break;
case 'e':
cntE ++;
argE = argumentString;
lenE = argumentLength;
break;
case 'f':
cntF ++;
argF = argumentString;
lenF = argumentLength;
break;
default:
cntDef ++;
argDef = argumentString;
lenDef = argumentLength;
break;
}
}
shellmatta_printf(handle, "parseOpts - cnt: %u\r\n", optionCount);
return SHELLMATTA_OK;
}
shellmatta_cmd_t parseOptsCmd = {(char*)"parseOpts", (char*)"opt", NULL, NULL, parseOpts, NULL};
TEST_CASE( "shellmatta option parser 1" ) {
shellmatta_instance_t inst;
shellmatta_handle_t handle;
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"parseOpts -a -e meow\r\nparseOpts - cnt: 2\r\n\r\nshellmatta->";
shellmatta_doInit( &inst,
&handle,
buffer,
sizeof(buffer),
historyBuffer,
sizeof(historyBuffer),
"shellmatta->",
NULL,
writeFct);
initTestcase();
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_length = 0u;
shellmatta_addCmd(handle, &parseOptsCmd);
shellmatta_processData(handle, (char*)"parseOpts -a -e meow\r", 21);
CHECK( cntA == 1u );
CHECK( NULL == argA);
CHECK( 0u == lenA );
CHECK( cntE == 1u );
CHECK(((NULL != argE) && (0u == memcmp(argE, "meow", 4))));
CHECK( lenE == 4u );
CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
CHECK( write_length == strlen(dummyData));
REQUIRE( strcmp(dummyData, write_data) == 0);
}
TEST_CASE( "shellmatta option parser 2 - ignore \"--\"" ) {
shellmatta_instance_t inst;
shellmatta_handle_t handle;
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"parseOpts -a -e meow --\r\nparseOpts - cnt: 2\r\n\r\nshellmatta->";
shellmatta_doInit( &inst,
&handle,
buffer,
sizeof(buffer),
historyBuffer,
sizeof(historyBuffer),
"shellmatta->",
NULL,
writeFct);
initTestcase();
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_length = 0u;
shellmatta_addCmd(handle, &parseOptsCmd);
shellmatta_processData(handle, (char*)"parseOpts -a -e meow --\r", 24);
CHECK( cntA == 1u );
CHECK( NULL == argA);
CHECK( 0u == lenA );
CHECK( cntE == 1u );
CHECK(((NULL != argE) && (0u == memcmp(argE, "meow", 4))));
CHECK( lenE == 4u );
CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
CHECK( write_length == strlen(dummyData));
REQUIRE( strcmp(dummyData, write_data) == 0);
}

View File

@ -0,0 +1,229 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/**
* @file test_integration_optLong.cpp
* @brief integration test implementation for the long option parser of the shellmatta
* @author Stefan Strobel <stefan.strobel@shimatta.net>
*/
#include "test/framework/catch.hpp"
extern "C" {
#include "shellmatta.h"
}
#include <string.h>
static uint32_t write_callCnt = 0u;
static char write_data[1024];
static uint32_t write_length;
static uint32_t cntA = 0u;
static uint32_t cntB = 0u;
static uint32_t cntC = 0u;
static uint32_t cntD = 0u;
static uint32_t cntE = 0u;
static uint32_t cntF = 0u;
static uint32_t cntDef = 0u;
static char *argA = NULL;
static char *argB = NULL;
static char *argC = NULL;
static char *argD = NULL;
static char *argE = NULL;
static char *argF = NULL;
static char *argDef = NULL;
static uint32_t lenA = 0u;
static uint32_t lenB = 0u;
static uint32_t lenC = 0u;
static uint32_t lenD = 0u;
static uint32_t lenE = 0u;
static uint32_t lenF = 0u;
static uint32_t lenDef = 0u;
static void initTestcase(void)
{
cntA = 0u;
cntB = 0u;
cntC = 0u;
cntD = 0u;
cntE = 0u;
cntF = 0u;
cntDef = 0u;
argA = NULL;
argB = NULL;
argC = NULL;
argD = NULL;
argE = NULL;
argF = NULL;
argDef = NULL;
lenA = 0u;
lenB = 0u;
lenC = 0u;
lenD = 0u;
lenE = 0u;
lenF = 0u;
lenDef = 0u;
}
static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
{
write_callCnt ++;
while((length > 0) && (write_length < sizeof(write_data)))
{
write_data[write_length] = *data;
data ++;
length --;
write_length ++;
}
return SHELLMATTA_OK;
}
static shellmatta_retCode_t parseOptsLong(shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
(void) arguments;
(void) length;
char option;
char *argumentString;
uint32_t argumentLength;
uint32_t optionCount = 0u;
shellmatta_opt_long_t longOptions[] = {
{"auto", 'a', SHELLMATTA_OPT_ARG_NONE},
{"build", 'b', SHELLMATTA_OPT_ARG_NONE},
{"cat", 'c', SHELLMATTA_OPT_ARG_NONE},
{"doom", 'd', SHELLMATTA_OPT_ARG_NONE},
{"erase", 'e', SHELLMATTA_OPT_ARG_REQUIRED},
{"fuck", 'f', SHELLMATTA_OPT_ARG_OPTIONAL},
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE}
};
while(SHELLMATTA_OK == shellmatta_opt_long(handle, longOptions, &option, &argumentString, &argumentLength))
{
optionCount ++;
switch(option)
{
case 'a':
cntA ++;
argA = argumentString;
lenA = argumentLength;
break;
case 'b':
cntB ++;
argB = argumentString;
lenB = argumentLength;
break;
case 'c':
cntC ++;
argC = argumentString;
lenC = argumentLength;
break;
case 'd':
cntD ++;
argD = argumentString;
lenD = argumentLength;
break;
case 'e':
cntE ++;
argE = argumentString;
lenE = argumentLength;
break;
case 'f':
cntF ++;
argF = argumentString;
lenF = argumentLength;
break;
default:
cntDef ++;
argDef = argumentString;
lenDef = argumentLength;
break;
}
}
shellmatta_printf(handle, "parseOpts - cnt: %u\r\n", optionCount);
return SHELLMATTA_OK;
}
shellmatta_cmd_t parseOptsLongCmd = {(char*)"parseOpts", (char*)"opt", NULL, NULL, parseOptsLong, NULL};
TEST_CASE( "shellmatta long option parser 1" ) {
shellmatta_instance_t inst;
shellmatta_handle_t handle;
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"parseOpts -a -e meow\r\nparseOpts - cnt: 2\r\n\r\nshellmatta->";
shellmatta_doInit( &inst,
&handle,
buffer,
sizeof(buffer),
historyBuffer,
sizeof(historyBuffer),
"shellmatta->",
NULL,
writeFct);
initTestcase();
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_length = 0u;
shellmatta_addCmd(handle, &parseOptsLongCmd);
shellmatta_processData(handle, (char*)"parseOpts -a -e meow\r", 21);
CHECK( cntA == 1u );
CHECK( NULL == argA);
CHECK( 0u == lenA );
CHECK( cntE == 1u );
CHECK(((NULL != argE) && (0u == memcmp(argE, "meow", 4))));
CHECK( lenE == 4u );
CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u && cntDef == 0u) );
CHECK( write_length == strlen(dummyData));
REQUIRE( strcmp(dummyData, write_data) == 0);
}
TEST_CASE( "shellmatta long option parser 2" ) {
shellmatta_instance_t inst;
shellmatta_handle_t handle;
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"parseOpts --auto --erase meow -- --lalelu -u\r\nparseOpts - cnt: 4\r\n\r\nshellmatta->";
shellmatta_doInit( &inst,
&handle,
buffer,
sizeof(buffer),
historyBuffer,
sizeof(historyBuffer),
"shellmatta->",
NULL,
writeFct);
initTestcase();
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_length = 0u;
shellmatta_addCmd(handle, &parseOptsLongCmd);
shellmatta_processData(handle, (char*)"parseOpts --auto --erase meow -- --lalelu -u\r", 45);
CHECK( cntA == 1u );
CHECK( NULL == argA);
CHECK( 0u == lenA );
CHECK( cntE == 1u );
CHECK(((NULL != argE) && (0u == memcmp(argE, "meow", 4))));
CHECK( lenE == 4u );
CHECK( (cntB == 0u && cntC == 0u && cntD == 0u && cntF == 0u) );
CHECK( cntDef == 2u );
CHECK( lenDef == 2u );
CHECK( write_length == strlen(dummyData));
REQUIRE( strcmp(dummyData, write_data) == 0);
}

View File

@ -0,0 +1,162 @@
#include "test/framework/catch.hpp"
#include "src/shellmatta_opt.c"
#include <string.h>
TEST_CASE( "shellmatta_opt findNextHunk easy" ) {
shellmatta_retCode_t ret = SHELLMATTA_OK;
shellmatta_instance_t inst;
char *dummyData = (char*) "This is Sparta";
char buffer[1024u];
memcpy(buffer, dummyData, strlen(dummyData));
inst.buffer = buffer;
inst.bufferSize = sizeof(buffer);
inst.inputCount = 14;
inst.optionParser.nextOffset = 4u;
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 5u);
CHECK( inst.optionParser.len == 2u);
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 8u);
CHECK( inst.optionParser.len == 6u);
ret = findNextHunk(&inst);
REQUIRE( ret == SHELLMATTA_ERROR );
}
TEST_CASE( "shellmatta_opt findNextHunk quotation 1" ) {
shellmatta_retCode_t ret = SHELLMATTA_OK;
shellmatta_instance_t inst;
char *dummyData = (char*) "This is Sparta \"argument with spaces\"";
char buffer[1024u];
memcpy(buffer, dummyData, strlen(dummyData));
inst.buffer = buffer;
inst.bufferSize = sizeof(buffer);
inst.inputCount = strlen(dummyData);
inst.optionParser.nextOffset = 4u;
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 5u);
CHECK( inst.optionParser.len == 2u);
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 8u);
CHECK( inst.optionParser.len == 6u);
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 15u);
CHECK( inst.optionParser.len == 20u);
CHECK( 0 == memcmp(&(inst.buffer[inst.optionParser.offset]), "argument with spaces", 20));
ret = findNextHunk(&inst);
REQUIRE( ret == SHELLMATTA_ERROR );
}
TEST_CASE( "shellmatta_opt findNextHunk quotation 2" ) {
shellmatta_retCode_t ret = SHELLMATTA_OK;
shellmatta_instance_t inst;
char *dummyData = (char*) "This is Sparta 'argument with spaces'";
char buffer[1024u];
memcpy(buffer, dummyData, strlen(dummyData));
inst.buffer = buffer;
inst.bufferSize = sizeof(buffer);
inst.inputCount = strlen(dummyData);
inst.optionParser.nextOffset = 4u;
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 5u);
CHECK( inst.optionParser.len == 2u);
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 8u);
CHECK( inst.optionParser.len == 6u);
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 15u);
CHECK( inst.optionParser.len == 20u);
CHECK( 0 == memcmp(&(inst.buffer[inst.optionParser.offset]), "argument with spaces", 20));
ret = findNextHunk(&inst);
REQUIRE( ret == SHELLMATTA_ERROR );
}
TEST_CASE( "shellmatta_opt findNextHunk quotation escaped" ) {
shellmatta_retCode_t ret = SHELLMATTA_OK;
shellmatta_instance_t inst;
char *dummyData = (char*) "This is Sparta \"argument with \\\"spaces\"";
char buffer[1024u];
memcpy(buffer, dummyData, strlen(dummyData));
inst.buffer = buffer;
inst.bufferSize = sizeof(buffer);
inst.inputCount = strlen(dummyData);
inst.optionParser.nextOffset = 4u;
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 5u);
CHECK( inst.optionParser.len == 2u);
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 8u);
CHECK( inst.optionParser.len == 6u);
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 15u);
CHECK( inst.optionParser.len == 21u);
CHECK( 0 == memcmp(&(inst.buffer[inst.optionParser.offset]), "argument with \"spaces", 21));
ret = findNextHunk(&inst);
REQUIRE( ret == SHELLMATTA_ERROR );
}
TEST_CASE( "shellmatta_opt findNextHunk quotation missing closing quotation" ) {
shellmatta_retCode_t ret = SHELLMATTA_OK;
shellmatta_instance_t inst;
char *dummyData = (char*) "This is Sparta \"argument with \\\"spaces";
char buffer[1024u];
memcpy(buffer, dummyData, strlen(dummyData));
inst.buffer = buffer;
inst.bufferSize = sizeof(buffer);
inst.inputCount = strlen(dummyData);
inst.optionParser.nextOffset = 4u;
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 5u);
CHECK( inst.optionParser.len == 2u);
ret = findNextHunk(&inst);
CHECK( ret == SHELLMATTA_OK );
CHECK( inst.optionParser.offset == 8u);
CHECK( inst.optionParser.len == 6u);
ret = findNextHunk(&inst);
REQUIRE( ret == SHELLMATTA_ERROR );
}

View File

@ -19,6 +19,7 @@ TEST_CASE( "shellmatta_insertChars normal call" ) {
shellmatta_instance_t inst;
char buffer[20] = "abcdefghij\0\0\0\0\0\0\0\0\0";
memset(&inst, 0, sizeof(inst));
inst.buffer = buffer;
inst.bufferSize = 20;
inst.cursor = 8;
@ -45,6 +46,7 @@ TEST_CASE( "shellmatta_insertChars overwrite" ) {
shellmatta_instance_t inst;
char buffer[20] = "abcdefghij\0\0\0\0\0\0\0\0\0";
memset(&inst, 0, sizeof(inst));
inst.buffer = buffer;
inst.bufferSize = 20;
inst.cursor = 8;
@ -72,6 +74,7 @@ TEST_CASE( "shellmatta_insertChars append" ) {
shellmatta_instance_t inst;
char buffer[20] = "abcdefghij\0\0\0\0\0\0\0\0\0";
memset(&inst, 0, sizeof(inst));
inst.buffer = buffer;
inst.bufferSize = 20;
inst.cursor = 10;
@ -99,6 +102,7 @@ TEST_CASE( "shellmatta_insertChars 0 length" ) {
shellmatta_instance_t inst;
char buffer[20] = "abcdefghij\0\0\0\0\0\0\0\0\0";
memset(&inst, 0, sizeof(inst));
inst.buffer = buffer;
inst.bufferSize = 20;
inst.cursor = 8;