Merge branch 'develop'

This commit is contained in:
Tilen Majerle 2021-04-13 23:56:51 +02:00
commit 45b1d75d20
32 changed files with 4018 additions and 1 deletions

27
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,27 @@
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
name: Create Release
jobs:
build:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
body: |
See the CHANGELOG.md
draft: false
prerelease: false

5
CHANGELOG.md Normal file
View File

@ -0,0 +1,5 @@
# Changelog
## v1.0.0

View File

@ -12,6 +12,8 @@ It targets communication with embedded systems from remote terminal to quickly s
* Development of library under Win32 platform
* Written in C language (C99)
* No dynamic allocation, maximum number of commands assigned at compile time
* Highly configurable
* Simple help-text with `cmd -v` option
* User friendly MIT license
## Contribute

View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28922.388
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lwshell_dev", "lwshell_dev.vcxproj", "{C095C533-523E-4604-B3EF-B5025E4D96C0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C095C533-523E-4604-B3EF-B5025E4D96C0}.Debug|x64.ActiveCfg = Debug|x64
{C095C533-523E-4604-B3EF-B5025E4D96C0}.Debug|x64.Build.0 = Debug|x64
{C095C533-523E-4604-B3EF-B5025E4D96C0}.Debug|x86.ActiveCfg = Debug|Win32
{C095C533-523E-4604-B3EF-B5025E4D96C0}.Debug|x86.Build.0 = Debug|Win32
{C095C533-523E-4604-B3EF-B5025E4D96C0}.Release|x64.ActiveCfg = Release|x64
{C095C533-523E-4604-B3EF-B5025E4D96C0}.Release|x64.Build.0 = Release|x64
{C095C533-523E-4604-B3EF-B5025E4D96C0}.Release|x86.ActiveCfg = Release|Win32
{C095C533-523E-4604-B3EF-B5025E4D96C0}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0D5E996C-005B-40C8-95F9-90D0632471E5}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{C095C533-523E-4604-B3EF-B5025E4D96C0}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>dynamic_memory</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>lwshell_dev</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>..\..\lwshell\src\include;.;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>..\..\lwmem\src\include;.;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level1</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;LWMEM_DEV;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;LWMEM_DEV;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\examples\example_minimal.c" />
<ClCompile Include="..\..\lwshell\src\lwshell\lwshell.c" />
<ClCompile Include="main.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Source Files\Tests">
<UniqueIdentifier>{87f67bb1-45c3-4724-b7de-f1e8551453e3}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\LWSHELL">
<UniqueIdentifier>{a9bad49b-d114-4596-8fe8-162c60f482ee}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\lwshell\src\lwshell\lwshell.c">
<Filter>Source Files\LWSHELL</Filter>
</ClCompile>
<ClCompile Include="..\..\examples\example_minimal.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,43 @@
/**
* \file lwshell_opts.h
* \brief LwSHELL application options
*/
/*
* Copyright (c) 2020 Tilen MAJERLE
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* This file is part of Lightweight shell library.
*
* Author: Tilen MAJERLE <tilen@majerle.eu>
* Version: v0.1.0
*/
#ifndef LWSHELL_HDR_OPTS_H
#define LWSHELL_HDR_OPTS_H
/* Rename this file to "lwshell_opts.h" for your application */
#include "windows.h"
#define LWSHELL_CFG_USE_OUTPUT 1
#endif /* LWSHELL_HDR_OPTS_H */

97
dev/VisualStudio/main.c Normal file
View File

@ -0,0 +1,97 @@
#include <stdio.h>
#include "lwshell/lwshell.h"
#include <string.h>
#include <stdint.h>
void example_minimal(void);
int32_t
addint_cmd(int32_t argc, char** argv) {
long long i1, i2;
if (argc < 3) {
return -1;
}
i1 = lwshell_parse_long_long(argv[1]);
i2 = lwshell_parse_long_long(argv[2]);
printf("%lld\r\n", i1 + i2);
return 0;
}
int32_t
subint_cmd(int32_t argc, char** argv) {
long long i1, i2;
if (argc < 3) {
return -1;
}
i1 = lwshell_parse_long_long(argv[1]);
i2 = lwshell_parse_long_long(argv[2]);
printf("%lld\r\n", i1 - i2);
return 0;
}
int32_t
adddbl_cmd(int32_t argc, char** argv) {
double i1, i2;
if (argc < 3) {
return -1;
}
i1 = lwshell_parse_double(argv[1]);
i2 = lwshell_parse_double(argv[2]);
printf("%f\r\n", (i1 + i2));
return 0;
}
int32_t
subdbl_cmd(int32_t argc, char** argv) {
double i1, i2;
if (argc < 3) {
return -1;
}
i1 = lwshell_parse_double(argv[1]);
i2 = lwshell_parse_double(argv[2]);
printf("%f\r\n", (i1 - i2));
return 0;
}
/**
* \brief Application output function
* \param[in] str: String to print, null-terminated
* \param[in] lw: LwSHELL instance
*/
void
shell_output(const char* str, lwshell_t* lw) {
printf("%s", str);
if (*str == '\r') {
printf("\n");
}
}
/* Program entry point */
int
main(void) {
/* Init library */
lwshell_init();
/* Add optional output function for the purpose of the feedback */
lwshell_set_output_fn(shell_output);
/* Define shell commands */
lwshell_register_cmd("addint", addint_cmd, "Adds 2 integer numbers and prints them");
lwshell_register_cmd("subint", subint_cmd, "Substitute 2 integer numbers and prints them");
lwshell_register_cmd("adddbl", adddbl_cmd, "Adds 2 double numbers and prints them");
lwshell_register_cmd("subdbl", subdbl_cmd, "Substitute 2 double numbers and prints them");
/* User input to process every character */
printf("Start entering your command and press enter...\r\n");
while (1) {
char c = getch();
/* Insert input to library */
lwshell_input(&c, 1);
}
return 0;
}

