summaryrefslogtreecommitdiff
path: root/third_party/libscpi
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libscpi')
-rw-r--r--third_party/libscpi/inc/scpi/cc.h211
-rw-r--r--third_party/libscpi/inc/scpi/config.h291
-rw-r--r--third_party/libscpi/inc/scpi/constants.h62
-rw-r--r--third_party/libscpi/inc/scpi/error.h208
-rw-r--r--third_party/libscpi/inc/scpi/expression.h62
-rw-r--r--third_party/libscpi/inc/scpi/ieee488.h94
-rw-r--r--third_party/libscpi/inc/scpi/minimal.h69
-rw-r--r--third_party/libscpi/inc/scpi/parser.h151
-rw-r--r--third_party/libscpi/inc/scpi/scpi.h51
-rw-r--r--third_party/libscpi/inc/scpi/types.h459
-rw-r--r--third_party/libscpi/inc/scpi/units.h60
-rw-r--r--third_party/libscpi/inc/scpi/utils.h62
-rw-r--r--third_party/libscpi/src/error.c237
-rw-r--r--third_party/libscpi/src/expression.c316
-rw-r--r--third_party/libscpi/src/fifo.c146
-rw-r--r--third_party/libscpi/src/fifo_private.h61
-rw-r--r--third_party/libscpi/src/ieee488.c424
-rw-r--r--third_party/libscpi/src/lexer.c935
-rw-r--r--third_party/libscpi/src/lexer_private.h69
-rw-r--r--third_party/libscpi/src/minimal.c215
-rw-r--r--third_party/libscpi/src/parser.c1834
-rw-r--r--third_party/libscpi/src/parser_private.h56
-rw-r--r--third_party/libscpi/src/scpi.g141
-rw-r--r--third_party/libscpi/src/units.c512
-rw-r--r--third_party/libscpi/src/utils.c1142
-rw-r--r--third_party/libscpi/src/utils_private.h128
26 files changed, 7996 insertions, 0 deletions
diff --git a/third_party/libscpi/inc/scpi/cc.h b/third_party/libscpi/inc/scpi/cc.h
new file mode 100644
index 00000000..3487ed1a
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/cc.h
@@ -0,0 +1,211 @@
+/*-
+ * 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 cc.h
+ *
+ * @brief compiler detection
+ *
+ *
+ */
+
+#ifndef __SCPI_CC_H_
+#define __SCPI_CC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__STDC__)
+# define C89 1
+# if defined(__STDC_VERSION__)
+# define C90 1
+# if (__STDC_VERSION__ >= 199409L)
+# define C94 1
+# endif
+# if (__STDC_VERSION__ >= 199901L)
+# define C99 1
+# endif
+# endif
+#endif
+
+#if defined(__cplusplus)
+# if (__cplusplus >= 199711)
+# define CXX98 1
+# endif
+#endif
+
+#if (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200809L) || \
+ (defined _XOPEN_SOURCE && _XOPEN_SOURCE >= 700)
+ #define HAVE_STRNDUP 1
+ #define HAVE_STRNLEN 1
+#endif
+
+#if (defined _BSD_SOURCE && _BSD_SOURCE) || \
+ (defined _XOPEN_SOURCE && _XOPEN_SOURCE >= 500) || \
+ (defined _ISOC99_SOURCE && _ISOC99_SOURCE) || \
+ (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L) || \
+ C99
+ #define HAVE_SNPRINTF 1
+#endif
+
+#if (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L)
+ #define HAVE_STRNCASECMP 1
+#endif
+
+#if (defined _BSD_SOURCE && _BSD_SOURCE) || \
+ (defined _SVID_SOURCE && _SVID_SOURCE) || \
+ (defined _XOPEN_SOURCE && _XOPEN_SOURCE) || \
+ (defined _ISOC99_SOURCE && _ISOC99_SOURCE) || \
+ (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L) ||\
+ C99
+ #define HAVE_ISNAN 1
+#endif
+
+#if (defined _XOPEN_SOURCE && _XOPEN_SOURCE >= 600)|| \
+ (defined _ISOC99_SOURCE && _ISOC99_SOURCE) || \
+ (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L) || \
+ C99
+ #define HAVE_ISFINITE 1
+ #define HAVE_SIGNBIT 1
+#endif
+
+#if (defined _XOPEN_SOURCE && XOPEN_SOURCE >= 600) || \
+ (defined _BSD_SOURCE && _BSD_SOURCE) || \
+ (defined _SVID_SOURCE && _SVID_SOURCE) || \
+ (defined _ISOC99_SOURCE && _ISOC99_SOURCE) || \
+ (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L)
+ #define HAVE_STRTOLL 1
+#endif
+
+#if (defined _XOPEN_SOURCE && _XOPEN_SOURCE >= 600) || \
+ (defined _ISOC99_SOURCE && _ISOC99_SOURCE) || \
+ (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L) || \
+ C99
+ #define HAVE_STRTOF 1
+#endif
+
+#if (defined _ISOC99_SOURCE && _ISOC99_SOURCE) || C99 || CXX98
+ #define HAVE_STDBOOL 1
+#endif
+
+/* Compiler specific */
+/* RealView/Keil ARM Compiler, e.g. Cortex-M CPUs */
+#if defined(__CC_ARM)
+#define HAVE_STRNCASECMP 1
+#endif
+
+/* National Instruments (R) CVI x86/x64 PC platform */
+#if defined(_CVI_)
+#define HAVE_STRNICMP 1
+#endif
+
+/* 8bit PIC - PIC16, etc */
+#if defined(_MPC_)
+#define HAVE_STRNICMP 1
+#endif
+
+/* PIC24 */
+#if defined(__C30__)
+#endif
+
+/* PIC32mx */
+#if defined(__C32__)
+#define HAVE_FINITE 1
+#endif
+
+/* AVR libc */
+#if defined(__AVR__)
+#include <stdlib.h>
+#define HAVE_DTOSTRE 1
+#undef HAVE_STRTOF
+#define HAVE_STRTOF 0
+#endif
+
+/* default values */
+#ifndef HAVE_STRNLEN
+#define HAVE_STRNLEN 0
+#endif
+
+#ifndef HAVE_STRDUP
+#define HAVE_STRDUP 0
+#endif
+
+#ifndef HAVE_STRNDUP
+#define HAVE_STRNDUP 0
+#endif
+
+#ifndef HAVE_STRNICMP
+#define HAVE_STRNICMP 0
+#endif
+
+#ifndef HAVE_STDBOOL
+#define HAVE_STDBOOL 0
+#endif
+
+#ifndef HAVE_SNPRINTF
+#define HAVE_SNPRINTF 0
+#endif
+
+#ifndef HAVE_STRNCASECMP
+#define HAVE_STRNCASECMP 0
+#endif
+
+#ifndef HAVE_ISNAN
+#define HAVE_ISNAN 0
+#endif
+
+#ifndef HAVE_ISFINITE
+#define HAVE_ISFINITE 0
+#endif
+
+#ifndef HAVE_FINITE
+#define HAVE_FINITE 0
+#endif
+
+#ifndef HAVE_SIGNBIT
+#define HAVE_SIGNBIT 0
+#endif
+
+#ifndef HAVE_STRTOLL
+#define HAVE_STRTOLL 0
+#endif
+
+#ifndef HAVE_STRTOF
+#define HAVE_STRTOF 0
+#endif
+
+#ifndef HAVE_DTOSTRE
+#define HAVE_DTOSTRE 0
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SCPI_CC_H_ */
diff --git a/third_party/libscpi/inc/scpi/config.h b/third_party/libscpi/inc/scpi/config.h
new file mode 100644
index 00000000..2d3652e6
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/config.h
@@ -0,0 +1,291 @@
+/*-
+ * 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 config.h
+ * @date Wed Mar 20 12:21:26 UTC 2013
+ *
+ * @brief SCPI Configuration
+ *
+ *
+ */
+
+#ifndef __SCPI_CONFIG_H_
+#define __SCPI_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "cc.h"
+
+#ifdef SCPI_USER_CONFIG
+#include "scpi_user_config.h"
+#endif
+
+/* set the termination character(s) */
+#define LINE_ENDING_CR "\r" /* use a <CR> carriage return as termination charcter */
+#define LINE_ENDING_LF "\n" /* use a <LF> line feed as termination charcter */
+#define LINE_ENDING_CRLF "\r\n" /* use <CR><LF> carriage return + line feed as termination charcters */
+
+#ifndef SCPI_LINE_ENDING
+#define SCPI_LINE_ENDING LINE_ENDING_CRLF
+#endif
+
+#ifndef USE_CUSTOM_REGISTERS
+#define USE_CUSTOM_REGISTERS 0
+#endif
+
+/**
+ * Detect, if it has limited resources or it is running on a full blown operating system.
+ * All values can be overiden by scpi_user_config.h
+ */
+#define SYSTEM_BARE_METAL 0
+#define SYSTEM_FULL_BLOWN 1
+
+/* This should cover all windows compilers (msvc, mingw, cvi) and all Linux/OSX/BSD and other UNIX compatible systems (gcc, clang) */
+#if defined(_WIN32) || defined(_WIN64) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
+#define SYSTEM_TYPE SYSTEM_FULL_BLOWN
+#else
+#define SYSTEM_TYPE SYSTEM_BARE_METAL
+#endif
+
+/**
+ * Enable full error list
+ * 0 = Minimal set of errors
+ * 1 = Full set of errors
+ *
+ * For small systems, full set of errors will occupy large ammount of data
+ * It is enabled by default on full blown systems and disabled on limited bare metal systems
+ */
+#ifndef USE_FULL_ERROR_LIST
+#define USE_FULL_ERROR_LIST SYSTEM_TYPE
+#endif
+
+/**
+ * Enable also LIST_OF_USER_ERRORS to be included
+ * 0 = Use only library defined errors
+ * 1 = Use also LIST_OF_USER_ERRORS
+ */
+#ifndef USE_USER_ERROR_LIST
+#define USE_USER_ERROR_LIST 0
+#endif
+
+#ifndef USE_DEVICE_DEPENDENT_ERROR_INFORMATION
+#define USE_DEVICE_DEPENDENT_ERROR_INFORMATION SYSTEM_TYPE
+#endif
+
+#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION
+#ifndef USE_MEMORY_ALLOCATION_FREE
+#define USE_MEMORY_ALLOCATION_FREE 1
+#endif
+#else
+#ifndef USE_MEMORY_ALLOCATION_FREE
+#define USE_MEMORY_ALLOCATION_FREE 0
+#endif
+#endif
+
+#ifndef USE_COMMAND_TAGS
+#define USE_COMMAND_TAGS 1
+#endif
+
+#ifndef USE_DEPRECATED_FUNCTIONS
+#define USE_DEPRECATED_FUNCTIONS 1
+#endif
+
+#ifndef USE_CUSTOM_DTOSTRE
+#define USE_CUSTOM_DTOSTRE 0
+#endif
+
+#ifndef USE_UNITS_IMPERIAL
+#define USE_UNITS_IMPERIAL 0
+#endif
+
+#ifndef USE_UNITS_ANGLE
+#define USE_UNITS_ANGLE SYSTEM_TYPE
+#endif
+
+#ifndef USE_UNITS_PARTICLES
+#define USE_UNITS_PARTICLES SYSTEM_TYPE
+#endif
+
+#ifndef USE_UNITS_DISTANCE
+#define USE_UNITS_DISTANCE SYSTEM_TYPE
+#endif
+
+#ifndef USE_UNITS_MAGNETIC
+#define USE_UNITS_MAGNETIC SYSTEM_TYPE
+#endif
+
+#ifndef USE_UNITS_LIGHT
+#define USE_UNITS_LIGHT SYSTEM_TYPE
+#endif
+
+#ifndef USE_UNITS_ENERGY_FORCE_MASS
+#define USE_UNITS_ENERGY_FORCE_MASS SYSTEM_TYPE
+#endif
+
+#ifndef USE_UNITS_TIME
+#define USE_UNITS_TIME SYSTEM_TYPE
+#endif
+
+#ifndef USE_UNITS_TEMPERATURE
+#define USE_UNITS_TEMPERATURE SYSTEM_TYPE
+#endif
+
+#ifndef USE_UNITS_RATIO
+#define USE_UNITS_RATIO SYSTEM_TYPE
+#endif
+
+#ifndef USE_UNITS_POWER
+#define USE_UNITS_POWER 1
+#endif
+
+#ifndef USE_UNITS_FREQUENCY
+#define USE_UNITS_FREQUENCY 1
+#endif
+
+#ifndef USE_UNITS_ELECTRIC
+#define USE_UNITS_ELECTRIC 1
+#endif
+
+#ifndef USE_UNITS_ELECTRIC_CHARGE_CONDUCTANCE
+#define USE_UNITS_ELECTRIC_CHARGE_CONDUCTANCE SYSTEM_TYPE
+#endif
+
+/* define local macros depending on existance of strnlen */
+#if HAVE_STRNLEN
+#define SCPIDEFINE_strnlen(s, l) strnlen((s), (l))
+#else
+#define SCPIDEFINE_strnlen(s, l) BSD_strnlen((s), (l))
+#endif
+
+/* define local macros depending on existance of strncasecmp and strnicmp */
+#if HAVE_STRNCASECMP
+#define SCPIDEFINE_strncasecmp(s1, s2, l) strncasecmp((s1), (s2), (l))
+#elif HAVE_STRNICMP
+#define SCPIDEFINE_strncasecmp(s1, s2, l) strnicmp((s1), (s2), (l))
+#else
+#define SCPIDEFINE_strncasecmp(s1, s2, l) OUR_strncasecmp((s1), (s2), (l))
+#endif
+
+#if HAVE_DTOSTRE
+#define SCPIDEFINE_floatToStr(v, s, l) dtostre((double)(v), (s), 6, DTOSTR_PLUS_SIGN | DTOSTR_ALWAYS_SIGN | DTOSTR_UPPERCASE)
+#elif USE_CUSTOM_DTOSTRE
+#define SCPIDEFINE_floatToStr(v, s, l) SCPI_dtostre((v), (s), (l), 6, 0)
+#elif HAVE_SNPRINTF
+#define SCPIDEFINE_floatToStr(v, s, l) snprintf((s), (l), "%g", (v))
+#else
+#define SCPIDEFINE_floatToStr(v, s, l) SCPI_dtostre((v), (s), (l), 6, 0)
+#endif
+
+#if HAVE_DTOSTRE
+#define SCPIDEFINE_doubleToStr(v, s, l) dtostre((v), (s), 15, DTOSTR_PLUS_SIGN | DTOSTR_ALWAYS_SIGN | DTOSTR_UPPERCASE)
+#elif USE_CUSTOM_DTOSTRE
+#define SCPIDEFINE_doubleToStr(v, s, l) SCPI_dtostre((v), (s), (l), 15, 0)
+#elif HAVE_SNPRINTF
+#define SCPIDEFINE_doubleToStr(v, s, l) snprintf((s), (l), "%.15lg", (v))
+#else
+#define SCPIDEFINE_doubleToStr(v, s, l) SCPI_dtostre((v), (s), (l), 15, 0)
+#endif
+
+#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION
+
+ #if USE_MEMORY_ALLOCATION_FREE
+ #include <stdlib.h>
+ #include <string.h>
+ #define SCPIDEFINE_DESCRIPTION_MAX_PARTS 2
+ #if HAVE_STRNDUP
+ #define SCPIDEFINE_strndup(h, s, l) strndup((s), (l))
+ #else
+ #define SCPIDEFINE_strndup(h, s, l) OUR_strndup((s), (l))
+ #endif
+ #define SCPIDEFINE_free(h, s, r) free((s))
+ #else
+ #define SCPIDEFINE_DESCRIPTION_MAX_PARTS 3
+ #define SCPIDEFINE_strndup(h, s, l) scpiheap_strndup((h), (s), (l))
+ #define SCPIDEFINE_free(h, s, r) scpiheap_free((h), (s), (r))
+ #define SCPIDEFINE_get_parts(h, s, l1, s2, l2) scpiheap_get_parts((h), (s), (l1), (s2), (l2))
+ #endif
+#else
+ #define SCPIDEFINE_DESCRIPTION_MAX_PARTS 1
+ #define SCPIDEFINE_strndup(h, s, l) NULL
+ #define SCPIDEFINE_free(h, s, r)
+#endif
+
+#if HAVE_SIGNBIT
+ #define SCPIDEFINE_signbit(n) signbit(n)
+#else
+ #define SCPIDEFINE_signbit(n) ((n)<0)
+#endif
+
+#if HAVE_FINITE
+ #define SCPIDEFINE_isfinite(n) finite(n)
+#elif HAVE_ISFINITE
+ #define SCPIDEFINE_isfinite(n) isfinite(n)
+#else
+ #define SCPIDEFINE_isfinite(n) (!SCPIDEFINE_isnan((n)) && ((n) < INFINITY) && ((n) > -INFINITY))
+#endif
+
+#if HAVE_STRTOF
+ #define SCPIDEFINE_strtof(n, p) strtof((n), (p))
+#else
+ #define SCPIDEFINE_strtof(n, p) strtod((n), (p))
+#endif
+
+#if HAVE_STRTOLL
+ #define SCPIDEFINE_strtoll(n, p, b) strtoll((n), (p), (b))
+ #define SCPIDEFINE_strtoull(n, p, b) strtoull((n), (p), (b))
+#else
+ #define SCPIDEFINE_strtoll(n, p, b) strtoll((n), (p), (b))
+ #define SCPIDEFINE_strtoull(n, p, b) strtoull((n), (p), (b))
+ extern long long int strtoll(const char *nptr, char **endptr, int base);
+ extern unsigned long long int strtoull(const char *nptr, char **endptr, int base);
+ /* TODO: implement OUR_strtoll and OUR_strtoull */
+ /* #warning "64bit string to int conversion not implemented" */
+#endif
+
+#if HAVE_ISNAN
+ #define SCPIDEFINE_isnan(n) isnan((n))
+#else
+ #define SCPIDEFINE_isnan(n) ((n) != (n))
+#endif
+
+#ifndef NAN
+ #define NAN (0.0 / 0.0)
+#endif
+
+#ifndef INFINITY
+ #define INFINITY (1.0 / 0.0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/third_party/libscpi/inc/scpi/constants.h b/third_party/libscpi/inc/scpi/constants.h
new file mode 100644
index 00000000..14b58b43
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/constants.h
@@ -0,0 +1,62 @@
+/*-
+ * 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_constants.h
+ * @date Thu Nov 15 10:58:45 UTC 2012
+ *
+ * @brief SCPI Device constants
+ *
+ *
+ */
+
+#ifndef SCPI_CONSTANTS_H
+#define SCPI_CONSTANTS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* 21.21 :VERSion?
+ * YYYY.V
+ * YYYY = SCPI year
+ * V = SCPI revision
+ */
+#define SCPI_STD_VERSION_REVISION "1999.0"
+
+/* 21.8 :ERRor Subsystem
+ * The maximum string length of <Error/event_description> plus <Device-dependent_info> is 255 characters.
+ */
+#define SCPI_STD_ERROR_DESC_MAX_STRING_LENGTH 255
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SCPI_CONSTANTS_H */
+
diff --git a/third_party/libscpi/inc/scpi/error.h b/third_party/libscpi/inc/scpi/error.h
new file mode 100644
index 00000000..d931e4f8
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/error.h
@@ -0,0 +1,208 @@
+/*-
+ * 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.h
+ * @date Thu Nov 15 10:58:45 UTC 2012
+ *
+ * @brief Error handling and storing routines
+ *
+ *
+ */
+
+#ifndef SCPI_ERROR_H
+#define SCPI_ERROR_H
+
+#include "scpi/config.h"
+#include "scpi/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ void SCPI_ErrorInit(scpi_t * context, scpi_error_t * data, int16_t size);
+ void SCPI_ErrorClear(scpi_t * context);
+ scpi_bool_t SCPI_ErrorPop(scpi_t * context, scpi_error_t * error);
+ void SCPI_ErrorPushEx(scpi_t * context, int16_t err, char * info, size_t info_len);
+ void SCPI_ErrorPush(scpi_t * context, int16_t err);
+ int32_t SCPI_ErrorCount(scpi_t * context);
+ const char * SCPI_ErrorTranslate(int16_t err);
+
+
+ /* Using X-Macro technique to define everything once
+ * http://en.wikipedia.org/wiki/X_Macro
+ *
+ * X macro is for minimal set of errors for library itself
+ * XE macro is for full set of SCPI errors available to user application
+ */
+#define LIST_OF_ERRORS \
+ X(SCPI_ERROR_NO_ERROR, 0, "No error") \
+ XE(SCPI_ERROR_COMMAND, -100, "Command error") \
+ X(SCPI_ERROR_INVALID_CHARACTER, -101, "Invalid character") \
+ XE(SCPI_ERROR_SYNTAX, -102, "Syntax error") \
+ X(SCPI_ERROR_INVALID_SEPARATOR, -103, "Invalid separator") \
+ X(SCPI_ERROR_DATA_TYPE_ERROR, -104, "Data type error") \
+ XE(SCPI_ERROR_GET_NOT_ALLOWED, -105, "GET not allowed") \
+ X(SCPI_ERROR_PARAMETER_NOT_ALLOWED, -108, "Parameter not allowed") \
+ X(SCPI_ERROR_MISSING_PARAMETER, -109, "Missing parameter") \
+ XE(SCPI_ERROR_COMMAND_HEADER, -110, "Command header error") \
+ XE(SCPI_ERROR_HEADER_SEPARATOR, -111, "Header separator error") \
+ XE(SCPI_ERROR_PRG_MNEMONIC_TOO_LONG, -112, "Program mnemonic too long") \
+ X(SCPI_ERROR_UNDEFINED_HEADER, -113, "Undefined header") \
+ XE(SCPI_ERROR_HEADER_SUFFIX_OUTOFRANGE, -114, "Header suffix out of range") \
+ XE(SCPI_ERROR_UNEXP_NUM_OF_PARAMETER, -115, "Unexpected number of parameters") \
+ XE(SCPI_ERROR_NUMERIC_DATA_ERROR, -120, "Numeric data error") \
+ XE(SCPI_ERROR_INVAL_CHAR_IN_NUMBER, -121, "Invalid character in number") \
+ XE(SCPI_ERROR_EXPONENT_TOO_LONG, -123, "Exponent too large") \
+ XE(SCPI_ERROR_TOO_MANY_DIGITS, -124, "Too many digits") \
+ XE(SCPI_ERROR_NUMERIC_DATA_NOT_ALLOWED, -128, "Numeric data not allowed") \
+ XE(SCPI_ERROR_SUFFIX_ERROR, -130, "Suffix error") \
+ X(SCPI_ERROR_INVALID_SUFFIX, -131, "Invalid suffix") \
+ XE(SCPI_ERROR_SUFFIX_TOO_LONG, -134, "Suffix too long") \
+ X(SCPI_ERROR_SUFFIX_NOT_ALLOWED, -138, "Suffix not allowed") \
+ XE(SCPI_ERROR_CHARACTER_DATA_ERROR, -140, "Character data error") \
+ XE(SCPI_ERROR_INVAL_CHARACTER_DATA, -141, "Invalid character data") \
+ XE(SCPI_ERROR_CHARACTER_DATA_TOO_LONG, -144, "Character data too long") \
+ XE(SCPI_ERROR_CHARACTER_DATA_NOT_ALLOWED, -148, "Character data not allowed") \
+ XE(SCPI_ERROR_STRING_DATA_ERROR, -150, "String data error") \
+ X(SCPI_ERROR_INVALID_STRING_DATA, -151, "Invalid string data") \
+ XE(SCPI_ERROR_STRING_DATA_NOT_ALLOWED, -158, "String data not allowed") \
+ XE(SCPI_ERROR_BLOCK_DATA_ERROR, -160, "Block data error") \
+ XE(SCPI_ERROR_INVALID_BLOCK_DATA, -161, "Invalid block data") \
+ XE(SCPI_ERROR_BLOCK_DATA_NOT_ALLOWED, -168, "Block data not allowed") \
+ X(SCPI_ERROR_EXPRESSION_PARSING_ERROR, -170, "Expression error") \
+ XE(SCPI_ERROR_INVAL_EXPRESSION, -171, "Invalid expression") \
+ XE(SCPI_ERROR_EXPRESSION_DATA_NOT_ALLOWED, -178, "Expression data not allowed") \
+ XE(SCPI_ERROR_MACRO_DEFINITION_ERROR, -180, "Macro error") \
+ XE(SCPI_ERROR_INVAL_OUTSIDE_MACRO_DEF, -181, "Invalid outside macro definition") \
+ XE(SCPI_ERROR_INVAL_INSIDE_MACRO_DEF, -183, "Invalid inside macro definition") \
+ XE(SCPI_ERROR_MACRO_PARAMETER_ERROR, -184, "Macro parameter error") \
+ X(SCPI_ERROR_EXECUTION_ERROR, -200, "Execution error") \
+ XE(SCPI_ERROR_INVAL_WHILE_IN_LOCAL, -201, "Invalid while in local") \
+ XE(SCPI_ERROR_SETTINGS_LOST_DUE_TO_RTL, -202, "Settings lost due to rtl") \
+ XE(SCPI_ERROR_COMMAND_PROTECTED, -203, "Command protected") \
+ XE(SCPI_ERROR_TRIGGER_ERROR, -210, "Trigger error") \
+ XE(SCPI_ERROR_TRIGGER_IGNORED, -211, "Trigger ignored") \
+ XE(SCPI_ERROR_ARM_IGNORED, -212, "Arm ignored") \
+ XE(SCPI_ERROR_INIT_IGNORED, -213, "Init ignored") \
+ XE(SCPI_ERROR_TRIGGER_DEADLOCK, -214, "Trigger deadlock") \
+ XE(SCPI_ERROR_ARM_DEADLOCK, -215, "Arm deadlock") \
+ XE(SCPI_ERROR_PARAMETER_ERROR, -220, "Parameter error") \
+ XE(SCPI_ERROR_SETTINGS_CONFLICT, -221, "Settings conflict") \
+ XE(SCPI_ERROR_DATA_OUT_OF_RANGE, -222, "Data out of range") \
+ XE(SCPI_ERROR_TOO_MUCH_DATA, -223, "Too much data") \
+ X(SCPI_ERROR_ILLEGAL_PARAMETER_VALUE, -224, "Illegal parameter value") \
+ XE(SCPI_ERROR_OUT_OF_MEMORY_FOR_REQ_OP, -225, "Out of memory") \
+ XE(SCPI_ERROR_LISTS_NOT_SAME_LENGTH, -226, "Lists not same length") \
+ XE(SCPI_ERROR_DATA_CORRUPT, -230, "Data corrupt or stale") \
+ XE(SCPI_ERROR_DATA_QUESTIONABLE, -231, "Data questionable") \
+ XE(SCPI_ERROR_INVAL_VERSION, -233, "Invalid version") \
+ XE(SCPI_ERROR_HARDWARE_ERROR, -240, "Hardware error") \
+ XE(SCPI_ERROR_HARDWARE_MISSING, -241, "Hardware missing") \
+ XE(SCPI_ERROR_MASS_STORAGE_ERROR, -250, "Mass storage error") \
+ XE(SCPI_ERROR_MISSING_MASS_STORAGE, -251, "Missing mass storage") \
+ XE(SCPI_ERROR_MISSING_MASS_MEDIA, -252, "Missing media") \
+ XE(SCPI_ERROR_CORRUPT_MEDIA, -253, "Corrupt media") \
+ XE(SCPI_ERROR_MEDIA_FULL, -254, "Media full") \
+ XE(SCPI_ERROR_DIRECTORY_FULL, -255, "Directory full") \
+ XE(SCPI_ERROR_FILE_NAME_NOT_FOUND, -256, "File name not found") \
+ XE(SCPI_ERROR_FILE_NAME_ERROR, -257, "File name error") \
+ XE(SCPI_ERROR_MEDIA_PROTECTED, -258, "Media protected") \
+ XE(SCPI_ERROR_EXPRESSION_EXECUTING_ERROR, -260, "Expression error") \
+ XE(SCPI_ERROR_MATH_ERROR_IN_EXPRESSION, -261, "Math error in expression") \
+ XE(SCPI_ERROR_MACRO_UNDEF_EXEC_ERROR, -270, "Macro error") \
+ XE(SCPI_ERROR_MACRO_SYNTAX_ERROR, -271, "Macro syntax error") \
+ XE(SCPI_ERROR_MACRO_EXECUTION_ERROR, -272, "Macro execution error") \
+ XE(SCPI_ERROR_ILLEGAL_MACRO_LABEL, -273, "Illegal macro label") \
+ XE(SCPI_ERROR_IMPROPER_USED_MACRO_PARAM, -274, "Macro parameter error") \
+ XE(SCPI_ERROR_MACRO_DEFINITION_TOO_LONG, -275, "Macro definition too long") \
+ XE(SCPI_ERROR_MACRO_RECURSION_ERROR, -276, "Macro recursion error") \
+ XE(SCPI_ERROR_MACRO_REDEF_NOT_ALLOWED, -277, "Macro redefinition not allowed") \
+ XE(SCPI_ERROR_MACRO_HEADER_NOT_FOUND, -278, "Macro header not found") \
+ XE(SCPI_ERROR_PROGRAM_ERROR, -280, "Program error") \
+ XE(SCPI_ERROR_CANNOT_CREATE_PROGRAM, -281, "Cannot create program") \
+ XE(SCPI_ERROR_ILLEGAL_PROGRAM_NAME, -282, "Illegal program name") \
+ XE(SCPI_ERROR_ILLEGAL_VARIABLE_NAME, -283, "Illegal variable name") \
+ XE(SCPI_ERROR_PROGRAM_CURRENTLY_RUNNING, -284, "Program currently running") \
+ XE(SCPI_ERROR_PROGRAM_SYNTAX_ERROR, -285, "Program syntax error") \
+ XE(SCPI_ERROR_PROGRAM_RUNTIME_ERROR, -286, "Program runtime error") \
+ XE(SCPI_ERROR_MEMORY_USE_ERROR, -290, "Memory use error") \
+ XE(SCPI_ERROR_OUT_OF_MEMORY, -291, "Out of memory") \
+ XE(SCPI_ERROR_REF_NAME_DOES_NOT_EXIST, -292, "Referenced name does not exist") \
+ XE(SCPI_ERROR_REF_NAME_ALREADY_EXISTS, -293, "Referenced name already exists") \
+ XE(SCPI_ERROR_INCOMPATIBLE_TYPE, -294, "Incompatible type") \
+ XE(SCPI_ERROR_DEVICE_ERROR, -300, "Device specific error") \
+ X(SCPI_ERROR_SYSTEM_ERROR, -310, "System error") \
+ XE(SCPI_ERROR_MEMORY_ERROR, -311, "Memory error") \
+ XE(SCPI_ERROR_PUD_MEMORY_LOST, -312, "PUD memory lost") \
+ XE(SCPI_ERROR_CALIBRATION_MEMORY_LOST, -313, "Calibration memory lost") \
+ XE(SCPI_ERROR_SAVE_RECALL_MEMORY_LOST, -314, "Save/recall memory lost") \
+ XE(SCPI_ERROR_CONFIGURATION_MEMORY_LOST, -315, "Configuration memory lost") \
+ XE(SCPI_ERROR_STORAGE_FAULT, -320, "Storage fault") \
+ XE(SCPI_ERROR_OUT_OF_DEVICE_MEMORY, -321, "Out of memory") \
+ XE(SCPI_ERROR_SELF_TEST_FAILED, -330, "Self-test failed") \
+ XE(SCPI_ERROR_CALIBRATION_FAILED, -340, "Calibration failed") \
+ X(SCPI_ERROR_QUEUE_OVERFLOW, -350, "Queue overflow") \
+ XE(SCPI_ERROR_COMMUNICATION_ERROR, -360, "Communication error") \
+ XE(SCPI_ERROR_PARITY_ERROR_IN_CMD_MSG, -361, "Parity error in program message") \
+ XE(SCPI_ERROR_FRAMING_ERROR_IN_CMD_MSG, -362, "Framing error in program message") \
+ X(SCPI_ERROR_INPUT_BUFFER_OVERRUN, -363, "Input buffer overrun") \
+ XE(SCPI_ERROR_TIME_OUT, -365, "Time out error") \
+ XE(SCPI_ERROR_QUERY_ERROR, -400, "Query error") \
+ XE(SCPI_ERROR_QUERY_INTERRUPTED, -410, "Query INTERRUPTED") \
+ XE(SCPI_ERROR_QUERY_UNTERMINATED, -420, "Query UNTERMINATED") \
+ XE(SCPI_ERROR_QUERY_DEADLOCKED, -430, "Query DEADLOCKED") \
+ XE(SCPI_ERROR_QUERY_UNTERM_INDEF_RESP, -440, "Query UNTERMINATED after indefinite response") \
+ XE(SCPI_ERROR_POWER_ON, -500, "Power on") \
+ XE(SCPI_ERROR_USER_REQUEST, -600, "User request") \
+ XE(SCPI_ERROR_REQUEST_CONTROL, -700, "Request control") \
+ XE(SCPI_ERROR_OPERATION_COMPLETE, -800, "Operation complete") \
+
+
+ enum {
+#define X(def, val, str) def = val,
+#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
+ };
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SCPI_ERROR_H */
+
diff --git a/third_party/libscpi/inc/scpi/expression.h b/third_party/libscpi/inc/scpi/expression.h
new file mode 100644
index 00000000..897eb14d
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/expression.h
@@ -0,0 +1,62 @@
+/*-
+ * 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.h
+ *
+ * @brief Expressions handling
+ *
+ *
+ */
+#ifndef SCPI_EXPRESSION_H
+#define SCPI_EXPRESSION_H
+
+#include "scpi/config.h"
+#include "scpi/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ enum _scpi_expr_result_t {
+ SCPI_EXPR_OK = 0,
+ SCPI_EXPR_ERROR,
+ SCPI_EXPR_NO_MORE,
+ };
+ typedef enum _scpi_expr_result_t scpi_expr_result_t;
+
+ 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);
+ 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 SCPI_ExprNumericListEntryDouble(scpi_t * context, scpi_parameter_t * param, int index, scpi_bool_t * isRange, double * valueFrom, double * valueTo);
+ 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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SCPI_EXPRESSION_H */
diff --git a/third_party/libscpi/inc/scpi/ieee488.h b/third_party/libscpi/inc/scpi/ieee488.h
new file mode 100644
index 00000000..15fb527d
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/ieee488.h
@@ -0,0 +1,94 @@
+/*-
+ * 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.h
+ * @date Thu Nov 15 10:58:45 UTC 2012
+ *
+ * @brief Implementation of IEEE488.2 commands and state model
+ *
+ *
+ */
+
+#ifndef SCPI_IEEE488_H
+#define SCPI_IEEE488_H
+
+#include "scpi/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ scpi_result_t SCPI_CoreCls(scpi_t * context);
+ scpi_result_t SCPI_CoreEse(scpi_t * context);
+ scpi_result_t SCPI_CoreEseQ(scpi_t * context);
+ scpi_result_t SCPI_CoreEsrQ(scpi_t * context);
+ scpi_result_t SCPI_CoreIdnQ(scpi_t * context);
+ scpi_result_t SCPI_CoreOpc(scpi_t * context);
+ scpi_result_t SCPI_CoreOpcQ(scpi_t * context);
+ scpi_result_t SCPI_CoreRst(scpi_t * context);
+ scpi_result_t SCPI_CoreSre(scpi_t * context);
+ scpi_result_t SCPI_CoreSreQ(scpi_t * context);
+ scpi_result_t SCPI_CoreStbQ(scpi_t * context);
+ scpi_result_t SCPI_CoreTstQ(scpi_t * context);
+ scpi_result_t SCPI_CoreWai(scpi_t * context);
+
+
+#define STB_R01 0x01 /* Not used */
+#define STB_PRO 0x02 /* Protection Event Flag */
+#define STB_QMA 0x04 /* Error/Event queue message available */
+#define STB_QES 0x08 /* Questionable status */
+#define STB_MAV 0x10 /* Message Available */
+#define STB_ESR 0x20 /* Standard Event Status Register */
+#define STB_SRQ 0x40 /* Service Request */
+#define STB_OPS 0x80 /* Operation Status Flag */
+
+
+#define ESR_OPC 0x01 /* Operation complete */
+#define ESR_REQ 0x02 /* Request Control */
+#define ESR_QER 0x04 /* Query Error */
+#define ESR_DER 0x08 /* Device Dependent Error */
+#define ESR_EER 0x10 /* Execution Error (e.g. range error) */
+#define ESR_CER 0x20 /* Command error (e.g. syntax error) */
+#define ESR_URQ 0x40 /* User Request */
+#define ESR_PON 0x80 /* Power On */
+
+
+ scpi_reg_val_t SCPI_RegGet(scpi_t * context, scpi_reg_name_t name);
+ void SCPI_RegSet(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t val);
+ void SCPI_RegSetBits(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t bits);
+ void SCPI_RegClearBits(scpi_t * context, scpi_reg_name_t name, scpi_reg_val_t bits);
+
+ void SCPI_EventClear(scpi_t * context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SCPI_IEEE488_H */
+
diff --git a/third_party/libscpi/inc/scpi/minimal.h b/third_party/libscpi/inc/scpi/minimal.h
new file mode 100644
index 00000000..e14f1ed6
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/minimal.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 scpi_minimal.h
+ * @date Thu Nov 15 10:58:45 UTC 2012
+ *
+ * @brief SCPI minimal implementation
+ *
+ *
+ */
+
+#ifndef SCPI_MINIMAL_H
+#define SCPI_MINIMAL_H
+
+#include "scpi/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ scpi_result_t SCPI_Stub(scpi_t * context);
+ scpi_result_t SCPI_StubQ(scpi_t * context);
+
+ scpi_result_t SCPI_SystemVersionQ(scpi_t * context);
+ scpi_result_t SCPI_SystemErrorNextQ(scpi_t * context);
+ scpi_result_t SCPI_SystemErrorCountQ(scpi_t * context);
+ scpi_result_t SCPI_StatusQuestionableEventQ(scpi_t * context);
+ scpi_result_t SCPI_StatusQuestionableConditionQ(scpi_t * context);
+ scpi_result_t SCPI_StatusQuestionableEnableQ(scpi_t * context);
+ scpi_result_t SCPI_StatusQuestionableEnable(scpi_t * context);
+ scpi_result_t SCPI_StatusOperationConditionQ(scpi_t * context);
+ scpi_result_t SCPI_StatusOperationEventQ(scpi_t * context);
+ scpi_result_t SCPI_StatusOperationEnableQ(scpi_t * context);
+ scpi_result_t SCPI_StatusOperationEnable(scpi_t * context);
+ scpi_result_t SCPI_StatusPreset(scpi_t * context);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SCPI_MINIMAL_H */
+
diff --git a/third_party/libscpi/inc/scpi/parser.h b/third_party/libscpi/inc/scpi/parser.h
new file mode 100644
index 00000000..643cec61
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/parser.h
@@ -0,0 +1,151 @@
+/*-
+ * 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.h
+ * @date Thu Nov 15 10:58:45 UTC 2012
+ *
+ * @brief SCPI parser implementation
+ *
+ *
+ */
+
+#ifndef SCPI_PARSER_H
+#define SCPI_PARSER_H
+
+#include <string.h>
+#include "scpi/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ 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);
+#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION && !USE_MEMORY_ALLOCATION_FREE
+ void SCPI_InitHeap(scpi_t * context, char * error_info_heap, size_t error_info_heap_length);
+#endif
+
+ scpi_bool_t SCPI_Input(scpi_t * context, const char * data, int len);
+ scpi_bool_t SCPI_Parse(scpi_t * context, char * data, int len);
+
+ size_t SCPI_ResultCharacters(scpi_t * context, const char * data, size_t len);
+#define SCPI_ResultMnemonic(context, data) SCPI_ResultCharacters((context), (data), strlen(data))
+#define SCPI_ResultUInt8Base(c, v, b) SCPI_ResultUInt32Base((c), (v), (uint8_t)(b))
+#define SCPI_ResultUInt8(c, v) SCPI_ResultUInt32Base((c), (uint8_t)(v), 10)
+#define SCPI_ResultInt8(c, v) SCPI_ResultInt32((c), (int8_t)(v))
+#define SCPI_ResultUInt16Base(c, v, b) SCPI_ResultUInt32Base((c), (uint16_t)(v), (b))
+#define SCPI_ResultUInt16(c, v) SCPI_ResultUInt32Base((c), (uint16_t)(v), 10)
+#define SCPI_ResultInt16(c, v) SCPI_ResultInt32((c), (int16_t)(v))
+ size_t SCPI_ResultUInt32Base(scpi_t * context, uint32_t val, int8_t base);
+#define SCPI_ResultUInt32(c, v) SCPI_ResultUInt32Base((c), (v), 10)
+ size_t SCPI_ResultInt32(scpi_t * context, int32_t val);
+ size_t SCPI_ResultUInt64Base(scpi_t * context, uint64_t val, int8_t base);
+#define SCPI_ResultUInt64(c, v) SCPI_ResultUInt64Base((c), (v), 10)
+ size_t SCPI_ResultInt64(scpi_t * context, int64_t val);
+ size_t SCPI_ResultFloat(scpi_t * context, float val);
+ size_t SCPI_ResultDouble(scpi_t * context, double val);
+ size_t SCPI_ResultText(scpi_t * context, const char * data);
+ size_t SCPI_ResultError(scpi_t * context, scpi_error_t * error);
+ size_t SCPI_ResultArbitraryBlock(scpi_t * context, const void * data, size_t len);
+ size_t SCPI_ResultArbitraryBlockHeader(scpi_t * context, size_t len);
+ size_t SCPI_ResultArbitraryBlockData(scpi_t * context, const void * data, size_t len);
+ size_t SCPI_ResultBool(scpi_t * context, scpi_bool_t val);
+
+ size_t SCPI_ResultArrayInt8(scpi_t * context, const int8_t * array, size_t count, scpi_array_format_t format);
+ size_t SCPI_ResultArrayUInt8(scpi_t * context, const uint8_t * array, size_t count, scpi_array_format_t format);
+ size_t SCPI_ResultArrayInt16(scpi_t * context, const int16_t * array, size_t count, scpi_array_format_t format);
+ size_t SCPI_ResultArrayUInt16(scpi_t * context, const uint16_t * array, size_t count, scpi_array_format_t format);
+ size_t SCPI_ResultArrayInt32(scpi_t * context, const int32_t * array, size_t count, scpi_array_format_t format);
+ size_t SCPI_ResultArrayUInt32(scpi_t * context, const uint32_t * array, size_t count, scpi_array_format_t format);
+ size_t SCPI_ResultArrayInt64(scpi_t * context, const int64_t * array, size_t count, scpi_array_format_t format);
+ size_t SCPI_ResultArrayUInt64(scpi_t * context, const uint64_t * array, size_t count, scpi_array_format_t format);
+ size_t SCPI_ResultArrayFloat(scpi_t * context, const float * array, size_t count, scpi_array_format_t format);
+ size_t SCPI_ResultArrayDouble(scpi_t * context, const double * array, size_t count, scpi_array_format_t format);
+
+ scpi_bool_t SCPI_Parameter(scpi_t * context, scpi_parameter_t * parameter, scpi_bool_t mandatory);
+ scpi_bool_t SCPI_ParamIsValid(scpi_parameter_t * parameter);
+ scpi_bool_t SCPI_ParamErrorOccurred(scpi_t * context);
+ scpi_bool_t SCPI_ParamIsNumber(scpi_parameter_t * parameter, scpi_bool_t suffixAllowed);
+ scpi_bool_t SCPI_ParamToInt32(scpi_t * context, scpi_parameter_t * parameter, int32_t * value);
+ scpi_bool_t SCPI_ParamToUInt32(scpi_t * context, scpi_parameter_t * parameter, uint32_t * value);
+ scpi_bool_t SCPI_ParamToInt64(scpi_t * context, scpi_parameter_t * parameter, int64_t * value);
+ scpi_bool_t SCPI_ParamToUInt64(scpi_t * context, scpi_parameter_t * parameter, uint64_t * value);
+ scpi_bool_t SCPI_ParamToFloat(scpi_t * context, scpi_parameter_t * parameter, float * value);
+ scpi_bool_t SCPI_ParamToDouble(scpi_t * context, scpi_parameter_t * parameter, double * value);
+ scpi_bool_t SCPI_ParamToChoice(scpi_t * context, scpi_parameter_t * parameter, const scpi_choice_def_t * options, int32_t * value);
+ scpi_bool_t SCPI_ChoiceToName(const scpi_choice_def_t * options, int32_t tag, const char ** text);
+
+ scpi_bool_t SCPI_ParamInt32(scpi_t * context, int32_t * value, scpi_bool_t mandatory);
+ scpi_bool_t SCPI_ParamUInt32(scpi_t * context, uint32_t * value, scpi_bool_t mandatory);
+ scpi_bool_t SCPI_ParamInt64(scpi_t * context, int64_t * value, scpi_bool_t mandatory);
+ scpi_bool_t SCPI_ParamUInt64(scpi_t * context, uint64_t * value, scpi_bool_t mandatory);
+ scpi_bool_t SCPI_ParamFloat(scpi_t * context, float * value, scpi_bool_t mandatory);
+ scpi_bool_t SCPI_ParamDouble(scpi_t * context, double * value, scpi_bool_t mandatory);
+ scpi_bool_t SCPI_ParamCharacters(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory);
+ scpi_bool_t SCPI_ParamArbitraryBlock(scpi_t * context, const char ** value, size_t * len, scpi_bool_t mandatory);
+ scpi_bool_t SCPI_ParamCopyText(scpi_t * context, char * buffer, size_t buffer_len, size_t * copy_len, scpi_bool_t mandatory);
+
+ extern const scpi_choice_def_t scpi_bool_def[];
+ scpi_bool_t SCPI_ParamBool(scpi_t * context, scpi_bool_t * value, scpi_bool_t mandatory);
+ scpi_bool_t SCPI_ParamChoice(scpi_t * context, const scpi_choice_def_t * options, int32_t * value, scpi_bool_t mandatory);
+
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+
+ scpi_bool_t SCPI_IsCmd(scpi_t * context, const char * cmd);
+#if USE_COMMAND_TAGS
+ int32_t SCPI_CmdTag(scpi_t * context);
+#endif /* USE_COMMAND_TAGS */
+ scpi_bool_t SCPI_Match(const char * pattern, const char * value, size_t len);
+ scpi_bool_t SCPI_CommandNumbers(scpi_t * context, int32_t * numbers, size_t len, int32_t default_value);
+
+#if USE_DEPRECATED_FUNCTIONS
+ /* deprecated finction, should be removed later */
+#define SCPI_ResultIntBase(context, val, base) SCPI_ResultInt32Base ((context), (val), (base), TRUE)
+#define SCPI_ResultInt(context, val) SCPI_ResultInt32 ((context), (val))
+#define SCPI_ParamToInt(context, parameter, value) SCPI_ParamToInt32((context), (parameter), (value))
+#define SCPI_ParamToUnsignedInt(context, parameter, value) SCPI_ParamToUInt32((context), (parameter), (value))
+#define SCPI_ParamInt(context, value, mandatory) SCPI_ParamInt32((context), (value), (mandatory))
+#define SCPI_ParamUnsignedInt(context, value, mandatory) SCPI_ParamUInt32((context), (value), (mandatory))
+#endif /* USE_DEPRECATED_FUNCTIONS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SCPI_PARSER_H */
+
diff --git a/third_party/libscpi/inc/scpi/scpi.h b/third_party/libscpi/inc/scpi/scpi.h
new file mode 100644
index 00000000..ea28dd44
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/scpi.h
@@ -0,0 +1,51 @@
+/*-
+ * 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.h
+ * @date Thu Nov 15 10:58:45 UTC 2012
+ *
+ * @brief SCPI library include file
+ *
+ *
+ */
+
+#ifndef SCPI_H
+#define SCPI_H
+
+#include "scpi/parser.h"
+#include "scpi/ieee488.h"
+#include "scpi/error.h"
+#include "scpi/constants.h"
+#include "scpi/minimal.h"
+#include "scpi/units.h"
+#include "scpi/utils.h"
+#include "scpi/expression.h"
+
+#endif /* SCPI_H */
+
diff --git a/third_party/libscpi/inc/scpi/types.h b/third_party/libscpi/inc/scpi/types.h
new file mode 100644
index 00000000..eb9b2153
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/types.h
@@ -0,0 +1,459 @@
+/*-
+ * 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_types.h
+ * @date Thu Nov 15 10:58:45 UTC 2012
+ *
+ * @brief SCPI data types
+ *
+ *
+ */
+
+#ifndef SCPI_TYPES_H
+#define SCPI_TYPES_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "scpi/config.h"
+
+#if HAVE_STDBOOL
+#include <stdbool.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !HAVE_STDBOOL
+ typedef unsigned char bool;
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+ /* basic data types */
+ typedef bool scpi_bool_t;
+ /* typedef enum { FALSE = 0, TRUE } scpi_bool_t; */
+
+ /* IEEE 488.2 registers */
+ enum _scpi_reg_name_t {
+ SCPI_REG_STB = 0, /* Status Byte */
+ SCPI_REG_SRE, /* Service Request Enable Register */
+ SCPI_REG_ESR, /* Standard Event Status Register (ESR, SESR) */
+ SCPI_REG_ESE, /* Event Status Enable Register */
+ SCPI_REG_OPER, /* OPERation Status Register */
+ SCPI_REG_OPERE, /* OPERation Status Enable Register */
+ SCPI_REG_OPERC, /* OPERation Status Condition Register */
+ SCPI_REG_QUES, /* QUEStionable status register */
+ SCPI_REG_QUESE, /* QUEStionable status Enable Register */
+ SCPI_REG_QUESC, /* QUEStionable status Condition Register */
+
+#if USE_CUSTOM_REGISTERS
+#ifndef USER_REGISTERS
+#error "No user registers defined"
+#else
+ USER_REGISTERS
+#endif
+#endif
+
+ /* number of registers */
+ SCPI_REG_COUNT,
+ /* last definition - a value for no register */
+ SCPI_REG_NONE
+ };
+ typedef enum _scpi_reg_name_t scpi_reg_name_t;
+
+ enum _scpi_ctrl_name_t {
+ SCPI_CTRL_SRQ = 1, /* service request */
+ SCPI_CTRL_GTL, /* Go to local */
+ SCPI_CTRL_SDC, /* Selected device clear */
+ SCPI_CTRL_PPC, /* Parallel poll configure */
+ SCPI_CTRL_GET, /* Group execute trigger */
+ SCPI_CTRL_TCT, /* Take control */
+ SCPI_CTRL_LLO, /* Device clear */
+ SCPI_CTRL_DCL, /* Local lockout */
+ SCPI_CTRL_PPU, /* Parallel poll unconfigure */
+ SCPI_CTRL_SPE, /* Serial poll enable */
+ SCPI_CTRL_SPD, /* Serial poll disable */
+ SCPI_CTRL_MLA, /* My local address */
+ SCPI_CTRL_UNL, /* Unlisten */
+ SCPI_CTRL_MTA, /* My talk address */
+ SCPI_CTRL_UNT, /* Untalk */
+ SCPI_CTRL_MSA /* My secondary address */
+ };
+ typedef enum _scpi_ctrl_name_t scpi_ctrl_name_t;
+
+ typedef uint16_t scpi_reg_val_t;
+
+ enum _scpi_reg_class_t {
+ SCPI_REG_CLASS_STB = 0,
+ SCPI_REG_CLASS_SRE,
+ SCPI_REG_CLASS_EVEN,
+ SCPI_REG_CLASS_ENAB,
+ SCPI_REG_CLASS_COND,
+ SCPI_REG_CLASS_NTR,
+ SCPI_REG_CLASS_PTR,
+ };
+ typedef enum _scpi_reg_class_t scpi_reg_class_t;
+
+ enum _scpi_reg_group_t {
+ SCPI_REG_GROUP_STB = 0,
+ SCPI_REG_GROUP_ESR,
+ SCPI_REG_GROUP_OPER,
+ SCPI_REG_GROUP_QUES,
+
+#if USE_CUSTOM_REGISTERS
+#ifndef USER_REGISTER_GROUPS
+#error "No user register groups defined"
+#else
+ USER_REGISTER_GROUPS
+#endif
+#endif
+
+ /* last definition - number of register groups */
+ SCPI_REG_GROUP_COUNT
+ };
+ typedef enum _scpi_reg_group_t scpi_reg_group_t;
+
+ struct _scpi_reg_info_t {
+ scpi_reg_class_t type;
+ scpi_reg_group_t group;
+ };
+ typedef struct _scpi_reg_info_t scpi_reg_info_t;
+
+ struct _scpi_reg_group_info_t {
+ scpi_reg_name_t event;
+ scpi_reg_name_t enable;
+ scpi_reg_name_t condition;
+ scpi_reg_name_t ptfilt;
+ scpi_reg_name_t ntfilt;
+ scpi_reg_name_t parent_reg;
+ scpi_reg_val_t parent_bit;
+ };
+ typedef struct _scpi_reg_group_info_t scpi_reg_group_info_t;
+
+ /* scpi commands */
+ enum _scpi_result_t {
+ SCPI_RES_OK = 1,
+ SCPI_RES_ERR = -1
+ };
+ typedef enum _scpi_result_t scpi_result_t;
+
+ typedef struct _scpi_command_t scpi_command_t;
+
+#if USE_COMMAND_TAGS
+ #define SCPI_CMD_LIST_END {NULL, NULL, 0}
+#else
+ #define SCPI_CMD_LIST_END {NULL, NULL}
+#endif
+
+
+ /* scpi interface */
+ typedef struct _scpi_t scpi_t;
+ typedef struct _scpi_interface_t scpi_interface_t;
+
+ struct _scpi_buffer_t {
+ size_t length;
+ size_t position;
+ char * data;
+ };
+ typedef struct _scpi_buffer_t scpi_buffer_t;
+
+ struct _scpi_const_buffer_t {
+ size_t length;
+ size_t position;
+ const char * data;
+ };
+ typedef struct _scpi_const_buffer_t scpi_const_buffer_t;
+
+ typedef size_t(*scpi_write_t)(scpi_t * context, const char * data, size_t len);
+ typedef scpi_result_t(*scpi_write_control_t)(scpi_t * context, scpi_ctrl_name_t ctrl, scpi_reg_val_t val);
+ typedef int (*scpi_error_callback_t)(scpi_t * context, int_fast16_t error);
+
+ /* scpi lexer */
+ enum _scpi_token_type_t {
+ SCPI_TOKEN_COMMA,
+ SCPI_TOKEN_SEMICOLON,
+ SCPI_TOKEN_COLON,
+ SCPI_TOKEN_SPECIFIC_CHARACTER,
+ SCPI_TOKEN_QUESTION,
+ SCPI_TOKEN_NL,
+ SCPI_TOKEN_HEXNUM,
+ SCPI_TOKEN_OCTNUM,
+ SCPI_TOKEN_BINNUM,
+ SCPI_TOKEN_PROGRAM_MNEMONIC,
+ SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA,
+ SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA_WITH_SUFFIX,
+ SCPI_TOKEN_SUFFIX_PROGRAM_DATA,
+ SCPI_TOKEN_ARBITRARY_BLOCK_PROGRAM_DATA,
+ SCPI_TOKEN_SINGLE_QUOTE_PROGRAM_DATA,
+ SCPI_TOKEN_DOUBLE_QUOTE_PROGRAM_DATA,
+ SCPI_TOKEN_PROGRAM_EXPRESSION,
+ SCPI_TOKEN_COMPOUND_PROGRAM_HEADER,
+ SCPI_TOKEN_INCOMPLETE_COMPOUND_PROGRAM_HEADER,
+ SCPI_TOKEN_COMMON_PROGRAM_HEADER,
+ SCPI_TOKEN_INCOMPLETE_COMMON_PROGRAM_HEADER,
+ SCPI_TOKEN_COMPOUND_QUERY_PROGRAM_HEADER,
+ SCPI_TOKEN_COMMON_QUERY_PROGRAM_HEADER,
+ SCPI_TOKEN_WS,
+ SCPI_TOKEN_ALL_PROGRAM_DATA,
+ SCPI_TOKEN_INVALID,
+ SCPI_TOKEN_UNKNOWN,
+ };
+ typedef enum _scpi_token_type_t scpi_token_type_t;
+
+ struct _scpi_token_t {
+ scpi_token_type_t type;
+ char * ptr;
+ int len;
+ };
+ typedef struct _scpi_token_t scpi_token_t;
+
+ struct _lex_state_t {
+ char * buffer;
+ char * pos;
+ int len;
+ };
+ typedef struct _lex_state_t lex_state_t;
+
+ /* scpi parser */
+ enum _message_termination_t {
+ SCPI_MESSAGE_TERMINATION_NONE,
+ SCPI_MESSAGE_TERMINATION_NL,
+ SCPI_MESSAGE_TERMINATION_SEMICOLON,
+ };
+ typedef enum _message_termination_t message_termination_t;
+
+ struct _scpi_parser_state_t {
+ scpi_token_t programHeader;
+ scpi_token_t programData;
+ int numberOfParameters;
+ message_termination_t termination;
+ };
+ typedef struct _scpi_parser_state_t scpi_parser_state_t;
+
+ typedef scpi_result_t(*scpi_command_callback_t)(scpi_t *);
+
+ struct _scpi_error_info_heap_t {
+ size_t wr;
+ /* size_t rd; */
+ size_t count;
+ size_t size;
+ char * data;
+ };
+ typedef struct _scpi_error_info_heap_t scpi_error_info_heap_t;
+
+ struct _scpi_error_t {
+ int16_t error_code;
+#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION
+ char * device_dependent_info;
+#endif
+ };
+ typedef struct _scpi_error_t scpi_error_t;
+
+ struct _scpi_fifo_t {
+ int16_t wr;
+ int16_t rd;
+ int16_t count;
+ int16_t size;
+ scpi_error_t * data;
+ };
+ typedef struct _scpi_fifo_t scpi_fifo_t;
+
+ /* scpi units */
+ enum _scpi_unit_t {
+ SCPI_UNIT_NONE,
+ SCPI_UNIT_VOLT,
+ SCPI_UNIT_AMPER,
+ SCPI_UNIT_OHM,
+ SCPI_UNIT_HERTZ,
+ SCPI_UNIT_CELSIUS,
+ SCPI_UNIT_SECOND,
+ SCPI_UNIT_METER,
+ SCPI_UNIT_GRAY,
+ SCPI_UNIT_BECQUEREL,
+ SCPI_UNIT_MOLE,
+ SCPI_UNIT_DEGREE,
+ SCPI_UNIT_GRADE,
+ SCPI_UNIT_RADIAN,
+ SCPI_UNIT_REVOLUTION,
+ SCPI_UNIT_STERADIAN,
+ SCPI_UNIT_SIEVERT,
+ SCPI_UNIT_FARAD,
+ SCPI_UNIT_COULOMB,
+ SCPI_UNIT_SIEMENS,
+ SCPI_UNIT_ELECTRONVOLT,
+ SCPI_UNIT_JOULE,
+ SCPI_UNIT_NEWTON,
+ SCPI_UNIT_LUX,
+ SCPI_UNIT_HENRY,
+ SCPI_UNIT_ASTRONOMIC_UNIT,
+ SCPI_UNIT_INCH,
+ SCPI_UNIT_FOOT,
+ SCPI_UNIT_PARSEC,
+ SCPI_UNIT_MILE,
+ SCPI_UNIT_NAUTICAL_MILE,
+ SCPI_UNIT_LUMEN,
+ SCPI_UNIT_CANDELA,
+ SCPI_UNIT_WEBER,
+ SCPI_UNIT_TESLA,
+ SCPI_UNIT_ATOMIC_MASS,
+ SCPI_UNIT_KILOGRAM,
+ SCPI_UNIT_WATT,
+ SCPI_UNIT_DBM,
+ SCPI_UNIT_ATMOSPHERE,
+ SCPI_UNIT_INCH_OF_MERCURY,
+ SCPI_UNIT_MM_OF_MERCURY,
+ SCPI_UNIT_PASCAL,
+ SCPI_UNIT_TORT,
+ SCPI_UNIT_BAR,
+ SCPI_UNIT_DECIBEL,
+ SCPI_UNIT_UNITLESS,
+ SCPI_UNIT_FAHRENHEIT,
+ SCPI_UNIT_KELVIN,
+ SCPI_UNIT_DAY,
+ SCPI_UNIT_YEAR,
+ SCPI_UNIT_STROKES,
+ SCPI_UNIT_POISE,
+ SCPI_UNIT_LITER
+ };
+ typedef enum _scpi_unit_t scpi_unit_t;
+
+ struct _scpi_unit_def_t {
+ const char * name;
+ scpi_unit_t unit;
+ double mult;
+ };
+#define SCPI_UNITS_LIST_END {NULL, SCPI_UNIT_NONE, 0}
+ typedef struct _scpi_unit_def_t scpi_unit_def_t;
+
+ enum _scpi_special_number_t {
+ SCPI_NUM_NUMBER,
+ SCPI_NUM_MIN,
+ SCPI_NUM_MAX,
+ SCPI_NUM_DEF,
+ SCPI_NUM_UP,
+ SCPI_NUM_DOWN,
+ SCPI_NUM_NAN,
+ SCPI_NUM_INF,
+ SCPI_NUM_NINF,
+ SCPI_NUM_AUTO
+ };
+ typedef enum _scpi_special_number_t scpi_special_number_t;
+
+ struct _scpi_choice_def_t {
+ const char * name;
+ int32_t tag;
+ };
+#define SCPI_CHOICE_LIST_END {NULL, -1}
+ typedef struct _scpi_choice_def_t scpi_choice_def_t;
+
+ struct _scpi_param_list_t {
+ const scpi_command_t * cmd;
+ lex_state_t lex_state;
+ scpi_const_buffer_t cmd_raw;
+ };
+ typedef struct _scpi_param_list_t scpi_param_list_t;
+
+ struct _scpi_number_parameter_t {
+ scpi_bool_t special;
+
+ union {
+ double value;
+ int32_t tag;
+ } content;
+ scpi_unit_t unit;
+ int8_t base;
+ };
+ typedef struct _scpi_number_parameter_t scpi_number_t;
+
+ struct _scpi_data_parameter_t {
+ const char * ptr;
+ int32_t len;
+ };
+ typedef struct _scpi_data_parameter_t scpi_data_parameter_t;
+
+ typedef scpi_token_t scpi_parameter_t;
+
+ struct _scpi_command_t {
+ const char * pattern;
+ scpi_command_callback_t callback;
+#if USE_COMMAND_TAGS
+ int32_t tag;
+#endif /* USE_COMMAND_TAGS */
+ };
+
+ struct _scpi_interface_t {
+ scpi_error_callback_t error;
+ scpi_write_t write;
+ scpi_write_control_t control;
+ scpi_command_callback_t flush;
+ scpi_command_callback_t reset;
+ };
+
+ struct _scpi_t {
+ const scpi_command_t * cmdlist;
+ scpi_buffer_t buffer;
+ scpi_param_list_t param_list;
+ scpi_interface_t * interface;
+ int_fast16_t output_count;
+ int_fast16_t input_count;
+ scpi_bool_t first_output;
+ scpi_bool_t cmd_error;
+ scpi_fifo_t error_queue;
+#if USE_DEVICE_DEPENDENT_ERROR_INFORMATION && !USE_MEMORY_ALLOCATION_FREE
+ scpi_error_info_heap_t error_info_heap;
+#endif
+ scpi_reg_val_t registers[SCPI_REG_COUNT];
+ const scpi_unit_def_t * units;
+ void * user_context;
+ scpi_parser_state_t parser_state;
+ const char * idn[4];
+ size_t arbitrary_remaining;
+ };
+
+ enum _scpi_array_format_t {
+ SCPI_FORMAT_ASCII = 0,
+ SCPI_FORMAT_NORMAL = 1,
+ SCPI_FORMAT_SWAPPED = 2,
+ SCPI_FORMAT_BIGENDIAN = SCPI_FORMAT_NORMAL,
+ SCPI_FORMAT_LITTLEENDIAN = SCPI_FORMAT_SWAPPED,
+ };
+ typedef enum _scpi_array_format_t scpi_array_format_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SCPI_TYPES_H */
+
diff --git a/third_party/libscpi/inc/scpi/units.h b/third_party/libscpi/inc/scpi/units.h
new file mode 100644
index 00000000..345e9fa4
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/units.h
@@ -0,0 +1,60 @@
+/*-
+ * 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.h
+ * @date Thu Nov 15 10:58:45 UTC 2012
+ *
+ * @brief SCPI Units
+ *
+ *
+ */
+
+#ifndef SCPI_UNITS_H
+#define SCPI_UNITS_H
+
+#include "scpi/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ extern const scpi_unit_def_t scpi_units_def[];
+ extern const scpi_choice_def_t scpi_special_numbers_def[];
+
+ scpi_bool_t SCPI_ParamNumber(scpi_t * context, const scpi_choice_def_t * special, scpi_number_t * value, scpi_bool_t mandatory);
+
+ scpi_bool_t SCPI_ParamTranslateNumberVal(scpi_t * context, scpi_parameter_t * parameter);
+ size_t SCPI_NumberToStr(scpi_t * context, const scpi_choice_def_t * special, scpi_number_t * value, char * str, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SCPI_UNITS_H */
+
diff --git a/third_party/libscpi/inc/scpi/utils.h b/third_party/libscpi/inc/scpi/utils.h
new file mode 100644
index 00000000..055e4683
--- /dev/null
+++ b/third_party/libscpi/inc/scpi/utils.h
@@ -0,0 +1,62 @@
+/*-
+ * 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 utils.h
+ *
+ * @brief Conversion routines and string manipulation routines
+ *
+ *
+ */
+
+#ifndef SCPI_UTILS_H
+#define SCPI_UTILS_H
+
+#include <stdint.h>
+#include "scpi/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ size_t SCPI_UInt32ToStrBase(uint32_t val, char * str, size_t len, int8_t base);
+ size_t SCPI_Int32ToStr(int32_t val, char * str, size_t len);
+ size_t SCPI_UInt64ToStrBase(uint64_t val, char * str, size_t len, int8_t base);
+ size_t SCPI_Int64ToStr(int64_t val, char * str, size_t len);
+ size_t SCPI_FloatToStr(float val, char * str, size_t len);
+ size_t SCPI_DoubleToStr(double val, char * str, size_t len);
+
+ /* deprecated finction, should be removed later */
+#define SCPI_LongToStr(val, str, len, base) SCPI_Int32ToStr((val), (str), (len), (base), TRUE)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SCPI_UTILS_H */
+
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, &paramFrom, &paramTo);
+ if (res == SCPI_EXPR_OK) {
+ *isRange = range;
+ SCPI_ParamToInt32(context, &paramFrom, valueFrom);
+ if (range) {
+ SCPI_ParamToInt32(context, &paramTo, 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, &paramFrom, &paramTo);
+ if (res == SCPI_EXPR_OK) {
+ *isRange = range;
+ SCPI_ParamToDouble(context, &paramFrom, valueFrom);
+ if (range) {
+ SCPI_ParamToDouble(context, &paramTo, 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, &param)) {
+ if (i < length) {
+ SCPI_ParamToInt32(context, &param, &values[i]);
+ }
+
+ if (scpiLex_SpecificCharacter(state, &param, '!')) {
+ 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, &param, mandatory);
+ if (result) {
+ if (SCPI_ParamIsNumber(&param, FALSE)) {
+ SCPI_ParamToFloat(context, &param, value);
+ } else if (SCPI_ParamIsNumber(&param, 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, &param, mandatory);
+ if (result) {
+ if (SCPI_ParamIsNumber(&param, FALSE)) {
+ SCPI_ParamToDouble(context, &param, value);
+ } else if (SCPI_ParamIsNumber(&param, 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, &param, mandatory);
+ if (result) {
+ if (SCPI_ParamIsNumber(&param, FALSE)) {
+ result = ParamSignToUInt32(context, &param, value, sign);
+ } else if (SCPI_ParamIsNumber(&param, 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, &param, mandatory);
+ if (result) {
+ if (SCPI_ParamIsNumber(&param, FALSE)) {
+ result = ParamSignToUInt64(context, &param, value, sign);
+ } else if (SCPI_ParamIsNumber(&param, 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, &param, 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, &param, 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, &param, 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, &param, mandatory);
+
+ if (result) {
+ if (param.type == SCPI_TOKEN_DECIMAL_NUMERIC_PROGRAM_DATA) {
+ SCPI_ParamToInt32(context, &param, &intval);
+ *value = intval ? TRUE : FALSE;
+ } else {
+ result = SCPI_ParamToChoice(context, &param, 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, &param, mandatory);
+ if (result) {
+ result = SCPI_ParamToChoice(context, &param, 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, &param, 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, &param, &(value->content.value));
+ break;
+ case SCPI_TOKEN_HEXNUM:
+ SCPI_ParamToDouble(context, &param, &(value->content.value));
+ break;
+ case SCPI_TOKEN_OCTNUM:
+ SCPI_ParamToDouble(context, &param, &(value->content.value));
+ break;
+ case SCPI_TOKEN_BINNUM:
+ SCPI_ParamToDouble(context, &param, &(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, &param, &(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 */
+