Resolve #59: input buffer overrun handling
| | |
| | | XE(SCPI_ERROR_COMMUNICATION_ERROR, -360, "Communication error") \ |
| | | XE(SCPI_ERROR_PARITY_ERROR_IN_CMD_MSG, -361, "Parity error in program message") \ |
| | | XE(SCPI_ERROR_FRAMING_ERROR_IN_CMD_MSG, -362, "Framing error in program message") \ |
| | | XE(SCPI_ERROR_INPUT_BUFFER_OVERRUN, -363, "Input buffer overrun") \ |
| | | X(SCPI_ERROR_INPUT_BUFFER_OVERRUN, -363, "Input buffer overrun") \ |
| | | XE(SCPI_ERROR_TIME_OUT, -365, "Time out error") \ |
| | | XE(SCPI_ERROR_QUERY_ERROR, -400, "Query error") \ |
| | | XE(SCPI_ERROR_QUERY_INTERRUPTED, -410, "Query INTERRUPTED") \ |
| | |
| | | #endif |
| | | void SCPI_Init(scpi_t * context); |
| | | |
| | | int SCPI_Input(scpi_t * context, const char * data, int len); |
| | | int SCPI_Parse(scpi_t * context, char * data, int len); |
| | | scpi_bool_t SCPI_Input(scpi_t * context, const char * data, int len); |
| | | scpi_bool_t SCPI_Parse(scpi_t * context, char * data, int len); |
| | | |
| | | size_t SCPI_ResultCharacters(scpi_t * context, const char * data, size_t len); |
| | | #define SCPI_ResultMnemonic(context, data) SCPI_ResultCharacters((context), (data), strlen(data)) |
| | |
| | | int32_t new_ESE; |
| | | if (SCPI_ParamInt32(context, &new_ESE, TRUE)) { |
| | | SCPI_RegSet(context, SCPI_REG_ESE, (scpi_reg_val_t) new_ESE); |
| | | } |
| | | return SCPI_RES_OK; |
| | | } |
| | | return SCPI_RES_ERR; |
| | | } |
| | | |
| | | /** |
| | |
| | | int32_t new_SRE; |
| | | if (SCPI_ParamInt32(context, &new_SRE, TRUE)) { |
| | | SCPI_RegSet(context, SCPI_REG_SRE, (scpi_reg_val_t) new_SRE); |
| | | } |
| | | return SCPI_RES_OK; |
| | | } |
| | | return SCPI_RES_ERR; |
| | | } |
| | | |
| | | /** |
| | | * *SRE? |
| | |
| | | * Process command |
| | | * @param context |
| | | */ |
| | | static void processCommand(scpi_t * context) { |
| | | static scpi_bool_t processCommand(scpi_t * context) { |
| | | const scpi_command_t * cmd = context->param_list.cmd; |
| | | lex_state_t * state = &context->param_list.lex_state; |
| | | scpi_bool_t result = TRUE; |
| | | |
| | | /* conditionaly write ; */ |
| | | writeSemicolon(context); |
| | |
| | | |
| | | /* if callback exists - call command callback */ |
| | | if (cmd->callback != NULL) { |
| | | if ((cmd->callback(context) != SCPI_RES_OK) && !context->cmd_error) { |
| | | if ((cmd->callback(context) != SCPI_RES_OK)) { |
| | | if (!context->cmd_error) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_EXECUTION_ERROR); |
| | | } |
| | | result = FALSE; |
| | | } else { |
| | | if (context->cmd_error) { |
| | | result = FALSE; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* set error if command callback did not read all parameters */ |
| | | if (state->pos < (state->buffer + state->len) && !context->cmd_error) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_PARAMETER_NOT_ALLOWED); |
| | | result = FALSE; |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param context |
| | | * @param data - complete command line |
| | | * @param len - command line length |
| | | * @return 1 if the last evaluated command was found |
| | | * @return FALSE if there was some error during evaluation of commands |
| | | */ |
| | | int SCPI_Parse(scpi_t * context, char * data, int len) { |
| | | int result = 0; |
| | | scpi_bool_t SCPI_Parse(scpi_t * context, char * data, int len) { |
| | | scpi_bool_t result = TRUE; |
| | | scpi_parser_state_t * state; |
| | | int r; |
| | | scpi_token_t cmd_prev = {SCPI_TOKEN_UNKNOWN, NULL, 0}; |
| | | |
| | | if (context == NULL) { |
| | | return -1; |
| | | return FALSE; |
| | | } |
| | | |
| | | state = &context->parser_state; |
| | | context->output_count = 0; |
| | | |
| | | while (1) { |
| | | result = 0; |
| | | |
| | | r = scpiParser_detectProgramMessageUnit(state, data, len); |
| | | |
| | | if (state->programHeader.type == SCPI_TOKEN_INVALID) { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_INVALID_CHARACTER); |
| | | result = FALSE; |
| | | } else if (state->programHeader.len > 0) { |
| | | |
| | | composeCompoundCommand(&cmd_prev, &state->programHeader); |
| | |
| | | context->param_list.cmd_raw.position = 0; |
| | | context->param_list.cmd_raw.length = state->programHeader.len; |
| | | |
| | | processCommand(context); |
| | | |
| | | result = 1; |
| | | result &= processCommand(context); |
| | | cmd_prev = state->programHeader; |
| | | } else { |
| | | SCPI_ErrorPush(context, SCPI_ERROR_UNDEFINED_HEADER); |
| | | result = FALSE; |
| | | } |
| | | } |
| | | |
| | |
| | | * @param len - length of data |
| | | * @return |
| | | */ |
| | | int SCPI_Input(scpi_t * context, const char * data, int len) { |
| | | int result = 0; |
| | | scpi_bool_t SCPI_Input(scpi_t * context, const char * data, int len) { |
| | | scpi_bool_t result = TRUE; |
| | | size_t totcmdlen = 0; |
| | | int cmdlen = 0; |
| | | |
| | |
| | | |
| | | buffer_free = context->buffer.length - context->buffer.position; |
| | | if (len > (buffer_free - 1)) { |
| | | return -1; |
| | | /* Input buffer overrun - invalidate buffer */ |
| | | context->buffer.position = 0; |
| | | context->buffer.data[context->buffer.position] = 0; |
| | | SCPI_ErrorPush(context, SCPI_ERROR_INPUT_BUFFER_OVERRUN); |
| | | return FALSE; |
| | | } |
| | | memcpy(&context->buffer.data[context->buffer.position], data, len); |
| | | context->buffer.position += len; |
| | |
| | | output_buffer_clear(); |
| | | error_buffer_clear(); |
| | | |
| | | #define TEST_ERROR(data, output, err_num) { \ |
| | | #define TEST_ERROR(data, output, expected_result, err_num) { \ |
| | | output_buffer_clear(); \ |
| | | error_buffer_clear(); \ |
| | | SCPI_Input(&scpi_context, data, strlen(data)); \ |
| | | scpi_bool_t result = SCPI_Input(&scpi_context, data, strlen(data)); \ |
| | | CU_ASSERT_STRING_EQUAL(output, output_buffer); \ |
| | | CU_ASSERT_EQUAL(err_buffer[0], err_num) \ |
| | | CU_ASSERT_EQUAL(err_buffer[0], err_num); \ |
| | | CU_ASSERT_EQUAL(result, expected_result); \ |
| | | } |
| | | |
| | | TEST_ERROR("*IDN?\r\n", "MA,IN,0,VER\r\n", 0); |
| | | TEST_ERROR("IDN?\r\n", "", SCPI_ERROR_UNDEFINED_HEADER); |
| | | TEST_ERROR("*ESE\r\n", "", SCPI_ERROR_MISSING_PARAMETER); |
| | | TEST_ERROR("*IDN? 12\r\n", "MA,IN,0,VER\r\n", SCPI_ERROR_PARAMETER_NOT_ALLOWED); |
| | | TEST_ERROR("TEXT? \"PARAM1\", \"PARAM2\"\r\n", "\"PARAM2\"\r\n", 0); |
| | | TEST_ERROR("*IDN?\r\n", "MA,IN,0,VER\r\n", TRUE, 0); |
| | | TEST_ERROR("IDN?\r\n", "", FALSE, SCPI_ERROR_UNDEFINED_HEADER); |
| | | TEST_ERROR("*ESE\r\n", "", FALSE, SCPI_ERROR_MISSING_PARAMETER); |
| | | TEST_ERROR("*IDN? 12\r\n", "MA,IN,0,VER\r\n", FALSE, SCPI_ERROR_PARAMETER_NOT_ALLOWED); |
| | | TEST_ERROR("TEXT? \"PARAM1\", \"PARAM2\"\r\n", "\"PARAM2\"\r\n", TRUE, 0); |
| | | TEST_ERROR("ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ" |
| | | "ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ" |
| | | "ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ", |
| | | "", FALSE, SCPI_ERROR_INPUT_BUFFER_OVERRUN); |
| | | |
| | | // TODO: SCPI_ERROR_INVALID_SEPARATOR |
| | | // TODO: SCPI_ERROR_INVALID_SUFFIX |