20
docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -0,0 +1,12 @@
.. _api_reference:
API reference
=============
List of all the modules:
.. toctree::
:maxdepth: 2
lwshell
lwshell_opt

View File

@ -0,0 +1,6 @@
.. _api_lwshell:
LwSHELL
=======
.. doxygengroup:: LWSHELL

View File

@ -0,0 +1,12 @@
.. _api_lwshell_opt:
Configuration
=============
This is the default configuration of the middleware.
When any of the settings shall be modified, it shall be done in dedicated application config ``lwshell_opts.h`` file.
.. note::
Check :ref:`getting_started` for guidelines on how to create and use configuration file.
.. doxygengroup:: LWSHELL_OPT

134
docs/conf.py Normal file
View File

@ -0,0 +1,134 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
from sphinx.builders.html import StandaloneHTMLBuilder
import subprocess, os
# Run doxygen first
# read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True'
# if read_the_docs_build:
subprocess.call('doxygen doxyfile.doxy', shell=True)
# -- Project information -----------------------------------------------------
project = 'LwSHELL'
copyright = '2020, Tilen MAJERLE'
author = 'Tilen MAJERLE'
# Try to get branch at which this is running
# and try to determine which version to display in sphinx
# Version is using git tag if on master or "latest-develop" if on develop branch
version = ''
git_branch = ''
# Get current branch
res = os.popen('git branch').read().strip()
for line in res.split("\n"):
if line[0] == '*':
git_branch = line[1:].strip()
# Decision for display version
git_branch = git_branch.replace('(HEAD detached at ', '').replace(')', '')
if git_branch.find('master') >= 0 or git_branch.find('main') >= 0:
version = os.popen('git describe --tags --abbrev=0').read().strip()
if version == '':
version = 'v0.0.0'
elif git_branch.find('develop') != -1 and not (git_branch.find('develop-') >= 0 or git_branch.find('develop/') >= 0):
version = 'latest-develop'
else:
version = 'branch-' + git_branch
# For debugging purpose only
print("GIT BRANCH: " + git_branch)
print("GIT VERSION: " + version)
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.autosectionlabel',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.mathjax',
'sphinx.ext.ifconfig',
'sphinx.ext.viewcode',
'sphinx_sitemap',
'breathe',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
highlight_language = 'c'
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
html_theme_options = {
'canonical_url': '',
'analytics_id': '', # Provided by Google in your dashboard
'display_version': True,
'prev_next_buttons_location': 'bottom',
'style_external_links': False,
'logo_only': False,
# Toc options
'collapse_navigation': True,
'sticky_navigation': True,
'navigation_depth': 4,
'includehidden': True,
'titles_only': False
}
html_logo = 'static/images/logo.svg'
github_url = 'https://github.com/MaJerle/lwshell'
html_baseurl = 'https://docs.majerle.eu/projects/lwshell/'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['static']
html_css_files = [
'css/common.css',
'css/custom.css',
]
html_js_files = [
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css'
]
master_doc = 'index'
#
# Breathe configuration
#
#
#
breathe_projects = {
"lwshell": "_build/xml/"
}
breathe_default_project = "lwshell"
breathe_default_members = ('members', 'undoc-members')

2456
docs/doxyfile.doxy Normal file

File diff suppressed because it is too large Load Diff

37
docs/examples/index.rst Normal file
View File

