diff options
Diffstat (limited to 'third_party/libscpi/src')
| -rw-r--r-- | third_party/libscpi/src/error.c | 237 | ||||
| -rw-r--r-- | third_party/libscpi/src/expression.c | 316 | ||||
| -rw-r--r-- | third_party/libscpi/src/fifo.c | 146 | ||||
| -rw-r--r-- | third_party/libscpi/src/fifo_private.h | 61 | ||||
| -rw-r--r-- | third_party/libscpi/src/ieee488.c | 424 | ||||
| -rw-r--r-- | third_party/libscpi/src/lexer.c | 935 | ||||
| -rw-r--r-- | third_party/libscpi/src/lexer_private.h | 69 | ||||
| -rw-r--r-- | third_party/libscpi/src/minimal.c | 215 | ||||
| -rw-r--r-- | third_party/libscpi/src/parser.c | 1834 | ||||
| -rw-r--r-- | third_party/libscpi/src/parser_private.h | 56 | ||||
| -rw-r--r-- | third_party/libscpi/src/scpi.g | 141 | ||||
| -rw-r--r-- | third_party/libscpi/src/units.c | 512 | ||||
| -rw-r--r-- | third_party/libscpi/src/utils.c | 1142 | ||||
| -rw-r--r-- | third_party/libscpi/src/utils_private.h | 128 |
14 files changed, 6216 insertions, 0 deletions
diff --git a/third_party/libscpi/src/error.c b/third_party/libscpi/src/error.c new file mode 100644 index 00000000..88071406 --- /dev/null +++ b/third_party/libscpi/src/error.c @@ -0,0 +1,237 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/** + * @file scpi_error.c + * @date Thu Nov 15 10:58:45 UTC 2012 + * + * @brief Error handling and storing routines + * + * + */ + +#include <stdint.h> + +#include "scpi/parser.h" +#include "scpi/ieee488.h" +#include "scpi/error.h" +#include "fifo_private.h" +#include "scpi/constants.h" + +#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION +#define SCPI_ERROR_SETVAL(e, c, i) do { (e)->error_code = (c); (e)->device_dependent_info = (i); } while(0) +#else +#define SCPI_ERROR_SETVAL(e, c, i) do { (e)->error_code = (c); (void)(i);} while(0) +#endif + +/** + * Initialize error queue + * @param context - scpi context + */ +void SCPI_ErrorInit(scpi_t * context, scpi_error_t * data, int16_t size) { + fifo_init(&context->error_queue, data, size); +} + +/** + * Emit no error + * @param context scpi context + */ +static void SCPI_ErrorEmitEmpty(scpi_t * context) { + if ((SCPI_ErrorCount(context) == 0) && (SCPI_RegGet(context, SCPI_REG_STB) & STB_QMA)) { + SCPI_RegClearBits(context, SCPI_REG_STB, STB_QMA); + + if (context->interface && context->interface->error) { + context->interface->error(context, 0); + } + } +} + +/** + * Emit error + * @param context scpi context + * @param err Error to emit + */ +static void SCPI_ErrorEmit(scpi_t * context, int16_t err) { + SCPI_RegSetBits(context, SCPI_REG_STB, STB_QMA); + + if (context->interface && context->interface->error) { + context->interface->error(context, err); + } +} + +/** + * Clear error queue + * @param context - scpi context + */ +void SCPI_ErrorClear(scpi_t * context) { +#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION + scpi_error_t error; + while (fifo_remove(&context->error_queue, &error)) { + SCPIDEFINE_free(&context->error_info_heap, error.device_dependent_info, false); + } +#endif + fifo_clear(&context->error_queue); + + SCPI_ErrorEmitEmpty(context); +} + +/** + * Pop error from queue + * @param context - scpi context + * @param error + * @return + */ +scpi_bool_t SCPI_ErrorPop(scpi_t * context, scpi_error_t * error) { + if (!error || !context) return FALSE; + SCPI_ERROR_SETVAL(error, 0, NULL); + fifo_remove(&context->error_queue, error); + + SCPI_ErrorEmitEmpty(context); + + return TRUE; +} + +/** + * Return number of errors/events in the queue + * @param context + * @return + */ +int32_t SCPI_ErrorCount(scpi_t * context) { + int16_t result = 0; + + fifo_count(&context->error_queue, &result); + + return result; +} + +static scpi_bool_t SCPI_ErrorAddInternal(scpi_t * context, int16_t err, char * info, size_t info_len) { + scpi_error_t error_value; + /* SCPIDEFINE_strndup is sometimes a dumy that does not reference it's arguments. + Since info_len is not referenced elsewhere caoing to void prevents unusd argument warnings */ + (void) info_len; + char * info_ptr = NULL; + if (info) { + info_ptr = SCPIDEFINE_strndup(&context->error_info_heap, info, info_len); + } + SCPI_ERROR_SETVAL(&error_value, err, info_ptr); + if (!fifo_add(&context->error_queue, &error_value)) { + SCPIDEFINE_free(&context->error_info_heap, error_value.device_dependent_info, true); + fifo_remove_last(&context->error_queue, &error_value); + SCPIDEFINE_free(&context->error_info_heap, error_value.device_dependent_info, true); + SCPI_ERROR_SETVAL(&error_value, SCPI_ERROR_QUEUE_OVERFLOW, NULL); + fifo_add(&context->error_queue, &error_value); + return FALSE; + } + return TRUE; +} + +struct error_reg { + int16_t from; + int16_t to; + scpi_reg_val_t esrBit; +}; + +#define ERROR_DEFS_N 9 + +static const struct error_reg errs[ERROR_DEFS_N] = { + {-100, -199, ESR_CER}, /* Command error (e.g. syntax error) ch 21.8.9 */ + {-200, -299, ESR_EER}, /* Execution Error (e.g. range error) ch 21.8.10 */ + {-300, -399, ESR_DER}, /* Device specific error -300, -399 ch 21.8.11 */ + { 1, 32767, ESR_DER}, /* Device designer provided specific error 1, 32767 ch 21.8.11 */ + {-400, -499, ESR_QER}, /* Query error -400, -499 ch 21.8.12 */ + {-500, -599, ESR_PON}, /* Power on event -500, -599 ch 21.8.13 */ + {-600, -699, ESR_URQ}, /* User Request Event -600, -699 ch 21.8.14 */ + {-700, -799, ESR_REQ}, /* Request Control Event -700, -799 ch 21.8.15 */ + {-800, -899, ESR_OPC}, /* Operation Complete Event -800, -899 ch 21.8.16 */ +}; + +/** + * Push error to queue + * @param context + * @param err - error number + * @param info - additional text information or NULL for no text + * @param info_len - length of text or 0 for automatic length + */ +void SCPI_ErrorPushEx(scpi_t * context, int16_t err, char * info, size_t info_len) { + int i; + /* automatic calculation of length */ + if (info && info_len == 0) { + info_len = SCPIDEFINE_strnlen(info, SCPI_STD_ERROR_DESC_MAX_STRING_LENGTH); + } + scpi_bool_t queue_overflow = !SCPI_ErrorAddInternal(context, err, info, info_len); + + for (i = 0; i < ERROR_DEFS_N; i++) { + if ((err <= errs[i].from) && (err >= errs[i].to)) { + SCPI_RegSetBits(context, SCPI_REG_ESR, errs[i].esrBit); + } + } + + SCPI_ErrorEmit(context, err); + if (queue_overflow) { + SCPI_ErrorEmit(context, SCPI_ERROR_QUEUE_OVERFLOW); + } + + if (context) { + context->cmd_error = TRUE; + } +} + +/** + * Push error to queue + * @param context - scpi context + * @param err - error number + */ +void SCPI_ErrorPush(scpi_t * context, int16_t err) { + SCPI_ErrorPushEx(context, err, NULL, 0); + return; +} + +/** + * Translate error number to string + * @param err - error number + * @return Error string representation + */ +const char * SCPI_ErrorTranslate(int16_t err) { + switch (err) { +#define X(def, val, str) case def: return str; +#if USE_FULL_ERROR_LIST +#define XE X +#else +#define XE(def, val, str) +#endif + LIST_OF_ERRORS + +#if USE_USER_ERROR_LIST + LIST_OF_USER_ERRORS +#endif +#undef X +#undef XE + default: return "Unknown error"; + } +} + + diff --git a/third_party/libscpi/src/expression.c b/third_party/libscpi/src/expression.c new file mode 100644 index 00000000..27a38dd5 --- /dev/null +++ b/third_party/libscpi/src/expression.c @@ -0,0 +1,316 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file expression.c + * + * @brief Expressions handling + * + * + */ + +#include "scpi/expression.h" +#include "scpi/error.h" +#include "scpi/parser.h" + +#include "lexer_private.h" + +/** + * Parse one range or single value + * @param state lexer state + * @param isRange return true if parsed expression is range + * @param valueFrom return parsed value from + * @param valueTo return parsed value to + * @return SCPI_EXPR_OK - parsing was succesful + * SCPI_EXPR_ERROR - parser error + * SCPI_EXPR_NO_MORE - no more data + */ +static scpi_expr_result_t numericRange(lex_state_t * state, scpi_bool_t * isRange, scpi_token_t * valueFrom, scpi_token_t * valueTo) { + if (scpiLex_DecimalNumericProgramData(state, valueFrom)) { + if (scpiLex_Colon(state, valueTo)) { + *isRange = TRUE; + if (scpiLex_DecimalNumericProgramData(state, valueTo)) { + return SCPI_EXPR_OK; + } else { + return SCPI_EXPR_ERROR; + } + } else { + *isRange = FALSE; + return SCPI_EXPR_OK; + } + } + + return SCPI_EXPR_NO_MORE; +} + +/** + * Parse entry on specified position + * @param context scpi context + * @param param input parameter + * @param index index of position (start from 0) + * @param isRange return true if expression at index was range + * @param valueFrom return value from + * @param valueTo return value to + * @return SCPI_EXPR_OK - parsing was succesful + * SCPI_EXPR_ERROR - parser error + * SCPI_EXPR_NO_MORE - no more data + * @see SCPI_ExprNumericListEntryInt, SCPI_ExprNumericListEntryDouble + */ +scpi_expr_result_t SCPI_ExprNumericListEntry(scpi_t * context, scpi_parameter_t * param, int index, scpi_bool_t * isRange, scpi_parameter_t * valueFrom, scpi_parameter_t * valueTo) { + lex_state_t lex; + int i; + scpi_expr_result_t res = SCPI_EXPR_OK; + + if (!isRange || !valueFrom || !valueTo || !param) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return SCPI_EXPR_ERROR; + } + + if (param->type != SCPI_TOKEN_PROGRAM_EXPRESSION) { + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + return SCPI_EXPR_ERROR; + } + + lex.buffer = param->ptr + 1; + lex.pos = lex.buffer; + lex.len = param->len - 2; + + for (i = 0; i <= index; i++) { + res = numericRange(&lex, isRange, valueFrom, valueTo); + if (res != SCPI_EXPR_OK) { + break; + } + if (i != index) { + if (!scpiLex_Comma(&lex, valueFrom)) { + res = scpiLex_IsEos(&lex) ? SCPI_EXPR_NO_MORE : SCPI_EXPR_ERROR; + break; + } + } + } + + if (res == SCPI_EXPR_ERROR) { + SCPI_ErrorPush(context, SCPI_ERROR_EXPRESSION_PARSING_ERROR); + } + return res; +} + +/** + * Parse entry on specified position and convert result to int32_t + * @param context scpi context + * @param param input parameter + * @param index index of position (start from 0) + * @param isRange return true if expression at index was range + * @param valueFrom return value from + * @param valueTo return value to + * @return SCPI_EXPR_OK - parsing was succesful + * SCPI_EXPR_ERROR - parser error + * SCPI_EXPR_NO_MORE - no more data + * @see SCPI_ExprNumericListEntry, SCPI_ExprNumericListEntryDouble + */ +scpi_expr_result_t SCPI_ExprNumericListEntryInt(scpi_t * context, scpi_parameter_t * param, int index, scpi_bool_t * isRange, int32_t * valueFrom, int32_t * valueTo) { + scpi_expr_result_t res; + scpi_bool_t range = FALSE; + scpi_parameter_t paramFrom; + scpi_parameter_t paramTo; + + res = SCPI_ExprNumericListEntry(context, param, index, &range, ¶mFrom, ¶mTo); + if (res == SCPI_EXPR_OK) { + *isRange = range; + SCPI_ParamToInt32(context, ¶mFrom, valueFrom); + if (range) { + SCPI_ParamToInt32(context, ¶mTo, valueTo); + } + } + + return res; +} + +/** + * Parse entry on specified position and convert result to double + * @param context scpi context + * @param param input parameter + * @param index index of position (start from 0) + * @param isRange return true if expression at index was range + * @param valueFrom return value from + * @param valueTo return value to + * @return SCPI_EXPR_OK - parsing was succesful + * SCPI_EXPR_ERROR - parser error + * SCPI_EXPR_NO_MORE - no more data + * @see SCPI_ExprNumericListEntry, SCPI_ExprNumericListEntryInt + */ +scpi_expr_result_t SCPI_ExprNumericListEntryDouble(scpi_t * context, scpi_parameter_t * param, int index, scpi_bool_t * isRange, double * valueFrom, double * valueTo) { + scpi_expr_result_t res; + scpi_bool_t range = FALSE; + scpi_parameter_t paramFrom; + scpi_parameter_t paramTo; + + res = SCPI_ExprNumericListEntry(context, param, index, &range, ¶mFrom, ¶mTo); + if (res == SCPI_EXPR_OK) { + *isRange = range; + SCPI_ParamToDouble(context, ¶mFrom, valueFrom); + if (range) { + SCPI_ParamToDouble(context, ¶mTo, valueTo); + } + } + + return res; +} + +/** + * Parse one channel_spec e.g. "1!5!8" + * @param context + * @param state lexer state + * @param values range values + * @param length length of values array + * @param dimensions real number of dimensions + */ +static scpi_expr_result_t channelSpec(scpi_t * context, lex_state_t * state, int32_t * values, size_t length, size_t * dimensions) { + scpi_parameter_t param; + size_t i = 0; + while (scpiLex_DecimalNumericProgramData(state, ¶m)) { + if (i < length) { + SCPI_ParamToInt32(context, ¶m, &values[i]); + } + + if (scpiLex_SpecificCharacter(state, ¶m, '!')) { + i++; + } else { + *dimensions = i + 1; + return SCPI_EXPR_OK; + } + } + + if (i == 0) { + return SCPI_EXPR_NO_MORE; + } else { + /* there was at least one number followed by !, but after ! was not another number */ + return SCPI_EXPR_ERROR; + } +} + +/** + * Parse channel_range e.g. "1!2:5!6" + * @param context + * @param state lexer state + * @param isRange return true if it is range + * @param valuesFrom return array of values from + * @param valuesTo return array of values to + * @param length length of values arrays + * @param dimensions real number of dimensions + */ +static scpi_expr_result_t channelRange(scpi_t * context, lex_state_t * state, scpi_bool_t * isRange, int32_t * valuesFrom, int32_t * valuesTo, size_t length, size_t * dimensions) { + scpi_token_t token; + scpi_expr_result_t err; + size_t fromDimensions; + size_t toDimensions; + + err = channelSpec(context, state, valuesFrom, length, &fromDimensions); + if (err == SCPI_EXPR_OK) { + if (scpiLex_Colon(state, &token)) { + *isRange = TRUE; + err = channelSpec(context, state, valuesTo, length, &toDimensions); + if (err != SCPI_EXPR_OK) { + return SCPI_EXPR_ERROR; + } + if (fromDimensions != toDimensions) { + return SCPI_EXPR_ERROR; + } + *dimensions = fromDimensions; + } else { + *isRange = FALSE; + *dimensions = fromDimensions; + return SCPI_EXPR_OK; + } + } else if (err == SCPI_EXPR_NO_MORE) { + err = SCPI_EXPR_ERROR; + } + + return err; +} + +/** + * Parse one list entry at specific position e.g. "1!2:5!6" + * @param context + * @param param + * @param index + * @param isRange return true if it is range + * @param valuesFrom return array of values from + * @param valuesTo return array of values to + * @param length length of values arrays + * @param dimensions real number of dimensions + */ +scpi_expr_result_t SCPI_ExprChannelListEntry(scpi_t * context, scpi_parameter_t * param, int index, scpi_bool_t * isRange, int32_t * valuesFrom, int32_t * valuesTo, size_t length, size_t * dimensions) { + lex_state_t lex; + int i; + scpi_expr_result_t res = SCPI_EXPR_OK; + scpi_token_t token; + + if (!isRange || !param || !dimensions || (length && (!valuesFrom || !valuesTo))) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return SCPI_EXPR_ERROR; + } + + if (param->type != SCPI_TOKEN_PROGRAM_EXPRESSION) { + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + return SCPI_EXPR_ERROR; + } + + lex.buffer = param->ptr + 1; + lex.pos = lex.buffer; + lex.len = param->len - 2; + + /* detect channel list expression */ + if (!scpiLex_SpecificCharacter(&lex, &token, '@')) { + SCPI_ErrorPush(context, SCPI_ERROR_EXPRESSION_PARSING_ERROR); + return SCPI_EXPR_ERROR; + } + + for (i = 0; i <= index; i++) { + res = channelRange(context, &lex, isRange, valuesFrom, valuesTo, (i == index) ? length : 0, dimensions); + if (res != SCPI_EXPR_OK) { + break; + } + if (i != index) { + if (!scpiLex_Comma(&lex, &token)) { + res = scpiLex_IsEos(&lex) ? SCPI_EXPR_NO_MORE : SCPI_EXPR_ERROR; + break; + } + } + } + + if (res == SCPI_EXPR_ERROR) { + SCPI_ErrorPush(context, SCPI_ERROR_EXPRESSION_PARSING_ERROR); + } + if (res == SCPI_EXPR_NO_MORE) { + if (!scpiLex_IsEos(&lex)) { + res = SCPI_EXPR_ERROR; + SCPI_ErrorPush(context, SCPI_ERROR_EXPRESSION_PARSING_ERROR); + } + } + return res; +} diff --git a/third_party/libscpi/src/fifo.c b/third_party/libscpi/src/fifo.c new file mode 100644 index 00000000..8cecc4e2 --- /dev/null +++ b/third_party/libscpi/src/fifo.c @@ -0,0 +1,146 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fifo_private.h" + +/** + * Initialize fifo + * @param fifo + */ +void fifo_init(scpi_fifo_t * fifo, scpi_error_t * data, int16_t size) { + fifo->wr = 0; + fifo->rd = 0; + fifo->count = 0; + fifo->data = data; + fifo->size = size; +} + +/** + * Empty fifo + * @param fifo + */ +void fifo_clear(scpi_fifo_t * fifo) { + fifo->wr = 0; + fifo->rd = 0; + fifo->count = 0; +} + +/** + * Test if fifo is empty + * @param fifo + * @return + */ +scpi_bool_t fifo_is_empty(scpi_fifo_t * fifo) { + return fifo->count == 0; +} + +/** + * Test if fifo is full + * @param fifo + * @return + */ +scpi_bool_t fifo_is_full(scpi_fifo_t * fifo) { + return fifo->count == fifo->size; +} + +/** + * Add element to fifo. If fifo is full, return FALSE. + * @param fifo + * @param err + * @param info + * @return + */ +scpi_bool_t fifo_add(scpi_fifo_t * fifo, const scpi_error_t * value) { + /* FIFO full? */ + if (fifo_is_full(fifo)) { + return FALSE; + } + if (!value) { + return FALSE; + } + + fifo->data[fifo->wr] = *value; + fifo->wr = (fifo->wr + 1) % (fifo->size); + fifo->count += 1; + return TRUE; +} + +/** + * Remove element form fifo + * @param fifo + * @param value + * @return FALSE - fifo is empty + */ +scpi_bool_t fifo_remove(scpi_fifo_t * fifo, scpi_error_t * value) { + /* FIFO empty? */ + if (fifo_is_empty(fifo)) { + return FALSE; + } + + if (value) { + *value = fifo->data[fifo->rd]; + } + + fifo->rd = (fifo->rd + 1) % (fifo->size); + fifo->count -= 1; + + return TRUE; +} + +/** + * Remove last element from fifo + * @param fifo + * @param value + * @return FALSE - fifo is empty + */ +scpi_bool_t fifo_remove_last(scpi_fifo_t * fifo, scpi_error_t * value) { + /* FIFO empty? */ + if (fifo_is_empty(fifo)) { + return FALSE; + } + + fifo->wr = (fifo->wr + fifo->size - 1) % (fifo->size); + + if (value) { + *value = fifo->data[fifo->wr]; + } + fifo->count -= 1; + + return TRUE; +} + +/** + * Retrive number of elements in fifo + * @param fifo + * @param value + * @return + */ +scpi_bool_t fifo_count(scpi_fifo_t * fifo, int16_t * value) { + *value = fifo->count; + return TRUE; +} diff --git a/third_party/libscpi/src/fifo_private.h b/third_party/libscpi/src/fifo_private.h new file mode 100644 index 00000000..8678763c --- /dev/null +++ b/third_party/libscpi/src/fifo_private.h @@ -0,0 +1,61 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file scpi_fifo.h + * @date Thu Nov 15 10:58:45 UTC 2012 + * + * @brief basic FIFO implementation + * + * + */ + +#ifndef SCPI_FIFO_H +#define SCPI_FIFO_H + +#include "scpi/types.h" +#include "utils_private.h" + +#ifdef __cplusplus +extern "C" { +#endif + + void fifo_init(scpi_fifo_t * fifo, scpi_error_t * data, int16_t size) LOCAL; + void fifo_clear(scpi_fifo_t * fifo) LOCAL; + scpi_bool_t fifo_is_empty(scpi_fifo_t * fifo) LOCAL; + scpi_bool_t fifo_is_full(scpi_fifo_t * fifo) LOCAL; + scpi_bool_t fifo_add(scpi_fifo_t * fifo, const scpi_error_t * value) LOCAL; + scpi_bool_t fifo_remove(scpi_fifo_t * fifo, scpi_error_t * value) LOCAL; + scpi_bool_t fifo_remove_last(scpi_fifo_t * fifo, scpi_error_t * value) LOCAL; + scpi_bool_t fifo_count(scpi_fifo_t * fifo, int16_t * value) LOCAL; + +#ifdef __cplusplus +} +#endif + +#endif /* SCPI_FIFO_H */ diff --git a/third_party/libscpi/src/ieee488.c b/third_party/libscpi/src/ieee488.c new file mode 100644 index 00000000..c5f26653 --- /dev/null +++ b/third_party/libscpi/src/ieee488.c @@ -0,0 +1,424 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file scpi_ieee488.c + * @date Thu Nov 15 10:58:45 UTC 2012 + * + * @brief Implementation of IEEE488.2 commands and state model + * + * + */ + +#include "scpi/parser.h" +#include "scpi/ieee488.h" +#include "scpi/error.h" +#include "scpi/constants.h" + +#include <stdio.h> + +static const scpi_reg_info_t scpi_reg_details[SCPI_REG_COUNT] = { + { SCPI_REG_CLASS_STB, SCPI_REG_GROUP_STB }, + { SCPI_REG_CLASS_SRE, SCPI_REG_GROUP_STB }, + { SCPI_REG_CLASS_EVEN, SCPI_REG_GROUP_ESR }, + { SCPI_REG_CLASS_ENAB, SCPI_REG_GROUP_ESR }, + { SCPI_REG_CLASS_EVEN, SCPI_REG_GROUP_OPER }, + { SCPI_REG_CLASS_ENAB, SCPI_REG_GROUP_OPER }, + { SCPI_REG_CLASS_COND, SCPI_REG_GROUP_OPER }, + { SCPI_REG_CLASS_EVEN, SCPI_REG_GROUP_QUES }, + { SCPI_REG_CLASS_ENAB, SCPI_REG_GROUP_QUES }, + { SCPI_REG_CLASS_COND, SCPI_REG_GROUP_QUES }, + +#if USE_CUSTOM_REGISTERS +#ifndef USER_REGISTER_DETAILS +#error "No user register details defined" +#else + USER_REGISTER_DETAILS +#endif +#endif + +}; + +static const scpi_reg_group_info_t scpi_reg_group_details[SCPI_REG_GROUP_COUNT] = { + { + SCPI_REG_STB, + SCPI_REG_SRE, + SCPI_REG_NONE, + SCPI_REG_NONE, + SCPI_REG_NONE, + SCPI_REG_NONE, + 0 + }, /* SCPI_REG_GROUP_STB */ + { + SCPI_REG_ESR, + SCPI_REG_ESE, + SCPI_REG_NONE, + SCPI_REG_NONE, + SCPI_REG_NONE, + SCPI_REG_STB, + STB_ESR + }, /* SCPI_REG_GROUP_ESR */ + { + SCPI_REG_OPER, + SCPI_REG_OPERE, + SCPI_REG_OPERC, + SCPI_REG_NONE, + SCPI_REG_NONE, + SCPI_REG_STB, + STB_OPS + }, /* SCPI_REG_GROUP_OPER */ + { + SCPI_REG_QUES, + SCPI_REG_QUESE, + SCPI_REG_QUESC, + SCPI_REG_NONE, + SCPI_REG_NONE, + SCPI_REG_STB, + STB_QES + }, /* SCPI_REG_GROUP_QUES */ + +#if USE_CUSTOM_REGISTERS +#ifndef USER_REGISTER_GROUP_DETAILS +#error "No user register group details defined" +#else + USER_REGISTER_GROUP_DETAILS +#endif +#endif + +}; + +/** + * Get register value + * @param name - register name + * @return register value + */ +scpi_reg_val_t SCPI_RegGet(scpi_t * context, scpi_reg_name_t name) { + if ((name < SCPI_REG_COUNT) && context) { + return context->registers[name]; + } else { + return 0; + } +} + +/** + * Wrapper function to control interface from context + * @param context + * @param ctrl number of controll message + * @param value value of related register + */ +static size_t writeControl(scpi_t * context, scpi_ctrl_name_t ctrl, scpi_reg_val_t val) { + if (context && context->interface && context->interface->control) { + return context->interface->control(context, ctrl, val); + } else { + return 0; + } +} + +/** + * Set register value + * @param name - register name + * @param val - new value + */ +void SCPI_RegSet(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t val) { + if ((name >= SCPI_REG_COUNT) || (context == NULL)) { + return; + } + + scpi_reg_group_info_t register_group; + + do { + scpi_reg_class_t register_type = scpi_reg_details[name].type; + register_group = scpi_reg_group_details[scpi_reg_details[name].group]; + + scpi_reg_val_t ptrans; + + /* store old register value */ + scpi_reg_val_t old_val = context->registers[name]; + + if (old_val == val) { + return; + } else { + context->registers[name] = val; + } + + switch (register_type) { + case SCPI_REG_CLASS_STB: + case SCPI_REG_CLASS_SRE: + { + scpi_reg_val_t stb = context->registers[SCPI_REG_STB] & ~STB_SRQ; + scpi_reg_val_t sre = context->registers[SCPI_REG_SRE] & ~STB_SRQ; + + if (stb & sre) { + ptrans = ((old_val ^ val) & val); + context->registers[SCPI_REG_STB] |= STB_SRQ; + if (ptrans & val) { + writeControl(context, SCPI_CTRL_SRQ, context->registers[SCPI_REG_STB]); + } + } else { + context->registers[SCPI_REG_STB] &= ~STB_SRQ; + } + break; + } + case SCPI_REG_CLASS_EVEN: + { + scpi_reg_val_t enable; + if(register_group.enable != SCPI_REG_NONE) { + enable = SCPI_RegGet(context, register_group.enable); + } else { + enable = 0xFFFF; + } + + scpi_bool_t summary = val & enable; + + name = register_group.parent_reg; + val = SCPI_RegGet(context, register_group.parent_reg); + if (summary) { + val |= register_group.parent_bit; + } else { + val &= ~(register_group.parent_bit); + } + break; + } + case SCPI_REG_CLASS_COND: + { + name = register_group.event; + + if(register_group.ptfilt == SCPI_REG_NONE && register_group.ntfilt == SCPI_REG_NONE) { + val = ((old_val ^ val) & val) | SCPI_RegGet(context, register_group.event); + } else { + scpi_reg_val_t ptfilt = 0, ntfilt = 0; + scpi_reg_val_t transitions; + scpi_reg_val_t ntrans; + + if(register_group.ptfilt != SCPI_REG_NONE) { + ptfilt = SCPI_RegGet(context, register_group.ptfilt); + } + + if(register_group.ntfilt != SCPI_REG_NONE) { + ntfilt = SCPI_RegGet(context, register_group.ntfilt); + } + + transitions = old_val ^ val; + ptrans = transitions & val; + ntrans = transitions & ~ptrans; + + val = ((ptrans & ptfilt) | (ntrans & ntfilt)) | SCPI_RegGet(context, register_group.event); + } + break; + } + case SCPI_REG_CLASS_ENAB: + case SCPI_REG_CLASS_NTR: + case SCPI_REG_CLASS_PTR: + return; + } + } while(register_group.parent_reg != SCPI_REG_NONE); +} + +/** + * Set register bits + * @param name - register name + * @param bits bit mask + */ +void SCPI_RegSetBits(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t bits) { + SCPI_RegSet(context, name, SCPI_RegGet(context, name) | bits); +} + +/** + * Clear register bits + * @param name - register name + * @param bits bit mask + */ +void SCPI_RegClearBits(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t bits) { + SCPI_RegSet(context, name, SCPI_RegGet(context, name) & ~bits); +} + +/** + * *CLS - This command clears all status data structures in a device. + * For a device which minimally complies with SCPI. (SCPI std 4.1.3.2) + * @param context + * @return + */ +scpi_result_t SCPI_CoreCls(scpi_t * context) { + SCPI_ErrorClear(context); + int i; + for (i = 0; i < SCPI_REG_GROUP_COUNT; ++i) { + scpi_reg_name_t event_reg = scpi_reg_group_details[i].event; + if (event_reg != SCPI_REG_STB) { + SCPI_RegSet(context, event_reg, 0); + } + } + return SCPI_RES_OK; +} + +/** + * *ESE + * @param context + * @return + */ +scpi_result_t SCPI_CoreEse(scpi_t * context) { + 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; +} + +/** + * *ESE? + * @param context + * @return + */ +scpi_result_t SCPI_CoreEseQ(scpi_t * context) { + SCPI_ResultInt32(context, SCPI_RegGet(context, SCPI_REG_ESE)); + return SCPI_RES_OK; +} + +/** + * *ESR? + * @param context + * @return + */ +scpi_result_t SCPI_CoreEsrQ(scpi_t * context) { + SCPI_ResultInt32(context, SCPI_RegGet(context, SCPI_REG_ESR)); + SCPI_RegSet(context, SCPI_REG_ESR, 0); + return SCPI_RES_OK; +} + +/** + * *IDN? + * + * field1: MANUFACTURE + * field2: MODEL + * field4: SUBSYSTEMS REVISIONS + * + * example: MANUFACTURE,MODEL,0,01-02-01 + * @param context + * @return + */ +scpi_result_t SCPI_CoreIdnQ(scpi_t * context) { + int i; + for (i = 0; i < 4; i++) { + if (context->idn[i]) { + SCPI_ResultMnemonic(context, context->idn[i]); + } else { + SCPI_ResultMnemonic(context, "0"); + } + } + return SCPI_RES_OK; +} + +/** + * *OPC + * @param context + * @return + */ +scpi_result_t SCPI_CoreOpc(scpi_t * context) { + SCPI_RegSetBits(context, SCPI_REG_ESR, ESR_OPC); + return SCPI_RES_OK; +} + +/** + * *OPC? + * @param context + * @return + */ +scpi_result_t SCPI_CoreOpcQ(scpi_t * context) { + /* Operation is always completed */ + SCPI_ResultInt32(context, 1); + return SCPI_RES_OK; +} + +/** + * *RST + * @param context + * @return + */ +scpi_result_t SCPI_CoreRst(scpi_t * context) { + if (context && context->interface && context->interface->reset) { + return context->interface->reset(context); + } + return SCPI_RES_OK; +} + +/** + * *SRE + * @param context + * @return + */ +scpi_result_t SCPI_CoreSre(scpi_t * context) { + 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? + * @param context + * @return + */ +scpi_result_t SCPI_CoreSreQ(scpi_t * context) { + SCPI_ResultInt32(context, SCPI_RegGet(context, SCPI_REG_SRE)); + return SCPI_RES_OK; +} + +/** + * *STB? + * @param context + * @return + */ +scpi_result_t SCPI_CoreStbQ(scpi_t * context) { + SCPI_ResultInt32(context, SCPI_RegGet(context, SCPI_REG_STB)); + return SCPI_RES_OK; +} + +/** + * *TST? + * @param context + * @return + */ +scpi_result_t SCPI_CoreTstQ(scpi_t * context) { + (void) context; + SCPI_ResultInt32(context, 0); + return SCPI_RES_OK; +} + +/** + * *WAI + * @param context + * @return + */ +scpi_result_t SCPI_CoreWai(scpi_t * context) { + (void) context; + /* NOP */ + return SCPI_RES_OK; +} + diff --git a/third_party/libscpi/src/lexer.c b/third_party/libscpi/src/lexer.c new file mode 100644 index 00000000..dad45a09 --- /dev/null +++ b/third_party/libscpi/src/lexer.c @@ -0,0 +1,935 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file lexer.c + * @date Wed Mar 20 19:35:19 UTC 2013 + * + * @brief SCPI Lexer + * + * + */ + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include "lexer_private.h" +#include "scpi/error.h" + +/** + * Is white space + * @param c + * @return + */ +static int isws(int c) { + if ((c == ' ') || (c == '\t')) { + return 1; + } + return 0; +} + +/** + * Is binary digit + * @param c + * @return + */ +static int isbdigit(int c) { + if ((c == '0') || (c == '1')) { + return 1; + } + return 0; +} + +/** + * Is hexadecimal digit + * @param c + * @return + */ +static int isqdigit(int c) { + if ((c == '0') || (c == '1') || (c == '2') || (c == '3') || (c == '4') || (c == '5') || (c == '6') || (c == '7')) { + return 1; + } + return 0; +} + +/** + * Is end of string + * @param state + * @return + */ +static int iseos(lex_state_t * state) { + if ((state->buffer + state->len) <= (state->pos)) { + return 1; + } else { + return 0; + } +} + +/** + * Private export of iseos + * @param state + * @return + */ +int scpiLex_IsEos(lex_state_t * state) { + return iseos(state); +} + +/** + * Test current character + * @param state + * @param chr + * @return + */ +static int ischr(lex_state_t * state, char chr) { + return (state->pos[0] == chr); +} + +/** + * Is plus or minus + * @param c + * @return + */ +static int isplusmn(int c) { + return c == '+' || c == '-'; +} + +/** + * Is letter H + * @param c + * @return + */ +static int isH(int c) { + return c == 'h' || c == 'H'; +} + +/** + * Is letter B + * @param c + * @return + */ +static int isB(int c) { + return c == 'b' || c == 'B'; +} + +/** + * Is letter Q + * @param c + * @return + */ +static int isQ(int c) { + return c == 'q' || c == 'Q'; +} + +/** + * Is letter E + * @param c + * @return + */ +static int isE(int c) { + return c == 'e' || c == 'E'; +} + +#define SKIP_NONE 0 +#define SKIP_OK 1 +#define SKIP_INCOMPLETE -1 + +/* skip characters */ +/* 7.4.1 <PROGRAM MESSAGE UNIT SEPARATOR>*/ +/* TODO: static int skipProgramMessageUnitSeparator(lex_state_t * state) */ + +/** + * Skip all whitespaces + * @param state + * @return + */ +static int skipWs(lex_state_t * state) { + int someSpace = 0; + while (!iseos(state) && isws(state->pos[0])) { + state->pos++; + someSpace++; + } + + return someSpace; +} + +/* 7.4.2 <PROGRAM DATA SEPARATOR> */ +/* static int skipProgramDataSeparator(lex_state_t * state) */ + +/* 7.5.2 <PROGRAM MESSAGE TERMINATOR> */ +/* static int skipProgramMessageTerminator(lex_state_t * state) */ + +/** + * Skip decimal digit + * @param state + * @return + */ +static int skipDigit(lex_state_t * state) { + if (!iseos(state) && isdigit((uint8_t)(state->pos[0]))) { + state->pos++; + return SKIP_OK; + } else { + return SKIP_NONE; + } +} + +/** + * Skip multiple decimal digits + * @param state + * @return + */ +static int skipNumbers(lex_state_t * state) { + int someNumbers = 0; + while (!iseos(state) && isdigit((uint8_t)(state->pos[0]))) { + state->pos++; + someNumbers++; + } + return someNumbers; +} + +/** + * Skip plus or minus + * @param state + * @return + */ +static int skipPlusmn(lex_state_t * state) { + if (!iseos(state) && isplusmn(state->pos[0])) { + state->pos++; + return SKIP_OK; + } else { + return SKIP_NONE; + } +} + +/** + * Skip any character from 'a'-'Z' + * @param state + * @return + */ +static int skipAlpha(lex_state_t * state) { + int someLetters = 0; + while (!iseos(state) && isalpha((uint8_t)(state->pos[0]))) { + state->pos++; + someLetters++; + } + return someLetters; +} + +/** + * Skip exact character chr or nothing + * @param state + * @param chr + * @return + */ +static int skipChr(lex_state_t * state, char chr) { + if (!iseos(state) && ischr(state, chr)) { + state->pos++; + return SKIP_OK; + } else { + return SKIP_NONE; + } +} + +/** + * Skip slash or dot + * @param state + * @return + */ +static int skipSlashDot(lex_state_t * state) { + if (!iseos(state) && (ischr(state, '/') | ischr(state, '.'))) { + state->pos++; + return SKIP_OK; + } else { + return SKIP_NONE; + } +} + +/** + * Skip star + * @param state + * @return + */ +static int skipStar(lex_state_t * state) { + if (!iseos(state) && ischr(state, '*')) { + state->pos++; + return SKIP_OK; + } else { + return SKIP_NONE; + } +} + +/** + * Skip colon + * @param state + * @return + */ +static int skipColon(lex_state_t * state) { + if (!iseos(state) && ischr(state, ':')) { + state->pos++; + return SKIP_OK; + } else { + return SKIP_NONE; + } +} + +/* 7.6.1.2 <COMMAND PROGRAM HEADER> */ + +/** + * Skip program mnemonic [a-z][a-z0-9_]* + * @param state + * @return + */ +static int skipProgramMnemonic(lex_state_t * state) { + const char * startPos = state->pos; + if (!iseos(state) && isalpha((uint8_t)(state->pos[0]))) { + state->pos++; + while (!iseos(state) && (isalnum((uint8_t)(state->pos[0])) || ischr(state, '_'))) { + state->pos++; + } + } + + if (iseos(state)) { + return (state->pos - startPos) * SKIP_INCOMPLETE; + } else { + return (state->pos - startPos) * SKIP_OK; + } +} + +/* tokens */ + +/** + * Detect token white space + * @param state + * @param token + * @return + */ +int scpiLex_WhiteSpace(lex_state_t * state, scpi_token_t * token) { + token->ptr = state->pos; + + skipWs(state); + + token->len = state->pos - token->ptr; + + if (token->len > 0) { + token->type = SCPI_TOKEN_WS; + } else { + token->type = SCPI_TOKEN_UNKNOWN; + } + + return token->len; +} + +/* 7.6.1 <COMMAND PROGRAM HEADER> */ + +/** + * Skip command program header \*<PROGRAM MNEMONIC> + * @param state + * @return + */ +static int skipCommonProgramHeader(lex_state_t * state) { + int res; + if (skipStar(state)) { + res = skipProgramMnemonic(state); + if (res == SKIP_NONE && iseos(state)) { + return SKIP_INCOMPLETE; + } else if (res <= SKIP_INCOMPLETE) { + return SKIP_OK; + } else if (res >= SKIP_OK) { + return SKIP_OK; + } else { + return SKIP_INCOMPLETE; + } + } + return SKIP_NONE; +} + +/** + * Skip compound program header :<PROGRAM MNEMONIC>:<PROGRAM MNEMONIC>... + * @param state + * @return + */ +static int skipCompoundProgramHeader(lex_state_t * state) { + int res; + int firstColon = skipColon(state); + + res = skipProgramMnemonic(state); + if (res >= SKIP_OK) { + while (skipColon(state)) { + res = skipProgramMnemonic(state); + if (res <= SKIP_INCOMPLETE) { + return SKIP_OK; + } else if (res == SKIP_NONE) { + return SKIP_INCOMPLETE; + } + } + return SKIP_OK; + } else if (res <= SKIP_INCOMPLETE) { + return SKIP_OK; + } else if (firstColon) { + return SKIP_INCOMPLETE; + } else { + return SKIP_NONE; + } +} + +/** + * Detect token command or compound program header + * @param state + * @param token + * @return + */ +int scpiLex_ProgramHeader(lex_state_t * state, scpi_token_t * token) { + int res; + token->ptr = state->pos; + token->type = SCPI_TOKEN_UNKNOWN; + + res = skipCommonProgramHeader(state); + if (res >= SKIP_OK) { + if (skipChr(state, '?') >= SKIP_OK) { + token->type = SCPI_TOKEN_COMMON_QUERY_PROGRAM_HEADER; + } else { + token->type = SCPI_TOKEN_COMMON_PROGRAM_HEADER; + } + } else if (res <= SKIP_INCOMPLETE) { + token->type = SCPI_TOKEN_INCOMPLETE_COMMON_PROGRAM_HEADER; + } else if (res == SKIP_NONE) { + res = skipCompoundProgramHeader(state); + + if (res >= SKIP_OK) { + if (skipChr(state, '?') >= SKIP_OK) { + token->type = SCPI_TOKEN_COMPOUND_QUERY_PROGRAM_HEADER; + } else { + token->type = SCPI_TOKEN_COMPOUND_PROGRAM_HEADER; + } + } else if (res <= SKIP_INCOMPLETE) { + token->type = SCPI_TOKEN_INCOMPLETE_COMPOUND_PROGRAM_HEADER; + } + } + + if (token->type != SCPI_TOKEN_UNKNOWN) { + token->len = state->pos - token->ptr; + } else { + token->len = 0; + state->pos = token->ptr; + } + + return token->len; +} + +/* 7.7.1 <CHARACTER PROGRAM DATA> */ + +/** + * Detect token "Character program data" + * @param state + * @param token + * @return + */ +int scpiLex_CharacterProgramData(lex_state_t * state, scpi_token_t * token) { + token->ptr = state->pos; + + if (!iseos(state) && isalpha((uint8_t)(state->pos[0]))) { + state->pos++; + while (!iseos(state) && (isalnum((uint8_t)(state->pos[0])) || ischr(state, '_'))) { + state->pos++; + } + } + + token->len = state->pos - token->ptr; + if (token->len > 0) { + token->type = SCPI_TOKEN_PROGRAM_MNEMONIC; + } else { + token->type = SCPI_TOKEN_UNKNOWN; + } + + return token->len; +} + +/* 7.7.2 <DECIMAL NUMERIC PROGRAM DATA> */ +static int skipMantisa(lex_state_t * state) { + int someNumbers = 0; + + skipPlusmn(state); + + someNumbers += skipNumbers(state); + + if (skipChr(state, '.')) { + someNumbers += skipNumbers(state); + } + + return someNumbers; +} + +static int skipExponent(lex_state_t * state) { + int someNumbers = 0; + + if (!iseos(state) && isE(state->pos[0])) { + state->pos++; + + skipWs(state); + + skipPlusmn(state); + + someNumbers = skipNumbers(state); + } + + return someNumbers; +} + +/** + * Detect token Decimal number + * @param state + * @param token + * @return + */ +int scpiLex_DecimalNumericProgramData(lex_state_t * state, scpi_token_t * token) { + char * rollback; + token->ptr = state->pos; + + if (skipMantisa(state)) { + rollback = state->pos; + skipWs(state); + if (!skipExponent(state)) { + state->pos = rollback; + } + } else { + state->pos = token->ptr; + } + + token->len = state->pos - token->ptr; + if (token->len > 0) { + token->type = SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA; + } else { + token->type = SCPI_TOKEN_UNKNOWN; + } + + return token->len; +} + +/* 7.7.3 <SUFFIX PROGRAM DATA> */ +int scpiLex_SuffixProgramData(lex_state_t * state, scpi_token_t * token) { + token->ptr = state->pos; + + skipChr(state, '/'); + + /* TODO: strict parsing : SLASH? (ALPHA+ (MINUS? DIGIT)?) ((SLASH | DOT) (ALPHA+ (MINUS? DIGIT)?))* */ + if (skipAlpha(state)) { + skipChr(state, '-'); + skipDigit(state); + + while (skipSlashDot(state)) { + skipAlpha(state); + skipChr(state, '-'); + skipDigit(state); + } + } + + token->len = state->pos - token->ptr; + if ((token->len > 0)) { + token->type = SCPI_TOKEN_SUFFIX_PROGRAM_DATA; + } else { + token->type = SCPI_TOKEN_UNKNOWN; + state->pos = token->ptr; + token->len = 0; + } + + return token->len; +} + +/* 7.7.4 <NONDECIMAL NUMERIC PROGRAM DATA> */ +static int skipHexNum(lex_state_t * state) { + int someNumbers = 0; + while (!iseos(state) && isxdigit((uint8_t)(state->pos[0]))) { + state->pos++; + someNumbers++; + } + return someNumbers; +} + +static int skipOctNum(lex_state_t * state) { + int someNumbers = 0; + while (!iseos(state) && isqdigit(state->pos[0])) { + state->pos++; + someNumbers++; + } + return someNumbers; +} + +static int skipBinNum(lex_state_t * state) { + int someNumbers = 0; + while (!iseos(state) && isbdigit(state->pos[0])) { + state->pos++; + someNumbers++; + } + return someNumbers; +} + +/** + * Detect token nondecimal number + * @param state + * @param token + * @return + */ +int scpiLex_NondecimalNumericData(lex_state_t * state, scpi_token_t * token) { + int someNumbers = 0; + token->ptr = state->pos; + if (skipChr(state, '#')) { + if (!iseos(state)) { + if (isH(state->pos[0])) { + state->pos++; + someNumbers = skipHexNum(state); + token->type = SCPI_TOKEN_HEXNUM; + } else if (isQ(state->pos[0])) { + state->pos++; + someNumbers = skipOctNum(state); + token->type = SCPI_TOKEN_OCTNUM; + } else if (isB(state->pos[0])) { + state->pos++; + someNumbers = skipBinNum(state); + token->type = SCPI_TOKEN_BINNUM; + } + } + } + + if (someNumbers) { + token->ptr += 2; /* ignore number prefix */ + token->len = state->pos - token->ptr; + } else { + token->type = SCPI_TOKEN_UNKNOWN; + state->pos = token->ptr; + token->len = 0; + } + return token->len > 0 ? token->len + 2 : 0; +} + +/* 7.7.5 <STRING PROGRAM DATA> */ +static int isascii7bit(int c) { + return (c >= 0) && (c <= 0x7f); +} + +static void skipQuoteProgramData(lex_state_t * state, char quote) { + while (!iseos(state)) { + if (isascii7bit(state->pos[0]) && !ischr(state, quote)) { + state->pos++; + } else if (ischr(state, quote)) { + state->pos++; + if (!iseos(state) && ischr(state, quote)) { + state->pos++; + } else { + state->pos--; + break; + } + } else { + break; + } + } +} + +static void skipDoubleQuoteProgramData(lex_state_t * state) { + skipQuoteProgramData(state, '"'); +} + +static void skipSingleQuoteProgramData(lex_state_t * state) { + skipQuoteProgramData(state, '\''); +} + +/** + * Detect token String data + * @param state + * @param token + * @return + */ +int scpiLex_StringProgramData(lex_state_t * state, scpi_token_t * token) { + token->ptr = state->pos; + + if (!iseos(state)) { + if (ischr(state, '"')) { + state->pos++; + token->type = SCPI_TOKEN_DOUBLE_QUOTE_PROGRAM_DATA; + skipDoubleQuoteProgramData(state); + if (!iseos(state) && ischr(state, '"')) { + state->pos++; + token->len = state->pos - token->ptr; + } else { + state->pos = token->ptr; + } + } else if (ischr(state, '\'')) { + state->pos++; + token->type = SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA; + skipSingleQuoteProgramData(state); + if (!iseos(state) && ischr(state, '\'')) { + state->pos++; + token->len = state->pos - token->ptr; + } else { + state->pos = token->ptr; + } + } + } + + token->len = state->pos - token->ptr; + + if ((token->len > 0)) { + /* token->ptr++; + * token->len -= 2; */ + } else { + token->type = SCPI_TOKEN_UNKNOWN; + state->pos = token->ptr; + token->len = 0; + } + + return token->len > 0 ? token->len : 0; +} + +/* 7.7.6 <ARBITRARY BLOCK PROGRAM DATA> */ +static int isNonzeroDigit(int c) { + return isdigit(c) && (c != '0'); +} + +/** + * Detect token Block Data + * @param state + * @param token + * @return + */ +int scpiLex_ArbitraryBlockProgramData(lex_state_t * state, scpi_token_t * token) { + int i; + int arbitraryBlockLength = 0; + const char * ptr = state->pos; + int validData = -1; + token->ptr = state->pos; + + if (skipChr(state, '#')) { + if (!iseos(state) && isNonzeroDigit(state->pos[0])) { + /* Get number of digits */ + i = state->pos[0] - '0'; + state->pos++; + + for (; i > 0; i--) { + if (!iseos(state) && isdigit((uint8_t)(state->pos[0]))) { + arbitraryBlockLength *= 10; + arbitraryBlockLength += (state->pos[0] - '0'); + state->pos++; + } else { + break; + } + } + + if (i == 0) { + state->pos += arbitraryBlockLength; + if ((state->buffer + state->len) >= (state->pos)) { + token->ptr = state->pos - arbitraryBlockLength; + token->len = arbitraryBlockLength; + validData = 1; + } else { + validData = 0; + } + } else if (iseos(state)) { + validData = 0; + } + } else if (iseos(state)) { + validData = 0; + } + } + + if (validData == 1) { + /* valid */ + token->type = SCPI_TOKEN_ARBITRARY_BLOCK_PROGRAM_DATA; + } else if (validData == 0) { + /* incomplete */ + token->type = SCPI_TOKEN_UNKNOWN; + token->len = 0; + state->pos = state->buffer + state->len; + } else { + /* invalid */ + token->type = SCPI_TOKEN_UNKNOWN; + state->pos = token->ptr; + token->len = 0; + } + + return token->len + (token->ptr - ptr); +} + +/* 7.7.7 <EXPRESSION PROGRAM DATA> */ +static int isProgramExpression(int c) { + if ((c >= 0x20) && (c <= 0x7e)) { + if ((c != '"') + && (c != '#') + && (c != '\'') + && (c != '(') + && (c != ')') + && (c != ';')) { + return 1; + } + } + + return 0; +} + +static void skipProgramExpression(lex_state_t * state) { + while (!iseos(state) && isProgramExpression(state->pos[0])) { + state->pos++; + } +} + +/* TODO: 7.7.7.2-2 recursive - any program data */ + +/** + * Detect token Expression + * @param state + * @param token + * @return + */ +int scpiLex_ProgramExpression(lex_state_t * state, scpi_token_t * token) { + token->ptr = state->pos; + + if (!iseos(state) && ischr(state, '(')) { + state->pos++; + skipProgramExpression(state); + + if (!iseos(state) && ischr(state, ')')) { + state->pos++; + token->len = state->pos - token->ptr; + } else { + token->len = 0; + } + } + + if ((token->len > 0)) { + token->type = SCPI_TOKEN_PROGRAM_EXPRESSION; + } else { + token->type = SCPI_TOKEN_UNKNOWN; + state->pos = token->ptr; + token->len = 0; + } + + return token->len; +} + +/** + * Detect token comma + * @param state + * @param token + * @return + */ +int scpiLex_Comma(lex_state_t * state, scpi_token_t * token) { + token->ptr = state->pos; + + if (skipChr(state, ',')) { + token->len = 1; + token->type = SCPI_TOKEN_COMMA; + } else { + token->len = 0; + token->type = SCPI_TOKEN_UNKNOWN; + } + + return token->len; +} + +/** + * Detect token semicolon + * @param state + * @param token + * @return + */ +int scpiLex_Semicolon(lex_state_t * state, scpi_token_t * token) { + token->ptr = state->pos; + + if (skipChr(state, ';')) { + token->len = 1; + token->type = SCPI_TOKEN_SEMICOLON; + } else { + token->len = 0; + token->type = SCPI_TOKEN_UNKNOWN; + } + + return token->len; +} + +/** + * Detect token colon + * @param state + * @param token + * @return + */ +int scpiLex_Colon(lex_state_t * state, scpi_token_t * token) { + token->ptr = state->pos; + + if (skipChr(state, ':')) { + token->len = 1; + token->type = SCPI_TOKEN_COLON; + } else { + token->len = 0; + token->type = SCPI_TOKEN_UNKNOWN; + } + + return token->len; +} + +/** + * Detect specified character + * @param state + * @param token + * @return + */ +int scpiLex_SpecificCharacter(lex_state_t * state, scpi_token_t * token, char chr) { + token->ptr = state->pos; + + if (skipChr(state, chr)) { + token->len = 1; + token->type = SCPI_TOKEN_SPECIFIC_CHARACTER; + } else { + token->len = 0; + token->type = SCPI_TOKEN_UNKNOWN; + } + + return token->len; +} + +/** + * Detect token New line + * @param state + * @param token + * @return + */ +int scpiLex_NewLine(lex_state_t * state, scpi_token_t * token) { + token->ptr = state->pos; + + skipChr(state, '\r'); + skipChr(state, '\n'); + + token->len = state->pos - token->ptr; + + if ((token->len > 0)) { + token->type = SCPI_TOKEN_NL; + } else { + token->type = SCPI_TOKEN_UNKNOWN; + state->pos = token->ptr; + token->len = 0; + } + + return token->len; +} diff --git a/third_party/libscpi/src/lexer_private.h b/third_party/libscpi/src/lexer_private.h new file mode 100644 index 00000000..914ee157 --- /dev/null +++ b/third_party/libscpi/src/lexer_private.h @@ -0,0 +1,69 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file lexer.h + * @date Thu Mar 21 15:00:58 UTC 2013 + * + * @brief SCPI Lexer + * + * + */ + +#ifndef SCPI_LEXER_H +#define SCPI_LEXER_H + +#include "scpi/types.h" +#include "utils_private.h" + +#ifdef __cplusplus +extern "C" { +#endif + + int scpiLex_IsEos(lex_state_t * state) LOCAL; + int scpiLex_WhiteSpace(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_ProgramHeader(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_CharacterProgramData(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_DecimalNumericProgramData(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_SuffixProgramData(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_NondecimalNumericData(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_StringProgramData(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_ArbitraryBlockProgramData(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_ProgramExpression(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_Comma(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_Semicolon(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_Colon(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_NewLine(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiLex_SpecificCharacter(lex_state_t * state, scpi_token_t * token, char chr) LOCAL; + +#ifdef __cplusplus +} +#endif + +#endif /* SCPI_LEXER_H */ + diff --git a/third_party/libscpi/src/minimal.c b/third_party/libscpi/src/minimal.c new file mode 100644 index 00000000..29dff824 --- /dev/null +++ b/third_party/libscpi/src/minimal.c @@ -0,0 +1,215 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file scpi_minimal.c + * @date Thu Nov 15 10:58:45 UTC 2012 + * + * @brief SCPI minimal implementation + * + * + */ + + +#include "scpi/parser.h" +#include "scpi/minimal.h" +#include "scpi/constants.h" +#include "scpi/error.h" +#include "scpi/ieee488.h" +#include "utils_private.h" + +/** + * Command stub function + * @param context + * @return + */ +scpi_result_t SCPI_Stub(scpi_t * context) { + (void) context; + return SCPI_RES_OK; +} + +/** + * Query command stub function + * @param context + * @return + */ +scpi_result_t SCPI_StubQ(scpi_t * context) { + SCPI_ResultInt32(context, 0); + return SCPI_RES_OK; +} + +/** + * SYSTem:VERSion? + * @param context + * @return + */ +scpi_result_t SCPI_SystemVersionQ(scpi_t * context) { + SCPI_ResultMnemonic(context, SCPI_STD_VERSION_REVISION); + return SCPI_RES_OK; +} + +/** + * SYSTem:ERRor[:NEXT]? + * @param context + * @return + */ +scpi_result_t SCPI_SystemErrorNextQ(scpi_t * context) { + scpi_error_t error; + SCPI_ErrorPop(context, &error); + SCPI_ResultError(context, &error); +#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION + SCPIDEFINE_free(&context->error_info_heap, error.device_dependent_info, false); +#endif + return SCPI_RES_OK; +} + +/** + * SYSTem:ERRor:COUNt? + * @param context + * @return + */ +scpi_result_t SCPI_SystemErrorCountQ(scpi_t * context) { + SCPI_ResultInt32(context, SCPI_ErrorCount(context)); + + return SCPI_RES_OK; +} + +/** + * STATus:QUEStionable:CONDition? + * @param context + * @return + */ +scpi_result_t SCPI_StatusQuestionableConditionQ(scpi_t * context) { + /* return value */ + SCPI_ResultInt32(context, SCPI_RegGet(context, SCPI_REG_QUESC)); + + return SCPI_RES_OK; +} + +/** + * STATus:QUEStionable[:EVENt]? + * @param context + * @return + */ +scpi_result_t SCPI_StatusQuestionableEventQ(scpi_t * context) { + /* return value */ + SCPI_ResultInt32(context, SCPI_RegGet(context, SCPI_REG_QUES)); + + /* clear register */ + SCPI_RegSet(context, SCPI_REG_QUES, 0); + + return SCPI_RES_OK; +} + +/** + * STATus:QUEStionable:ENABle? + * @param context + * @return + */ +scpi_result_t SCPI_StatusQuestionableEnableQ(scpi_t * context) { + /* return value */ + SCPI_ResultInt32(context, SCPI_RegGet(context, SCPI_REG_QUESE)); + + return SCPI_RES_OK; +} + +/** + * STATus:QUEStionable:ENABle + * @param context + * @return + */ +scpi_result_t SCPI_StatusQuestionableEnable(scpi_t * context) { + int32_t new_QUESE; + if (SCPI_ParamInt32(context, &new_QUESE, TRUE)) { + SCPI_RegSet(context, SCPI_REG_QUESE, (scpi_reg_val_t) new_QUESE); + } + return SCPI_RES_OK; +} + +/** + * STATus:OPERation:CONDition? + * @param context + * @return + */ +scpi_result_t SCPI_StatusOperationConditionQ(scpi_t * context) { + /* return value */ + SCPI_ResultInt32(context, SCPI_RegGet(context, SCPI_REG_OPERC)); + + return SCPI_RES_OK; +} + +/** + * STATus:OPERation[:EVENt]? + * @param context + * @return + */ +scpi_result_t SCPI_StatusOperationEventQ(scpi_t * context) { + /* return value */ + SCPI_ResultInt32(context, SCPI_RegGet(context, SCPI_REG_OPER)); + + /* clear register */ + SCPI_RegSet(context, SCPI_REG_OPER, 0); + + return SCPI_RES_OK; +} + +/** + * STATus:OPERation:ENABle? + * @param context + * @return + */ + scpi_result_t SCPI_StatusOperationEnableQ(scpi_t * context) { + /* return value */ + SCPI_ResultInt32(context, SCPI_RegGet(context, SCPI_REG_OPERE)); + + return SCPI_RES_OK; +} + +/** + * STATus:OPERation:ENABle + * @param context + * @return + */ +scpi_result_t SCPI_StatusOperationEnable(scpi_t * context) { + int32_t new_OPERE; + if (SCPI_ParamInt32(context, &new_OPERE, TRUE)) { + SCPI_RegSet(context, SCPI_REG_OPERE, (scpi_reg_val_t) new_OPERE); + } + return SCPI_RES_OK; +} + +/** + * STATus:PRESet + * @param context + * @return + */ +scpi_result_t SCPI_StatusPreset(scpi_t * context) { + /* clear STATUS:... */ + SCPI_RegSet(context, SCPI_REG_QUES, 0); + return SCPI_RES_OK; +} diff --git a/third_party/libscpi/src/parser.c b/third_party/libscpi/src/parser.c new file mode 100644 index 00000000..de14418d --- /dev/null +++ b/third_party/libscpi/src/parser.c @@ -0,0 +1,1834 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file scpi_parser.c + * @date Thu Nov 15 10:58:45 UTC 2012 + * + * @brief SCPI parser implementation + * + * + */ + +#include <ctype.h> +#include <string.h> + +#include "scpi/config.h" +#include "scpi/parser.h" +#include "parser_private.h" +#include "lexer_private.h" +#include "scpi/error.h" +#include "scpi/constants.h" +#include "scpi/utils.h" + +/** + * Write data to SCPI output + * @param context + * @param data + * @param len - length of data to be written + * @return number of bytes written + */ +static size_t writeData(scpi_t * context, const char * data, size_t len) { + if ((len > 0) && (data != NULL)) { + return context->interface->write(context, data, len); + } else { + return 0; + } +} + +/** + * Flush data to SCPI output + * @param context + * @return + */ +static int flushData(scpi_t * context) { + if (context && context->interface && context->interface->flush) { + return context->interface->flush(context); + } else { + return SCPI_RES_OK; + } +} + +/** + * Write result delimiter to output + * @param context + * @return number of bytes written + */ +static size_t writeDelimiter(scpi_t * context) { + if (context->output_count > 0) { + return writeData(context, ",", 1); + } else { + return 0; + } +} + +/** + * Conditionaly write "New Line" + * @param context + * @return number of characters written + */ +static size_t writeNewLine(scpi_t * context) { + if (!context->first_output) { + size_t len; +#ifndef SCPI_LINE_ENDING +#error no termination character defined +#endif + len = writeData(context, SCPI_LINE_ENDING, strlen(SCPI_LINE_ENDING)); + flushData(context); + return len; + } else { + return 0; + } +} + +/** + * Conditionaly write ";" + * @param context + * @return number of characters written + */ +static size_t writeSemicolon(scpi_t * context) { + if (context->output_count > 0) { + return writeData(context, ";", 1); + } else { + return 0; + } +} + +/** + * Process command + * @param 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; + scpi_bool_t is_query = context->param_list.cmd_raw.data[context->param_list.cmd_raw.length - 1] == '?'; + + /* conditionally write ; */ + if(!context->first_output && is_query) { + writeData(context, ";", 1); + } + + context->cmd_error = FALSE; + context->output_count = 0; + context->input_count = 0; + context->arbitrary_remaining = 0; + + /* if callback exists - call command callback */ + if (cmd->callback != NULL) { + 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; + } else { + if(context->first_output && is_query) { + context->first_output = 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; +} + +/** + * Cycle all patterns and search matching pattern. Execute command callback. + * @param context + * @result TRUE if context->paramlist is filled with correct values + */ +static scpi_bool_t findCommandHeader(scpi_t * context, const char * header, int 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, header, len, NULL, 0, 0)) { + context->param_list.cmd = cmd; + return TRUE; + } + } + return FALSE; +} + +/** + * Parse one command line + * @param context + * @param data - complete command line + * @param len - command line length + * @return FALSE if there was some error during evaluation of commands + */ +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 FALSE; + } + + state = &context->parser_state; + context->output_count = 0; + context->first_output = TRUE; + + while (1) { + 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); + + if (findCommandHeader(context, state->programHeader.ptr, state->programHeader.len)) { + + context->param_list.lex_state.buffer = state->programData.ptr; + context->param_list.lex_state.pos = context->param_list.lex_state.buffer; + context->param_list.lex_state.len = state->programData.len; + context->param_list.cmd_raw.data = state->programHeader.ptr; + context->param_list.cmd_raw.position = 0; + context->param_list.cmd_raw.length = state->programHeader.len; + + result &= processCommand(context); + cmd_prev = state->programHeader; + } else { + /* place undefined header with error */ + /* calculate length of errorenous header and trim \r\n */ + size_t r2 = r; + while (r2 > 0 && (data[r2 - 1] == '\r' || data[r2 - 1] == '\n')) r2--; + SCPI_ErrorPushEx(context, SCPI_ERROR_UNDEFINED_HEADER, data, r2); + result = FALSE; + } + } + + if (r < len) { + data += r; + len -= r; + } else { + break; + } + + } + + /* conditionally write new line */ + writeNewLine(context); + + return result; +} + +/** + * Initialize SCPI context structure + * @param context + * @param commands + * @param interface + * @param units + * @param idn1 + * @param idn2 + * @param idn3 + * @param idn4 + * @param input_buffer + * @param input_buffer_length + * @param error_queue_data + * @param error_queue_size + */ +void SCPI_Init(scpi_t * context, + const scpi_command_t * commands, + scpi_interface_t * interface, + const scpi_unit_def_t * units, + const char * idn1, const char * idn2, const char * idn3, const char * idn4, + char * input_buffer, size_t input_buffer_length, + scpi_error_t * error_queue_data, int16_t error_queue_size) { + memset(context, 0, sizeof (*context)); + context->cmdlist = commands; + context->interface = interface; + context->units = units; + context->idn[0] = idn1; + context->idn[1] = idn2; + context->idn[2] = idn3; + context->idn[3] = idn4; + context->buffer.data = input_buffer; + context->buffer.length = input_buffer_length; + context->buffer.position = 0; + SCPI_ErrorInit(context, error_queue_data, error_queue_size); +} + +#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION && !USE_MEMORY_ALLOCATION_FREE + +/** + * Initialize context's + * @param context + * @param data + * @param len + * @return + */ +void SCPI_InitHeap(scpi_t * context, + char * error_info_heap, size_t error_info_heap_length) { + scpiheap_init(&context->error_info_heap, error_info_heap, error_info_heap_length); +} +#endif + +/** + * Interface to the application. Adds data to system buffer and try to search + * command line termination. If the termination is found or if len=0, command + * parser is called. + * + * @param context + * @param data - data to process + * @param len - length of data + * @return + */ +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; + + 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 { + int buffer_free; + + buffer_free = context->buffer.length - context->buffer.position; + if (len > (buffer_free - 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; + context->buffer.data[context->buffer.position] = 0; + + + while (1) { + cmdlen = scpiParser_detectProgramMessageUnit(&context->parser_state, context->buffer.data + totcmdlen, context->buffer.position - totcmdlen); + totcmdlen += cmdlen; + + if (context->parser_state.termination == SCPI_MESSAGE_TERMINATION_NL) { + result = SCPI_Parse(context, context->buffer.data, totcmdlen); + memmove(context->buffer.data, context->buffer.data + totcmdlen, context->buffer.position - totcmdlen); + context->buffer.position -= totcmdlen; + totcmdlen = 0; + } else { + if (context->parser_state.programHeader.type == SCPI_TOKEN_UNKNOWN + && context->parser_state.termination == SCPI_MESSAGE_TERMINATION_NONE) break; + if (totcmdlen >= context->buffer.position) break; + } + } + } + + return result; +} + +/* writing results */ + +/** + * Write raw string result to the output + * @param context + * @param data + * @return + */ +size_t SCPI_ResultCharacters(scpi_t * context, const char * data, size_t len) { + size_t result = 0; + result += writeDelimiter(context); + result += writeData(context, data, len); + context->output_count++; + return result; +} + +/** + * Return prefix of nondecimal base + * @param base + * @return + */ +static const char * getBasePrefix(int8_t base) { + switch (base) { + case 2: return "#B"; + case 8: return "#Q"; + case 16: return "#H"; + default: return NULL; + } +} + +/** + * Write signed/unsigned 32 bit integer value in specific base to the result + * @param context + * @param val + * @param base + * @param sign + * @return + */ +static size_t resultUInt32BaseSign(scpi_t * context, uint32_t val, int8_t base, scpi_bool_t sign) { + char buffer[32 + 1]; + const char * basePrefix; + size_t result = 0; + size_t len; + + len = UInt32ToStrBaseSign(val, buffer, sizeof (buffer), base, sign); + basePrefix = getBasePrefix(base); + + result += writeDelimiter(context); + if (basePrefix != NULL) { + result += writeData(context, basePrefix, 2); + } + result += writeData(context, buffer, len); + context->output_count++; + return result; +} + +/** + * Write signed/unsigned 64 bit integer value in specific base to the result + * @param context + * @param val + * @param base + * @param sign + * @return + */ +static size_t resultUInt64BaseSign(scpi_t * context, uint64_t val, int8_t base, scpi_bool_t sign) { + char buffer[64 + 1]; + const char * basePrefix; + size_t result = 0; + size_t len; + + len = UInt64ToStrBaseSign(val, buffer, sizeof (buffer), base, sign); + basePrefix = getBasePrefix(base); + + result += writeDelimiter(context); + if (basePrefix != NULL) { + result += writeData(context, basePrefix, 2); + } + result += writeData(context, buffer, len); + context->output_count++; + return result; +} + +/** + * Write signed 32 bit integer value to the result + * @param context + * @param val + * @return + */ +size_t SCPI_ResultInt32(scpi_t * context, int32_t val) { + return resultUInt32BaseSign(context, val, 10, TRUE); +} + +/** + * Write unsigned 32 bit integer value in specific base to the result + * Write signed/unsigned 32 bit integer value in specific base to the result + * @param context + * @param val + * @return + */ +size_t SCPI_ResultUInt32Base(scpi_t * context, uint32_t val, int8_t base) { + return resultUInt32BaseSign(context, val, base, FALSE); +} + +/** + * Write signed 64 bit integer value to the result + * @param context + * @param val + * @return + */ +size_t SCPI_ResultInt64(scpi_t * context, int64_t val) { + return resultUInt64BaseSign(context, val, 10, TRUE); +} + +/** + * Write unsigned 64 bit integer value in specific base to the result + * @param context + * @param val + * @return + */ +size_t SCPI_ResultUInt64Base(scpi_t * context, uint64_t val, int8_t base) { + return resultUInt64BaseSign(context, val, base, FALSE); +} + +/** + * Write float (32 bit) value to the result + * @param context + * @param val + * @return + */ +size_t SCPI_ResultFloat(scpi_t * context, float val) { + char buffer[32]; + size_t result = 0; + size_t len = SCPI_FloatToStr(val, buffer, sizeof (buffer)); + result += writeDelimiter(context); + result += writeData(context, buffer, len); + context->output_count++; + return result; +} + +/** + * Write double (64bit) value to the result + * @param context + * @param val + * @return + */ +size_t SCPI_ResultDouble(scpi_t * context, double val) { + char buffer[32]; + size_t result = 0; + size_t len = SCPI_DoubleToStr(val, buffer, sizeof (buffer)); + result += writeDelimiter(context); + result += writeData(context, buffer, len); + context->output_count++; + return result; +} + +/** + * Write string within "" to the result + * @param context + * @param data + * @return + */ +size_t SCPI_ResultText(scpi_t * context, const char * data) { + size_t result = 0; + size_t len = strlen(data); + const char * quote; + result += writeDelimiter(context); + result += writeData(context, "\"", 1); + while ((quote = strnpbrk(data, len, "\""))) { + result += writeData(context, data, quote - data + 1); + result += writeData(context, "\"", 1); + len -= quote - data + 1; + data = quote + 1; + } + result += writeData(context, data, len); + result += writeData(context, "\"", 1); + context->output_count++; + return result; +} + +/** + * SCPI-99:21.8 Device-dependent error information. + * Write error information with the following syntax: + * <Error/event_number>,"<Error/event_description>[;<Device-dependent_info>]" + * The maximum string length of <Error/event_description> plus <Device-dependent_info> + * is SCPI_STD_ERROR_DESC_MAX_STRING_LENGTH (255) characters. + * + * @param context + * @param error + * @return + */ +size_t SCPI_ResultError(scpi_t * context, scpi_error_t * error) { + size_t result = 0; + size_t outputlimit = SCPI_STD_ERROR_DESC_MAX_STRING_LENGTH; + size_t step = 0; + const char * quote; + + const char * data[SCPIDEFINE_DESCRIPTION_MAX_PARTS]; + size_t len[SCPIDEFINE_DESCRIPTION_MAX_PARTS]; + size_t i; + + data[0] = SCPI_ErrorTranslate(error->error_code); + len[0] = strlen(data[0]); + +#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION + data[1] = error->device_dependent_info; +#if USE_MEMORY_ALLOCATION_FREE + len[1] = error->device_dependent_info ? strlen(data[1]) : 0; +#else + SCPIDEFINE_get_parts(&context->error_info_heap, data[1], &len[1], &data[2], &len[2]); +#endif +#endif + + result += SCPI_ResultInt32(context, error->error_code); + result += writeDelimiter(context); + result += writeData(context, "\"", 1); + + for (i = 0; (i < SCPIDEFINE_DESCRIPTION_MAX_PARTS) && data[i] && outputlimit; i++) { + if (i == 1) { + result += writeSemicolon(context); + outputlimit -= 1; + } + if (len[i] > outputlimit) { + len[i] = outputlimit; + } + + while ((quote = strnpbrk(data[i], len[i], "\""))) { + if ((step = quote - data[i] + 1) >= outputlimit) { + len[i] -= 1; + outputlimit -= 1; + break; + } + result += writeData(context, data[i], step); + result += writeData(context, "\"", 1); + len[i] -= step; + outputlimit -= step + 1; + data[i] = quote + 1; + if (len[i] > outputlimit) { + len[i] = outputlimit; + } + } + + result += writeData(context, data[i], len[i]); + outputlimit -= len[i]; + } + result += writeData(context, "\"", 1); + + return result; +} + +/** + * Write arbitrary block header with length + * @param context + * @param len + * @return + */ +size_t SCPI_ResultArbitraryBlockHeader(scpi_t * context, size_t len) { + size_t result = 0; + char block_header[12]; + size_t header_len; + block_header[0] = '#'; + SCPI_UInt32ToStrBase((uint32_t) len, block_header + 2, 10, 10); + + header_len = strlen(block_header + 2); + block_header[1] = (char) (header_len + '0'); + + context->arbitrary_remaining = len; + result = writeDelimiter(context); + result += writeData(context, block_header, header_len + 2); + return result; +} + +/** + * Add data to arbitrary block + * @param context + * @param data + * @param len + * @return + */ +size_t SCPI_ResultArbitraryBlockData(scpi_t * context, const void * data, size_t len) { + + if (context->arbitrary_remaining < len) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return 0; + } + + context->arbitrary_remaining -= len; + + if (context->arbitrary_remaining == 0) { + context->output_count++; + } + + return writeData(context, (const char *) data, len); +} + +/** + * Write arbitrary block program data to the result + * @param context + * @param data + * @param len + * @return + */ +size_t SCPI_ResultArbitraryBlock(scpi_t * context, const void * data, size_t len) { + size_t result = 0; + result += SCPI_ResultArbitraryBlockHeader(context, len); + result += SCPI_ResultArbitraryBlockData(context, data, len); + return result; +} + +/** + * Write boolean value to the result + * @param context + * @param val + * @return + */ +size_t SCPI_ResultBool(scpi_t * context, scpi_bool_t val) { + return resultUInt32BaseSign(context, val ? 1 : 0, 10, FALSE); +} + +/* parsing parameters */ + +/** + * Invalidate token + * @param token + * @param ptr + */ +static void invalidateToken(scpi_token_t * token, char * ptr) { + token->len = 0; + token->ptr = ptr; + token->type = SCPI_TOKEN_UNKNOWN; +} + +/** + * Get one parameter from command line + * @param context + * @param parameter + * @param mandatory + * @return + */ +scpi_bool_t SCPI_Parameter(scpi_t * context, scpi_parameter_t * parameter, scpi_bool_t mandatory) { + lex_state_t * state; + + if (!parameter) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + invalidateToken(parameter, NULL); + + state = &context->param_list.lex_state; + + if (state->pos >= (state->buffer + state->len)) { + if (mandatory) { + SCPI_ErrorPush(context, SCPI_ERROR_MISSING_PARAMETER); + } else { + parameter->type = SCPI_TOKEN_PROGRAM_MNEMONIC; /* TODO: select something different */ + } + return FALSE; + } + if (context->input_count != 0) { + scpiLex_Comma(state, parameter); + if (parameter->type != SCPI_TOKEN_COMMA) { + invalidateToken(parameter, NULL); + SCPI_ErrorPush(context, SCPI_ERROR_INVALID_SEPARATOR); + return FALSE; + } + } + + context->input_count++; + + scpiParser_parseProgramData(&context->param_list.lex_state, parameter); + + switch (parameter->type) { + case SCPI_TOKEN_HEXNUM: + case SCPI_TOKEN_OCTNUM: + case SCPI_TOKEN_BINNUM: + case SCPI_TOKEN_PROGRAM_MNEMONIC: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + case SCPI_TOKEN_ARBITRARY_BLOCK_PROGRAM_DATA: + case SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA: + case SCPI_TOKEN_DOUBLE_QUOTE_PROGRAM_DATA: + case SCPI_TOKEN_PROGRAM_EXPRESSION: + return TRUE; + default: + invalidateToken(parameter, NULL); + SCPI_ErrorPush(context, SCPI_ERROR_INVALID_STRING_DATA); + return FALSE; + } +} + +/** + * Detect if parameter is number + * @param parameter + * @param suffixAllowed + * @return + */ +scpi_bool_t SCPI_ParamIsNumber(scpi_parameter_t * parameter, scpi_bool_t suffixAllowed) { + switch (parameter->type) { + case SCPI_TOKEN_HEXNUM: + case SCPI_TOKEN_OCTNUM: + case SCPI_TOKEN_BINNUM: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + return TRUE; + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + return suffixAllowed; + default: + return FALSE; + } +} + +/** + * Convert parameter to signed/unsigned 32 bit integer + * @param context + * @param parameter + * @param value result + * @param sign + * @return TRUE if succesful + */ +static scpi_bool_t ParamSignToUInt32(scpi_t * context, scpi_parameter_t * parameter, uint32_t * value, scpi_bool_t sign) { + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + switch (parameter->type) { + case SCPI_TOKEN_HEXNUM: + return strBaseToUInt32(parameter->ptr, value, 16) > 0 ? TRUE : FALSE; + case SCPI_TOKEN_OCTNUM: + return strBaseToUInt32(parameter->ptr, value, 8) > 0 ? TRUE : FALSE; + case SCPI_TOKEN_BINNUM: + return strBaseToUInt32(parameter->ptr, value, 2) > 0 ? TRUE : FALSE; + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + if (sign) { + return strBaseToInt32(parameter->ptr, (int32_t *) value, 10) > 0 ? TRUE : FALSE; + } else { + return strBaseToUInt32(parameter->ptr, value, 10) > 0 ? TRUE : FALSE; + } + default: + return FALSE; + } +} + +/** + * Convert parameter to signed/unsigned 64 bit integer + * @param context + * @param parameter + * @param value result + * @param sign + * @return TRUE if succesful + */ +static scpi_bool_t ParamSignToUInt64(scpi_t * context, scpi_parameter_t * parameter, uint64_t * value, scpi_bool_t sign) { + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + switch (parameter->type) { + case SCPI_TOKEN_HEXNUM: + return strBaseToUInt64(parameter->ptr, value, 16) > 0 ? TRUE : FALSE; + case SCPI_TOKEN_OCTNUM: + return strBaseToUInt64(parameter->ptr, value, 8) > 0 ? TRUE : FALSE; + case SCPI_TOKEN_BINNUM: + return strBaseToUInt64(parameter->ptr, value, 2) > 0 ? TRUE : FALSE; + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + if (sign) { + return strBaseToInt64(parameter->ptr, (int64_t *) value, 10) > 0 ? TRUE : FALSE; + } else { + return strBaseToUInt64(parameter->ptr, value, 10) > 0 ? TRUE : FALSE; + } + default: + return FALSE; + } +} + +/** + * Convert parameter to signed 32 bit integer + * @param context + * @param parameter + * @param value result + * @return TRUE if succesful + */ +scpi_bool_t SCPI_ParamToInt32(scpi_t * context, scpi_parameter_t * parameter, int32_t * value) { + return ParamSignToUInt32(context, parameter, (uint32_t *) value, TRUE); +} + +/** + * Convert parameter to unsigned 32 bit integer + * @param context + * @param parameter + * @param value result + * @return TRUE if succesful + */ +scpi_bool_t SCPI_ParamToUInt32(scpi_t * context, scpi_parameter_t * parameter, uint32_t * value) { + return ParamSignToUInt32(context, parameter, value, FALSE); +} + +/** + * Convert parameter to signed 64 bit integer + * @param context + * @param parameter + * @param value result + * @return TRUE if succesful + */ +scpi_bool_t SCPI_ParamToInt64(scpi_t * context, scpi_parameter_t * parameter, int64_t * value) { + return ParamSignToUInt64(context, parameter, (uint64_t *) value, TRUE); +} + +/** + * Convert parameter to unsigned 32 bit integer + * @param context + * @param parameter + * @param value result + * @return TRUE if succesful + */ +scpi_bool_t SCPI_ParamToUInt64(scpi_t * context, scpi_parameter_t * parameter, uint64_t * value) { + return ParamSignToUInt64(context, parameter, value, FALSE); +} + +/** + * Convert parameter to float (32 bit) + * @param context + * @param parameter + * @param value result + * @return TRUE if succesful + */ +scpi_bool_t SCPI_ParamToFloat(scpi_t * context, scpi_parameter_t * parameter, float * value) { + scpi_bool_t result; + uint32_t valint; + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + switch (parameter->type) { + case SCPI_TOKEN_HEXNUM: + case SCPI_TOKEN_OCTNUM: + case SCPI_TOKEN_BINNUM: + result = SCPI_ParamToUInt32(context, parameter, &valint); + *value = valint; + break; + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + result = strToFloat(parameter->ptr, value) > 0 ? TRUE : FALSE; + break; + default: + result = FALSE; + } + return result; +} + +/** + * Convert parameter to double (64 bit) + * @param context + * @param parameter + * @param value result + * @return TRUE if succesful + */ +scpi_bool_t SCPI_ParamToDouble(scpi_t * context, scpi_parameter_t * parameter, double * value) { + scpi_bool_t result; + uint64_t valint; + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + switch (parameter->type) { + case SCPI_TOKEN_HEXNUM: + case SCPI_TOKEN_OCTNUM: + case SCPI_TOKEN_BINNUM: + result = SCPI_ParamToUInt64(context, parameter, &valint); + *value = valint; + break; + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + result = strToDouble(parameter->ptr, value) > 0 ? TRUE : FALSE; + break; + default: + result = FALSE; + } + return result; +} + +/** + * Read floating point float (32 bit) parameter + * @param context + * @param value + * @param mandatory + * @return + */ +scpi_bool_t SCPI_ParamFloat(scpi_t * context, float * value, scpi_bool_t mandatory) { + scpi_bool_t result; + scpi_parameter_t param; + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + if (result) { + if (SCPI_ParamIsNumber(¶m, FALSE)) { + SCPI_ParamToFloat(context, ¶m, value); + } else if (SCPI_ParamIsNumber(¶m, TRUE)) { + SCPI_ErrorPush(context, SCPI_ERROR_SUFFIX_NOT_ALLOWED); + result = FALSE; + } else { + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + result = FALSE; + } + } + return result; +} + +/** + * Read floating point double (64 bit) parameter + * @param context + * @param value + * @param mandatory + * @return + */ +scpi_bool_t SCPI_ParamDouble(scpi_t * context, double * value, scpi_bool_t mandatory) { + scpi_bool_t result; + scpi_parameter_t param; + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + if (result) { + if (SCPI_ParamIsNumber(¶m, FALSE)) { + SCPI_ParamToDouble(context, ¶m, value); + } else if (SCPI_ParamIsNumber(¶m, TRUE)) { + SCPI_ErrorPush(context, SCPI_ERROR_SUFFIX_NOT_ALLOWED); + result = FALSE; + } else { + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + result = FALSE; + } + } + return result; +} + +/** + * Read signed/unsigned 32 bit integer parameter + * @param context + * @param value + * @param mandatory + * @param sign + * @return + */ +static scpi_bool_t ParamSignUInt32(scpi_t * context, uint32_t * value, scpi_bool_t mandatory, scpi_bool_t sign) { + scpi_bool_t result; + scpi_parameter_t param; + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + if (result) { + if (SCPI_ParamIsNumber(¶m, FALSE)) { + result = ParamSignToUInt32(context, ¶m, value, sign); + } else if (SCPI_ParamIsNumber(¶m, TRUE)) { + SCPI_ErrorPush(context, SCPI_ERROR_SUFFIX_NOT_ALLOWED); + result = FALSE; + } else { + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + result = FALSE; + } + } + return result; +} + +/** + * Read signed/unsigned 64 bit integer parameter + * @param context + * @param value + * @param mandatory + * @param sign + * @return + */ +static scpi_bool_t ParamSignUInt64(scpi_t * context, uint64_t * value, scpi_bool_t mandatory, scpi_bool_t sign) { + scpi_bool_t result; + scpi_parameter_t param; + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + if (result) { + if (SCPI_ParamIsNumber(¶m, FALSE)) { + result = ParamSignToUInt64(context, ¶m, value, sign); + } else if (SCPI_ParamIsNumber(¶m, TRUE)) { + SCPI_ErrorPush(context, SCPI_ERROR_SUFFIX_NOT_ALLOWED); + result = FALSE; + } else { + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + result = FALSE; + } + } + return result; +} + +/** + * Read signed 32 bit integer parameter + * @param context + * @param value + * @param mandatory + * @return + */ +scpi_bool_t SCPI_ParamInt32(scpi_t * context, int32_t * value, scpi_bool_t mandatory) { + return ParamSignUInt32(context, (uint32_t *) value, mandatory, TRUE); +} + +/** + * Read unsigned 32 bit integer parameter + * @param context + * @param value + * @param mandatory + * @return + */ +scpi_bool_t SCPI_ParamUInt32(scpi_t * context, uint32_t * value, scpi_bool_t mandatory) { + return ParamSignUInt32(context, value, mandatory, FALSE); +} + +/** + * Read signed 64 bit integer parameter + * @param context + * @param value + * @param mandatory + * @return + */ +scpi_bool_t SCPI_ParamInt64(scpi_t * context, int64_t * value, scpi_bool_t mandatory) { + return ParamSignUInt64(context, (uint64_t *) value, mandatory, TRUE); +} + +/** + * Read unsigned 64 bit integer parameter + * @param context + * @param value + * @param mandatory + * @return + */ +scpi_bool_t SCPI_ParamUInt64(scpi_t * context, uint64_t * value, scpi_bool_t mandatory) { + return ParamSignUInt64(context, value, mandatory, FALSE); +} + +/** + * Read character parameter + * @param context + * @param value + * @param len + * @param mandatory + * @return + */ +scpi_bool_t SCPI_ParamCharacters(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory) { + scpi_bool_t result; + scpi_parameter_t param; + + if (!value || !len) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + if (result) { + switch (param.type) { + case SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA: + case SCPI_TOKEN_DOUBLE_QUOTE_PROGRAM_DATA: + *value = param.ptr + 1; + *len = param.len - 2; + break; + default: + *value = param.ptr; + *len = param.len; + break; + } + + /* TODO: return also parameter type (ProgramMnemonic, ArbitraryBlockProgramData, SingleQuoteProgramData, DoubleQuoteProgramData */ + } + + return result; +} + +/** + * Get arbitrary block program data and returns pointer to data + * @param context + * @param value result pointer to data + * @param len result length of data + * @param mandatory + * @return + */ +scpi_bool_t SCPI_ParamArbitraryBlock(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory) { + scpi_bool_t result; + scpi_parameter_t param; + + if (!value || !len) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + if (result) { + if (param.type == SCPI_TOKEN_ARBITRARY_BLOCK_PROGRAM_DATA) { + *value = param.ptr; + *len = param.len; + } else { + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + result = FALSE; + } + } + + return result; +} + +scpi_bool_t SCPI_ParamCopyText(scpi_t * context, char * buffer, size_t buffer_len, size_t * copy_len, scpi_bool_t mandatory) { + scpi_bool_t result; + scpi_parameter_t param; + size_t i_from; + size_t i_to; + char quote; + + if (!buffer || !copy_len) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + if (result) { + + switch (param.type) { + case SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA: + case SCPI_TOKEN_DOUBLE_QUOTE_PROGRAM_DATA: + quote = param.type == SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA ? '\'' : '"'; + for (i_from = 1, i_to = 0; i_from < (size_t) (param.len - 1); i_from++) { + if (i_from >= buffer_len) { + break; + } + buffer[i_to] = param.ptr[i_from]; + i_to++; + if (param.ptr[i_from] == quote) { + i_from++; + } + } + *copy_len = i_to; + if (i_to < buffer_len) { + buffer[i_to] = 0; + } + break; + default: + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + result = FALSE; + } + } + + return result; +} + +/** + * Convert parameter to choice + * @param context + * @param parameter - should be PROGRAM_MNEMONIC + * @param options - NULL terminated list of choices + * @param value - index to options + * @return + */ +scpi_bool_t SCPI_ParamToChoice(scpi_t * context, scpi_parameter_t * parameter, const scpi_choice_def_t * options, int32_t * value) { + size_t res; + scpi_bool_t result = FALSE; + + if (!options || !value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + if (parameter->type == SCPI_TOKEN_PROGRAM_MNEMONIC) { + for (res = 0; options[res].name; ++res) { + if (matchPattern(options[res].name, strlen(options[res].name), parameter->ptr, parameter->len, NULL)) { + *value = options[res].tag; + result = TRUE; + break; + } + } + + if (!result) { + SCPI_ErrorPush(context, SCPI_ERROR_ILLEGAL_PARAMETER_VALUE); + } + } else { + SCPI_ErrorPush(context, SCPI_ERROR_DATA_TYPE_ERROR); + } + + return result; +} + +/** + * Find tag in choices and returns its first textual representation + * @param options specifications of choices numbers (patterns) + * @param tag numerical representatio of choice + * @param text result text + * @return TRUE if succesfule, else FALSE + */ +scpi_bool_t SCPI_ChoiceToName(const scpi_choice_def_t * options, int32_t tag, const char ** text) { + int i; + + for (i = 0; options[i].name != NULL; i++) { + if (options[i].tag == tag) { + *text = options[i].name; + return TRUE; + } + } + + return FALSE; +} + +/* + * Definition of BOOL choice list + */ +const scpi_choice_def_t scpi_bool_def[] = { + {"OFF", 0}, + {"ON", 1}, + SCPI_CHOICE_LIST_END /* termination of option list */ +}; + +/** + * Read BOOL parameter (0,1,ON,OFF) + * @param context + * @param value + * @param mandatory + * @return + */ +scpi_bool_t SCPI_ParamBool(scpi_t * context, scpi_bool_t * value, scpi_bool_t mandatory) { + scpi_bool_t result; + scpi_parameter_t param; + int32_t intval; + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + + if (result) { + if (param.type == SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA) { + SCPI_ParamToInt32(context, ¶m, &intval); + *value = intval ? TRUE : FALSE; + } else { + result = SCPI_ParamToChoice(context, ¶m, scpi_bool_def, &intval); + if (result) { + *value = intval ? TRUE : FALSE; + } + } + } + + return result; +} + +/** + * Read value from list of options + * @param context + * @param options + * @param value + * @param mandatory + * @return + */ +scpi_bool_t SCPI_ParamChoice(scpi_t * context, const scpi_choice_def_t * options, int32_t * value, scpi_bool_t mandatory) { + scpi_bool_t result; + scpi_parameter_t param; + + if (!options || !value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + if (result) { + result = SCPI_ParamToChoice(context, ¶m, options, value); + } + + return result; +} + +/** + * Parse one parameter and detect type + * @param state + * @param token + * @return + */ +int scpiParser_parseProgramData(lex_state_t * state, scpi_token_t * token) { + scpi_token_t tmp; + int result = 0; + int wsLen; + int suffixLen; + int realLen = 0; + realLen += scpiLex_WhiteSpace(state, &tmp); + + if (result == 0) result = scpiLex_NondecimalNumericData(state, token); + if (result == 0) result = scpiLex_CharacterProgramData(state, token); + if (result == 0) { + result = scpiLex_DecimalNumericProgramData(state, token); + if (result != 0) { + wsLen = scpiLex_WhiteSpace(state, &tmp); + suffixLen = scpiLex_SuffixProgramData(state, &tmp); + if (suffixLen > 0) { + token->len += wsLen + suffixLen; + token->type = SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX; + result = token->len; + } + } + } + + if (result == 0) result = scpiLex_StringProgramData(state, token); + if (result == 0) result = scpiLex_ArbitraryBlockProgramData(state, token); + if (result == 0) result = scpiLex_ProgramExpression(state, token); + + realLen += scpiLex_WhiteSpace(state, &tmp); + + return result + realLen; +} + +/** + * Skip all parameters to correctly detect end of command line. + * @param state + * @param token + * @param numberOfParameters + * @return + */ +int scpiParser_parseAllProgramData(lex_state_t * state, scpi_token_t * token, int * numberOfParameters) { + + int result; + scpi_token_t tmp; + int paramCount = 0; + + token->len = -1; + token->type = SCPI_TOKEN_ALL_PROGRAM_DATA; + token->ptr = state->pos; + + + for (result = 1; result != 0; result = scpiLex_Comma(state, &tmp)) { + token->len += result; + + result = scpiParser_parseProgramData(state, &tmp); + if (tmp.type != SCPI_TOKEN_UNKNOWN) { + token->len += result; + } else { + token->type = SCPI_TOKEN_UNKNOWN; + token->len = 0; + paramCount = -1; + break; + } + paramCount++; + } + + if (numberOfParameters != NULL) { + *numberOfParameters = paramCount; + } + return token->len; +} + +/** + * Skip complete command line - program header and parameters + * @param state + * @param buffer + * @param len + * @return + */ +int scpiParser_detectProgramMessageUnit(scpi_parser_state_t * state, char * buffer, int len) { + lex_state_t lex_state; + scpi_token_t tmp; + int result = 0; + + lex_state.buffer = lex_state.pos = buffer; + lex_state.len = len; + state->numberOfParameters = 0; + + /* ignore whitespace at the begginig */ + scpiLex_WhiteSpace(&lex_state, &tmp); + + if (scpiLex_ProgramHeader(&lex_state, &state->programHeader) >= 0) { + if (scpiLex_WhiteSpace(&lex_state, &tmp) > 0) { + scpiParser_parseAllProgramData(&lex_state, &state->programData, &state->numberOfParameters); + } else { + invalidateToken(&state->programData, lex_state.pos); + } + } else { + invalidateToken(&state->programHeader, lex_state.buffer); + invalidateToken(&state->programData, lex_state.buffer); + } + + if (result == 0) result = scpiLex_NewLine(&lex_state, &tmp); + if (result == 0) result = scpiLex_Semicolon(&lex_state, &tmp); + + if (!scpiLex_IsEos(&lex_state) && (result == 0)) { + lex_state.pos++; + + state->programHeader.len = 1; + state->programHeader.type = SCPI_TOKEN_INVALID; + + invalidateToken(&state->programData, lex_state.buffer); + } + + if (SCPI_TOKEN_SEMICOLON == tmp.type) { + state->termination = SCPI_MESSAGE_TERMINATION_SEMICOLON; + } else if (SCPI_TOKEN_NL == tmp.type) { + state->termination = SCPI_MESSAGE_TERMINATION_NL; + } else { + state->termination = SCPI_MESSAGE_TERMINATION_NONE; + } + + return lex_state.pos - lex_state.buffer; +} + +/** + * Check current command + * - suitable for one handle to multiple commands + * @param context + * @param cmd + * @return + */ +scpi_bool_t SCPI_IsCmd(scpi_t * context, const char * cmd) { + const char * pattern; + + if (!context->param_list.cmd) { + return FALSE; + } + + pattern = context->param_list.cmd->pattern; + return matchCommand(pattern, cmd, strlen(cmd), NULL, 0, 0); +} + +#if USE_COMMAND_TAGS + +/** + * Return the .tag field of the matching scpi_command_t + * @param context + * @return + */ +int32_t SCPI_CmdTag(scpi_t * context) { + if (context->param_list.cmd) { + return context->param_list.cmd->tag; + } else { + return 0; + } +} +#endif /* USE_COMMAND_TAGS */ + +scpi_bool_t SCPI_Match(const char * pattern, const char * value, size_t len) { + return matchCommand(pattern, value, len, NULL, 0, 0); +} + +scpi_bool_t SCPI_CommandNumbers(scpi_t * context, int32_t * numbers, size_t len, int32_t default_value) { + return matchCommand(context->param_list.cmd->pattern, context->param_list.cmd_raw.data, context->param_list.cmd_raw.length, numbers, len, default_value); +} + +/** + * If SCPI_Parameter() returns FALSE, this function can detect, if the parameter + * is just missing (TRUE) or if there was an error during processing of the command (FALSE) + * @param parameter + * @return + */ +scpi_bool_t SCPI_ParamIsValid(scpi_parameter_t * parameter) { + return parameter->type == SCPI_TOKEN_UNKNOWN ? FALSE : TRUE; +} + +/** + * Returns TRUE if there was an error during parameter handling + * @param context + * @return + */ +scpi_bool_t SCPI_ParamErrorOccurred(scpi_t * context) { + return context->cmd_error; +} + +/** + * Result binary array and swap bytes if needed (native endiannes != required endiannes) + * @param context + * @param array + * @param count + * @param item_size + * @param format + * @return + */ +static size_t produceResultArrayBinary(scpi_t * context, const void * array, size_t count, size_t item_size, scpi_array_format_t format) { + + if (SCPI_GetNativeFormat() == format) { + switch (item_size) { + case 1: + case 2: + case 4: + case 8: + return SCPI_ResultArbitraryBlock(context, array, count * item_size); + default: + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return 0; + } + } else { + size_t result = 0; + size_t i; + switch (item_size) { + case 1: + case 2: + case 4: + case 8: + result += SCPI_ResultArbitraryBlockHeader(context, count * item_size); + break; + default: + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return 0; + } + + switch (item_size) { + case 1: + result += SCPI_ResultArbitraryBlockData(context, array, count); + break; + case 2: + for (i = 0; i < count; i++) { + uint16_t val = SCPI_Swap16(((uint16_t*) array)[i]); + result += SCPI_ResultArbitraryBlockData(context, &val, item_size); + } + break; + case 4: + for (i = 0; i < count; i++) { + uint32_t val = SCPI_Swap32(((uint32_t*) array)[i]); + result += SCPI_ResultArbitraryBlockData(context, &val, item_size); + } + break; + case 8: + for (i = 0; i < count; i++) { + uint64_t val = SCPI_Swap64(((uint64_t*) array)[i]); + result += SCPI_ResultArbitraryBlockData(context, &val, item_size); + } + break; + } + + return result; + } +} + + +#define RESULT_ARRAY(func) do {\ + size_t result = 0;\ + if (format == SCPI_FORMAT_ASCII) {\ + size_t i;\ + for (i = 0; i < count; i++) {\ + result += func(context, array[i]);\ + }\ + } else {\ + result = produceResultArrayBinary(context, array, count, sizeof(*array), format);\ + }\ + return result;\ +} while(0) + +/** + * Result array of signed 8bit integers + * @param context + * @param array + * @param count + * @param format + * @return + */ +size_t SCPI_ResultArrayInt8(scpi_t * context, const int8_t * array, size_t count, scpi_array_format_t format) { + RESULT_ARRAY(SCPI_ResultInt8); +} + +/** + * Result array of unsigned 8bit integers + * @param context + * @param array + * @param count + * @param format + * @return + */ +size_t SCPI_ResultArrayUInt8(scpi_t * context, const uint8_t * array, size_t count, scpi_array_format_t format) { + RESULT_ARRAY(SCPI_ResultUInt8); +} + +/** + * Result array of signed 16bit integers + * @param context + * @param array + * @param count + * @param format + * @return + */ +size_t SCPI_ResultArrayInt16(scpi_t * context, const int16_t * array, size_t count, scpi_array_format_t format) { + RESULT_ARRAY(SCPI_ResultInt16); +} + +/** + * Result array of unsigned 16bit integers + * @param context + * @param array + * @param count + * @param format + * @return + */ +size_t SCPI_ResultArrayUInt16(scpi_t * context, const uint16_t * array, size_t count, scpi_array_format_t format) { + RESULT_ARRAY(SCPI_ResultUInt16); +} + +/** + * Result array of signed 32bit integers + * @param context + * @param array + * @param count + * @param format + * @return + */ +size_t SCPI_ResultArrayInt32(scpi_t * context, const int32_t * array, size_t count, scpi_array_format_t format) { + RESULT_ARRAY(SCPI_ResultInt32); +} + +/** + * Result array of unsigned 32bit integers + * @param context + * @param array + * @param count + * @param format + * @return + */ +size_t SCPI_ResultArrayUInt32(scpi_t * context, const uint32_t * array, size_t count, scpi_array_format_t format) { + RESULT_ARRAY(SCPI_ResultUInt32); +} + +/** + * Result array of signed 64bit integers + * @param context + * @param array + * @param count + * @param format + * @return + */ +size_t SCPI_ResultArrayInt64(scpi_t * context, const int64_t * array, size_t count, scpi_array_format_t format) { + RESULT_ARRAY(SCPI_ResultInt64); +} + +/** + * Result array of unsigned 64bit integers + * @param context + * @param array + * @param count + * @param format + * @return + */ +size_t SCPI_ResultArrayUInt64(scpi_t * context, const uint64_t * array, size_t count, scpi_array_format_t format) { + RESULT_ARRAY(SCPI_ResultUInt64); +} + +/** + * Result array of floats + * @param context + * @param array + * @param count + * @param format + * @return + */ +size_t SCPI_ResultArrayFloat(scpi_t * context, const float * array, size_t count, scpi_array_format_t format) { + RESULT_ARRAY(SCPI_ResultFloat); +} + +/** + * Result array of doubles + * @param context + * @param array + * @param count + * @param format + * @return + */ +size_t SCPI_ResultArrayDouble(scpi_t * context, const double * array, size_t count, scpi_array_format_t format) { + RESULT_ARRAY(SCPI_ResultDouble); +} + +/* + * Template macro to generate all SCPI_ParamArrayXYZ function + */ +#define PARAM_ARRAY_TEMPLATE(func) do{\ + if (format != SCPI_FORMAT_ASCII) return FALSE;\ + for (*o_count = 0; *o_count < i_count; (*o_count)++) {\ + if (!func(context, &data[*o_count], mandatory)) {\ + break;\ + }\ + mandatory = FALSE;\ + }\ + return mandatory ? FALSE : TRUE;\ +}while(0) + +/** + * Read list of values up to i_count + * @param context + * @param data - array to fill + * @param i_count - number of elements of data + * @param o_count - real number of filled elements + * @param mandatory + * @return TRUE on success + */ +scpi_bool_t SCPI_ParamArrayInt32(scpi_t * context, int32_t *data, size_t i_count, size_t *o_count, scpi_array_format_t format, scpi_bool_t mandatory) { + PARAM_ARRAY_TEMPLATE(SCPI_ParamInt32); +} + +/** + * Read list of values up to i_count + * @param context + * @param data - array to fill + * @param i_count - number of elements of data + * @param o_count - real number of filled elements + * @param mandatory + * @return TRUE on success + */ +scpi_bool_t SCPI_ParamArrayUInt32(scpi_t * context, uint32_t *data, size_t i_count, size_t *o_count, scpi_array_format_t format, scpi_bool_t mandatory) { + PARAM_ARRAY_TEMPLATE(SCPI_ParamUInt32); +} + +/** + * Read list of values up to i_count + * @param context + * @param data - array to fill + * @param i_count - number of elements of data + * @param o_count - real number of filled elements + * @param mandatory + * @return TRUE on success + */ +scpi_bool_t SCPI_ParamArrayInt64(scpi_t * context, int64_t *data, size_t i_count, size_t *o_count, scpi_array_format_t format, scpi_bool_t mandatory) { + PARAM_ARRAY_TEMPLATE(SCPI_ParamInt64); +} + +/** + * Read list of values up to i_count + * @param context + * @param data - array to fill + * @param i_count - number of elements of data + * @param o_count - real number of filled elements + * @param mandatory + * @return TRUE on success + */ +scpi_bool_t SCPI_ParamArrayUInt64(scpi_t * context, uint64_t *data, size_t i_count, size_t *o_count, scpi_array_format_t format, scpi_bool_t mandatory) { + PARAM_ARRAY_TEMPLATE(SCPI_ParamUInt64); +} + +/** + * Read list of values up to i_count + * @param context + * @param data - array to fill + * @param i_count - number of elements of data + * @param o_count - real number of filled elements + * @param mandatory + * @return TRUE on success + */ +scpi_bool_t SCPI_ParamArrayFloat(scpi_t * context, float *data, size_t i_count, size_t *o_count, scpi_array_format_t format, scpi_bool_t mandatory) { + PARAM_ARRAY_TEMPLATE(SCPI_ParamFloat); +} + +/** + * Read list of values up to i_count + * @param context + * @param data - array to fill + * @param i_count - number of elements of data + * @param o_count - real number of filled elements + * @param mandatory + * @return TRUE on success + */ +scpi_bool_t SCPI_ParamArrayDouble(scpi_t * context, double *data, size_t i_count, size_t *o_count, scpi_array_format_t format, scpi_bool_t mandatory) { + PARAM_ARRAY_TEMPLATE(SCPI_ParamDouble); +} diff --git a/third_party/libscpi/src/parser_private.h b/third_party/libscpi/src/parser_private.h new file mode 100644 index 00000000..bb8642fa --- /dev/null +++ b/third_party/libscpi/src/parser_private.h @@ -0,0 +1,56 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file parser_private.h + * + * @brief SCPI Parser private definitions + * + * + */ + +#ifndef SCPI_PARSER_PRIVATE_H +#define SCPI_PARSER_PRIVATE_H + +#include "scpi/types.h" +#include "utils_private.h" + +#ifdef __cplusplus +extern "C" { +#endif + + int scpiParser_parseProgramData(lex_state_t * state, scpi_token_t * token) LOCAL; + int scpiParser_parseAllProgramData(lex_state_t * state, scpi_token_t * token, int * numberOfParameters) LOCAL; + int scpiParser_detectProgramMessageUnit(scpi_parser_state_t * state, char * buffer, int len) LOCAL; + +#ifdef __cplusplus +} +#endif + +#endif /* SCPI_PARSER_PRIVATE_H */ + diff --git a/third_party/libscpi/src/scpi.g b/third_party/libscpi/src/scpi.g new file mode 100644 index 00000000..a3993ffd --- /dev/null +++ b/third_party/libscpi/src/scpi.g @@ -0,0 +1,141 @@ +grammar scpi; + +terminatedProgramMessage + : programMessage NL? EOF + ; + +programMessage + : programMessageUnit (SEMICOLON programMessageUnit)* + ; + + +programMessageUnit + : WS* programHeader (WS programData (COMMA programData)*)? + ; + +programHeader + : compoundProgramHeader + | commonProgramHeader + ; + +compoundProgramHeader + : COLON? PROGRAM_MNEMONIC (COLON PROGRAM_MNEMONIC)* QUESTION? + ; + +commonProgramHeader + : STAR PROGRAM_MNEMONIC QUESTION? + ; + +programDataSeparator + : WS* + ; + +programData + : WS* programDataType WS* + ; + +programDataType + : nondecimalNumericProgramData + | characterProgramData + | decimalNumericProgramData + | stringProgramData + | arbitraryBlockProgramData + | expressionProgramData +// | suffixProgramData + ; + +nondecimalNumericProgramData + : HEXNUM + | OCTNUM + | BINNUM + ; + +characterProgramData + : PROGRAM_MNEMONIC + ; + +decimalNumericProgramData + : DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX + ; + +//suffixProgramData +// : PROGRAM_MNEMONIC//SUFFIX_PROGRAM_DATA +// ; + +stringProgramData + : SINGLE_QUOTE_PROGRAM_DATA + | DOUBLE_QUOTE_PROGRAM_DATA + ; + +expressionProgramData + : PROGRAM_EXPRESSION + ; + +// support only nonzero prefix +arbitraryBlockProgramData + : SHARP NONZERO_DIGIT NUMBER .* + ; + +PROGRAM_MNEMONIC : ALPHA (ALPHA | DIGIT | UNDERSCORE)*; +HEXNUM : SHARP H HEXDIGIT*; +BINNUM : SHARP Q OCTDIGIT*; +OCTNUM : SHARP B BINDIGIT*; +UNDERSCORE : '_'; +SEMICOLON : ';'; +QUESTION : '?'; +COLON : ':'; +COMMA : ','; +STAR : '*'; +NL : '\r'? '\n' ; +WS : (SPACE | TAB); + +DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX : DECIMAL_NUMERIC_PROGRAM_DATA WS* (SUFFIX_PROGRAM_DATA)?; +fragment DECIMAL_NUMERIC_PROGRAM_DATA : MANTISA WS* (EXPONENT)?; +SINGLE_QUOTE_PROGRAM_DATA : SINGLE_QUOTE ( (NON_SINGLE_QUOTE) | (SINGLE_QUOTE SINGLE_QUOTE))* SINGLE_QUOTE; +DOUBLE_QUOTE_PROGRAM_DATA : DOUBLE_QUOTE ( (NON_DOUBLE_QUOTE) | (DOUBLE_QUOTE DOUBLE_QUOTE))* DOUBLE_QUOTE; +//SUFFIX_PROGRAM_DATA : SLASH? (ALPHA+ (MINUS? DIGIT)?) ((SLASH | DOT) (ALPHA+ (MINUS? DIGIT)?))*; +fragment SUFFIX_PROGRAM_DATA : SLASH? ALPHA+ ((SLASH | DOT) ALPHA+)*; +//fragment SUFFIX_PROGRAM_DATA : ALPHA+; + +fragment PROGRAM_EXPRESSION_CHARACTER : (SPACE | '!' | '$'..'&' | '*'..':' | '<' ..'~'); +PROGRAM_EXPRESSION : LBRACKET PROGRAM_EXPRESSION_CHARACTER RBRACKET; + +fragment PLUSMN : (PLUS | MINUS); +fragment MANTISA : PLUSMN? ( (NUMBER) | (NUMBER DOT NUMBER?) | (DOT NUMBER)); + +//fragment EXPONENT : WS* E WS* PLUSMN? NUMBER; +fragment EXPONENT : E WS* PLUSMN? NUMBER; + +fragment NUMBER : DIGIT+; + +fragment LBRACKET : '('; +fragment RBRACKET : ')'; + +fragment ALPHA : ('a'..'z'|'A'..'Z'); +fragment DIGIT : ('0'..'9'); +fragment NONZERO_DIGIT : ('1'..'9'); + +fragment HEXDIGIT : (DIGIT | 'a'..'f' | 'A'..'F'); +fragment OCTDIGIT : ('0'..'7'); +fragment BINDIGIT : ('0' | '1'); + +fragment SHARP : '#'; + +fragment E : ('E'|'e'); +fragment H : ('H'|'h'); +fragment Q : ('Q'|'q'); +fragment B : ('B'|'b'); + +fragment SPACE : ' '; +fragment TAB : '\t'; + +fragment PLUS : '+'; +fragment MINUS : '-'; +fragment DOT : '.'; +fragment SLASH : '/'; +fragment SINGLE_QUOTE : '\''; +fragment DOUBLE_QUOTE : '"'; +fragment NON_SINGLE_QUOTE : ~SINGLE_QUOTE; +fragment NON_DOUBLE_QUOTE : ~DOUBLE_QUOTE; + + diff --git a/third_party/libscpi/src/units.c b/third_party/libscpi/src/units.c new file mode 100644 index 00000000..e68af418 --- /dev/null +++ b/third_party/libscpi/src/units.c @@ -0,0 +1,512 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file scpi_units.c + * @date Thu Nov 15 10:58:45 UTC 2012 + * + * @brief SCPI units + * + * + */ + +#include <string.h> +#include "scpi/parser.h" +#include "scpi/units.h" +#include "utils_private.h" +#include "scpi/utils.h" +#include "scpi/error.h" +#include "lexer_private.h" + + +/* + * multipliers IEEE 488.2-1992 tab 7-2 + * 1E18 EX + * 1E15 PE + * 1E12 T + * 1E9 G + * 1E6 MA (use M for OHM and HZ) + * 1E3 K + * 1E-3 M (disaalowed for OHM and HZ) + * 1E-6 U + * 1E-9 N + * 1E-12 P + * 1E-15 F + * 1E-18 A + */ + +/* + * units definition IEEE 488.2-1992 tab 7-1 + */ +const scpi_unit_def_t scpi_units_def[] = { +#if USE_UNITS_PARTICLES + /* Absorbet dose */ + {/* name */ "GY", /* unit */ SCPI_UNIT_GRAY, /* mult */ 1}, + + /* Activity of radionuclide */ + {/* name */ "BQ", /* unit */ SCPI_UNIT_BECQUEREL, /* mult */ 1}, + + /* Amount of substance */ + {/* name */ "MOL", /* unit */ SCPI_UNIT_MOLE, /* mult */ 1}, + + /* Dose equivalent */ + {/* name */ "NSV", /* unit */ SCPI_UNIT_SIEVERT, /* mult */ 1e-9}, + {/* name */ "USV", /* unit */ SCPI_UNIT_SIEVERT, /* mult */ 1e-6}, + {/* name */ "MSV", /* unit */ SCPI_UNIT_SIEVERT, /* mult */ 1e-3}, + {/* name */ "SV", /* unit */ SCPI_UNIT_SIEVERT, /* mult */ 1}, + {/* name */ "KSV", /* unit */ SCPI_UNIT_SIEVERT, /* mult */ 1e3}, + {/* name */ "MASV", /* unit */ SCPI_UNIT_SIEVERT, /* mult */ 1e6}, + + /* Energy */ + {/* name */ "EV", /* unit */ SCPI_UNIT_ELECTRONVOLT, /* mult */ 1}, + {/* name */ "KEV", /* unit */ SCPI_UNIT_ELECTRONVOLT, /* mult */ 1e3}, + {/* name */ "MAEV", /* unit */ SCPI_UNIT_ELECTRONVOLT, /* mult */ 1e6}, + {/* name */ "GEV", /* unit */ SCPI_UNIT_ELECTRONVOLT, /* mult */ 1e9}, + {/* name */ "TEV", /* unit */ SCPI_UNIT_ELECTRONVOLT, /* mult */ 1e12}, + + /* Mass */ + {/* name */ "U", /* unit */ SCPI_UNIT_ATOMIC_MASS, /* mult */ 1}, +#endif /* USE_UNITS_PARTICLES */ + +#if USE_UNITS_ANGLE + /* Angle */ + {/* name */ "DEG", /* unit */ SCPI_UNIT_DEGREE, /* mult */ 1}, + {/* name */ "GON", /* unit */ SCPI_UNIT_GRADE, /* mult */ 1}, + {/* name */ "MNT", /* unit */ SCPI_UNIT_DEGREE, /* mult */ 1. / 60.}, + {/* name */ "RAD", /* unit */ SCPI_UNIT_RADIAN, /* mult */ 1}, + {/* name */ "SEC", /* unit */ SCPI_UNIT_DEGREE, /* mult */ 1. / 3600.}, + {/* name */ "REV", /* unit */ SCPI_UNIT_REVOLUTION, /* mult */ 1}, + {/* name */ "RS", /* unit */ SCPI_UNIT_STERADIAN, /* mult */ 1}, +#endif /* USE_UNITS_ANGLE */ + +#if USE_UNITS_ELECTRIC + /* Electric - capacitance */ + {/* name */ "PF", /* unit */ SCPI_UNIT_FARAD, /* mult */ 1e-12}, + {/* name */ "NF", /* unit */ SCPI_UNIT_FARAD, /* mult */ 1e-9}, + {/* name */ "UF", /* unit */ SCPI_UNIT_FARAD, /* mult */ 1e-6}, + {/* name */ "MF", /* unit */ SCPI_UNIT_FARAD, /* mult */ 1e-3}, + {/* name */ "F", /* unit */ SCPI_UNIT_FARAD, /* mult */ 1}, + + /* Electric - current */ + {/* name */ "UA", /* unit */ SCPI_UNIT_AMPER, /* mult */ 1e-6}, + {/* name */ "MA", /* unit */ SCPI_UNIT_AMPER, /* mult */ 1e-3}, + {/* name */ "A", /* unit */ SCPI_UNIT_AMPER, /* mult */ 1}, + {/* name */ "KA", /* unit */ SCPI_UNIT_AMPER, /* mult */ 1e3}, + + /* Electric - potential */ + {/* name */ "UV", /* unit */ SCPI_UNIT_VOLT, /* mult */ 1e-6}, + {/* name */ "MV", /* unit */ SCPI_UNIT_VOLT, /* mult */ 1e-3}, + {/* name */ "V", /* unit */ SCPI_UNIT_VOLT, /* mult */ 1}, + {/* name */ "KV", /* unit */ SCPI_UNIT_VOLT, /* mult */ 1e3}, + + /* Electric - resistance */ + {/* name */ "OHM", /* unit */ SCPI_UNIT_OHM, /* mult */ 1}, + {/* name */ "KOHM", /* unit */ SCPI_UNIT_OHM, /* mult */ 1e3}, + {/* name */ "MOHM", /* unit */ SCPI_UNIT_OHM, /* mult */ 1e6}, + + /* Inductance */ + {/* name */ "UH", /* unit */ SCPI_UNIT_HENRY, /* mult */ 1e-6}, + {/* name */ "MH", /* unit */ SCPI_UNIT_HENRY, /* mult */ 1e-3}, + {/* name */ "H", /* unit */ SCPI_UNIT_HENRY, /* mult */ 1}, +#endif /* USE_UNITS_ELECTRIC */ + +#if USE_UNITS_ELECTRIC_CHARGE_CONDUCTANCE + /* Electric - charge */ + {/* name */ "C", /* unit */ SCPI_UNIT_COULOMB, /* mult */ 1}, + + /* Electric - conductance */ + {/* name */ "USIE", /* unit */ SCPI_UNIT_SIEMENS, /* mult */ 1e-6}, + {/* name */ "MSIE", /* unit */ SCPI_UNIT_SIEMENS, /* mult */ 1e-3}, + {/* name */ "SIE", /* unit */ SCPI_UNIT_SIEMENS, /* mult */ 1}, +#endif /* USE_UNITS_ELECTRIC_CHARGE_CONDUCTANCE */ + +#if USE_UNITS_ENERGY_FORCE_MASS + /* Energy */ + {/* name */ "J", /* unit */ SCPI_UNIT_JOULE, /* mult */ 1}, + {/* name */ "KJ", /* unit */ SCPI_UNIT_JOULE, /* mult */ 1e3}, + {/* name */ "MAJ", /* unit */ SCPI_UNIT_JOULE, /* mult */ 1e6}, + + /* Force */ + {/* name */ "N", /* unit */ SCPI_UNIT_NEWTON, /* mult */ 1}, + {/* name */ "KN", /* unit */ SCPI_UNIT_NEWTON, /* mult */ 1e3}, + + /* Pressure */ + {/* name */ "ATM", /* unit */ SCPI_UNIT_ATMOSPHERE, /* mult */ 1}, + {/* name */ "INHG", /* unit */ SCPI_UNIT_INCH_OF_MERCURY, /* mult */ 1}, + {/* name */ "MMHG", /* unit */ SCPI_UNIT_MM_OF_MERCURY, /* mult */ 1}, + + {/* name */ "TORR", /* unit */ SCPI_UNIT_TORT, /* mult */ 1}, + {/* name */ "BAR", /* unit */ SCPI_UNIT_BAR, /* mult */ 1}, + + {/* name */ "PAL", /* unit */ SCPI_UNIT_PASCAL, /* mult */ 1}, + {/* name */ "KPAL", /* unit */ SCPI_UNIT_PASCAL, /* mult */ 1e3}, + {/* name */ "MAPAL", /* unit */ SCPI_UNIT_PASCAL, /* mult */ 1e6}, + + /* Viscosity kinematic */ + {/* name */ "ST", /* unit */ SCPI_UNIT_STROKES, /* mult */ 1}, + + /* Viscosity dynamic */ + {/* name */ "P", /* unit */ SCPI_UNIT_POISE, /* mult */ 1}, + + /* Viscosity dynamic */ + {/* name */ "L", /* unit */ SCPI_UNIT_LITER, /* mult */ 1}, + + /* Mass */ + {/* name */ "MG", /* unit */ SCPI_UNIT_KILOGRAM, /* mult */ 1e-6}, + {/* name */ "G", /* unit */ SCPI_UNIT_KILOGRAM, /* mult */ 1e-3}, + {/* name */ "KG", /* unit */ SCPI_UNIT_KILOGRAM, /* mult */ 1}, + {/* name */ "TNE", /* unit */ SCPI_UNIT_KILOGRAM, /* mult */ 1000}, +#endif /* USE_UNITS_ENERGY_FORCE_MASS */ + +#if USE_UNITS_FREQUENCY + /* Frequency */ + {/* name */ "HZ", /* unit */ SCPI_UNIT_HERTZ, /* mult */ 1}, + {/* name */ "KHZ", /* unit */ SCPI_UNIT_HERTZ, /* mult */ 1e3}, + {/* name */ "MHZ", /* unit */ SCPI_UNIT_HERTZ, /* mult */ 1e6}, + {/* name */ "GHZ", /* unit */ SCPI_UNIT_HERTZ, /* mult */ 1e9}, +#endif /* USE_UNITS_FREQUENCY */ + +#if USE_UNITS_DISTANCE + /* Length */ + {/* name */ "ASU", /* unit */ SCPI_UNIT_ASTRONOMIC_UNIT, /* mult */ 1}, + {/* name */ "PRS", /* unit */ SCPI_UNIT_PARSEC, /* mult */ 1}, +#if USE_UNITS_IMPERIAL + {/* name */ "IN", /* unit */ SCPI_UNIT_INCH, /* mult */ 1}, + {/* name */ "FT", /* unit */ SCPI_UNIT_FOOT, /* mult */ 1}, + {/* name */ "MI", /* unit */ SCPI_UNIT_MILE, /* mult */ 1}, + {/* name */ "NAMI", /* unit */ SCPI_UNIT_NAUTICAL_MILE, /* mult */ 1}, +#endif /* USE_UNITS_IMPERIAL */ + + {/* name */ "NM", /* unit */ SCPI_UNIT_METER, /* mult */ 1e-9}, + {/* name */ "UM", /* unit */ SCPI_UNIT_METER, /* mult */ 1e-6}, + {/* name */ "MM", /* unit */ SCPI_UNIT_METER, /* mult */ 1e-3}, + {/* name */ "M", /* unit */ SCPI_UNIT_METER, /* mult */ 1}, + {/* name */ "KM", /* unit */ SCPI_UNIT_METER, /* mult */ 1e3}, +#endif /* USE_UNITS_DISTANCE */ + +#if USE_UNITS_LIGHT + /* Illuminance */ + {/* name */ "LX", /* unit */ SCPI_UNIT_LUX, /* mult */ 1}, + + /* Luminous flux */ + {/* name */ "LM", /* unit */ SCPI_UNIT_LUMEN, /* mult */ 1}, + + /* Luminous intensity */ + {/* name */ "CD", /* unit */ SCPI_UNIT_CANDELA, /* mult */ 1}, +#endif /* USE_UNITS_LIGHT */ + +#if USE_UNITS_MAGNETIC + /* Magnetic flux */ + {/* name */ "WB", /* unit */ SCPI_UNIT_WEBER, /* mult */ 1}, + + /* Magnetic induction */ + {/* name */ "NT", /* unit */ SCPI_UNIT_TESLA, /* mult */ 1e-9}, + {/* name */ "UT", /* unit */ SCPI_UNIT_TESLA, /* mult */ 1e-6}, + {/* name */ "MT", /* unit */ SCPI_UNIT_TESLA, /* mult */ 1e-3}, + {/* name */ "T", /* unit */ SCPI_UNIT_TESLA, /* mult */ 1}, +#endif /* USE_UNITS_MAGNETIC */ + +#if USE_UNITS_POWER + /* Power */ + {/* name */ "W", /* unit */ SCPI_UNIT_WATT, /* mult */ 1}, + {/* name */ "DBM", /* unit */ SCPI_UNIT_DBM, /* mult */ 1}, + {/* name */ "DBMW", /* unit */ SCPI_UNIT_DBM, /* mult */ 1}, +#endif /* USE_UNITS_POWER */ + +#if USE_UNITS_RATIO + /* Ratio */ + {/* name */ "DB", /* unit */ SCPI_UNIT_DECIBEL, /* mult */ 1}, + {/* name */ "PCT", /* unit */ SCPI_UNIT_UNITLESS, /* mult */ 1e-2}, + {/* name */ "PPM", /* unit */ SCPI_UNIT_UNITLESS, /* mult */ 1e-6}, +#endif /* USE_UNITS_RATIO */ + +#if USE_UNITS_TEMPERATURE + /* Temperature */ + {/* name */ "CEL", /* unit */ SCPI_UNIT_CELSIUS, /* mult */ 1}, +#if USE_UNITS_IMPERIAL + {/* name */ "FAR", /* unit */ SCPI_UNIT_FAHRENHEIT, /* mult */ 1}, +#endif /* USE_UNITS_IMPERIAL */ + {/* name */ "K", /* unit */ SCPI_UNIT_KELVIN, /* mult */ 1}, +#endif /* USE_UNITS_TEMPERATURE */ + +#if USE_UNITS_TIME + /* Time */ + {/* name */ "PS", /* unit */ SCPI_UNIT_SECOND, /* mult */ 1e-12}, + {/* name */ "NS", /* unit */ SCPI_UNIT_SECOND, /* mult */ 1e-9}, + {/* name */ "US", /* unit */ SCPI_UNIT_SECOND, /* mult */ 1e-6}, + {/* name */ "MS", /* unit */ SCPI_UNIT_SECOND, /* mult */ 1e-3}, + {/* name */ "S", /* unit */ SCPI_UNIT_SECOND, /* mult */ 1}, + {/* name */ "MIN", /* unit */ SCPI_UNIT_SECOND, /* mult */ 60}, + {/* name */ "HR", /* unit */ SCPI_UNIT_SECOND, /* mult */ 3600}, + {/* name */ "D", /* unit */ SCPI_UNIT_DAY, /* mult */ 1}, + {/* name */ "ANN", /* unit */ SCPI_UNIT_YEAR, /* mult */ 1}, +#endif /* USE_UNITS_TIME */ + + SCPI_UNITS_LIST_END, +}; + +/* + * Special number values definition + */ +const scpi_choice_def_t scpi_special_numbers_def[] = { + {/* name */ "MINimum", /* type */ SCPI_NUM_MIN}, + {/* name */ "MAXimum", /* type */ SCPI_NUM_MAX}, + {/* name */ "DEFault", /* type */ SCPI_NUM_DEF}, + {/* name */ "UP", /* type */ SCPI_NUM_UP}, + {/* name */ "DOWN", /* type */ SCPI_NUM_DOWN}, + {/* name */ "NAN", /* type */ SCPI_NUM_NAN}, + {/* name */ "INFinity", /* type */ SCPI_NUM_INF}, + {/* name */ "NINF", /* type */ SCPI_NUM_NINF}, + {/* name */ "AUTO", /* type */ SCPI_NUM_AUTO}, + SCPI_CHOICE_LIST_END, +}; + +/** + * Convert string describing unit to its representation + * @param units units patterns + * @param unit text representation of unknown unit + * @param len length of text representation + * @return pointer of related unit definition or NULL + */ +static const scpi_unit_def_t * translateUnit(const scpi_unit_def_t * units, const char * unit, size_t len) { + int i; + + if (units == NULL) { + return NULL; + } + + for (i = 0; units[i].name != NULL; i++) { + if (compareStr(unit, len, units[i].name, strlen(units[i].name))) { + return &units[i]; + } + } + + return NULL; +} + +/** + * Convert unit definition to string + * @param units units definitions (patterns) + * @param unit type of unit + * @return string representation of unit + */ +static const char * translateUnitInverse(const scpi_unit_def_t * units, const scpi_unit_t unit) { + int i; + + if (units == NULL) { + return NULL; + } + + for (i = 0; units[i].name != NULL; i++) { + if ((units[i].unit == unit) && (units[i].mult == 1)) { + return units[i].name; + } + } + + return NULL; +} + +/** + * Transform number to base units + * @param context + * @param unit text representation of unit + * @param len length of text representation + * @param value preparsed numeric value + * @return TRUE if value parameter was converted to base units + */ +static scpi_bool_t transformNumber(scpi_t * context, const char * unit, size_t len, scpi_number_t * value) { + size_t s; + const scpi_unit_def_t * unitDef; + s = skipWhitespace(unit, len); + + if (s == len) { + value->unit = SCPI_UNIT_NONE; + return TRUE; + } + + unitDef = translateUnit(context->units, unit + s, len - s); + + if (unitDef == NULL) { + SCPI_ErrorPush(context, SCPI_ERROR_INVALID_SUFFIX); + return FALSE; + } + + value->content.value *= unitDef->mult; + value->unit = unitDef->unit; + + return TRUE; +} + +/** + * Parse parameter as number, number with unit or special value (min, max, default, ...) + * @param context + * @param value return value + * @param mandatory if the parameter is mandatory + * @return + */ +scpi_bool_t SCPI_ParamNumber(scpi_t * context, const scpi_choice_def_t * special, scpi_number_t * value, scpi_bool_t mandatory) { + scpi_token_t token; + lex_state_t state; + scpi_parameter_t param; + scpi_bool_t result; + int32_t tag; + + if (!value) { + SCPI_ErrorPush(context, SCPI_ERROR_SYSTEM_ERROR); + return FALSE; + } + + result = SCPI_Parameter(context, ¶m, mandatory); + + if (!result) { + return result; + } + + state.buffer = param.ptr; + state.pos = state.buffer; + state.len = param.len; + + switch (param.type) { + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + case SCPI_TOKEN_HEXNUM: + case SCPI_TOKEN_OCTNUM: + case SCPI_TOKEN_BINNUM: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + case SCPI_TOKEN_PROGRAM_MNEMONIC: + value->unit = SCPI_UNIT_NONE; + value->special = FALSE; + result = TRUE; + break; + default: + break; + } + + switch (param.type) { + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + case SCPI_TOKEN_PROGRAM_MNEMONIC: + value->base = 10; + break; + case SCPI_TOKEN_BINNUM: + value->base = 2; + break; + case SCPI_TOKEN_HEXNUM: + value->base = 16; + break; + case SCPI_TOKEN_OCTNUM: + value->base = 8; + break; + default: + break; + } + + switch (param.type) { + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA: + SCPI_ParamToDouble(context, ¶m, &(value->content.value)); + break; + case SCPI_TOKEN_HEXNUM: + SCPI_ParamToDouble(context, ¶m, &(value->content.value)); + break; + case SCPI_TOKEN_OCTNUM: + SCPI_ParamToDouble(context, ¶m, &(value->content.value)); + break; + case SCPI_TOKEN_BINNUM: + SCPI_ParamToDouble(context, ¶m, &(value->content.value)); + break; + case SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX: + scpiLex_DecimalNumericProgramData(&state, &token); + scpiLex_WhiteSpace(&state, &token); + scpiLex_SuffixProgramData(&state, &token); + + SCPI_ParamToDouble(context, ¶m, &(value->content.value)); + + result = transformNumber(context, token.ptr, token.len, value); + break; + case SCPI_TOKEN_PROGRAM_MNEMONIC: + scpiLex_WhiteSpace(&state, &token); + scpiLex_CharacterProgramData(&state, &token); + + /* convert string to special number type */ + result = SCPI_ParamToChoice(context, &token, special, &tag); + + value->special = TRUE; + value->content.tag = tag; + + break; + default: + result = FALSE; + } + + return result; +} + +/** + * Convert scpi_number_t to string + * @param context + * @param value number value + * @param str target string + * @param len max length of string including null-character termination + * @return number of chars written to string + */ +size_t SCPI_NumberToStr(scpi_t * context, const scpi_choice_def_t * special, scpi_number_t * value, char * str, size_t len) { + const char * type; + const char * unit; + size_t result; + + if (!value || !str || len==0) { + return 0; + } + + if (value->special) { + if (SCPI_ChoiceToName(special, value->content.tag, &type)) { + strncpy(str, type, len); + result = SCPIDEFINE_strnlen(str, len - 1); + str[result] = '\0'; + return result; + } else { + str[0] = '\0'; + return 0; + } + } + + result = SCPI_DoubleToStr(value->content.value, str, len); + + if (result + 1 < len) { + unit = translateUnitInverse(context->units, value->unit); + + if (unit) { + strncat(str, " ", len - result); + if (result + 2 < len) { + strncat(str, unit, len - result - 1); + } + result = strlen(str); + } + } + + return result; +} diff --git a/third_party/libscpi/src/utils.c b/third_party/libscpi/src/utils.c new file mode 100644 index 00000000..328f8be7 --- /dev/null +++ b/third_party/libscpi/src/utils.c @@ -0,0 +1,1142 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer, Richard.hmm + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file scpi_utils.c + * @date Thu Nov 15 10:58:45 UTC 2012 + * + * @brief Conversion routines and string manipulation routines + * + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> + +#include "utils_private.h" +#include "scpi/utils.h" + +static size_t patternSeparatorShortPos(const char * pattern, size_t len); +static size_t patternSeparatorPos(const char * pattern, size_t len); +static size_t cmdSeparatorPos(const char * cmd, size_t len); + +/** + * Find the first occurrence in str of a character in set. + * @param str + * @param size + * @param set + * @return + */ +char * strnpbrk(const char *str, size_t size, const char *set) { + const char *scanp; + long c, sc; + const char * strend = str + size; + + while ((strend != str) && ((c = *str++) != 0)) { + for (scanp = set; (sc = *scanp++) != '\0';) + if (sc == c) + return ((char *) (str - 1)); + } + return (NULL); +} + +/** + * Converts signed/unsigned 32 bit integer value to string in specific base + * @param val integer value + * @param str converted textual representation + * @param len string buffer length + * @param base output base + * @param sign + * @return number of bytes written to str (without '\0') + */ +size_t UInt32ToStrBaseSign(uint32_t val, char * str, size_t len, int8_t base, scpi_bool_t sign) { + const char digits[] = "0123456789ABCDEF"; + +#define ADD_CHAR(c) if (pos < len) str[pos++] = (c) + uint32_t x = 0; + int_fast8_t digit; + size_t pos = 0; + uint32_t uval = val; + + if (uval == 0) { + ADD_CHAR('0'); + } else { + + switch (base) { + case 2: + x = 0x80000000L; + break; + case 8: + x = 0x40000000L; + break; + default: + case 10: + base = 10; + x = 1000000000L; + break; + case 16: + x = 0x10000000L; + break; + } + + /* add sign for numbers in base 10 */ + if (sign && ((int32_t) val < 0) && (base == 10)) { + uval = -val; + ADD_CHAR('-'); + } + + /* remove leading zeros */ + while ((uval / x) == 0) { + x /= base; + } + + do { + digit = (uint8_t) (uval / x); + ADD_CHAR(digits[digit]); + uval -= digit * x; + x /= base; + } while (x && (pos < len)); + } + + if (pos < len) str[pos] = 0; + return pos; +#undef ADD_CHAR +} + +/** + * Converts signed 32 bit integer value to string + * @param val integer value + * @param str converted textual representation + * @param len string buffer length + * @return number of bytes written to str (without '\0') + */ +size_t SCPI_Int32ToStr(int32_t val, char * str, size_t len) { + return UInt32ToStrBaseSign((uint32_t) val, str, len, 10, TRUE); +} + +/** + * Converts unsigned 32 bit integer value to string in specific base + * @param val integer value + * @param str converted textual representation + * @param len string buffer length + * @param base output base + * @return number of bytes written to str (without '\0') + */ +size_t SCPI_UInt32ToStrBase(uint32_t val, char * str, size_t len, int8_t base) { + return UInt32ToStrBaseSign(val, str, len, base, FALSE); +} + +/** + * Converts signed/unsigned 64 bit integer value to string in specific base + * @param val integer value + * @param str converted textual representation + * @param len string buffer length + * @param base output base + * @param sign + * @return number of bytes written to str (without '\0') + */ +size_t UInt64ToStrBaseSign(uint64_t val, char * str, size_t len, int8_t base, scpi_bool_t sign) { + const char digits[] = "0123456789ABCDEF"; + +#define ADD_CHAR(c) if (pos < len) str[pos++] = (c) + uint64_t x = 0; + int_fast8_t digit; + size_t pos = 0; + uint64_t uval = val; + + if (uval == 0) { + ADD_CHAR('0'); + } else { + + switch (base) { + case 2: + x = 0x8000000000000000ULL; + break; + case 8: + x = 0x8000000000000000ULL; + break; + default: + case 10: + x = 10000000000000000000ULL; + base = 10; + break; + case 16: + x = 0x1000000000000000ULL; + break; + } + + /* add sign for numbers in base 10 */ + if (sign && ((int64_t) val < 0) && (base == 10)) { + uval = -val; + ADD_CHAR('-'); + } + + /* remove leading zeros */ + while ((uval / x) == 0) { + x /= base; + } + + do { + digit = (uint8_t) (uval / x); + ADD_CHAR(digits[digit]); + uval -= digit * x; + x /= base; + } while (x && (pos < len)); + } + + if (pos < len) str[pos] = 0; + return pos; +#undef ADD_CHAR +} + +/** + * Converts signed 64 bit integer value to string + * @param val integer value + * @param str converted textual representation + * @param len string buffer length + * @return number of bytes written to str (without '\0') + */ +size_t SCPI_Int64ToStr(int64_t val, char * str, size_t len) { + return UInt64ToStrBaseSign((uint64_t) val, str, len, 10, TRUE); +} + +/** + * Converts signed/unsigned 64 bit integer value to string in specific base + * @param val integer value + * @param str converted textual representation + * @param len string buffer length + * @param base output base + * @return number of bytes written to str (without '\0') + */ +size_t SCPI_UInt64ToStrBase(uint64_t val, char * str, size_t len, int8_t base) { + return UInt64ToStrBaseSign(val, str, len, base, FALSE); +} + +/** + * Converts float (32 bit) value to string + * @param val long value + * @param str converted textual representation + * @param len string buffer length + * @return number of bytes written to str (without '\0') + */ +size_t SCPI_FloatToStr(float val, char * str, size_t len) { + SCPIDEFINE_floatToStr(val, str, len); + return strlen(str); +} + +/** + * Converts double (64 bit) value to string + * @param val double value + * @param str converted textual representation + * @param len string buffer length + * @return number of bytes written to str (without '\0') + */ +size_t SCPI_DoubleToStr(double val, char * str, size_t len) { + SCPIDEFINE_doubleToStr(val, str, len); + return strlen(str); +} + +/** + * Converts string to signed 32bit integer representation + * @param str string value + * @param val 32bit integer result + * @return number of bytes used in string + */ +size_t strBaseToInt32(const char * str, int32_t * val, int8_t base) { + char * endptr; + *val = strtol(str, &endptr, base); + return endptr - str; +} + +/** + * Converts string to unsigned 32bit integer representation + * @param str string value + * @param val 32bit integer result + * @return number of bytes used in string + */ +size_t strBaseToUInt32(const char * str, uint32_t * val, int8_t base) { + char * endptr; + *val = strtoul(str, &endptr, base); + return endptr - str; +} + +/** + * Converts string to signed 64bit integer representation + * @param str string value + * @param val 64bit integer result + * @return number of bytes used in string + */ +size_t strBaseToInt64(const char * str, int64_t * val, int8_t base) { + char * endptr; + *val = SCPIDEFINE_strtoll(str, &endptr, base); + return endptr - str; +} + +/** + * Converts string to unsigned 64bit integer representation + * @param str string value + * @param val 64bit integer result + * @return number of bytes used in string + */ +size_t strBaseToUInt64(const char * str, uint64_t * val, int8_t base) { + char * endptr; + *val = SCPIDEFINE_strtoull(str, &endptr, base); + return endptr - str; +} + +/** + * Converts string to float (32 bit) representation + * @param str string value + * @param val float result + * @return number of bytes used in string + */ +size_t strToFloat(const char * str, float * val) { + char * endptr; + *val = SCPIDEFINE_strtof(str, &endptr); + return endptr - str; +} + +/** + * Converts string to double (64 bit) representation + * @param str string value + * @param val double result + * @return number of bytes used in string + */ +size_t strToDouble(const char * str, double * val) { + char * endptr; + *val = strtod(str, &endptr); + return endptr - str; +} + +/** + * Compare two strings with exact length + * @param str1 + * @param len1 + * @param str2 + * @param len2 + * @return TRUE if len1==len2 and "len" characters of both strings are equal + */ +scpi_bool_t compareStr(const char * str1, size_t len1, const char * str2, size_t len2) { + if (len1 != len2) { + return FALSE; + } + + if (SCPIDEFINE_strncasecmp(str1, str2, len2) == 0) { + return TRUE; + } + + return FALSE; +} + +/** + * Compare two strings, one be longer but may contains only numbers in that section + * @param str1 + * @param len1 + * @param str2 + * @param len2 + * @return TRUE if strings match + */ +scpi_bool_t compareStrAndNum(const char * str1, size_t len1, const char * str2, size_t len2, int32_t * num) { + scpi_bool_t result = FALSE; + size_t i; + + if (len2 < len1) { + return FALSE; + } + + if (SCPIDEFINE_strncasecmp(str1, str2, len1) == 0) { + result = TRUE; + + if (num) { + if (len1 == len2) { + /* *num = 1; */ + } else { + int32_t tmpNum; + i = len1 + strBaseToInt32(str2 + len1, &tmpNum, 10); + if (i != len2) { + result = FALSE; + } else { + *num = tmpNum; + } + } + } else { + for (i = len1; i < len2; i++) { + if (!isdigit((int) str2[i])) { + result = FALSE; + break; + } + } + } + } + + return result; +} + +/** + * Count white spaces from the beggining + * @param cmd - command + * @param len - max search length + * @return number of white spaces + */ +size_t skipWhitespace(const char * cmd, size_t len) { + size_t i; + for (i = 0; i < len; i++) { + if (!isspace((unsigned char) cmd[i])) { + return i; + } + } + return len; +} + +/** + * Pattern is composed from upper case an lower case letters. This function + * search the first lowercase letter + * @param pattern + * @param len - max search length + * @return position of separator or len + */ +static size_t patternSeparatorShortPos(const char * pattern, size_t len) { + size_t i; + for (i = 0; (i < len) && pattern[i]; i++) { + if (islower((unsigned char) pattern[i])) { + return i; + } + } + return i; +} + +/** + * Find pattern separator position + * @param pattern + * @param len - max search length + * @return position of separator or len + */ +static 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 + */ +static 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; +} + +/** + * Match pattern and str. Pattern is in format UPPERCASElowercase + * @param pattern + * @param pattern_len + * @param str + * @param str_len + * @return + */ +scpi_bool_t matchPattern(const char * pattern, size_t pattern_len, const char * str, size_t str_len, int32_t * num) { + int pattern_sep_pos_short; + + if ((pattern_len > 0) && pattern[pattern_len - 1] == '#') { + size_t new_pattern_len = pattern_len - 1; + + pattern_sep_pos_short = patternSeparatorShortPos(pattern, new_pattern_len); + + return compareStrAndNum(pattern, new_pattern_len, str, str_len, num) || + compareStrAndNum(pattern, pattern_sep_pos_short, str, str_len, num); + } else { + + pattern_sep_pos_short = patternSeparatorShortPos(pattern, pattern_len); + + return compareStr(pattern, pattern_len, str, str_len) || + compareStr(pattern, pattern_sep_pos_short, str, str_len); + } +} + +/** + * Compare pattern and command + * @param pattern eg. [:MEASure]:VOLTage:DC? + * @param cmd - command + * @param len - max search length + * @return TRUE if pattern matches, FALSE otherwise + */ +scpi_bool_t matchCommand(const char * pattern, const char * cmd, size_t len, int32_t *numbers, size_t numbers_len, int32_t default_value) { +#define SKIP_PATTERN(n) do {pattern_ptr += (n); pattern_len -= (n);} while(0) +#define SKIP_CMD(n) do {cmd_ptr += (n); cmd_len -= (n);} while(0) + + scpi_bool_t result = FALSE; + int brackets = 0; + int cmd_sep_pos = 0; + + size_t numbers_idx = 0; + int32_t *number_ptr = NULL; + + const char * pattern_ptr = pattern; + int pattern_len = strlen(pattern); + + const char * cmd_ptr = cmd; + size_t cmd_len = SCPIDEFINE_strnlen(cmd, len); + + /* both commands are query commands? */ + if (pattern_ptr[pattern_len - 1] == '?') { + if (cmd_ptr[cmd_len - 1] == '?') { + cmd_len -= 1; + pattern_len -= 1; + } else { + return FALSE; + } + } + + /* now support optional keywords in pattern style, e.g. [:MEASure]:VOLTage:DC? */ + if (pattern_ptr[0] == '[') { /* skip first '[' */ + SKIP_PATTERN(1); + brackets++; + } + if (pattern_ptr[0] == ':') { /* skip first ':' */ + SKIP_PATTERN(1); + } + + if (cmd_ptr[0] == ':') { + /* handle errornouse ":*IDN?" */ + if (cmd_len >= 2) { + if (cmd_ptr[1] != '*') { + SKIP_CMD(1); + } else { + return FALSE; + } + } + } + + while (1) { + int pattern_sep_pos = patternSeparatorPos(pattern_ptr, pattern_len); + + cmd_sep_pos = cmdSeparatorPos(cmd_ptr, cmd_len); + + if ((pattern_sep_pos > 0) && pattern_ptr[pattern_sep_pos - 1] == '#') { + if (numbers && (numbers_idx < numbers_len)) { + number_ptr = numbers + numbers_idx; + *number_ptr = default_value; /* default value */ + } else { + number_ptr = NULL; + } + numbers_idx++; + } else { + number_ptr = NULL; + } + + if (matchPattern(pattern_ptr, pattern_sep_pos, cmd_ptr, cmd_sep_pos, number_ptr)) { + SKIP_PATTERN(pattern_sep_pos); + SKIP_CMD(cmd_sep_pos); + result = TRUE; + + /* command is complete */ + if ((pattern_len == 0) && (cmd_len == 0)) { + break; + } + + /* pattern complete, but command not */ + if ((pattern_len == 0) && (cmd_len > 0)) { + result = FALSE; + break; + } + + /* command complete, but pattern not */ + if (cmd_len == 0) { + /* verify all subsequent pattern parts are also optional */ + while (pattern_len) { + pattern_sep_pos = patternSeparatorPos(pattern_ptr, pattern_len); + switch (pattern_ptr[pattern_sep_pos]) { + case '[': + brackets++; + break; + case ']': + brackets--; + break; + default: + break; + } + SKIP_PATTERN(pattern_sep_pos + 1); + if (brackets == 0) { + if ((pattern_len > 0) && (pattern_ptr[0] == '[')) { + continue; + } else { + break; + } + } + } + if (pattern_len != 0) { + result = FALSE; + } + break; /* exist optional keyword, command is complete */ + } + + /* both command and patter contains command separator at this position */ + if ((pattern_len > 0) + && ((pattern_ptr[0] == cmd_ptr[0]) + && (pattern_ptr[0] == ':'))) { + SKIP_PATTERN(1); + SKIP_CMD(1); + } else if ((pattern_len > 1) + && (pattern_ptr[1] == cmd_ptr[0]) + && (pattern_ptr[0] == '[') + && (pattern_ptr[1] == ':')) { + SKIP_PATTERN(2); /* for skip '[' in "[:" */ + SKIP_CMD(1); + brackets++; + } else if ((pattern_len > 1) + && (pattern_ptr[1] == cmd_ptr[0]) + && (pattern_ptr[0] == ']') + && (pattern_ptr[1] == ':')) { + SKIP_PATTERN(2); /* for skip ']' in "]:" */ + SKIP_CMD(1); + brackets--; + } else if ((pattern_len > 2) + && (pattern_ptr[2] == cmd_ptr[0]) + && (pattern_ptr[0] == ']') + && (pattern_ptr[1] == '[') + && (pattern_ptr[2] == ':')) { + SKIP_PATTERN(3); /* for skip '][' in "][:" */ + SKIP_CMD(1); + /* brackets++; */ + /* brackets--; */ + } else { + result = FALSE; + break; + } + } else { + SKIP_PATTERN(pattern_sep_pos); + if ((pattern_ptr[0] == ']') && (pattern_ptr[1] == ':')) { + SKIP_PATTERN(2); /* for skip ']' in "]:" , pattern_ptr continue, while cmd_ptr remain unchanged */ + brackets--; + } else if ((pattern_len > 2) && (pattern_ptr[0] == ']') + && (pattern_ptr[1] == '[') + && (pattern_ptr[2] == ':')) { + SKIP_PATTERN(3); /* for skip ']' in "][:" , pattern_ptr continue, while cmd_ptr remain unchanged */ + /* brackets++; */ + /* brackets--; */ + } else { + result = FALSE; + break; + } + } + } + + return result; +#undef SKIP_PATTERN +#undef SKIP_CMD +} + +/** + * Compose command from previous command anc current command + * + * @param prev pointer to previous command + * @param current pointer of current command + * + * prev and current should be in the same memory buffer + */ +scpi_bool_t composeCompoundCommand(const scpi_token_t * prev, scpi_token_t * current) { + size_t i; + + /* Invalid input */ + if (current == NULL || current->ptr == NULL || current->len == 0) + return FALSE; + + /* no previous command - nothing to do*/ + if (prev->ptr == NULL || prev->len == 0) + return TRUE; + + /* Common command or command root - nothing to do */ + if (current->ptr[0] == '*' || current->ptr[0] == ':') + return TRUE; + + /* Previsou command was common command - nothing to do */ + if (prev->ptr[0] == '*') + return TRUE; + + /* Find last occurence of ':' */ + for (i = prev->len; i > 0; i--) { + if (prev->ptr[i - 1] == ':') { + break; + } + } + + /* Previous command was simple command - nothing to do*/ + if (i == 0) + return TRUE; + + current->ptr -= i; + current->len += i; + memmove(current->ptr, prev->ptr, i); + return TRUE; +} + + + +#if !HAVE_STRNLEN +/* use FreeBSD strnlen */ + +/*- + * Copyright (c) 2009 David Schultz <[email protected]> + * All rights reserved. + */ +size_t +BSD_strnlen(const char *s, size_t maxlen) { + size_t len; + + for (len = 0; len < maxlen; len++, s++) { + if (!*s) + break; + } + return (len); +} +#endif + +#if !HAVE_STRNCASECMP && !HAVE_STRNICMP + +int OUR_strncasecmp(const char *s1, const char *s2, size_t n) { + unsigned char c1, c2; + + for (; n != 0; n--) { + c1 = tolower((unsigned char) *s1++); + c2 = tolower((unsigned char) *s2++); + if (c1 != c2) { + return c1 - c2; + } + if (c1 == '\0') { + return 0; + } + } + return 0; +} +#endif + +#if USE_MEMORY_ALLOCATION_FREE && !HAVE_STRNDUP +char *OUR_strndup(const char *s, size_t n) { + size_t len = SCPIDEFINE_strnlen(s, n); + char * result = malloc(len + 1); + if (!result) { + return NULL; + } + memcpy(result, s, len); + result[len] = '\0'; + return result; +} +#endif + +#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION && !USE_MEMORY_ALLOCATION_FREE + +/** + * Initialize heap structure + * @param heap - pointer to manual allocated heap buffer + * @param error_info_heap - buffer for the heap + * @param error_info_heap_length - length of the heap + */ +void scpiheap_init(scpi_error_info_heap_t * heap, char * error_info_heap, size_t error_info_heap_length) +{ + heap->data = error_info_heap; + heap->wr = 0; + heap->size = error_info_heap_length; + heap->count = heap->size; + memset(heap->data, 0, heap->size); +} + +/** + * Duplicate string if "strdup" ("malloc/free") not supported on system. + * Allocate space in heap if it possible + * + * @param heap - pointer to manual allocated heap buffer + * @param s - current pointer of duplication string + * @return - pointer of duplicated string or NULL, if duplicate is not possible. + */ +char * scpiheap_strndup(scpi_error_info_heap_t * heap, const char *s, size_t n) { + if (!s || !heap || !heap->size) { + return NULL; + } + + if (heap->data[heap->wr] != '\0') { + return NULL; + } + + if (*s == '\0') { + return NULL; + } + + size_t len = SCPIDEFINE_strnlen(s, n) + 1; /* additional '\0' at end */ + if (len > heap->count) { + return NULL; + } + const char * ptrs = s; + char * head = &heap->data[heap->wr]; + size_t rem = heap->size - (&heap->data[heap->wr] - heap->data); + + if (len >= rem) { + memcpy(&heap->data[heap->wr], s, rem); + len = len - rem; + ptrs += rem; + heap->wr = 0; + heap->count -= rem; + } + + memcpy(&heap->data[heap->wr], ptrs, len); + heap->wr += len; + heap->count -= len; + + /* ensure '\0' a the end */ + if (heap->wr > 0) { + heap->data[heap->wr - 1] = '\0'; + } else { + heap->data[heap->size - 1] = '\0'; + } + return head; +} + +/** + * Return pointers and lengths two parts of string in the circular buffer from heap + * + * @param heap - pointer to manual allocated heap buffer + * @param s - pointer of duplicate string. + * @return len1 - lenght of first part of string. + * @return s2 - pointer of second part of string, if string splited . + * @return len2 - lenght of second part of string. + */ +scpi_bool_t scpiheap_get_parts(scpi_error_info_heap_t * heap, const char * s, size_t * len1, const char ** s2, size_t * len2) { + if (!heap || !s || !len1 || !s2 || !len2) { + return FALSE; + } + + if (*s == '\0') { + return FALSE; + } + + *len1 = 0; + size_t rem = heap->size - (s - heap->data); + *len1 = strnlen(s, rem); + + if (&s[*len1 - 1] == &heap->data[heap->size - 1]) { + *s2 = heap->data; + *len2 = strnlen(*s2, heap->size); + } else { + *s2 = NULL; + *len2 = 0; + } + return TRUE; +} + +/** + * Frees space in heap, if "malloc/free" not supported on system, or nothing. + * + * @param heap - pointer to manual allocated heap buffer + * @param s - pointer of duplicate string + * @param rollback - backward write pointer in heap + */ +void scpiheap_free(scpi_error_info_heap_t * heap, char * s, scpi_bool_t rollback) { + + if (!s) return; + + char * data_add; + size_t len[2]; + + if (!scpiheap_get_parts(heap, s, &len[0], (const char **)&data_add, &len[1])) return; + + if (data_add) { + len[1]++; + memset(data_add, 0, len[1]); + heap->count += len[1]; + } else { + len[0]++; + } + memset(s, 0, len[0]); + heap->count += len[0]; + if (heap->count == heap->size) { + heap->wr = 0; + return; + } + if (rollback) { + size_t rb = len[0] + len[1]; + if (rb > heap->wr) { + heap->wr += heap->size; + } + heap->wr -= rb; + } +} + +#endif + +/* + * Floating point to string conversion routines + * + * Copyright (C) 2002 Michael Ringgaard. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +static char *scpi_ecvt(double arg, int ndigits, int *decpt, int *sign, char *buf, size_t bufsize) { + int r1, r2; + double fi, fj; + int w1, w2; + + if (ndigits < 0) ndigits = 0; + if (ndigits >= (int) (bufsize - 1)) ndigits = bufsize - 2; + + r2 = 0; + *sign = 0; + w1 = 0; + if (arg < 0) { + *sign = 1; + arg = -arg; + } + frexp(arg, &r1); + arg = modf(arg, &fi); + + if (fi != 0) { + r1 = r1 * 308 / 1024 - ndigits; + w2 = bufsize; + while (r1 > 0) { + fj = modf(fi / 10, &fi); + r2++; + r1--; + } + while (fi != 0) { + fj = modf(fi / 10, &fi); + buf[--w2] = (int) ((fj + .03) * 10) + '0'; + r2++; + } + while (w2 < (int) bufsize) buf[w1++] = buf[w2++]; + } else if (arg > 0) { + while ((fj = arg * 10) < 1) { + arg = fj; + r2--; + } + } + w2 = ndigits; + *decpt = r2; + if (w2 < 0) { + buf[0] = '\0'; + return buf; + } + while (w1 <= w2 && w1 < (int) bufsize) { + arg *= 10; + arg = modf(arg, &fj); + buf[w1++] = (int) fj + '0'; + } + if (w2 >= (int) bufsize) { + buf[bufsize - 1] = '\0'; + return buf; + } + w1 = w2; + buf[w2] += 5; + while (buf[w2] > '9') { + buf[w2] = '0'; + if (w2 > 0) { + ++buf[--w2]; + } else { + buf[w2] = '1'; + (*decpt)++; + } + } + buf[w1] = '\0'; + return buf; +} + +#define SCPI_DTOSTRE_BUFFER_SIZE 32 + +char * SCPI_dtostre(double __val, char * __s, size_t __ssize, unsigned char __prec, unsigned char __flags) { + char buffer[SCPI_DTOSTRE_BUFFER_SIZE]; + + int sign = SCPIDEFINE_signbit(__val); + char * s = buffer; + int decpt; + if (sign) { + __val = -__val; + s[0] = '-'; + s++; + } else if (!SCPIDEFINE_isnan(__val)) { + if (SCPI_DTOSTRE_PLUS_SIGN & __flags) { + s[0] = '+'; + s++; + } else if (SCPI_DTOSTRE_ALWAYS_SIGN & __flags) { + s[0] = ' '; + s++; + } + } + + if (!SCPIDEFINE_isfinite(__val)) { + if (SCPIDEFINE_isnan(__val)) { + strcpy(s, (__flags & SCPI_DTOSTRE_UPPERCASE) ? "NAN" : "nan"); + } else { + strcpy(s, (__flags & SCPI_DTOSTRE_UPPERCASE) ? "INF" : "inf"); + } + strncpy(__s, buffer, __ssize); + __s[__ssize - 1] = '\0'; + return __s; + } + + scpi_ecvt(__val, __prec, &decpt, &sign, s, SCPI_DTOSTRE_BUFFER_SIZE - 1); + if (decpt > 1 && decpt <= __prec) { + memmove(s + decpt + 1, s + decpt, __prec + 1 - decpt); + s[decpt] = '.'; + decpt = 0; + } else if (decpt > -4 && decpt <= 0) { + decpt = -decpt + 1; + memmove(s + decpt + 1, s, __prec + 1); + memset(s, '0', decpt + 1); + s[1] = '.'; + decpt = 0; + } else { + memmove(s + 2, s + 1, __prec + 1); + s[1] = '.'; + decpt--; + } + + s = &s[__prec]; + while (s[0] == '0') { + s[0] = 0; + s--; + } + if (s[0] == '.') { + s[0] = 0; + s--; + } + + if (decpt != 0) { + s++; + s[0] = 'e'; + s++; + if (decpt != 0) { + if (decpt > 0) { + s[0] = '+'; + } + if (decpt < 0) { + s[0] = '-'; + decpt = -decpt; + } + s++; + } + UInt32ToStrBaseSign(decpt, s, 5, 10, 0); + if (s[1] == 0) { + s[2] = s[1]; + s[1] = s[0]; + s[0] = '0'; + } + } + + strncpy(__s, buffer, __ssize); + __s[__ssize - 1] = '\0'; + return __s; +} + +/** + * Get native CPU endiannes + * @return + */ +scpi_array_format_t SCPI_GetNativeFormat(void) { + + union { + uint32_t i; + char c[4]; + } bint = {0x01020304}; + + return bint.c[0] == 1 ? SCPI_FORMAT_BIGENDIAN : SCPI_FORMAT_LITTLEENDIAN; +} + +/** + * Swap 16bit number + * @param val + * @return + */ +uint16_t SCPI_Swap16(uint16_t val) { + return ((val & 0x00FF) << 8) | + ((val & 0xFF00) >> 8); +} + +/** + * Swap 32bit number + * @param val + * @return + */ +uint32_t SCPI_Swap32(uint32_t val) { + return ((val & 0x000000FFul) << 24) | + ((val & 0x0000FF00ul) << 8) | + ((val & 0x00FF0000ul) >> 8) | + ((val & 0xFF000000ul) >> 24); +} + +/** + * Swap 64bit number + * @param val + * @return + */ +uint64_t SCPI_Swap64(uint64_t val) { + return ((val & 0x00000000000000FFull) << 56) | + ((val & 0x000000000000FF00ull) << 40) | + ((val & 0x0000000000FF0000ull) << 24) | + ((val & 0x00000000FF000000ull) << 8) | + ((val & 0x000000FF00000000ull) >> 8) | + ((val & 0x0000FF0000000000ull) >> 24) | + ((val & 0x00FF000000000000ull) >> 40) | + ((val & 0xFF00000000000000ull) >> 56); +} diff --git a/third_party/libscpi/src/utils_private.h b/third_party/libscpi/src/utils_private.h new file mode 100644 index 00000000..6ae1616a --- /dev/null +++ b/third_party/libscpi/src/utils_private.h @@ -0,0 +1,128 @@ +/*- + * BSD 2-Clause License + * + * Copyright (c) 2012-2018, Jan Breuer + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file scpi_utils.h + * @date Thu Nov 15 10:58:45 UTC 2012 + * + * @brief Conversion routines and string manipulation routines + * + * + */ + +#ifndef SCPI_UTILS_PRIVATE_H +#define SCPI_UTILS_PRIVATE_H + +#include <stdint.h> +#include "scpi/config.h" +#include "scpi/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define LOCAL __attribute__((visibility ("hidden"))) +#else +#define LOCAL +#endif + + char * strnpbrk(const char *str, size_t size, const char *set) LOCAL; + scpi_bool_t compareStr(const char * str1, size_t len1, const char * str2, size_t len2) LOCAL; + scpi_bool_t compareStrAndNum(const char * str1, size_t len1, const char * str2, size_t len2, int32_t * num) LOCAL; + size_t UInt32ToStrBaseSign(uint32_t val, char * str, size_t len, int8_t base, scpi_bool_t sign) LOCAL; + size_t UInt64ToStrBaseSign(uint64_t val, char * str, size_t len, int8_t base, scpi_bool_t sign) LOCAL; + size_t strBaseToInt32(const char * str, int32_t * val, int8_t base) LOCAL; + size_t strBaseToUInt32(const char * str, uint32_t * val, int8_t base) LOCAL; + size_t strBaseToInt64(const char * str, int64_t * val, int8_t base) LOCAL; + size_t strBaseToUInt64(const char * str, uint64_t * val, int8_t base) LOCAL; + size_t strToFloat(const char * str, float * val) LOCAL; + size_t strToDouble(const char * str, double * val) LOCAL; + scpi_bool_t locateText(const char * str1, size_t len1, const char ** str2, size_t * len2) LOCAL; + scpi_bool_t locateStr(const char * str1, size_t len1, const char ** str2, size_t * len2) LOCAL; + size_t skipWhitespace(const char * cmd, size_t len) LOCAL; + scpi_bool_t matchPattern(const char * pattern, size_t pattern_len, const char * str, size_t str_len, int32_t * num) LOCAL; + scpi_bool_t matchCommand(const char * pattern, const char * cmd, size_t len, int32_t *numbers, size_t numbers_len, int32_t default_value) LOCAL; + scpi_bool_t composeCompoundCommand(const scpi_token_t * prev, scpi_token_t * current) LOCAL; + +#define SCPI_DTOSTRE_UPPERCASE 1 +#define SCPI_DTOSTRE_ALWAYS_SIGN 2 +#define SCPI_DTOSTRE_PLUS_SIGN 4 + char * SCPI_dtostre(double __val, char * __s, size_t __ssize, unsigned char __prec, unsigned char __flags); + + scpi_array_format_t SCPI_GetNativeFormat(void); + uint16_t SCPI_Swap16(uint16_t val); + uint32_t SCPI_Swap32(uint32_t val); + uint64_t SCPI_Swap64(uint64_t val); + +#if !HAVE_STRNLEN + size_t BSD_strnlen(const char *s, size_t maxlen) LOCAL; +#endif + +#if !HAVE_STRNCASECMP && !HAVE_STRNICMP + int OUR_strncasecmp(const char *s1, const char *s2, size_t n) LOCAL; +#endif + +#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION && !USE_MEMORY_ALLOCATION_FREE + void scpiheap_init(scpi_error_info_heap_t * heap, char * error_info_heap, size_t error_info_heap_length); + char * scpiheap_strndup(scpi_error_info_heap_t * heap, const char *s, size_t n) LOCAL; + void scpiheap_free(scpi_error_info_heap_t * heap, char *s, scpi_bool_t rollback) LOCAL; + scpi_bool_t scpiheap_get_parts(scpi_error_info_heap_t * heap, const char *s1, size_t * len1, const char ** s2, size_t * len2) LOCAL; +#endif + +#if !HAVE_STRNDUP + char *OUR_strndup(const char *s, size_t n); +#endif + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef max +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#if 0 +#define max(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; }) + +#define min(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* SCPI_UTILS_PRIVATE_H */ + |
