Added BACnetRecipient and BACnetAddressBinding codecs for EPICS application (#1163)
* Added BACnetRecipient and BACnetAddressBinding encoding, decoding, ASCII conversion, comparison, and copy functions. * Added library specific string manipulation utilities including strcmp, strncmp, stptok, and snprintf with offset functions. * Added test/README about verification and validation testing.
This commit is contained in:
+173
-39
@@ -1087,6 +1087,32 @@ bool octetstring_init_ascii_hex(
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts an null terminated ASCII Hex EPICS formatted string
|
||||
* to an octet string.
|
||||
* @details format: X'c0:a8:00:0f'
|
||||
* @param octet_string Pointer to the octet string.
|
||||
* @param arg Pointer to the HEX-ASCII EPICS format string
|
||||
* @return true if successfully converted and fits; false if too long
|
||||
*/
|
||||
bool octetstring_init_ascii_epics(
|
||||
BACNET_OCTET_STRING *octet_string, const char *arg)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
|
||||
if (!octet_string) {
|
||||
return false;
|
||||
}
|
||||
if (!arg) {
|
||||
return false;
|
||||
}
|
||||
if (bacnet_strnicmp(arg, "X'", 2) == 0) {
|
||||
status = octetstring_init_ascii_hex(octet_string, arg + 2);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an octet string from source to destination.
|
||||
*
|
||||
@@ -1273,14 +1299,8 @@ bool octetstring_value_same(
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Compare two strings, case insensitive
|
||||
* @param a - first string
|
||||
* @param b - second string
|
||||
* @return 0 if the strings are equal, non-zero if not
|
||||
* @note The stricmp() function is not included in the C standard.
|
||||
*/
|
||||
int bacnet_stricmp(const char *a, const char *b)
|
||||
static int bacnet_strnicmp_internal(
|
||||
const char *a, const char *b, size_t length, bool case_insensitive)
|
||||
{
|
||||
int twin_a, twin_b;
|
||||
|
||||
@@ -1290,18 +1310,66 @@ int bacnet_stricmp(const char *a, const char *b)
|
||||
if (b == NULL) {
|
||||
return 1;
|
||||
}
|
||||
if (length == 0) {
|
||||
length = strlen(a);
|
||||
}
|
||||
do {
|
||||
twin_a = *(const unsigned char *)a;
|
||||
twin_b = *(const unsigned char *)b;
|
||||
twin_a = tolower(toupper(twin_a));
|
||||
twin_b = tolower(toupper(twin_b));
|
||||
if (case_insensitive) {
|
||||
twin_a = tolower(toupper(twin_a));
|
||||
twin_b = tolower(toupper(twin_b));
|
||||
}
|
||||
a++;
|
||||
b++;
|
||||
} while ((twin_a == twin_b) && (twin_a != '\0'));
|
||||
length--;
|
||||
} while ((twin_a == twin_b) && (twin_a != '\0') && (length > 0));
|
||||
|
||||
return twin_a - twin_b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare two strings, case sensitive
|
||||
* @param a - first string
|
||||
* @param b - second string
|
||||
* @return 0 if the strings are equal, non-zero if not
|
||||
*/
|
||||
int bacnet_strcmp(const char *a, const char *b)
|
||||
{
|
||||
return bacnet_strnicmp_internal(a, b, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare two strings, case insensitive
|
||||
* @param a - first string
|
||||
* @param b - second string
|
||||
* @return 0 if the strings are equal, non-zero if not
|
||||
* @note The stricmp() function is not included in the C standard.
|
||||
*/
|
||||
int bacnet_stricmp(const char *a, const char *b)
|
||||
{
|
||||
return bacnet_strnicmp_internal(a, b, 0, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare two strings, case sensitive, with length limit
|
||||
* @details The strncmp() function compares, at most, the first n characters
|
||||
* of string1 and string2 with sensitivity to case.
|
||||
*
|
||||
* The function operates on null terminated strings.
|
||||
* The string arguments to the function are expected to contain
|
||||
* a null character (\0) marking the end of the string.
|
||||
*
|
||||
* @param a - first string
|
||||
* @param b - second string
|
||||
* @param length - maximum length to compare
|
||||
* @return 0 if the strings are equal, non-zero if not
|
||||
*/
|
||||
int bacnet_strncmp(const char *a, const char *b, size_t length)
|
||||
{
|
||||
return bacnet_strnicmp_internal(a, b, length, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare two strings, case insensitive, with length limit
|
||||
* @details The strnicmp() function compares, at most, the first n characters
|
||||
@@ -1319,28 +1387,7 @@ int bacnet_stricmp(const char *a, const char *b)
|
||||
*/
|
||||
int bacnet_strnicmp(const char *a, const char *b, size_t length)
|
||||
{
|
||||
int twin_a, twin_b;
|
||||
|
||||
if (length == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (a == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (b == NULL) {
|
||||
return 1;
|
||||
}
|
||||
do {
|
||||
twin_a = *(const unsigned char *)a;
|
||||
twin_b = *(const unsigned char *)b;
|
||||
twin_a = tolower(toupper(twin_a));
|
||||
twin_b = tolower(toupper(twin_b));
|
||||
a++;
|
||||
b++;
|
||||
length--;
|
||||
} while ((twin_a == twin_b) && (twin_a != '\0') && (length > 0));
|
||||
|
||||
return twin_a - twin_b;
|
||||
return bacnet_strnicmp_internal(a, b, length, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1784,7 +1831,7 @@ bool bacnet_string_to_unsigned(
|
||||
* @return character string buffer
|
||||
*/
|
||||
char *
|
||||
bacnet_sprintf_to_ascii(char *buffer, size_t size, const char *format, ...)
|
||||
bacnet_snprintf_to_ascii(char *buffer, size_t size, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
@@ -1807,7 +1854,7 @@ bacnet_sprintf_to_ascii(char *buffer, size_t size, const char *format, ...)
|
||||
*/
|
||||
char *bacnet_dtoa(double value, char *buffer, size_t size, unsigned precision)
|
||||
{
|
||||
return bacnet_sprintf_to_ascii(buffer, size, "%.*f", precision, value);
|
||||
return bacnet_snprintf_to_ascii(buffer, size, "%.*f", precision, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1819,7 +1866,7 @@ char *bacnet_dtoa(double value, char *buffer, size_t size, unsigned precision)
|
||||
*/
|
||||
char *bacnet_itoa(int value, char *buffer, size_t size)
|
||||
{
|
||||
return bacnet_sprintf_to_ascii(buffer, size, "%d", value);
|
||||
return bacnet_snprintf_to_ascii(buffer, size, "%d", value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1831,7 +1878,7 @@ char *bacnet_itoa(int value, char *buffer, size_t size)
|
||||
*/
|
||||
char *bacnet_ltoa(long value, char *buffer, size_t size)
|
||||
{
|
||||
return bacnet_sprintf_to_ascii(buffer, size, "%ld", value);
|
||||
return bacnet_snprintf_to_ascii(buffer, size, "%ld", value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1843,7 +1890,7 @@ char *bacnet_ltoa(long value, char *buffer, size_t size)
|
||||
*/
|
||||
char *bacnet_utoa(unsigned value, char *buffer, size_t size)
|
||||
{
|
||||
return bacnet_sprintf_to_ascii(buffer, size, "%u", value);
|
||||
return bacnet_snprintf_to_ascii(buffer, size, "%u", value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1855,7 +1902,7 @@ char *bacnet_utoa(unsigned value, char *buffer, size_t size)
|
||||
*/
|
||||
char *bacnet_ultoa(unsigned long value, char *buffer, size_t size)
|
||||
{
|
||||
return bacnet_sprintf_to_ascii(buffer, size, "%lu", value);
|
||||
return bacnet_snprintf_to_ascii(buffer, size, "%lu", value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1909,3 +1956,90 @@ char *bacnet_trim(char *str, const char *trimmedchars)
|
||||
{
|
||||
return bacnet_ltrim(bacnet_rtrim(str, trimmedchars), trimmedchars);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse a token from a string.
|
||||
* @details From a string, a buffer to receive the "token" that gets scanned,
|
||||
* the length of the buffer, and a string of "break" characters that stop
|
||||
* the scan. The function will copy the string into the buffer up to any
|
||||
* of the break characters, or until the buffer is full, and will always
|
||||
* leave the buffer null-terminated. The function will return a pointer
|
||||
* to the first non-breaking character after the one that stopped the scan.
|
||||
* @param s string to parse
|
||||
* @param tok buffer that receives the "token" that gets scanned
|
||||
* @param toklen length of the buffer
|
||||
* @param brk string of break characters that will stop the scan
|
||||
* @return a pointer to the first non-breaking character after the one that
|
||||
* stopped the scan or NULL on error or end of string.
|
||||
* @note public domain by Ray Gardner, modified by Bob Stout and Steve Karg
|
||||
*/
|
||||
char *bacnet_stptok(const char *s, char *tok, size_t toklen, const char *brk)
|
||||
{
|
||||
char *lim; /* limit of token */
|
||||
const char *b; /* current break character */
|
||||
|
||||
/* check for invalid pointers */
|
||||
if (!s || !tok || !brk) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* check for empty string */
|
||||
if (!*s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lim = tok + toklen - 1;
|
||||
while (*s && tok < lim) {
|
||||
for (b = brk; *b; b++) {
|
||||
if (*s == *b) {
|
||||
*tok = 0;
|
||||
for (++s, b = brk; *s && *b; ++b) {
|
||||
if (*s == *b) {
|
||||
++s;
|
||||
b = brk;
|
||||
}
|
||||
}
|
||||
if (!*s) {
|
||||
return NULL;
|
||||
}
|
||||
return (char *)s;
|
||||
}
|
||||
}
|
||||
*tok++ = *s++;
|
||||
}
|
||||
*tok = 0;
|
||||
|
||||
if (!*s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (char *)s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief General purpose print formatter snprintf() function with offset
|
||||
* @param buffer - destination string
|
||||
* @param size - length of the destination string
|
||||
* @param offset - offset into the buffer to start the string
|
||||
* @param format - format string
|
||||
* @return total number of characters written from the beginning of buffer
|
||||
*/
|
||||
int bacnet_snprintf(
|
||||
char *buffer, size_t size, int offset, const char *format, ...)
|
||||
{
|
||||
int length = 0;
|
||||
va_list args;
|
||||
|
||||
if (offset < size) {
|
||||
size -= offset;
|
||||
if (buffer) {
|
||||
buffer += offset;
|
||||
}
|
||||
va_start(args, format);
|
||||
length = vsnprintf(buffer, size, format, args);
|
||||
va_end(args);
|
||||
return offset + length;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user