@ -0,0 +1,37 @@
.. _examples:
Examples and demos
==================
Various examples are provided for fast library evaluation on embedded systems. These are prepared and maintained for ``2`` platforms, but could be easily extended to more platforms:
* WIN32 examples, prepared as `Visual Studio Community <https://visualstudio.microsoft.com/vs/community/>`_ projects
* ARM Cortex-M examples for STM32, prepared as `STM32CubeIDE <https://www.st.com/en/development-tools/stm32cubeide.html>`_ GCC projects
.. warning::
Library is platform independent and can be used on any platform.
Example architectures
^^^^^^^^^^^^^^^^^^^^^
There are many platforms available today on a market, however supporting them all would be tough task for single person.
Therefore it has been decided to support (for purpose of examples) ``2`` platforms only, `WIN32` and `STM32`.
WIN32
*****
Examples for *WIN32* are prepared as `Visual Studio Community <https://visualstudio.microsoft.com/vs/community/>`_ projects.
You can directly open project in the IDE, compile & debug.
STM32
*****
Embedded market is supported by many vendors and STMicroelectronics is, with their `STM32 <https://www.st.com/en/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus.html>`_ series of microcontrollers, one of the most important players.
There are numerous amount of examples and topics related to this architecture.
Examples for *STM32* are natively supported with `STM32CubeIDE <https://www.st.com/en/development-tools/stm32cubeide.html>`_, an official development IDE from STMicroelectronics.
You can run examples on one of official development boards, available in repository examples.
.. toctree::
:maxdepth: 2

103
docs/get-started/index.rst Normal file
View File

