/* * Copyright (c) 2019 Stefan Strobel * * 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 */ /** * @addtogroup shellmatta_opt * @{ */ #include "shellmatta_opt.h" #include "shellmatta_utils.h" #include "shellmatta.h" #include /** * @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] handle shellmatta handle * @param[in] optionString option string e.g. "cd:e::" * @param[out] option pointer to store the detected option to * @param[out] argtype pointer to store the argument string to (can be 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, 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])) { /*! -# search for option character in option string */ for(i = 0u; ('\0' != optionString[i]) && (buffer[1u] != optionString[i]); i ++); if(buffer[1u] == optionString[i]) { /*! -# return found option character */ *option = buffer[1u]; ret = SHELLMATTA_OK; /*! -# 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; } } } else { *option = '\0'; } 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, char *optionString, 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 != optionString) && (NULL != option)) { *option = '\0'; *argument = NULL; *argLen = 0u; ret = findNextHunk(inst); if(SHELLMATTA_OK == ret) { ret = parseShortOpt(inst, optionString, option, &argtype); /*! -# when no option is found return this as raw argument */ if(SHELLMATTA_ERROR == ret) { *argument = &(inst->buffer[inst->optionParser.offset]); *argLen = inst->optionParser.len; ret = SHELLMATTA_OK; } else { switch(argtype) { case SHELLMATTA_OPT_ARG_REQUIRED: ret = findNextHunk(inst); 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(SHELLMATTA_OK == ret) { *argument = &(inst->buffer[inst->optionParser.offset]); *argLen = inst->optionParser.len; } } break; default: /* nothing to do */ break; } } } } (void)argument; (void)argLen; return ret; } /** * @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, 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; /** -# check parameters for plausibility */ if( (NULL != inst) && (SHELLMATTA_MAGIC == inst->magic) && (NULL != longOptions) && (NULL != option)) { } (void)argument; (void)argLen; return ret; } /** * @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; } /** * @} */