| | |
| | | #include <ctype.h> |
| | | #include <string.h> |
| | | |
| | | #include "scpi/config.h" |
| | | #include "scpi/parser.h" |
| | | #include "utils.h" |
| | | #include "scpi/error.h" |
| | | #include "scpi/constants.h" |
| | | |
| | | |
| | | static size_t patternSeparatorPos(const char * pattern, size_t len); |
| | | static size_t cmdSeparatorPos(const char * cmd, size_t len); |
| | | static size_t cmdTerminatorPos(const char * cmd, size_t len); |
| | | static size_t cmdlineSeparatorPos(const char * cmd, size_t len); |
| | | static char * cmdlineSeparator(const char * cmd, size_t len); |
| | | static char * cmdlineTerminator(const char * cmd, size_t len); |
| | | static const char * cmdlineSeparator(const char * cmd, size_t len); |
| | | static const char * cmdlineTerminator(const char * cmd, size_t len); |
| | | static const char * cmdlineNext(const char * cmd, size_t len); |
| | | static bool_t cmdMatch(const char * pattern, const char * cmd, size_t len); |
| | | |
| | | static void paramSkipBytes(scpi_t * context, size_t num); |
| | | static void paramSkipWhitespace(scpi_t * context); |
| | |
| | | */ |
| | | |
| | | /** |
| | | * Find pattern separator position |
| | | * @param pattern |
| | | * @param len - max search length |
| | | * @return position of separator or len |
| | | */ |
| | | size_t patternSeparatorPos(const char * pattern, size_t len) { |
| | | |
| | | char * separator = strnpbrk(pattern, len, "?:[]"); |
| | | if (separator == NULL) { |
| | | return len; |
| | | } else { |
| | | return separator - pattern; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Find command separator position |
| | | * @param cmd - input command |
| | | * @param len - max search length |
| | | * @return position of separator or len |
| | | */ |
| | | size_t cmdSeparatorPos(const char * cmd, size_t len) { |
| | | char * separator = strnpbrk(cmd, len, ":?"); |
| | | size_t result; |
| | | if (separator == NULL) { |
| | | result = len; |
| | | } else { |
| | | result = separator - cmd; |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Find command termination character |
| | | * @param cmd - input command |
| | | * @param len - max search length |
| | | * @return position of terminator or len |
| | | */ |
| | | size_t cmdTerminatorPos(const char * cmd, size_t len) { |
| | | char * terminator = strnpbrk(cmd, len, "; \r\n\t"); |
| | | const char * terminator = strnpbrk(cmd, len, "; \r\n\t"); |
| | | if (terminator == NULL) { |
| | | return len; |
| | | } else { |
| | |
| | | * @param len - max search length |
| | | * @return pointer to line separator or NULL |
| | | */ |
| | | char * cmdlineSeparator(const char * cmd, size_t len) { |
| | | const char * cmdlineSeparator(const char * cmd, size_t len) { |
| | | return strnpbrk(cmd, len, ";\r\n"); |
| | | } |
| | | |
| | |
| | | * @param len - max search length |
| | | * @return pointer to command line terminator or NULL |
| | | */ |
| | | char * cmdlineTerminator(const char * cmd, size_t len) { |
| | | const char * cmdlineTerminator(const char * cmd, size_t len) { |
| | | return strnpbrk(cmd, len, "\r\n"); |
| | | } |
| | | |
| | |
| | | * @return position of line separator or len |
| | | */ |
| | | size_t cmdlineSeparatorPos(const char * cmd, size_t len) { |
| | | char * separator = cmdlineSeparator(cmd, len); |
| | | const char * separator = cmdlineSeparator(cmd, len); |
| | | if (separator == NULL) { |
| | | return len; |
| | | } else { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Compare pattern and command |
| | | * @param pattern |
| | | * @param cmd - command |
| | | * @param len - max search length |
| | | * @return TRUE if pattern matches, FALSE otherwise |
| | | */ |
| | | bool_t cmdMatch(const char * pattern, const char * cmd, size_t len) { |
| | | int result = FALSE; |
| | | |
| | | const char * pattern_ptr = pattern; |
| | | int pattern_len = strlen(pattern); |
| | | const char * pattern_end = pattern + pattern_len; |
| | | |
| | | const char * cmd_ptr = cmd; |
| | | size_t cmd_len = strnlen(cmd, len); |
| | | const char * cmd_end = cmd + cmd_len; |
| | | |
| | | while (1) { |
| | | int pattern_sep_pos = patternSeparatorPos(pattern_ptr, pattern_end - pattern_ptr); |
| | | int cmd_sep_pos = cmdSeparatorPos(cmd_ptr, cmd_end - cmd_ptr); |
| | | |
| | | if (matchPattern(pattern_ptr, pattern_sep_pos, cmd_ptr, cmd_sep_pos)) { |
| | | pattern_ptr = pattern_ptr + pattern_sep_pos; |
| | | cmd_ptr = cmd_ptr + cmd_sep_pos; |
| | | result = TRUE; |
| | | |
| | | // command is complete |
| | | if ((pattern_ptr == pattern_end) && (cmd_ptr >= cmd_end)) { |
| | | break; |
| | | } |
| | | |
| | | // pattern complete, but command not |
| | | if ((pattern_ptr == pattern_end) && (cmd_ptr < cmd_end)) { |
| | | result = FALSE; |
| | | break; |
| | | } |
| | | |
| | | // command complete, but pattern not |
| | | if (cmd_ptr >= cmd_end) { |
| | | result = FALSE; |
| | | break; |
| | | } |
| | | |
| | | // both command and patter contains command separator at this position |
| | | if ((pattern_ptr[0] == cmd_ptr[0]) && ((pattern_ptr[0] == ':') || (pattern_ptr[0] == '?'))) { |
| | | pattern_ptr = pattern_ptr + 1; |
| | | cmd_ptr = cmd_ptr + 1; |
| | | } else { |
| | | result = FALSE; |
| | | break; |
| | | } |
| | | } else { |
| | | result = FALSE; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Write data to SCPI output |
| | | * @param context |
| | | * @param data |
| | | * @param len - lenght of data to be written |
| | | * @return number of bytes written |
| | | */ |
| | | static inline size_t writeData(scpi_t * context, const char * data, size_t len) { |
| | | static size_t writeData(scpi_t * context, const char * data, size_t len) { |
| | | return context->interface->write(context, data, len); |
| | | } |
| | | |
| | |
| | | * @param context |
| | | * @return |
| | | */ |
| | | static inline int flushData(scpi_t * context) { |
| | | static int flushData(scpi_t * context) { |
| | | if (context && context->interface && context->interface->flush) { |
| | | return context->interface->flush(context); |
| | | } else { |
| | |
| | | * @param context |
| | | * @return number of bytes written |
| | | */ |
| | | static inline size_t writeDelimiter(scpi_t * context) { |
| | | static size_t writeDelimiter(scpi_t * context) { |
| | | if (context->output_count > 0) { |
| | | return writeData(context, ", ", 2); |
| | | } else { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Zapis nove radky na SCPI vystup |
| | | * Conditionaly write "New Line" |
| | | * @param context |
| | | * @return pocet zapsanych znaku |
| | | * @return number of characters written |
| | | */ |
| | | static inline size_t writeNewLine(scpi_t * context) { |
| | | size_t len; |
| | | static size_t writeNewLine(scpi_t * context) { |
| | | if (context->output_count > 0) { |
| | | size_t len; |
| | | len = writeData(context, "\r\n", 2); |
| | | flushData(context); |
| | | return len; |
| | | } else { |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Process command |
| | | * @param context |
| | | */ |
| | | static void processCommand(scpi_t * context) { |
| | | const scpi_command_t * cmd = context->paramlist.cmd; |
| | | |
| | | context->cmd_error = FALSE; |
| | | context->output_count = 0; |
| | | context->input_count = 0; |
| | | |
| | | SCPI_DEBUG_COMMAND(context); |
| | | /* if callback exists - call command callback */ |
| | | if (cmd->callback != NULL) { |
| | | if ((cmd->callback(context) != SCPI_RES_OK) && !context->cmd_error) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_EXECUTION_ERROR); |
| | | } |
| | | } |
| | | |
| | | /* conditionaly write new line */ |
| | | writeNewLine(context); |
| | | |
| | | /* skip all whitespaces */ |
| | | paramSkipWhitespace(context); |
| | | |
| | | /* set error if command callback did not read all parameters */ |
| | | if (context->paramlist.length != 0 && !context->cmd_error) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_PARAMETER_NOT_ALLOWED); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Cycle all patterns and search matching pattern. Execute command callback. |
| | | * @param context |
| | | * @result TRUE if context->paramlist is filled with correct values |
| | | */ |
| | | static bool_t findCommand(scpi_t * context, const char * cmdline_ptr, size_t cmdline_len, size_t cmd_len) { |
| | | int32_t i; |
| | | const scpi_command_t * cmd; |
| | | |
| | | for (i = 0; context->cmdlist[i].pattern != NULL; i++) { |
| | | cmd = &context->cmdlist[i]; |
| | | if (matchCommand(cmd->pattern, cmdline_ptr, cmd_len)) { |
| | | context->paramlist.cmd = cmd; |
| | | context->paramlist.parameters = cmdline_ptr + cmd_len; |
| | | context->paramlist.length = cmdline_len - cmd_len; |
| | | return TRUE; |
| | | } |
| | | } |
| | | return FALSE; |
| | | } |
| | | |
| | | /** |
| | |
| | | * @return 1 if the last evaluated command was found |
| | | */ |
| | | int SCPI_Parse(scpi_t * context, const char * data, size_t len) { |
| | | int32_t i; |
| | | int result = 0; |
| | | const char * cmdline_end = data + len; |
| | | const char * cmdline_ptr = data; |
| | | size_t cmd_len; |
| | | size_t cmdline_len; |
| | | |
| | | if (context == NULL) { |
| | | return -1; |
| | | } |
| | | |
| | | while (cmdline_ptr < cmdline_end) { |
| | | |
| | | result = 0; |
| | | cmd_len = cmdTerminatorPos(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | cmdline_len = cmdlineSeparatorPos(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | if (cmd_len > 0) { |
| | | for (i = 0; context->cmdlist[i].pattern != NULL; i++) { |
| | | if (cmdMatch(context->cmdlist[i].pattern, cmdline_ptr, cmd_len)) { |
| | | if (context->cmdlist[i].callback != NULL) { |
| | | context->cmd_error = FALSE; |
| | | context->paramlist.cmd = &context->cmdlist[i]; |
| | | context->paramlist.parameters = cmdline_ptr + cmd_len; |
| | | context->paramlist.length = cmdlineSeparatorPos(context->paramlist.parameters, cmdline_end - context->paramlist.parameters); |
| | | context->output_count = 0; |
| | | context->input_count = 0; |
| | | |
| | | SCPI_DEBUG_COMMAND(context); |
| | | if ((context->cmdlist[i].callback(context) != SCPI_RES_OK) && !context->cmd_error) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_EXECUTION_ERROR); |
| | | } |
| | | |
| | | writeNewLine(context); // conditionaly write new line |
| | | |
| | | paramSkipWhitespace(context); |
| | | if (context->paramlist.length != 0 && !context->cmd_error) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_PARAMETER_NOT_ALLOWED); |
| | | } |
| | | |
| | | result = 1; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | if (result == 0) { |
| | | if(findCommand(context, cmdline_ptr, cmdline_len, cmd_len)) { |
| | | processCommand(context); |
| | | result = 1; |
| | | } else { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_UNDEFINED_HEADER); |
| | | } |
| | | } |
| | | cmdline_ptr = cmdlineNext(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | |
| | | cmdline_ptr += skipWhitespace(cmdline_ptr, cmdline_end - cmdline_ptr); |
| | | } |
| | | return result; |
| | | } |
| | |
| | | * @param interface |
| | | */ |
| | | void SCPI_Init(scpi_t * context) { |
| | | if (context->idn[0] == NULL) { |
| | | context->idn[0] = SCPI_DEFAULT_1_MANUFACTURE; |
| | | } |
| | | if (context->idn[1] == NULL) { |
| | | context->idn[1] = SCPI_DEFAULT_2_MODEL; |
| | | } |
| | | if (context->idn[2] == NULL) { |
| | | context->idn[2] = SCPI_DEFAULT_3; |
| | | } |
| | | if (context->idn[3] == NULL) { |
| | | context->idn[3] = SCPI_DEFAULT_4_REVISION; |
| | | } |
| | | |
| | | context->buffer.position = 0; |
| | | SCPI_ErrorInit(context); |
| | | } |
| | |
| | | */ |
| | | int SCPI_Input(scpi_t * context, const char * data, size_t len) { |
| | | int result = 0; |
| | | size_t buffer_free; |
| | | char * cmd_term; |
| | | int ws; |
| | | const char * cmd_term; |
| | | if (len == 0) { |
| | | context->buffer.data[context->buffer.position] = 0; |
| | | result = SCPI_Parse(context, context->buffer.data, context->buffer.position); |
| | | context->buffer.position = 0; |
| | | } else { |
| | | size_t buffer_free; |
| | | int ws; |
| | | buffer_free = context->buffer.length - context->buffer.position; |
| | | if (len > (buffer_free - 1)) { |
| | | return -1; |
| | |
| | | result += writeData(context, buffer, len); |
| | | context->output_count++; |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Write boolean value to the result |
| | | * @param context |
| | | * @param val |
| | | * @return |
| | | */ |
| | | size_t SCPI_ResultBool(scpi_t * context, bool_t val) { |
| | | return SCPI_ResultInt(context, val ? 1 : 0); |
| | | } |
| | | |
| | | /** |
| | |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamInt(scpi_t * context, int32_t * value, bool_t mandatory) { |
| | | char * param; |
| | | const char * param; |
| | | size_t param_len; |
| | | size_t num_len; |
| | | |
| | |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamDouble(scpi_t * context, double * value, bool_t mandatory) { |
| | | char * param; |
| | | const char * param; |
| | | size_t param_len; |
| | | size_t num_len; |
| | | |
| | |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamString(scpi_t * context, char ** value, size_t * len, bool_t mandatory) { |
| | | bool_t SCPI_ParamString(scpi_t * context, const char ** value, size_t * len, bool_t mandatory) { |
| | | size_t length; |
| | | |
| | | if (!value || !len) { |
| | |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamText(scpi_t * context, char ** value, size_t * len, bool_t mandatory) { |
| | | bool_t SCPI_ParamText(scpi_t * context, const char ** value, size_t * len, bool_t mandatory) { |
| | | size_t length; |
| | | |
| | | if (!value || !len) { |
| | |
| | | |
| | | return FALSE; |
| | | } |
| | | |
| | | /** |
| | | * Parse boolean parameter as described in the spec SCPI-99 7.3 Boolean Program Data |
| | | * @param context |
| | | * @param value |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamBool(scpi_t * context, bool_t * value, bool_t mandatory) { |
| | | const char * param; |
| | | size_t param_len; |
| | | size_t num_len; |
| | | int32_t i; |
| | | |
| | | if (!value) { |
| | | return FALSE; |
| | | } |
| | | |
| | | if (!SCPI_ParamString(context, ¶m, ¶m_len, mandatory)) { |
| | | return FALSE; |
| | | } |
| | | |
| | | if (matchPattern("ON", 2, param, param_len)) { |
| | | *value = TRUE; |
| | | } else if (matchPattern("OFF", 3, param, param_len)) { |
| | | *value = FALSE; |
| | | } else { |
| | | num_len = strToLong(param, &i); |
| | | |
| | | if (num_len != param_len) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_SUFFIX_NOT_ALLOWED); |
| | | return FALSE; |
| | | } |
| | | |
| | | *value = i ? TRUE : FALSE; |
| | | } |
| | | |
| | | return TRUE; |
| | | } |
| | | |
| | | /** |
| | | * Parse choice parameter |
| | | * @param context |
| | | * @param options |
| | | * @param value |
| | | * @param mandatory |
| | | * @return |
| | | */ |
| | | bool_t SCPI_ParamChoice(scpi_t * context, const char * options[], int32_t * value, bool_t mandatory) { |
| | | const char * param; |
| | | size_t param_len; |
| | | size_t res; |
| | | |
| | | if (!options || !value) { |
| | | return FALSE; |
| | | } |
| | | |
| | | if (!SCPI_ParamString(context, ¶m, ¶m_len, mandatory)) { |
| | | return FALSE; |
| | | } |
| | | |
| | | for (res = 0; options[res]; ++res) { |
| | | if (matchPattern(options[res], strlen(options[res]), param, param_len)) { |
| | | *value = res; |
| | | return TRUE; |
| | | } |
| | | } |
| | | |
| | | SCPI_ErrorPush(context, SCPI_ERROR_ILLEGAL_PARAMETER_VALUE); |
| | | return FALSE; |
| | | } |
| | | |