@ -0,0 +1,103 @@
.. _getting_started:
Getting started
===============
Getting started may be the most challenging part of every new library.
This guide is describing how to start with the library quickly and effectively
.. _download_library:
Download library
^^^^^^^^^^^^^^^^
Library is primarly hosted on `Github <https://github.com/MaJerle/lwshell>`_.
You can get it with:
* Downloading latest release from `releases area <https://github.com/MaJerle/lwshell/releases>`_ on Github
* Cloning ``master`` branch for latest stable version
* Cloning ``develop`` branch for latest development
Download from releases
**********************
All releases are available on Github `releases area <https://github.com/MaJerle/lwshell/releases>`_.
Clone from Github
*****************
First-time clone
""""""""""""""""
This is used when you do not have yet local copy on your machine.
* Make sure ``git`` is installed.
* Open console and navigate to path in the system to clone repository to. Use command ``cd your_path``
* Clone repository with one of available ``3`` options
* Run ``git clone --recurse-submodules https://github.com/MaJerle/lwshell`` command to clone entire repository, including submodules
* Run ``git clone --recurse-submodules --branch develop https://github.com/MaJerle/lwshell`` to clone `development` branch, including submodules
* Run ``git clone --recurse-submodules --branch master https://github.com/MaJerle/lwshell`` to clone `latest stable` branch, including submodules
* Navigate to ``examples`` directory and run favourite example
Update cloned to latest version
"""""""""""""""""""""""""""""""
* Open console and navigate to path in the system where your resources repository is. Use command ``cd your_path``
* Run ``git pull origin master --recurse-submodules`` command to pull latest changes and to fetch latest changes from submodules on ``master`` branch
* Run ``git pull origin develop --recurse-submodules`` command to pull latest changes and to fetch latest changes from submodules on ``develop`` branch
* Run ``git submodule foreach git pull origin master`` to update & merge all submodules
.. note::
This is preferred option to use when you want to evaluate library and run prepared examples.
Repository consists of multiple submodules which can be automatically downloaded when cloning and pulling changes from root repository.
Add library to project
^^^^^^^^^^^^^^^^^^^^^^
At this point it is assumed that you have successfully download library, either cloned it or from releases page.
Next step is to add the library to the project, by means of source files to compiler inputs and header files in search path
* Copy ``lwshell`` folder to your project, it contains library files
* Add ``lwshell/src/include`` folder to `include path` of your toolchain. This is where `C/C++` compiler can find the files during compilation process. Usually using ``-I`` flag
* Add source files from ``lwshell/src/`` folder to toolchain build. These files are built by `C/C++` compiler
* Copy ``lwshell/src/include/lwshell/lwshell_opts_template.h`` to project folder and rename it to ``lwshell_opts.h``
* Build the project
Configuration file
^^^^^^^^^^^^^^^^^^
Configuration file is used to overwrite default settings defined for the essential use case.
Library comes with template config file, which can be modified according to needs.
and it should be copied (or simply renamed in-place) and named ``lwshell_opts.h``
.. note::
Default configuration template file location: ``lwshell/src/include/lwshell/lwshell_opts_template.h``.
File must be renamed to ``lwshell_opts.h`` first and then copied to the project directory where compiler
include paths have access to it by using ``#include "lwshell_opts.h"``.
List of configuration options are available in the :ref:`api_lwshell_opt` section.
If any option is about to be modified, it should be done in configuration file
.. literalinclude:: ../../lwshell/src/include/lwshell/lwshell_opts_template.h
:language: c
:linenos:
:caption: Template configuration file
.. note::
If you prefer to avoid using configuration file, application must define
a global symbol ``LWSHELL_IGNORE_USER_OPTS``, visible across entire application.
This can be achieved with ``-D`` compiler option.
Minimal example code
^^^^^^^^^^^^^^^^^^^^
To verify proper library setup, minimal example has been prepared.
Run it in your main application file to verify its proper execution
.. literalinclude:: ../../examples/example_minimal.c
:language: c
:linenos:
:caption: Absolute minimum example

64
docs/index.rst Normal file
View File

@ -0,0 +1,64 @@
LwSHELL |version| documentation
===============================
Welcome to the documentation for version |version|.
LwSHELL is lightweight dynamic memory manager optimized for embedded systems.
.. image:: static/images/logo.svg
:align: center
.. rst-class:: center
.. rst-class:: index_links
:ref:`download_library` :ref:`getting_started` `Open Github <https://github.com/MaJerle/lwshell>`_ `Donate <https://paypal.me/tilz0R>`_
Features
^^^^^^^^
* Lightweight commands shell for embedded systems
* Platform independent and very easy to port
* Development of library under Win32 platform
* Written in C language (C99)
* No dynamic allocation, maximum number of commands assigned at compile time
* Highly configurable
* Simple help-text with `cmd -v` option
* User friendly MIT license
Requirements
^^^^^^^^^^^^
* C compiler
* Less than ``5kB`` of non-volatile memory
Contribute
^^^^^^^^^^
Fresh contributions are always welcome. Simple instructions to proceed:
#. Fork Github repository
#. Respect `C style & coding rules <https://github.com/MaJerle/c-code-style>`_ used by the library
#. Create a pull request to ``develop`` branch with new features or bug fixes
Alternatively you may:
#. Report a bug
#. Ask for a feature request
License
^^^^^^^
.. literalinclude:: ../LICENSE
Table of contents
^^^^^^^^^^^^^^^^^
.. toctree::
:maxdepth: 2
self
get-started/index
user-manual/index
api-reference/index
examples/index

35
docs/make.bat Normal file
View File

@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

8
docs/requirements.txt Normal file
View File

@ -0,0 +1,8 @@
breathe>=4.9.1
colorama
docutils==0.16
sphinx>=3.5.1
sphinx_rtd_theme
sphinx-tabs
sphinxcontrib-svg2pdfconverter
sphinx-sitemap

64
docs/static/css/common.css vendored Normal file
View File

@ -0,0 +1,64 @@
/* Center aligned text */
.center {
text-align: center;
}
/* Paragraph with main links on index page */
.index-links {
text-align: center;
margin-top: 10px;
}
.index-links a {
display: inline-block;
border: 1px solid #0E4263;
padding: 5px 20px;
margin: 2px 5px;
background: #2980B9;
border-radius: 4px;
color: #FFFFFF;
}
.index-links a:hover, .index-links a:active {
background: #0E4263;
}
/* Table header p w/0 margin */
.index-links a table thead th {
vertical-align: middle;
}
table thead th p {
margin: 0;
}
.table-nowrap td {
white-space: normal !important;
}
/* Breathe output changes */
.breathe-sectiondef.container {
background: #f9f9f9;
padding: 10px;
margin-bottom: 10px;
border: 1px solid #efefef;
}
.breathe-sectiondef.container .breathe-sectiondef-title {
background: #2980b9;
color: #FFFFFF;
padding: 4px;
margin: -10px -10px 0 -10px;
}
.breathe-sectiondef.container .function,
.breathe-sectiondef.container .member,
.breathe-sectiondef.container .class,
.breathe-sectiondef.container .type {
border-bottom: 1px solid #efefef;
}
.breathe-sectiondef.container .function:last-child,
.breathe-sectiondef.container .member:last-child,
.breathe-sectiondef.container .class:last-child,
.breathe-sectiondef.container .type:last-child {
border-bottom: none;
margin-bottom: 0;
}
/*# sourceMappingURL=common.css.map */

0
docs/static/css/custom.css vendored Normal file
View File

1
docs/static/images/logo.drawio vendored Normal file
View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2021-04-07T22:03:45.135Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/12.3.2 Chrome/78.0.3904.113 Electron/7.1.2 Safari/537.36" etag="W7BecXTfw0RkIekNUFhW" version="12.3.2" type="device" pages="1"><diagram id="Wsjaadh77UIxB9X1bxos" name="Page-1">jZJNb4MwDIZ/DcdKQGBqr+26VhrrhUPPEXFJtISgNCx0v35hOHyomrRT4scfsV8nIgfVnwxt+YdmIKM0Zn1EXqM0TeI088dAHiPJt2QEtREMg2ZQim8ImUg7weC+CrRaSyvaNax000BlV4wao9067Kbl+tWW1vAEyorKZ3oVzPKRbvN45mcQNQ8vJzF6FA3BCO6cMu0WiBwjcjBa2/Gm+gPIQbygy5j39od3asxAY/+TcNmwDHbKFRfV7bPd+01ekw1W+aKyw4ELV56PRYE920cQwuiuYTDUSiKyd1xYKFtaDV7nV+8Zt0qi+26N/pwEe/HkphuL2yWDPakRe+N5lNAXGAv9AuFoJ9AKrHn4EPSSLB9T8J+lwXbz1pItMr7YWNgkxY9ST6VnLf0F5QzmvLZf3+Lzk+MP</diagram></mxfile>

3
docs/static/images/logo.svg vendored Normal file
View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="191px" height="56px" viewBox="-0.5 -0.5 191 56" content="&lt;mxfile host=&quot;Electron&quot; modified=&quot;2021-04-07T22:03:50.459Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/12.3.2 Chrome/78.0.3904.113 Electron/7.1.2 Safari/537.36&quot; etag=&quot;Y1WfZ64eFs34UZLLlP_w&quot; version=&quot;12.3.2&quot; type=&quot;device&quot; pages=&quot;1&quot;&gt;&lt;diagram id=&quot;Wsjaadh77UIxB9X1bxos&quot; name=&quot;Page-1&quot;&gt;jZJNb4MwDIZ/DcdKQGBqr+26VhrrhUPPEXFJtISgNCx0v35hOHyomrRT4scfsV8nIgfVnwxt+YdmIKM0Zn1EXqM0TeI088dAHiPJt2QEtREMg2ZQim8ImUg7weC+CrRaSyvaNax000BlV4wao9067Kbl+tWW1vAEyorKZ3oVzPKRbvN45mcQNQ8vJzF6FA3BCO6cMu0WiBwjcjBa2/Gm+gPIQbygy5j39od3asxAY/+TcNmwDHbKFRfV7bPd+01ekw1W+aKyw4ELV56PRYE920cQwuiuYTDUSiKyd1xYKFtaDV7nV+8Zt0qi+26N/pwEe/HkphuL2yWDPakRe+N5lNAXGAv9AuFoJ9AKrHn4EPSSLB9T8J+lwXbz1pItMr7YWNgkxY9ST6VnLf0F5QzmvLZf3+Lzk+MP&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><rect x="3" y="3" width="185" height="50" rx="7.5" ry="7.5" fill="#ffffff" stroke="#000000" stroke-width="6" pointer-events="all"/><g transform="translate(15.5,7.5)"><switch><foreignObject style="overflow:visible;" pointer-events="all" width="160" height="41" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 36px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; vertical-align: top; width: 161px; white-space: nowrap; overflow-wrap: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;white-space:normal;">LwSHELL</div></div></foreignObject><text x="80" y="39" fill="#000000" text-anchor="middle" font-size="36px" font-family="Helvetica">LwSHELL</text></switch></g></g></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
docs/static/images/logo_tm.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
docs/static/images/logo_tm_full.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,61 @@
.. _how_it_works:
How it works
============
This section describes how library works from the basic perspective.
LwSHELL is designed to accept *computer-command-like* input, in format of ``cmdname param1 "param 2 with space"``,
parse it properly and search for function callback that is assigned for specific ``cmdname``.
Library starts processing input line on *line-feed* or *carriage-return* characters.
It splits tokens by space character:
* Tokens must not include ``space`` character or it will be considered as multi-token input
* To use *space* character as token input, encapsulate character in *double-quotes*
Command structure
^^^^^^^^^^^^^^^^^
Every command has assigned dedicated name and must start with it.
Application must take care to input exact command name since commands are case-sensitive, ``mycmd`` is a different command than ``Mycmd``.
Command structure looks like:
* It must start with command name and has at least one (``1``) parameter, eg. ``mycommand``. Command name is counted as first parameter
* It may have additional parameters split with *space* character
* Every input is parsed as string, even if parameter is string
.. tip::
To use space as an input, encapsulate it with *double quotes*, eg. ``mycmd param1 "param 1 has spaces"``
Register command
^^^^^^^^^^^^^^^^
Application must register command(s) to be used by the system.
This can be done using :cpp:func:`lwshell_register_cmd` function which accepts
*command name*, *command function* and optional *command description*
Command description
^^^^^^^^^^^^^^^^^^^
Every command can have assigned its very simple description text, know as *help text*.
Description is later accessible with special command input that has ``2`` parameters in total and second is ``-h``, ``cmdname -h``.
Data output
^^^^^^^^^^^
To properly work with the library, application must input data to process by using :cpp:func:`lwshell_input` function.
Thanks to the library implementation, it is possible to get data feedback and be able to implement OS-like console.
To enable data-output feature, define your output callback function and assign it with :cpp:func:`lwshell_set_output_fn` function.
Data outputs works on:
* Special characters for *carriage return* and *line-feed*
* Special character *backspace* that returns set of characters to implement backspace-like event on your output
* Actual input character printed back for user feedback
* ``cmdname -h`` feature works to print simple help text
.. toctree::
:maxdepth: 2

View File

@ -0,0 +1,9 @@
.. _um:
User manual
===========
.. toctree::
:maxdepth: 2
how-it-works

View File

@ -0,0 +1,31 @@
#include <string.h>
#include "lwshell/lwshell.h"
/* Command to get called */
int32_t
mycmd_fn(int32_t argc, char** argv) {
printf("mycmd_fn called. Number of argv: %d\r\n", (int)argc);
for (int32_t i = 0; i < argc; ++i) {
printf("ARG[%d]: %s\r\n", (int)argc, argv[i]);
}
/* Successful execution */
return 0;
}
/* Example code */
void
example_minimal(void) {
const char* input_str = "mycmd param1 \"param 2 with space\"";
/* Init library */
lwshell_init();
/* Define shell commands */
lwshell_register_cmd("mycmd", mycmd_fn, "Adds 2 integer numbers and prints them");
/* User input to process every character */
/* Now insert input */
lwshell_input(input_str, strlen(input_str));
}

View File

@ -0,0 +1,137 @@
/**
* \file lwshell.h
* \brief Lightweight shell
*/
/*
* Copyright (c) 2020 Tilen MAJERLE
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* This file is part of LwSHELL - Lightweight shell library.
*
* Author: Tilen MAJERLE <tilen@majerle.eu>
* Version: v0.1.0
*/
#ifndef LWSHELL_HDR_H
#define LWSHELL_HDR_H
#include <stdint.h>
#include <stdlib.h>
#include "lwshell/lwshell_opt.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* \defgroup LWSHELL Lightweight shell
* \brief Lightweight shell
* \{
*/
/**
* \brief Get size of statically allocated array
* \param[in] x: Object to get array size of
* \return Number of elements in array
*/
#define LWSHELL_ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
/**
* \brief LwSHELL result enumeration
*/
typedef enum {
lwshellOK = 0x00, /*!< Everything OK */
lwshellERRPAR, /*!< Parameter error */
lwshellERRMEM, /*!< Memory error */
} lwshellr_t;
/* Forward declaration */
struct lwshell;
/**
* \brief Command function prototype
* \param[in] argc: Number of arguments
* \param[in] argv: Pointer to arguments
* \return `0` on success, `-1` otherwise
*/
typedef int32_t (*lwshell_cmd_fn)(int32_t argc, char** argv);
/**
* \brief Callback function for character output
* \param[in] str: String to output
* \param[in] lw: LwSHELL instance
*/
typedef void (*lwshell_output_fn)(const char* str, struct lwshell* lw);
/**
* \brief LwSHELL main structure
*/
typedef struct lwshell {
lwshell_output_fn out_fn; /*!< Optional output function */
char buff[LWSHELL_CFG_MAX_INPUT_LEN + 1]; /*!< Shell command input buffer */
size_t buff_ptr; /*!< Buffer pointer for input */
int32_t argc; /*!< Number of arguments parsed in command */
char* argv[LWSHELL_CFG_MAX_CMD_ARGS]; /*!< Array of all arguments */
} lwshell_t;
lwshellr_t lwshell_init(void);
lwshellr_t lwshell_set_output_fn(lwshell_output_fn out_fn);
lwshellr_t lwshell_register_cmd(const char* cmd_name, lwshell_cmd_fn cmd_fn, const char* desc);
lwshellr_t lwshell_input(const void* in_data, size_t len);
/**
* \brief Parse input string as `integer`
* \param[in] str: String to parse
* \return String parsed as integer
*/
#define lwshell_parse_int(str) atoi(str)
/**
* \brief Parse input string as `double`
* \param[in] str: String to parse
* \return String parsed as `double`
*/
#define lwshell_parse_double(str) atof(str)
/**
* \brief Parse input string as `long`
* \param[in] str: String to parse
* \return String parsed as `long`
*/
#define lwshell_parse_long(str) atol(str)
/**
* \brief Parse input string as `long long`
* \param[in] str: String to parse
* \return String parsed as `long long`
*/
#define lwshell_parse_long_long(str) atoll(str)
/**
* \}
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LWSHELL_HDR_H */

View File

@ -0,0 +1,108 @@
/**
* \file lwshell_opt.h
* \brief LwSHELL options
*/
/*
* Copyright (c) 2020 Tilen MAJERLE
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* This file is part of LwSHELL - Lightweight shell library.
*
* Author: Tilen MAJERLE <tilen@majerle.eu>
* Version: v0.1.0
*/
#ifndef LWSHELL_HDR_OPT_H
#define LWSHELL_HDR_OPT_H
/* Uncomment to ignore user options (or set macro in compiler flags) */
/* #define LWSHELL_IGNORE_USER_OPTS */
/* Include application options */
#ifndef LWSHELL_IGNORE_USER_OPTS
#include "lwshell_opts.h"
#endif /* LWSHELL_IGNORE_USER_OPTS */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* \defgroup LWSHELL_OPT Configuration
* \brief LwSHELL options
* \{
*/
/**
* \brief Maximum number of different commands to be registered
*
*/
#ifndef LWSHELL_CFG_MAX_CMDS
#define LWSHELL_CFG_MAX_CMDS 8
#endif
/**
* \brief Maximum characters for command line input
*
* This includes new line character and trailing zero.
* Commands longer than this are automatically discarded
*/
#ifndef LWSHELL_CFG_MAX_INPUT_LEN
#define LWSHELL_CFG_MAX_INPUT_LEN 128
#endif
/**
* \brief Maximum characters for command name
*
*/
#ifndef LWSHELL_CFG_MAX_CMD_NAME_LEN
#define LWSHELL_CFG_MAX_CMD_NAME_LEN 16
#endif
/**
* \brief Maximum number of parameters accepted by command.
*
* Number includes command name itself
*/
#ifndef LWSHELL_CFG_MAX_CMD_ARGS
#define LWSHELL_CFG_MAX_CMD_ARGS 8
#endif
/**
* \brief Enables `1` or disables `0` output function to
* print data from library to application.
*
* This is useful to give library feedback to user
*/
#ifndef LWSHELL_CFG_USE_OUTPUT
#define LWSHELL_CFG_USE_OUTPUT 1
#endif
/**
* \}
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* LWSHELL_HDR_OPT_H */

View File

@ -0,0 +1,44 @@
/**
* \file lwshell_opts_template.h
* \brief Template config file
*/
/*
* Copyright (c) 2020 Tilen MAJERLE
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* This file is part of LwSHELL - Lightweight shell library.
*
* Author: Tilen MAJERLE <tilen@majerle.eu>
* Version: v0.1.0
*/
#ifndef LWSHELL_HDR_OPTS_H
#define LWSHELL_HDR_OPTS_H
/* Rename this file to "lwshell_opts.h" for your application */
/*
* Open "include/lwshell/lwshell_opt.h" and
* copy & replace here settings you want to change values
*/
#endif /* LWSHELL_HDR_OPTS_H */

View File

@ -0,0 +1,288 @@
/**
* \file lwshell.c
* \brief Lightweight shell
*/
/*
* Copyright (c) 2020 Tilen MAJERLE
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* This file is part of LwSHELL - Lightweight shell library.
*
* Author: Tilen MAJERLE <tilen@majerle.eu>
* Version: v0.1.0
*/
#include <string.h>
#include "lwshell/lwshell.h"
/* Default characters */
#define LWSHELL_ASCII_NULL 0x00 /*!< Null character */
#define LWSHELL_ASCII_BACKSPACE 0x08 /*!< Backspace */
#define LWSHELL_ASCII_LF 0x0A /*!< Line feed */
#define LWSHELL_ASCII_CR 0x0D /*!< Carriage return */
#define LWSHELL_ASCII_DEL 0x7F /*!< Delete character */
#define LWSHELL_ASCII_SPACE 0x20 /*!< Space character */
#if LWSHELL_CFG_USE_OUTPUT
#define LW_OUTPUT(lw, str) do { if ((lw)->out_fn != NULL && (str) != NULL) { (lw)->out_fn((str), (lw)); }} while (0)
#else
#define LW_OUTPUT(lw, str)
#endif
/**
* \brief Shell command structure
*/
typedef struct {
lwshell_cmd_fn fn; /*!< Command function to call on match */
const char* name; /*!< Command name to search for match */
const char* desc; /*!< Command description for help */
} lwshell_cmd_t;
/* Array of all commands */
static lwshell_cmd_t cmds[LWSHELL_CFG_MAX_CMDS];
static size_t cmds_cnt;
static lwshell_t shell;
/* Get shell instance from input */
#define LWSHELL_GET_LW(lw) ((lw) != NULL ? (lw) : (&shell))
/* Add character to instance */
#define LWSHELL_ADD_CH(lw, ch) do { \
if ((lw)->buff_ptr < (LWSHELL_ARRAYSIZE(lw->buff) - 1)) { \
(lw)->buff[(lw)->buff_ptr] = ch; \
(lw)->buff[++(lw)->buff_ptr] = '\0'; \
} \
} while (0)
/* Reset buffers */
#define LWSHELL_RESET_BUFF(lw) do { \
memset((lw)->buff, 0x00, sizeof((lw)->buff)); \
memset((lw)->argv, 0x00, sizeof((lw)->argv)); \
(lw)->buff_ptr = 0; \
} while (0)
/**
* \brief Parse input string
* \param[in] lw: LwSHELL instance
*/
static void
prv_parse_input(lwshell_t* lw) {
size_t s_len;
char ch, prev_ch;
char* str;
lw = LWSHELL_GET_LW(lw);
/* Check string length and compare */
s_len = strlen(lw->buff);
if (lw->buff_ptr != s_len) {
return;
}
/* Must be more than `1` character since we have to include end of line */
if (lw->buff_ptr > 0) {
uint8_t in_quote = 0;
/* Set default values */
lw->argc = 0;
lw->argv[0] = lw->buff;
/* Process complete input */
prev_ch = '\0';
str = lw->buff;
/* Process complete string */
lw->argc = 0;
while (*str != '\0') {
while (*str == ' ' && ++str) {} /* Remove leading spaces */
if (*str == '\0') {
break;
}
/* Check if it starts with quote to handle escapes */
if (*str == '"') {
++str;
lw->argv[lw->argc++] = str; /* Set start of argument after quotes */
/* Process until end of quote */
while (*str != '\0') {
if (*str == '\\') {
++str;
if (*str == '"') {
++str;
}
} else if (*str == '"') {
*str = '\0';
++str;
break;
} else {
++str;
}
}
} else {
lw->argv[lw->argc++] = str; /* Set start of argument directly on character */
while ((*str != ' ' && *str != '\0')) {
if (*str == '"') { /* Quote should not be here... */
*str = '\0'; /* ...add NULL termination to end token */
}
++str;
}
*str = '\0';
++str;
}
/* Check for number of arguments */
if (lw->argc == LWSHELL_ARRAYSIZE(lw->argv)) {
break;
}
}
/* Check for command */
if (lw->argc > 0 && cmds_cnt > 0) {
lwshell_cmd_t* c = NULL;
/* Process all commands */
for (size_t i = 0; i < cmds_cnt; ++i) {
if (strncmp(cmds[i].name, lw->argv[0], strlen(lw->argv[0])) == 0) {
c = &cmds[i];
break;
}
}
/* Valid command ready? */
if (c != NULL) {
if (lw->argc == 2
&& lw->argv[1][0] == '-' && lw->argv[1][1] == 'h' && lw->argv[1][2] == '\0') {
/* Here we can print version */
LW_OUTPUT(lw, c->desc);
LW_OUTPUT(lw, "\r\n");
} else {
c->fn(lw->argc, lw->argv);
}
}
}
}
}
/**
* \brief Initialize shell interface
* \return \ref lwshellOK on success, member of \ref lwshellr_t otherwise
*/
lwshellr_t
lwshell_init(void) {
lwshell_t* lw = LWSHELL_GET_LW(NULL);
memset(lw, 0x00, sizeof(*lw));
return lwshellOK;
}
#if LWSHELL_CFG_USE_OUTPUT || __DOXYGEN__
/**
* \brief Set output function to use to print data from library to user
* \param[in] out_fn: Output function to print library data.
* Set to `NULL` to disable the feature
* \return \ref lwshellOK on success, member of \ref lwshellr_t otherwise
*/
lwshellr_t
lwshell_set_output_fn(lwshell_output_fn out_fn) {
lwshell_t* lw = LWSHELL_GET_LW(NULL);
lw->out_fn = out_fn;
return lwshellOK;
}
#endif /* LWSHELL_CFG_USE_OUTPUT || __DOXYGEN__ */
/**
* \brief Register new command to shell
* \param[in] cmd_name: Command name. This one is used when entering shell command
* \param[in] cmd_fn: Function to call on command match
* \param[in] desc: Custom command description
* \return \ref lwshellOK on success, member of \ref lwshellr_t otherwise
*/
lwshellr_t
lwshell_register_cmd(const char* cmd_name, lwshell_cmd_fn cmd_fn, const char* desc) {
if (cmd_name == NULL || cmd_fn == NULL
|| strlen(cmd_name) == 0) {
return lwshellERRPAR;
}
/* Check for memory available */
if (cmds_cnt < LWSHELL_ARRAYSIZE(cmds)) {
cmds[cmds_cnt].name = cmd_name;
cmds[cmds_cnt].fn = cmd_fn;
cmds[cmds_cnt].desc = desc;
++cmds_cnt;
return lwshellOK;
}
return lwshellERRMEM;
}
/**
* \brief Input data to shell processing
* \param[in] in_data: Input data to process
* \param[in] len: Length of data for input
* \return \ref lwshellOK on success, member of \ref lwshellr_t otherwise
*/
lwshellr_t
lwshell_input(const void* in_data, size_t len) {
const char* d = in_data;
lwshell_t* lw = LWSHELL_GET_LW(NULL);
if (in_data == NULL || len == 0) {
return lwshellERRPAR;
}
/* Process all bytes */
for (size_t i = 0; i < len; ++i) {
switch (d[i]) {
case LWSHELL_ASCII_CR: {
LW_OUTPUT(lw, "\r");
prv_parse_input(lw);
LWSHELL_RESET_BUFF(lw);
break;
}
case LWSHELL_ASCII_LF: {
LW_OUTPUT(lw, "\n");
prv_parse_input(lw);
LWSHELL_RESET_BUFF(lw);
break;
}
case LWSHELL_ASCII_BACKSPACE: {
/* Try to delete character from buffer */
if (lw->buff_ptr > 0) {
lw->buff[lw->buff_ptr] = '\0';
--lw->buff_ptr;
LW_OUTPUT(lw, "\b \b");
}
break;
}
default: {
char str[2] = { d[i] };
LW_OUTPUT(lw, str);
if (d[i] >= 0x20 && d[i] < 0x7F) {
LWSHELL_ADD_CH(lw, d[i]);
}
}
}
}
return lwshellOK;
}