123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- /*
- * Copyright (c) 2019 - 2024 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])) && ('\0' == quotation))
- {
- 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;
- const 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;
- const 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((inst->optionParser.len >= 3u) && ('-' == 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)
- */
- void shellmatta_opt_init(shellmatta_instance_t *inst, uint32_t argStart)
- {
- /** -# initialize all relevant option parser variables */
- inst->optionParser.argStart = argStart;
- inst->optionParser.nextOffset = argStart;
- }
- /**
- * @brief re-initializes the option parser instance using the data from the last init
- * @param[in, out] inst pointer to a shellmatta instance
- */
- void shellmatta_opt_reInit(shellmatta_instance_t *inst)
- {
- /** -# initialize all relevant option parser variables */
- inst->optionParser.nextOffset = inst->optionParser.argStart;
- }
- /**
- * @}
- */
|