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:
Steve Karg
2025-11-27 23:12:39 -06:00
committed by GitHub
parent 21626d1ac5
commit ca9836b099
28 changed files with 1875 additions and 207 deletions
+448 -25
View File
@@ -16,10 +16,11 @@
#include "bacnet/bacdcode.h"
#include "bacnet/bacint.h"
#include "bacnet/bacstr.h"
#include "bacnet/bactext.h"
#include "bacnet/bacaddr.h"
/**
* @brief Copy a #BACNET_ADDRESS value to another
* @brief Copy a #BACNET_ADDRESS value to another, or initialize if src=NULL
* @param dest - #BACNET_ADDRESS to be copied into
* @param src - #BACNET_ADDRESS to be copied from
*/
@@ -37,6 +38,16 @@ void bacnet_address_copy(BACNET_ADDRESS *dest, const BACNET_ADDRESS *src)
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = src->adr[i];
}
} else if (dest) {
dest->mac_len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->mac[i] = 0;
}
dest->net = 0;
dest->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
}
@@ -87,6 +98,57 @@ bool bacnet_address_same(const BACNET_ADDRESS *dest, const BACNET_ADDRESS *src)
return true;
}
/**
* @brief Compare two BACnetAddress strictly from encoding based
* on network number.
* BACnetAddress ::= SEQUENCE {
* network-number Unsigned16,-- A value of 0 indicates the local network
* mac-address OctetString -- A string of length 0 indicates a broadcast
* }
* @param dest - BACnetAddress to be compared
* @param src - BACnetAddress to be compared
* @return true if the same values
*/
bool bacnet_address_net_same(
const BACNET_ADDRESS *dest, const BACNET_ADDRESS *src)
{
uint8_t i = 0; /* loop counter */
if (!dest || !src) {
return false;
}
if (dest->net != src->net) {
return false;
}
if (dest->net == 0) {
/* local address stored in MAC */
if (dest->mac_len != src->mac_len) {
return false;
}
for (i = 0; i < MAX_MAC_LEN; i++) {
if (i < dest->mac_len) {
if (dest->mac[i] != src->mac[i]) {
return false;
}
}
}
} else {
/* remote address stored in ADR */
if (dest->len != src->len) {
return false;
}
for (i = 0; i < MAX_MAC_LEN; i++) {
if (i < dest->len) {
if (dest->adr[i] != src->adr[i]) {
return false;
}
}
}
}
return true;
}
/**
* @brief Configure a #BACNET_ADDRESS from mac, dnet, and adr
* @param dest - #BACNET_ADDRESS to be configured
@@ -194,6 +256,10 @@ void bacnet_address_mac_init(
/**
* @brief Parse an ASCII string for a bacnet-address
* @details Address formats:
* 192.168.1.42:47808 - for BACnet/IP
* ff:aa:ff:bb:ff:cc - for Ethernet
* fa or 127 - for ARCNET or MS/TP
* @param mac [out] BACNET_MAC_ADDRESS structure to store the results
* @param arg [in] nul terminated ASCII string to parse
* @return true if the address was parsed
@@ -226,7 +292,6 @@ bool bacnet_address_mac_from_ascii(BACNET_MAC_ADDRESS *mac, const char *arg)
c = sscanf(
arg, "%2x:%2x:%2x:%2x:%2x:%2x", &a[0], &a[1], &a[2], &a[3], &a[4],
&a[5]);
if (c > 0) {
for (i = 0; i < c; i++) {
mac->adr[i] = a[i];
@@ -239,6 +304,65 @@ bool bacnet_address_mac_from_ascii(BACNET_MAC_ADDRESS *mac, const char *arg)
return status;
}
/**
* @brief Parse an ASCII string for a bacnet-address
* @details Address formats: mac net addr
* 192.168.1.42:47808 - for BACnet/IP
* ff:aa:ff:bb:ff:cc - for Ethernet
* fa - for ARCNET or MS/TP
* 65535 - network number, 0 if mac but no addr
* @param src [out] BACNET_MAC_ADDRESS structure to store the results
* @param arg [in] null terminated ASCII string to parse
* @return true if the address was parsed
*/
bool bacnet_address_from_ascii(BACNET_ADDRESS *src, const char *arg)
{
bool status = false;
int count = 0;
unsigned snet = 0, i;
char mac_string[80] = { "" }, sadr_string[80] = { "" };
BACNET_MAC_ADDRESS mac = { 0 };
if (!(src && arg)) {
return false;
}
count = sscanf(
arg, "{%79[^,],%u,%79[^}]}", &mac_string[0], &snet, &sadr_string[0]);
if (count > 0) {
if (bacnet_address_mac_from_ascii(&mac, mac_string)) {
if (src) {
src->mac_len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->mac[i] = mac.adr[i];
}
}
}
if (src) {
src->net = (uint16_t)snet;
}
if (snet) {
if (bacnet_address_mac_from_ascii(&mac, sadr_string)) {
if (src) {
src->len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->adr[i] = mac.adr[i];
}
}
}
} else {
if (src) {
src->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->adr[i] = 0;
}
}
}
status = true;
}
return status;
}
/**
* @brief Decodes a BACnetAddress value from APDU buffer
* From clause 21. FORMAL DESCRIPTION OF APPLICATION PROTOCOL DATA UNITS
@@ -260,19 +384,22 @@ int bacnet_address_decode(
int len = 0;
int apdu_len = 0;
uint8_t i = 0;
BACNET_UNSIGNED_INTEGER decoded_unsigned = 0;
BACNET_UNSIGNED_INTEGER snet = 0;
BACNET_OCTET_STRING mac_addr = { 0 };
if (!apdu) {
return BACNET_STATUS_ERROR;
}
/* network number */
len = bacnet_unsigned_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &decoded_unsigned);
&apdu[apdu_len], apdu_size - apdu_len, &snet);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (decoded_unsigned <= UINT16_MAX) {
if (snet <= UINT16_MAX) {
/* bounds checking - passed! */
if (value) {
value->net = (uint16_t)decoded_unsigned;
value->net = (uint16_t)snet;
}
} else {
return BACNET_STATUS_ERROR;
@@ -284,15 +411,29 @@ int bacnet_address_decode(
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (value) {
if (mac_addr.length > sizeof(value->mac)) {
return BACNET_STATUS_ERROR;
if (snet) {
if (value) {
if (mac_addr.length > sizeof(value->adr)) {
return BACNET_STATUS_ERROR;
}
/* bounds checking - passed! */
value->len = mac_addr.length;
/* copy address */
for (i = 0; i < value->len; i++) {
value->adr[i] = mac_addr.value[i];
}
}
/* bounds checking - passed! */
value->mac_len = mac_addr.length;
/* copy address */
for (i = 0; i < value->mac_len; i++) {
value->mac[i] = mac_addr.value[i];
} else {
if (value) {
if (mac_addr.length > sizeof(value->mac)) {
return BACNET_STATUS_ERROR;
}
/* bounds checking - passed! */
value->mac_len = mac_addr.length;
/* copy address */
for (i = 0; i < value->mac_len; i++) {
value->mac[i] = mac_addr.value[i];
}
}
}
apdu_len += len;
@@ -317,6 +458,9 @@ int bacnet_address_context_decode(
int len = 0;
int apdu_len = 0;
if (!apdu) {
return BACNET_STATUS_ERROR;
}
if (!bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) {
return BACNET_STATUS_ERROR;
@@ -346,23 +490,28 @@ int bacnet_address_context_decode(
*/
int encode_bacnet_address(uint8_t *apdu, const BACNET_ADDRESS *destination)
{
int apdu_len = 0;
BACNET_OCTET_STRING mac_addr;
int apdu_len = 0, len, adr_len, mac_len;
if (destination) {
/* network number */
apdu_len += encode_application_unsigned(apdu, destination->net);
/* encode mac address as an octet-string */
if (destination->len != 0) {
octetstring_init(&mac_addr, destination->adr, destination->len);
} else {
octetstring_init(&mac_addr, destination->mac, destination->mac_len);
}
len = encode_application_unsigned(apdu, destination->net);
apdu_len += len;
if (apdu) {
apdu += apdu_len;
apdu += len;
}
apdu_len += encode_application_octet_string(apdu, &mac_addr);
/* encode mac address as an octet-string */
if (destination->len > 0) {
adr_len = min(destination->len, sizeof(destination->adr));
len = encode_application_octet_string_buffer(
apdu, destination->adr, adr_len);
} else {
mac_len = min(destination->mac_len, sizeof(destination->mac));
len = encode_application_octet_string_buffer(
apdu, destination->mac, mac_len);
}
apdu_len += len;
}
return apdu_len;
}
@@ -500,6 +649,9 @@ int bacnet_vmac_entry_decode(
size_t i = 0;
BACNET_OCTET_STRING mac_addr = { 0 };
if (!apdu) {
return BACNET_STATUS_ERROR;
}
/* virtual-mac-address [0] OctetString */
len = bacnet_octet_string_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, &mac_addr);
@@ -565,3 +717,274 @@ bool bacnet_vmac_address_set(BACNET_ADDRESS *addr, uint32_t device_id)
return status;
}
/**
* @brief Encode a given BACnetAddressBinding
* @details
* BACnetAddressBinding ::= SEQUENCE {
* device-identifier BACnetObjectIdentifier,
* device-address BACnetAddress
* @param apdu - APDU buffer for storing the encoded data, or NULL for length
* @param value - BACnetAddressBinding value
* @return number of bytes in the APDU
*/
int bacnet_address_binding_type_encode(
uint8_t *apdu, const BACNET_ADDRESS_BINDING *value)
{
int apdu_len = 0, len = 0;
if (!value) {
return 0;
}
len = encode_application_object_id(
apdu, OBJECT_DEVICE, value->device_identifier);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_bacnet_address(apdu, &value->device_address);
apdu_len += len;
return apdu_len;
}
/**
* @brief Encode a given BACnetAddressBinding
* @details
* BACnetAddressBinding ::= SEQUENCE {
* device-identifier BACnetObjectIdentifier,
* device-address BACnetAddress
* @param apdu - APDU buffer for storing the encoded data, or NULL for length
* @param apdu_size - size of the APDU buffer
* @param value - BACnetAddressBinding value
* @return number of bytes in the APDU, or 0 if unable to fit.
*/
int bacnet_address_binding_encode(
uint8_t *apdu, size_t apdu_size, const BACNET_ADDRESS_BINDING *value)
{
size_t apdu_len = 0; /* total length of the apdu, return value */
apdu_len = bacnet_address_binding_type_encode(NULL, value);
if (apdu_len > apdu_size) {
apdu_len = 0;
} else {
apdu_len = bacnet_address_binding_type_encode(apdu, value);
}
return apdu_len;
}
/**
* @brief Decode a given BACnetAddressBinding
* @details
* BACnetAddressBinding ::= SEQUENCE {
* device-identifier BACnetObjectIdentifier,
* device-address BACnetAddress
* @param apdu - APDU buffer for decoding
* @param apdu_size - Count of valid bytes in the buffer
* @param value - BACnetAddressBinding value to store the decoded data
* @return number of bytes decoded or BACNET_STATUS_ERROR on error
*/
int bacnet_address_binding_decode(
const uint8_t *apdu, size_t apdu_size, BACNET_ADDRESS_BINDING *value)
{
int apdu_len = 0, len = 0;
BACNET_OBJECT_TYPE object_type;
uint32_t object_instance;
BACNET_ADDRESS *address = NULL;
if (!apdu) {
return BACNET_STATUS_ERROR;
}
len = bacnet_object_id_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &object_type, &object_instance);
if (len > 0) {
apdu_len += len;
if (object_type != OBJECT_DEVICE) {
return BACNET_STATUS_ERROR;
}
if (value) {
value->device_identifier = object_instance;
}
} else {
return BACNET_STATUS_ERROR;
}
if (value) {
address = &value->device_address;
}
len = bacnet_address_decode(&apdu[apdu_len], apdu_size - apdu_len, address);
if (len > 0) {
apdu_len += len;
} else {
return BACNET_STATUS_ERROR;
}
return apdu_len;
}
/**
* @brief Compare two BACnetAddressBinding values
* @param value1 [in] The first BACnetAddressBinding value
* @param value2 [in] The second BACnetAddressBinding value
* @return True if the values are the same, else False
*/
bool bacnet_address_binding_same(
const BACNET_ADDRESS_BINDING *value1, const BACNET_ADDRESS_BINDING *value2)
{
if (!value1) {
return false;
}
if (!value2) {
return false;
}
if (value1->device_identifier != value2->device_identifier) {
return false;
}
return bacnet_address_same(
&value1->device_address, &value2->device_address);
}
/**
* @brief Copy a BACnetAddressBinding to another
* @param value1 [in] The first BACnetAddressBinding value
* @param value2 [in] The second BACnetAddressBinding value
* @return true if the value was copied, else false
*/
bool bacnet_address_binding_copy(
BACNET_ADDRESS_BINDING *dest, const BACNET_ADDRESS_BINDING *src)
{
if (!(dest && src)) {
return false;
}
dest->device_identifier = src->device_identifier;
bacnet_address_copy(&dest->device_address, &src->device_address);
return true;
}
/**
* @brief Initialize a BACnetAddressBinding from device-id and address
* @param dest [out] The BACnetAddressBinding value
* @param device_identifier [in] The device-identifier value
* @param address [in] #BACNET_ADDRESS value
* @return true if the values were copied, else false
*/
bool bacnet_address_binding_init(
BACNET_ADDRESS_BINDING *dest,
uint32_t device_id,
const BACNET_ADDRESS *address)
{
if (!dest) {
return false;
}
dest->device_identifier = device_id;
bacnet_address_copy(&dest->device_address, address);
return true;
}
/**
* @brief Produce a string from a BACnetAddressBinding structure
* @param value [in] The BACnetAddressBinding value
* @param str [out] The string to produce, NULL to get length only
* @param str_size [in] The size of the string buffer
* @return length of the produced string
* @details Output format:
* {(device, 1234),1234,X'c0:a8:00:0f'}
*/
int bacnet_address_binding_to_ascii(
const BACNET_ADDRESS_BINDING *value, char *str, size_t str_len)
{
int offset = 0;
int i;
if (!value) {
return 0;
}
offset = bacnet_snprintf(str, str_len, offset, "{");
/* device-identifier */
offset = bacnet_snprintf(str, str_len, offset, "(");
offset = bacnet_snprintf(
str, str_len, offset, "%s, ", bactext_object_type_name(OBJECT_DEVICE));
offset = bacnet_snprintf(
str, str_len, offset, "%lu),", (unsigned long)value->device_identifier);
/* snet */
offset = bacnet_snprintf(
str, str_len, offset, "%lu,", (unsigned long)value->device_address.net);
/* octetstring */
offset = bacnet_snprintf(str, str_len, offset, "X'");
if (value->device_address.net) {
/* adr */
for (i = 0; i < value->device_address.len; i++) {
offset = bacnet_snprintf(
str, str_len, offset, "%02X",
(unsigned)value->device_address.adr[i]);
}
} else {
/* mac */
for (i = 0; i < value->device_address.mac_len; i++) {
offset = bacnet_snprintf(
str, str_len, offset, "%02X",
(unsigned)value->device_address.mac[i]);
}
}
offset = bacnet_snprintf(str, str_len, offset, "'");
offset = bacnet_snprintf(str, str_len, offset, "}");
return offset;
}
/**
* @brief Parse a string into a BACnetAddressBinding structure
* @param value [out] The BACnetAddressBinding value
* @param argv [in] The string to parse
* @return true on success, else false
* @details format:
* {(device, 1234),1234,X'c0:a8:00:0f'}
*/
bool bacnet_address_binding_from_ascii(
BACNET_ADDRESS_BINDING *value, const char *arg)
{
int count = 0, i = 0;
unsigned snet = 0;
char mac_string[80] = { "" }, obj_string[80] = { "" };
BACNET_MAC_ADDRESS mac = { 0 };
uint32_t object_type = 0;
unsigned long object_instance = 0;
if (!(value && arg)) {
return false;
}
count = sscanf(
arg, "{(%79[^,], %lu),%u,X'%79[^']'}", obj_string, &object_instance,
&snet, mac_string);
if (count == 4) {
if (!bactext_object_type_strtol(obj_string, &object_type)) {
return false;
}
if (object_type != OBJECT_DEVICE) {
return false;
}
if (!bacnet_address_mac_from_ascii(&mac, mac_string)) {
return false;
}
if (value) {
value->device_identifier = object_instance;
value->device_address.net = snet;
if (snet) {
value->device_address.len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
value->device_address.adr[i] = mac.adr[i];
}
} else {
value->device_address.mac_len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
value->device_address.mac[i] = mac.adr[i];
}
}
}
return true;
}
return false;
}
+43
View File
@@ -30,6 +30,17 @@ typedef struct BACnetVMACEntry {
struct BACnetVMACEntry *next;
} BACNET_VMAC_ENTRY;
/** BACnetAddressBinding ::= SEQUENCE {
* device-identifier BACnetObjectIdentifier,
* device-address BACnetAddress
*
*/
typedef struct BACnetAddressBinding {
uint32_t device_identifier;
BACNET_ADDRESS device_address;
struct BACnetAddressBinding *next;
} BACNET_ADDRESS_BINDING;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
@@ -39,6 +50,9 @@ void bacnet_address_copy(BACNET_ADDRESS *dest, const BACNET_ADDRESS *src);
BACNET_STACK_EXPORT
bool bacnet_address_same(const BACNET_ADDRESS *dest, const BACNET_ADDRESS *src);
BACNET_STACK_EXPORT
bool bacnet_address_net_same(
const BACNET_ADDRESS *dest, const BACNET_ADDRESS *src);
BACNET_STACK_EXPORT
bool bacnet_address_init(
BACNET_ADDRESS *dest,
const BACNET_MAC_ADDRESS *mac,
@@ -53,6 +67,8 @@ void bacnet_address_mac_init(
BACNET_MAC_ADDRESS *mac, const uint8_t *adr, uint8_t len);
BACNET_STACK_EXPORT
bool bacnet_address_mac_from_ascii(BACNET_MAC_ADDRESS *mac, const char *arg);
BACNET_STACK_EXPORT
bool bacnet_address_from_ascii(BACNET_ADDRESS *src, const char *arg);
BACNET_STACK_EXPORT
int bacnet_address_decode(
@@ -90,6 +106,33 @@ int bacnet_vmac_entry_decode(
BACNET_STACK_EXPORT
bool bacnet_vmac_address_set(BACNET_ADDRESS *addr, uint32_t device_id);
BACNET_STACK_EXPORT
int bacnet_address_binding_type_encode(
uint8_t *apdu, const BACNET_ADDRESS_BINDING *value);
BACNET_STACK_EXPORT
int bacnet_address_binding_decode(
const uint8_t *apdu, size_t adpu_size, BACNET_ADDRESS_BINDING *value);
BACNET_STACK_EXPORT
int bacnet_address_binding_encode(
uint8_t *apdu, size_t adpu_size, const BACNET_ADDRESS_BINDING *value);
BACNET_STACK_EXPORT
bool bacnet_address_binding_same(
const BACNET_ADDRESS_BINDING *value1, const BACNET_ADDRESS_BINDING *value2);
BACNET_STACK_EXPORT
bool bacnet_address_binding_copy(
BACNET_ADDRESS_BINDING *dest, const BACNET_ADDRESS_BINDING *src);
BACNET_STACK_EXPORT
bool bacnet_address_binding_init(
BACNET_ADDRESS_BINDING *dest,
uint32_t device_id,
const BACNET_ADDRESS *address);
BACNET_STACK_EXPORT
int bacnet_address_binding_to_ascii(
const BACNET_ADDRESS_BINDING *value, char *buf, size_t buf_size);
BACNET_STACK_EXPORT
bool bacnet_address_binding_from_ascii(
BACNET_ADDRESS_BINDING *value, const char *arg);
#ifdef __cplusplus
}
#endif /* __cplusplus */
+75 -7
View File
@@ -535,6 +535,18 @@ int bacapp_encode_application_data(
apdu, &value->type.Timer_Value);
break;
#endif
#if defined(BACAPP_RECIPIENT)
case BACNET_APPLICATION_TAG_RECIPIENT:
apdu_len =
bacnet_recipient_encode(apdu, &value->type.Recipient);
break;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
case BACNET_APPLICATION_TAG_ADDRESS_BINDING:
apdu_len = bacnet_address_binding_type_encode(
apdu, &value->type.Address_Binding);
break;
#endif
#if defined(BACAPP_NO_VALUE)
case BACNET_APPLICATION_TAG_NO_VALUE:
apdu_len = bacnet_timer_value_no_value_encode(apdu);
@@ -1308,14 +1320,14 @@ int bacapp_known_property_tag(
case PROP_TIME_SYNCHRONIZATION_RECIPIENTS:
case PROP_RESTART_NOTIFICATION_RECIPIENTS:
case PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS:
/* FIXME: Properties using BACnetRecipient */
return -1;
/* Properties using BACnetRecipient */
return BACNET_APPLICATION_TAG_RECIPIENT;
case PROP_DEVICE_ADDRESS_BINDING:
case PROP_MANUAL_SLAVE_ADDRESS_BINDING:
case PROP_SLAVE_ADDRESS_BINDING:
/* FIXME: BACnetAddressBinding */
return -1;
/* BACnetAddressBinding */
return BACNET_APPLICATION_TAG_ADDRESS_BINDING;
case PROP_LOG_BUFFER:
/* BACnetLogRecord */
@@ -1684,6 +1696,18 @@ int bacapp_decode_application_tag_value(
apdu, apdu_size, &value->type.Timer_Value);
break;
#endif
#if defined(BACAPP_RECIPIENT)
case BACNET_APPLICATION_TAG_RECIPIENT:
apdu_len = bacnet_recipient_decode(
apdu, apdu_size, &value->type.Recipient);
break;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
case BACNET_APPLICATION_TAG_ADDRESS_BINDING:
apdu_len = bacnet_address_binding_decode(
apdu, apdu_size, &value->type.Address_Binding);
break;
#endif
#if defined(BACAPP_LOG_RECORD)
case BACNET_APPLICATION_TAG_LOG_RECORD:
/* BACnetLogRecord */
@@ -2196,10 +2220,12 @@ int bacapp_snprintf_octet_string(
int len = 0;
int slen = 0;
int i = 0;
const uint8_t *octet_str;
slen = bacapp_snprintf(str, str_len, "X'");
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
len = octetstring_length(value);
if (len > 0) {
const uint8_t *octet_str;
octet_str = octetstring_value((BACNET_OCTET_STRING *)value);
for (i = 0; i < len; i++) {
slen = bacapp_snprintf(str, str_len, "%02X", *octet_str);
@@ -2207,6 +2233,8 @@ int bacapp_snprintf_octet_string(
octet_str++;
}
}
slen = bacapp_snprintf(str, str_len, "'");
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
return ret_val;
}
@@ -4027,6 +4055,18 @@ int bacapp_snprintf_value(
&value->type.Timer_Value, str, str_len);
break;
#endif
#if defined(BACAPP_RECIPIENT)
case BACNET_APPLICATION_TAG_RECIPIENT:
ret_val = bacnet_recipient_to_ascii(
&value->type.Recipient, str, str_len);
break;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
case BACNET_APPLICATION_TAG_ADDRESS_BINDING:
ret_val = bacnet_address_binding_to_ascii(
&value->type.Address_Binding, str, str_len);
break;
#endif
#if defined(BACAPP_NO_VALUE)
case BACNET_APPLICATION_TAG_NO_VALUE:
ret_val = bacnet_timer_value_no_value_to_ascii(str, str_len);
@@ -4542,8 +4582,11 @@ bool bacapp_parse_application_data(
#endif
#if defined(BACAPP_OCTET_STRING)
case BACNET_APPLICATION_TAG_OCTET_STRING:
status =
octetstring_init_ascii_hex(&value->type.Octet_String, argv);
if (!octetstring_init_ascii_epics(
&value->type.Octet_String, argv)) {
status = octetstring_init_ascii_hex(
&value->type.Octet_String, argv);
}
break;
#endif
#if defined(BACAPP_CHARACTER_STRING)
@@ -4741,6 +4784,18 @@ bool bacapp_parse_application_data(
&value->type.Timer_Value, argv);
break;
#endif
#if defined(BACAPP_RECIPIENT)
case BACNET_APPLICATION_TAG_RECIPIENT:
status =
bacnet_recipient_from_ascii(&value->type.Recipient, argv);
break;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
case BACNET_APPLICATION_TAG_ADDRESS_BINDING:
status = bacnet_address_binding_from_ascii(
&value->type.Address_Binding, argv);
break;
#endif
#if defined(BACAPP_NO_VALUE)
case BACNET_APPLICATION_TAG_NO_VALUE:
status =
@@ -5495,6 +5550,19 @@ bool bacapp_same_value(
&value->type.Timer_Value, &test_value->type.Timer_Value);
break;
#endif
#if defined(BACAPP_RECIPIENT)
case BACNET_APPLICATION_TAG_RECIPIENT:
status = bacnet_recipient_same(
&value->type.Recipient, &test_value->type.Recipient);
break;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
case BACNET_APPLICATION_TAG_ADDRESS_BINDING:
status = bacnet_address_binding_same(
&value->type.Address_Binding,
&test_value->type.Address_Binding);
break;
#endif
#if defined(BACAPP_NO_VALUE)
case BACNET_APPLICATION_TAG_NO_VALUE:
if (value->tag == test_value->tag) {
+7
View File
@@ -17,6 +17,7 @@
/* BACnet Stack API */
#include "bacnet/access_rule.h"
#include "bacnet/bacaction.h"
#include "bacnet/bacaddr.h"
#include "bacnet/bacdest.h"
#include "bacnet/bacint.h"
#include "bacnet/baclog.h"
@@ -179,6 +180,12 @@ typedef struct BACnet_Application_Data_Value {
#if defined(BACAPP_TIMER_VALUE)
BACNET_TIMER_STATE_CHANGE_VALUE Timer_Value;
#endif
#if defined(BACAPP_RECIPIENT)
BACNET_RECIPIENT Recipient;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
BACNET_ADDRESS_BINDING Address_Binding;
#endif
#if defined(BACAPP_LOG_RECORD)
BACNET_LOG_RECORD Log_Record;
#endif
+30
View File
@@ -2256,6 +2256,36 @@ int encode_application_octet_string(
return len;
}
/**
* @brief Encode the BACnet Octet String value as application tagged
* from clause 20.2.8 Encoding of an Octet String Value
* and 20.2.1 General Rules for Encoding BACnet Tags
* @param apdu - buffer to hold the bytes
* @param buffer - the octet string to be encoded
* @param buffer_size - the size of the octet string to be encoded
* @return returns the number of apdu bytes encoded
*/
int encode_application_octet_string_buffer(
uint8_t *apdu, const uint8_t *buffer, size_t buffer_size)
{
int apdu_len = 0, len = 0, i = 0;
len = encode_tag(
apdu, BACNET_APPLICATION_TAG_OCTET_STRING, false, buffer_size);
apdu_len += len;
if (apdu) {
apdu += len;
}
for (i = 0; i < buffer_size; i++) {
if (apdu && buffer) {
apdu[i] = buffer[i];
}
}
apdu_len += buffer_size;
return apdu_len;
}
/**
* @brief Encode the BACnet Octet String Value as context tagged
* from clause 20.2.8 Encoding of an Octet String Value
+3
View File
@@ -383,6 +383,9 @@ BACNET_STACK_EXPORT
int encode_application_octet_string(
uint8_t *apdu, const BACNET_OCTET_STRING *octet_string);
BACNET_STACK_EXPORT
int encode_application_octet_string_buffer(
uint8_t *apdu, const uint8_t *buffer, size_t buffer_size);
BACNET_STACK_EXPORT
int encode_context_octet_string(
uint8_t *apdu, uint8_t tag_number, const BACNET_OCTET_STRING *octet_string);
BACNET_STACK_EXPORT
+268 -68
View File
@@ -19,6 +19,7 @@
#include "bacnet/bacapp.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacdest.h"
#include "bacnet/bactext.h"
#include "bacnet/basic/binding/address.h"
/**
@@ -83,8 +84,8 @@ bool bacnet_recipient_same(
status = true;
}
} else if (r1->tag == BACNET_RECIPIENT_TAG_ADDRESS) {
status =
bacnet_address_same(&r1->type.address, &r2->type.address);
status = bacnet_address_net_same(
&r1->type.address, &r2->type.address);
} else {
status = false;
}
@@ -136,6 +137,20 @@ void bacnet_recipient_copy(BACNET_RECIPIENT *dest, const BACNET_RECIPIENT *src)
}
}
/**
* @brief Compare the BACnetRecipient data structure device object wildcard
* @param recipient - BACnetRecipient structure
* @return true if BACnetRecipient is equal to the device object wildcard
*/
void bacnet_recipient_device_wildcard_set(BACNET_RECIPIENT *recipient)
{
if (recipient) {
recipient->tag = BACNET_RECIPIENT_TAG_DEVICE;
recipient->type.device.type = OBJECT_DEVICE;
recipient->type.device.instance = BACNET_MAX_INSTANCE;
}
}
/**
* @brief Compare the BACnetRecipient data structure device object wildcard
* @param recipient - BACnetRecipient structure
@@ -440,6 +455,46 @@ int bacnet_destination_decode(
return apdu_len;
}
/**
* @brief Decode the BACnetDestination context tagged complex data
* @param apdu Pointer to the APDU buffer.
* @param apdu_size - the APDU buffer length
* @param tag_number The tag number that shall
* hold the time stamp.
* @param value Pointer to the variable that shall
* take the time stamp values.
* @return number of bytes decoded, zero if tag mismatch,
* or BACNET_STATUS_ERROR if an error occurs
*/
int bacnet_destination_context_decode(
const uint8_t *apdu,
uint32_t apdu_size,
uint8_t tag_number,
BACNET_DESTINATION *value)
{
int len = 0;
int apdu_len = 0;
if (!bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) {
return 0;
}
apdu_len += len;
len =
bacnet_destination_decode(&apdu[apdu_len], apdu_size - apdu_len, value);
if (len < 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
if (!bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
return apdu_len;
}
/**
* @brief Encode the BACnetRecipient complex data
*
@@ -648,13 +703,11 @@ int bacnet_recipient_context_decode(
int bacnet_destination_to_ascii(
const BACNET_DESTINATION *bacdest, char *buf, size_t buf_size)
{
int len = 0;
int buf_len = 0;
int offset = 0;
bool comma;
int i;
len = snprintf(buf, buf_size, "(");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "(");
/*
BACnetDaysOfWeek ::= BIT STRING {
monday (0),
@@ -667,39 +720,32 @@ int bacnet_destination_to_ascii(
}
*/
/* Use numbers 1-7 (ISO 8601) */
len = snprintf(buf, buf_size, "ValidDays=[");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "ValidDays=[");
comma = false;
for (i = 0; i < 7; i++) {
if (bitstring_bit(&bacdest->ValidDays, i)) {
if (comma) {
len = snprintf(buf, buf_size, ",");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, ",");
}
len = snprintf(buf, buf_size, "%d", i + 1);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "%d", i + 1);
comma = true;
}
}
len = snprintf(buf, buf_size, "];");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
len = snprintf(
buf, buf_size, "FromTime=%d:%02d:%02d.%02d;", bacdest->FromTime.hour,
bacdest->FromTime.min, bacdest->FromTime.sec,
offset = bacnet_snprintf(buf, buf_size, offset, "];");
offset = bacnet_snprintf(
buf, buf_size, offset, "FromTime=%d:%02d:%02d.%02d;",
bacdest->FromTime.hour, bacdest->FromTime.min, bacdest->FromTime.sec,
bacdest->FromTime.hundredths);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
len = snprintf(
buf, buf_size, "ToTime=%d:%02d:%02d.%02d;", bacdest->ToTime.hour,
bacdest->ToTime.min, bacdest->ToTime.sec, bacdest->ToTime.hundredths);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
len = snprintf(buf, buf_size, "Recipient=");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(
buf, buf_size, offset, "ToTime=%d:%02d:%02d.%02d;",
bacdest->ToTime.hour, bacdest->ToTime.min, bacdest->ToTime.sec,
bacdest->ToTime.hundredths);
offset = bacnet_snprintf(buf, buf_size, offset, "Recipient=");
if (bacdest->Recipient.tag == BACNET_RECIPIENT_TAG_DEVICE) {
len = snprintf(
buf, buf_size, "Device(type=%d,instance=%lu)",
offset = bacnet_snprintf(
buf, buf_size, offset, "Device(type=%d,instance=%lu)",
bacdest->Recipient.type.device.type,
(unsigned long)bacdest->Recipient.type.device.instance);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
} else {
/*
BACnetAddress ::= SEQUENCE {
@@ -708,39 +754,28 @@ int bacnet_destination_to_ascii(
a broadcast
}
*/
len = snprintf(
buf, buf_size,
offset = bacnet_snprintf(
buf, buf_size, offset,
"Address(net=%d,mac=", bacdest->Recipient.type.address.net);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
/* TODO determine if it's IPv4+port or Ethernet mac address and print it
* nicer - how? Both are 6 bytes long. */
for (i = 0; i < bacdest->Recipient.type.address.mac_len; i++) {
if (i > 0) {
len = snprintf(buf, buf_size, ":");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, ":");
}
len = snprintf(
buf, buf_size, "%02x", bacdest->Recipient.type.address.mac[i]);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(
buf, buf_size, offset, "%02x",
bacdest->Recipient.type.address.mac[i]);
}
len = snprintf(buf, buf_size, ")");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, ")");
}
len = snprintf(buf, buf_size, ";");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
len = snprintf(
buf, buf_size, "ProcessIdentifier=%lu;",
offset = bacnet_snprintf(buf, buf_size, offset, ";");
offset = bacnet_snprintf(
buf, buf_size, offset, "ProcessIdentifier=%lu;",
(unsigned long)bacdest->ProcessIdentifier);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
len = snprintf(
buf, buf_size, "ConfirmedNotify=%s;",
offset = bacnet_snprintf(
buf, buf_size, offset, "ConfirmedNotify=%s;",
bacdest->ConfirmedNotify ? "true" : "false");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
/*
BACnetEventTransitionBits ::= BIT STRING {
to-offnormal (0),
@@ -748,38 +783,28 @@ int bacnet_destination_to_ascii(
to-normal (2)
}
*/
len = snprintf(buf, buf_size, "Transitions=[");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "Transitions=[");
comma = false;
/* TODO remove casting when bitstring_bit() has const added - Github issue
* #320 */
if (bitstring_bit(&bacdest->Transitions, TRANSITION_TO_OFFNORMAL)) {
len = snprintf(buf, buf_size, "to-offnormal");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "to-offnormal");
comma = true;
}
if (bitstring_bit(&bacdest->Transitions, TRANSITION_TO_FAULT)) {
if (comma) {
len = snprintf(buf, buf_size, ",");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, ",");
}
len = snprintf(buf, buf_size, "to-fault");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "to-fault");
comma = true;
}
if (bitstring_bit(&bacdest->Transitions, TRANSITION_TO_NORMAL)) {
if (comma) {
len = snprintf(buf, buf_size, ",");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, ",");
}
len = snprintf(buf, buf_size, "to-normal");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "to-normal");
}
len = snprintf(buf, buf_size, "])"); /* end of the outer paren */
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "])");
return buf_len;
return offset;
}
/**
@@ -1165,3 +1190,178 @@ parse_end:
return true;
}
/**
* @brief Parse an ASCII string for a BACnetRecipient address
* @details Address format: {X'c0:a8:00:0f',1234,X'c0:a8:00:0f'}
* @param src [out] BACNET_MAC_ADDRESS structure to store the results
* @param arg [in] null terminated ASCII string to parse
* @return true if the address was parsed
*/
bool bacnet_recipient_address_from_ascii(BACNET_ADDRESS *src, const char *arg)
{
bool status = false;
int count = 0;
unsigned snet = 0, i;
char mac_string[80] = { "" }, sadr_string[80] = { "" };
BACNET_MAC_ADDRESS mac = { 0 };
if (!(src && arg)) {
return false;
}
count = sscanf(
arg, "{X'%79[^']',%u,X'%79[^']'}", &mac_string[0], &snet,
&sadr_string[0]);
if (count > 0) {
if (bacnet_address_mac_from_ascii(&mac, mac_string)) {
if (src) {
src->mac_len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->mac[i] = mac.adr[i];
}
}
}
if (src) {
src->net = (uint16_t)snet;
}
if (snet) {
if (bacnet_address_mac_from_ascii(&mac, sadr_string)) {
if (src) {
src->len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->adr[i] = mac.adr[i];
}
}
}
} else {
if (src) {
src->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->adr[i] = 0;
}
}
}
status = true;
}
return status;
}
/**
* Parse BACnet_Recipient from ASCII string (as entered by user)
* @param value - struct to store data parsed from the ASCII
* @param str - ASCII string, zero terminated
* @return true on success
* @note
* BACnetRecipient ::= CHOICE {
* device [0] BACnetObjectIdentifier,
* address [1] BACnetAddress
* }
*/
bool bacnet_recipient_from_ascii(BACNET_RECIPIENT *value_out, const char *str)
{
BACNET_RECIPIENT value = { 0 };
char object_string[80] = "";
unsigned long object_instance = 0;
uint32_t found_index = 0;
int count;
if (!str) {
return false;
}
if (str[0] == 0) {
return false;
}
if (str[0] == '(') {
/* (device, 1234) */
count = sscanf(str, "(%79[^,], %lu)", object_string, &object_instance);
if (count == 2) {
if (bactext_object_type_strtol(object_string, &found_index)) {
value.tag = BACNET_RECIPIENT_TAG_DEVICE;
value.type.device.type = (BACNET_OBJECT_TYPE)found_index;
value.type.device.instance = object_instance;
} else {
return false;
}
} else {
return false;
}
} else if (str[0] == '{') {
value.tag = BACNET_RECIPIENT_TAG_ADDRESS;
/* {X'c0:a8:00:0f',1234,X'c0:a8:00:0f'} */
if (!bacnet_recipient_address_from_ascii(&value.type.address, str)) {
return false;
}
}
if (value_out) {
bacnet_recipient_copy(value_out, &value);
}
return true;
}
/**
* @brief Convert BACnetRecipient to ASCII for printing
* @note
* BACnetRecipient ::= CHOICE {
* device [0] BACnetObjectIdentifier,
* address [1] BACnetAddress
* }
* Output format:
* (device, 1234)
* {X'c0:a8:00:0f',1234,X'c0:a8:00:0f'}
* @param value - struct to convert to ASCII
* @param buf - ASCII output buffer
* @param buf_size - ASCII output buffer capacity
* @return the number of characters which would be generated for the given
* input, excluding the trailing null.
* @note buf and buf_size may be null and zero to return only the size
*/
int bacnet_recipient_to_ascii(
const BACNET_RECIPIENT *value, char *str, size_t str_len)
{
int offset = 0;
int i;
if (!value) {
return 0;
}
if (value->tag == BACNET_RECIPIENT_TAG_DEVICE) {
/* device-identifier */
offset = bacnet_snprintf(str, str_len, offset, "(");
offset = bacnet_snprintf(
str, str_len, offset, "%s, ",
bactext_object_type_name(value->type.device.type));
offset = bacnet_snprintf(
str, str_len, offset, "%lu)",
(unsigned long)value->type.device.instance);
} else {
offset = bacnet_snprintf(str, str_len, offset, "{");
/* MAC */
offset = bacnet_snprintf(str, str_len, offset, "X'");
for (i = 0; i < value->type.address.mac_len; i++) {
offset = bacnet_snprintf(
str, str_len, offset, "%02X",
(unsigned)value->type.address.mac[i]);
}
offset = bacnet_snprintf(str, str_len, offset, "',");
/* snet */
offset = bacnet_snprintf(
str, str_len, offset, "%lu",
(unsigned long)value->type.address.net);
/* octetstring */
if (value->type.address.net) {
/* adr */
offset = bacnet_snprintf(str, str_len, offset, ",X'");
for (i = 0; i < value->type.address.len; i++) {
offset = bacnet_snprintf(
str, str_len, offset, "%02X",
(unsigned)value->type.address.adr[i]);
}
offset = bacnet_snprintf(str, str_len, offset, "'");
}
offset = bacnet_snprintf(str, str_len, offset, "}");
}
return offset;
}
+17
View File
@@ -71,6 +71,12 @@ BACNET_STACK_EXPORT
int bacnet_destination_decode(
const uint8_t *apdu, int apdu_len, BACNET_DESTINATION *destination);
BACNET_STACK_EXPORT
int bacnet_destination_context_decode(
const uint8_t *apdu,
uint32_t apdu_size,
uint8_t tag_number,
BACNET_DESTINATION *value);
BACNET_STACK_EXPORT
void bacnet_destination_default_init(BACNET_DESTINATION *destination);
BACNET_STACK_EXPORT
bool bacnet_destination_default(const BACNET_DESTINATION *destination);
@@ -92,6 +98,9 @@ void bacnet_recipient_copy(BACNET_RECIPIENT *dest, const BACNET_RECIPIENT *src);
BACNET_STACK_EXPORT
bool bacnet_recipient_same(
const BACNET_RECIPIENT *r1, const BACNET_RECIPIENT *r2);
BACNET_STACK_EXPORT
void bacnet_recipient_device_wildcard_set(BACNET_RECIPIENT *recipient);
BACNET_STACK_EXPORT
bool bacnet_recipient_device_wildcard(const BACNET_RECIPIENT *recipient);
BACNET_STACK_EXPORT
@@ -112,6 +121,14 @@ int bacnet_recipient_context_decode(
uint8_t tag_number,
BACNET_RECIPIENT *value);
BACNET_STACK_EXPORT
bool bacnet_recipient_address_from_ascii(BACNET_ADDRESS *src, const char *arg);
BACNET_STACK_EXPORT
bool bacnet_recipient_from_ascii(BACNET_RECIPIENT *value_out, const char *str);
BACNET_STACK_EXPORT
int bacnet_recipient_to_ascii(
const BACNET_RECIPIENT *value, char *buf, size_t buf_size);
BACNET_STACK_EXPORT
int bacnet_destination_to_ascii(
const BACNET_DESTINATION *bacdest, char *buf, size_t buf_size);
+2
View File
@@ -1691,6 +1691,8 @@ typedef enum {
BACNET_APPLICATION_TAG_PROPERTY_VALUE,
/* BACnetTimerStateChangeValue */
BACNET_APPLICATION_TAG_TIMER_VALUE,
/* BACnetAddressBinding */
BACNET_APPLICATION_TAG_ADDRESS_BINDING,
/* no-value - context tagged null */
BACNET_APPLICATION_TAG_NO_VALUE,
/* ABSTRACT-SYNTAX - constructed value */
+173 -39
View File
@@ -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;
}
+14 -1
View File
@@ -144,6 +144,9 @@ BACNET_STACK_EXPORT
bool octetstring_init_ascii_hex(
BACNET_OCTET_STRING *octet_string, const char *ascii_hex);
BACNET_STACK_EXPORT
bool octetstring_init_ascii_epics(
BACNET_OCTET_STRING *octet_string, const char *arg);
BACNET_STACK_EXPORT
bool octetstring_copy(
BACNET_OCTET_STRING *dest, const BACNET_OCTET_STRING *src);
BACNET_STACK_EXPORT
@@ -172,9 +175,13 @@ bool octetstring_value_same(
const BACNET_OCTET_STRING *octet_string1,
const BACNET_OCTET_STRING *octet_string2);
BACNET_STACK_EXPORT
int bacnet_strcmp(const char *a, const char *b);
BACNET_STACK_EXPORT
int bacnet_stricmp(const char *a, const char *b);
BACNET_STACK_EXPORT
int bacnet_strncmp(const char *a, const char *b, size_t length);
BACNET_STACK_EXPORT
int bacnet_strnicmp(const char *a, const char *b, size_t length);
BACNET_STACK_EXPORT
@@ -217,7 +224,10 @@ BACNET_STACK_EXPORT
char *bacnet_ultoa(unsigned long value, char *buffer, size_t size);
BACNET_STACK_EXPORT
char *
bacnet_sprintf_to_ascii(char *buffer, size_t count, const char *format, ...);
bacnet_snprintf_to_ascii(char *buffer, size_t count, const char *format, ...);
BACNET_STACK_EXPORT
int bacnet_snprintf(
char *buffer, size_t count, int offset, const char *format, ...);
BACNET_STACK_EXPORT
char *bacnet_ltrim(char *str, const char *trimmedchars);
@@ -226,6 +236,9 @@ char *bacnet_rtrim(char *str, const char *trimmedchars);
BACNET_STACK_EXPORT
char *bacnet_trim(char *str, const char *trimmedchars);
BACNET_STACK_EXPORT
char *bacnet_stptok(const char *s, char *tok, size_t toklen, const char *brk);
#ifdef __cplusplus
}
#endif /* __cplusplus */
+6
View File
@@ -226,6 +226,8 @@
defined(BACAPP_ACCESS_RULE) || \
defined(BACAPP_CHANNEL_VALUE) || \
defined(BACAPP_TIMER_VALUE) || \
defined(BACAPP_RECIPIENT) || \
defined(BACAPP_ADDRESS_BINDING) || \
defined(BACAPP_NO_VALUE) || \
defined(BACAPP_LOG_RECORD) || \
defined(BACAPP_SECURE_CONNECT) || \
@@ -278,6 +280,8 @@
#define BACAPP_ACCESS_RULE
#define BACAPP_CHANNEL_VALUE
#define BACAPP_TIMER_VALUE
#define BACAPP_RECIPIENT
#define BACAPP_ADDRESS_BINDING
#define BACAPP_NO_VALUE
#define BACAPP_LOG_RECORD
#define BACAPP_SECURE_CONNECT
@@ -307,6 +311,8 @@
defined(BACAPP_ACCESS_RULE) || \
defined(BACAPP_CHANNEL_VALUE) || \
defined(BACAPP_TIMER_VALUE) || \
defined(BACAPP_RECIPIENT) || \
defined(BACAPP_ADDRESS_BINDING) || \
defined(BACAPP_NO_VALUE) || \
defined(BACAPP_LOG_RECORD)
#define BACAPP_COMPLEX_TYPES