Refactor/snprintf next common shift function (#656)

This commit is contained in:
Steve Karg
2024-05-30 09:16:05 -05:00
committed by GitHub
parent 309964e929
commit 0cbf7414a4
5 changed files with 277 additions and 446 deletions
+174 -145
View File
@@ -71,8 +71,7 @@ void bacnet_destination_default_init(BACNET_DESTINATION *destination)
* @param r2 - BACnetRecipient 2 structure
* @return true if r1 and r2 are the same
*/
bool bacnet_recipient_same(
BACNET_RECIPIENT *r1, BACNET_RECIPIENT *r2)
bool bacnet_recipient_same(BACNET_RECIPIENT *r1, BACNET_RECIPIENT *r2)
{
bool status = false;
@@ -103,8 +102,7 @@ bool bacnet_recipient_same(
* @param src - BACnetRecipient 1 structure
* @param dest - BACnetRecipient 2 structure
*/
void bacnet_recipient_copy(
BACNET_RECIPIENT *dest, BACNET_RECIPIENT *src)
void bacnet_recipient_copy(BACNET_RECIPIENT *dest, BACNET_RECIPIENT *src)
{
if (dest && src) {
memmove(dest, src, sizeof(BACNET_RECIPIENT));
@@ -170,8 +168,7 @@ bool bacnet_destination_same(BACNET_DESTINATION *d1, BACNET_DESTINATION *d2)
status = (datetime_compare_time(&d1->ToTime, &d2->ToTime) == 0);
}
if (status) {
status = bacnet_recipient_same(
&d1->Recipient, &d2->Recipient);
status = bacnet_recipient_same(&d1->Recipient, &d2->Recipient);
}
if (status) {
status = (d1->ProcessIdentifier == d2->ProcessIdentifier);
@@ -252,7 +249,8 @@ int bacnet_destination_encode(uint8_t *apdu, BACNET_DESTINATION *destination)
apdu += len;
}
if (destination->Recipient.tag == BACNET_RECIPIENT_TAG_DEVICE) {
len = encode_context_object_id(apdu, 0, OBJECT_DEVICE,
len = encode_context_object_id(
apdu, 0, OBJECT_DEVICE,
destination->Recipient.type.device.instance);
apdu_len += len;
if (apdu) {
@@ -392,7 +390,8 @@ int bacnet_destination_decode(
if (decode_is_context_tag(apdu, BACNET_RECIPIENT_TAG_DEVICE)) {
/* device [0] BACnetObjectIdentifier */
destination->Recipient.tag = BACNET_RECIPIENT_TAG_DEVICE;
len = decode_context_object_id(apdu, BACNET_RECIPIENT_TAG_DEVICE,
len = decode_context_object_id(
apdu, BACNET_RECIPIENT_TAG_DEVICE,
&destination->Recipient.type.device.type,
&destination->Recipient.type.device.instance);
if (len == BACNET_STATUS_ERROR) {
@@ -462,56 +461,52 @@ int bacnet_destination_decode(
return apdu_len;
}
#define LEN_BRANCH(snprintf_expr) \
do { \
len = snprintf_expr; \
output_len += len; \
if (buf) { \
if (len > buf_size) { \
return -1; \
} else { \
buf += len; \
buf_size -= len; \
} \
} \
} while (0)
/**
* Convert BACnet_Destination to ASCII for printing
*
* Output format:
*
* (ValidDays=[1,2,5,6,7];FromTime=0:00:00.0;ToTime=23:59:59.9;Recipient=Device(type=8,instance=15);ProcessIdentifier=0;ConfirmedNotify=false;Transitions=[to-offnormal,to-fault,to-normal])
* (
* ValidDays=[1,2,5,6,7];
* FromTime=0:00:00.0;
* ToTime=23:59:59.9;
* Recipient=Device(type=8,instance=15);
* ProcessIdentifier=0;
* ConfirmedNotify=false;
* Transitions=[to-offnormal,to-fault,to-normal]
* )
*
* - ValidDays ... array of numbers, 1=Mon through 7=Sun
* - FromTime, ToTime ... HH:MM:SS.s
* - Recipient ... two variants: Recipient=Device(type=8,instance=15) or Recipient=Address(net=1234,mac=c0:a8:00:0f)
* - Recipient ... two variants: Recipient=Device(type=8,instance=15) or
* Recipient=Address(net=1234,mac=c0:a8:00:0f)
* - type ... bacnet object type enum
* - instance ... bacnet object instance
* - net ... bacnet network number
* - mac ... bacnet MAC address; can be separated by colons or periods.
* - ProcessIdentifier ... 32bit unsigned int, process ID
* - ConfirmedNotify ... true or false
* - Transitions ... array with any of the three items: to-offnormal, to-fault, to-normal
* - Transitions ... array with any of the three items: to-offnormal, to-fault,
* to-normal
*
* @param bacdest - Destination 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. negative is returned if the capacity was not sufficient.
* input, excluding the trailing null.
* @note buf and buf_size may be null and zero to return only the size
*/
int bacnet_destination_to_ascii(const BACNET_DESTINATION *bacdest, char *buf, size_t buf_size)
int bacnet_destination_to_ascii(
const BACNET_DESTINATION *bacdest, char *buf, size_t buf_size)
{
int len = 0;
int output_len = 0;
int buf_len = 0;
bool comma;
int i;
LEN_BRANCH(snprintf(buf, buf_size, "("));
len = snprintf(buf, buf_size, "(");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
/*
BACnetDaysOfWeek ::= BIT STRING {
monday (0),
@@ -523,68 +518,80 @@ int bacnet_destination_to_ascii(const BACNET_DESTINATION *bacdest, char *buf, si
sunday (6)
}
*/
/* Use numbers 1-7 (ISO 8601) */
LEN_BRANCH(snprintf(buf, buf_size, "ValidDays=["));
len = snprintf(buf, buf_size, "ValidDays=[");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
comma = false;
for (i = 0; i < 7; i++) {
if (bitstring_bit((BACNET_BIT_STRING *) &bacdest->ValidDays, i)) {
if (bitstring_bit((BACNET_BIT_STRING *)&bacdest->ValidDays, i)) {
if (comma) {
LEN_BRANCH(snprintf(buf, buf_size, ","));
len = snprintf(buf, buf_size, ",");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
}
LEN_BRANCH(snprintf(buf, buf_size, "%d", i + 1));
len = snprintf(buf, buf_size, "%d", i + 1);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
comma = true;
}
}
LEN_BRANCH(snprintf(buf, buf_size, "];"));
LEN_BRANCH(snprintf(buf, buf_size, "FromTime=%d:%02d:%02d.%02d;",
bacdest->FromTime.hour,
bacdest->FromTime.min,
bacdest->FromTime.sec,
bacdest->FromTime.hundredths
));
LEN_BRANCH(snprintf(buf, buf_size, "ToTime=%d:%02d:%02d.%02d;",
bacdest->ToTime.hour,
bacdest->ToTime.min,
bacdest->ToTime.sec,
bacdest->ToTime.hundredths
));
LEN_BRANCH(snprintf(buf, buf_size, "Recipient="));
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,
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);
if (bacdest->Recipient.tag == BACNET_RECIPIENT_TAG_DEVICE) {
LEN_BRANCH(snprintf(buf, buf_size, "Device(type=%d,instance=%lu)",
bacdest->Recipient.type.device.type,
(unsigned long)bacdest->Recipient.type.device.instance));
len = snprintf(
buf, buf_size, "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 {
network-number Unsigned16, -- A value of 0 indicates the local network
mac-address OCTET STRING -- A string of length 0 indicates a broadcast
network-number Unsigned16, -- A value of 0 indicates the local
network mac-address OCTET STRING -- A string of length 0 indicates
a broadcast
}
*/
LEN_BRANCH(snprintf(buf, buf_size, "Address(net=%d,mac=", bacdest->Recipient.type.address.net));
len = snprintf(
buf, buf_size,
"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. */
/* 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_BRANCH(snprintf(buf, buf_size, ":"));
len = snprintf(buf, buf_size, ":");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
}
LEN_BRANCH(snprintf(buf, buf_size, "%02x", bacdest->Recipient.type.address.mac[i]));
len = snprintf(
buf, buf_size, "%02x", bacdest->Recipient.type.address.mac[i]);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
}
LEN_BRANCH(snprintf(buf, buf_size, ")"));
len = snprintf(buf, buf_size, ")");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
}
LEN_BRANCH(snprintf(buf, buf_size, ";"));
len = snprintf(buf, buf_size, ";");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
LEN_BRANCH(snprintf(buf, buf_size, "ProcessIdentifier=%lu;",
(unsigned long)bacdest->ProcessIdentifier));
len = snprintf(
buf, buf_size, "ProcessIdentifier=%lu;",
(unsigned long)bacdest->ProcessIdentifier);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
LEN_BRANCH(snprintf(buf, buf_size, "ConfirmedNotify=%s;",
bacdest->ConfirmedNotify ? "true" : "false"
));
len = snprintf(
buf, buf_size, "ConfirmedNotify=%s;",
bacdest->ConfirmedNotify ? "true" : "false");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
/*
BACnetEventTransitionBits ::= BIT STRING {
@@ -593,36 +600,49 @@ int bacnet_destination_to_ascii(const BACNET_DESTINATION *bacdest, char *buf, si
to-normal (2)
}
*/
LEN_BRANCH(snprintf(buf, buf_size, "Transitions=["));
len = snprintf(buf, buf_size, "Transitions=[");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
comma = false;
/* TODO remove casting when bitstring_bit() has const added - Github issue #320 */
if (bitstring_bit((BACNET_BIT_STRING *) &bacdest->Transitions, TRANSITION_TO_OFFNORMAL)) {
LEN_BRANCH(snprintf(buf, buf_size, "to-offnormal"));
/* TODO remove casting when bitstring_bit() has const added - Github issue
* #320 */
if (bitstring_bit(
(BACNET_BIT_STRING *)&bacdest->Transitions,
TRANSITION_TO_OFFNORMAL)) {
len = snprintf(buf, buf_size, "to-offnormal");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
comma = true;
}
if (bitstring_bit((BACNET_BIT_STRING *) &bacdest->Transitions, TRANSITION_TO_FAULT)) {
if (bitstring_bit(
(BACNET_BIT_STRING *)&bacdest->Transitions, TRANSITION_TO_FAULT)) {
if (comma) {
LEN_BRANCH(snprintf(buf, buf_size, ","));
len = snprintf(buf, buf_size, ",");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
}
LEN_BRANCH(snprintf(buf, buf_size, "to-fault"));
len = snprintf(buf, buf_size, "to-fault");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
comma = true;
}
if (bitstring_bit((BACNET_BIT_STRING *) &bacdest->Transitions, TRANSITION_TO_NORMAL)) {
if (bitstring_bit(
(BACNET_BIT_STRING *)&bacdest->Transitions, TRANSITION_TO_NORMAL)) {
if (comma) {
LEN_BRANCH(snprintf(buf, buf_size, ","));
len = snprintf(buf, buf_size, ",");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
}
LEN_BRANCH(snprintf(buf, buf_size, "to-normal"));
len = snprintf(buf, buf_size, "to-normal");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
}
LEN_BRANCH(snprintf(buf, buf_size, "])")); /* end of the outer paren */
len = snprintf(buf, buf_size, "])"); /* end of the outer paren */
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
return output_len;
return buf_len;
}
/**
* Parse BACnet_Destination from ASCII string (as entered by user)
*
* @param bacdest - Destination struct to pupulate with data from the ASCII string
* @param bacdest - Destination struct to pupulate with data from the ASCII
* string
* @param buf - ASCII string, zero terminated
* @return true on success
*/
@@ -658,14 +678,9 @@ bool bacnet_destination_from_ascii(BACNET_DESTINATION *bacdest, const char *buf)
enum ParseKeyword kw = 0;
BACNET_TIME *ptime;
BACNET_MAC_ADDRESS tmpmac;
static const char * KW_LOOKUP[] = {
"ValidDays",
"FromTime",
"ToTime",
"Recipient",
"ProcessIdentifier",
"ConfirmedNotify",
"Transitions",
static const char *KW_LOOKUP[] = {
"ValidDays", "FromTime", "ToTime", "Recipient",
"ProcessIdentifier", "ConfirmedNotify", "Transitions",
};
if (bacdest == NULL || buf == NULL) {
@@ -674,7 +689,6 @@ bool bacnet_destination_from_ascii(BACNET_DESTINATION *bacdest, const char *buf)
bacnet_destination_default_init(bacdest);
/* Helper macros to simplify the parser ... */
/* true if the character is whitespace */
@@ -682,48 +696,49 @@ bool bacnet_destination_from_ascii(BACNET_DESTINATION *bacdest, const char *buf)
/* Discard characters while they match a given test. Goes to parse_end on NUL.
* ctest is a boolean expression where c is the tested character */
#define DISCARD_WHILE(ctest) \
do { \
while(1) { \
c = buf[pos]; \
if (c == 0) { \
goto parse_end; \
} \
if ((ctest)) { \
pos++; \
continue; \
} \
break; \
} \
} while(0)
#define DISCARD_WHILE(ctest) \
do { \
while (1) { \
c = buf[pos]; \
if (c == 0) { \
goto parse_end; \
} \
if ((ctest)) { \
pos++; \
continue; \
} \
break; \
} \
} while (0)
/* Discard all whitespace. Goes to parse_end on NUL. */
#define DISCARD_WHITESPACE() DISCARD_WHILE(ISWHITE(c))
/* Must consume a given word; return false otherwise. */
#define MUST_CONSUME(s) \
do { \
_must_consume_tmplen = strlen(s); \
if (0 == strncmp(&buf[pos], s, _must_consume_tmplen)) { \
pos += _must_consume_tmplen; \
} else { \
return false; \
} \
} while(0)
#define MUST_CONSUME(s) \
do { \
_must_consume_tmplen = strlen(s); \
if (0 == strncmp(&buf[pos], s, _must_consume_tmplen)) { \
pos += _must_consume_tmplen; \
} else { \
return false; \
} \
} while (0)
/* Collect a decimal number and store the result into tmp; stop on a non-digit. Clobbers "c" and "tmp". TODO replace with strtol? */
#define COLLECT_NUMBER_TMP(maxdigits) \
do { \
tmp = 0; \
for (_number_i = 0; _number_i < (maxdigits); _number_i++) { \
c = buf[pos]; \
if (c >= '0' && c <= '9') { \
tmp = (tmp * 10) + (c - '0'); \
pos++; \
} else { \
break; \
} \
} \
/* Collect a decimal number and store the result into tmp; stop on a non-digit.
* Clobbers "c" and "tmp". TODO replace with strtol? */
#define COLLECT_NUMBER_TMP(maxdigits) \
do { \
tmp = 0; \
for (_number_i = 0; _number_i < (maxdigits); _number_i++) { \
c = buf[pos]; \
if (c >= '0' && c <= '9') { \
tmp = (tmp * 10) + (c - '0'); \
pos++; \
} else { \
break; \
} \
} \
} while (0)
/* Go through all key=value pieces in the string */
@@ -763,7 +778,9 @@ bool bacnet_destination_from_ascii(BACNET_DESTINATION *bacdest, const char *buf)
}
break;
case PH_VALUE_SPACER: /* Equals between key and value, also consuming opening square bracket if present. */
case PH_VALUE_SPACER: /* Equals between key and value, also
consuming opening square bracket if
present. */
DISCARD_WHITESPACE();
MUST_CONSUME("=");
DISCARD_WHILE(c == '[' || ISWHITE(c));
@@ -792,7 +809,8 @@ bool bacnet_destination_from_ascii(BACNET_DESTINATION *bacdest, const char *buf)
}
if (j == 0) {
if (c >= '1' && c <= '7') {
bitstring_set_bit(&bacdest->ValidDays, c - '1', true);
bitstring_set_bit(
&bacdest->ValidDays, c - '1', true);
pos++;
j = 1;
} else {
@@ -815,7 +833,8 @@ bool bacnet_destination_from_ascii(BACNET_DESTINATION *bacdest, const char *buf)
ptime = &bacdest->ToTime;
}
/* TODO implemented in bacapp_parse_application_data - extract & reuse? */
/* TODO implemented in bacapp_parse_application_data -
* extract & reuse? */
/* Hour */
COLLECT_NUMBER_TMP(2);
@@ -887,17 +906,20 @@ bool bacnet_destination_from_ascii(BACNET_DESTINATION *bacdest, const char *buf)
if (j == 0) {
if (0 ==
strncmp(&buf[pos], "to-offnormal", 12)) {
bitstring_set_bit(&bacdest->Transitions,
bitstring_set_bit(
&bacdest->Transitions,
TRANSITION_TO_OFFNORMAL, true);
pos += 12;
} else if (0 ==
strncmp(&buf[pos], "to-fault", 8)) {
bitstring_set_bit(&bacdest->Transitions,
} else if (
0 == strncmp(&buf[pos], "to-fault", 8)) {
bitstring_set_bit(
&bacdest->Transitions,
TRANSITION_TO_FAULT, true);
pos += 8;
} else if (0 ==
strncmp(&buf[pos], "to-normal", 9)) {
bitstring_set_bit(&bacdest->Transitions,
} else if (
0 == strncmp(&buf[pos], "to-normal", 9)) {
bitstring_set_bit(
&bacdest->Transitions,
TRANSITION_TO_NORMAL, true);
pos += 9;
} else {
@@ -914,7 +936,8 @@ bool bacnet_destination_from_ascii(BACNET_DESTINATION *bacdest, const char *buf)
case KW_Recipient:
if (0 == strncmp(&buf[pos], "Device", 6)) {
pos += 6;
bacdest->Recipient.tag = BACNET_RECIPIENT_TAG_DEVICE;
bacdest->Recipient.tag =
BACNET_RECIPIENT_TAG_DEVICE;
DISCARD_WHITESPACE();
MUST_CONSUME("(");
@@ -943,7 +966,8 @@ bool bacnet_destination_from_ascii(BACNET_DESTINATION *bacdest, const char *buf)
} else if (0 == strncmp(&buf[pos], "Address", 7)) {
pos += 7;
bacdest->Recipient.tag = BACNET_RECIPIENT_TAG_ADDRESS;
bacdest->Recipient.tag =
BACNET_RECIPIENT_TAG_ADDRESS;
DISCARD_WHITESPACE();
MUST_CONSUME("(");
@@ -964,13 +988,18 @@ bool bacnet_destination_from_ascii(BACNET_DESTINATION *bacdest, const char *buf)
MUST_CONSUME("=");
DISCARD_WHITESPACE();
if (!bacnet_address_mac_from_ascii(&tmpmac, &buf[pos])) {
if (!bacnet_address_mac_from_ascii(
&tmpmac, &buf[pos])) {
return false;
}
bacdest->Recipient.type.address.mac_len = tmpmac.len;
memcpy(&bacdest->Recipient.type.address.mac, &tmpmac.adr, MAX_MAC_LEN);
bacdest->Recipient.type.address.mac_len =
tmpmac.len;
memcpy(
&bacdest->Recipient.type.address.mac,
&tmpmac.adr, MAX_MAC_LEN);
/* address_mac_from_ascii doesn't return number of digits
/* address_mac_from_ascii doesn't return number of
* digits
* - we have to discard until ) */
DISCARD_WHILE(c != ')');