Skip to content

Commit

Permalink
Merge pull request #3765 from TD-er/build/Aliasing
Browse files Browse the repository at this point in the history
[Aliasing] Prevent aliasing issues + remove C-style casts
  • Loading branch information
TD-er authored Aug 15, 2021
2 parents 15d85dc + 0844b76 commit e60b20d
Show file tree
Hide file tree
Showing 81 changed files with 248 additions and 241 deletions.
2 changes: 2 additions & 0 deletions platformio_core_defs.ini
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ build_flags = -D NDEBUG
-DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
-DVTABLES_IN_FLASH
-DPUYA_SUPPORT=1
-fno-strict-aliasing
lib_ignore = ESP32_ping, ESP32WebServer, ESP32HTTPUpdateServer, ServoESP32, IRremoteESP8266, HeatpumpIR, TinyWireM

[esp82xx_2_5_x]
Expand All @@ -48,6 +49,7 @@ build_flags = -DNDEBUG
-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
-DPUYA_SUPPORT=1
-DCORE_POST_2_5_0
-fno-strict-aliasing
lib_ignore = ${esp82xx_defaults.lib_ignore}

[esp82xx_2_6_x]
Expand Down
1 change: 1 addition & 0 deletions platformio_esp32_envs.ini
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ build_flags = ${core_esp32_stage.build_flags}
-DCONFIG_FREERTOS_ASSERT_DISABLE
-DCONFIG_LWIP_ESP_GRATUITOUS_ARP
-DCONFIG_LWIP_GARP_TMR_INTERVAL=30
-fno-strict-aliasing
monitor_filters = esp32_exception_decoder


Expand Down
2 changes: 1 addition & 1 deletion src/_C002.ino
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ bool CPlugin_002(CPlugin::Function function, struct EventStruct *event, String&
mustSendEvent = true;
int pwmValue = UserVar[baseVar];

switch ((int)nvalue)
switch (static_cast<int>(nvalue))
{
case 0: // Off
pwmValue = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/_C010.ino
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ bool do_process_c010_delay_queue(int controller_number, const C010_queue_element
}

C010_portUDP.write(
(uint8_t *)element.txt[element.valuesSent].c_str(),
reinterpret_cast<const uint8_t *>(element.txt[element.valuesSent].c_str()),
element.txt[element.valuesSent].length());
bool reply = C010_portUDP.endPacket();

Expand Down
4 changes: 2 additions & 2 deletions src/_C011.ino
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ bool CPlugin_011(CPlugin::Function function, struct EventStruct *event, String&
strlcpy(customConfig->HttpHeader, httpheader.c_str(), sizeof(customConfig->HttpHeader));
strlcpy(customConfig->HttpBody, httpbody.c_str(), sizeof(customConfig->HttpBody));
customConfig->zero_last();
SaveCustomControllerSettings(event->ControllerIndex, (uint8_t *)customConfig.get(), sizeof(C011_ConfigStruct));
SaveCustomControllerSettings(event->ControllerIndex, reinterpret_cast<const uint8_t *>(customConfig.get()), sizeof(C011_ConfigStruct));
}
break;
}
Expand Down Expand Up @@ -216,7 +216,7 @@ bool load_C011_ConfigStruct(controllerIndex_t ControllerIndex, String& HttpMetho
if (!customConfig) {
return false;
}
LoadCustomControllerSettings(ControllerIndex, (uint8_t *)customConfig.get(), sizeof(C011_ConfigStruct));
LoadCustomControllerSettings(ControllerIndex, reinterpret_cast<uint8_t *>(customConfig.get()), sizeof(C011_ConfigStruct));
customConfig->zero_last();
HttpMethod = customConfig->HttpMethod;
HttpUri = customConfig->HttpUri;
Expand Down
19 changes: 10 additions & 9 deletions src/_C013.ino
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,13 @@ void C013_SendUDPTaskInfo(uint8_t destUnit, uint8_t sourceTaskIndex, uint8_t des
if (destUnit != 0)
{
infoReply.destUnit = destUnit;
C013_sendUDP(destUnit, (uint8_t *)&infoReply, sizeof(C013_SensorInfoStruct));
C013_sendUDP(destUnit, reinterpret_cast<const uint8_t *>(&infoReply), sizeof(C013_SensorInfoStruct));
delay(10);
} else {
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it) {
if (it->first != Settings.Unit) {
infoReply.destUnit = it->first;
C013_sendUDP(it->first, (uint8_t *)&infoReply, sizeof(C013_SensorInfoStruct));
C013_sendUDP(it->first, reinterpret_cast<const uint8_t *>(&infoReply), sizeof(C013_SensorInfoStruct));
delay(10);
}
}
Expand Down Expand Up @@ -146,13 +146,13 @@ void C013_SendUDPTaskData(uint8_t destUnit, uint8_t sourceTaskIndex, uint8_t des
if (destUnit != 0)
{
dataReply.destUnit = destUnit;
C013_sendUDP(destUnit, (uint8_t *)&dataReply, sizeof(C013_SensorDataStruct));
C013_sendUDP(destUnit, reinterpret_cast<const uint8_t *>(&dataReply), sizeof(C013_SensorDataStruct));
delay(10);
} else {
for (NodesMap::iterator it = Nodes.begin(); it != Nodes.end(); ++it) {
if (it->first != Settings.Unit) {
dataReply.destUnit = it->first;
C013_sendUDP(it->first, (uint8_t *)&dataReply, sizeof(C013_SensorDataStruct));
C013_sendUDP(it->first, reinterpret_cast<const uint8_t *>(&dataReply), sizeof(C013_SensorDataStruct));
delay(10);
}
}
Expand All @@ -163,7 +163,7 @@ void C013_SendUDPTaskData(uint8_t destUnit, uint8_t sourceTaskIndex, uint8_t des
/*********************************************************************************************\
Send UDP message (unit 255=broadcast)
\*********************************************************************************************/
void C013_sendUDP(uint8_t unit, uint8_t *data, uint8_t size)
void C013_sendUDP(uint8_t unit, const uint8_t *data, uint8_t size)
{
if (!NetworkConnected(10)) {
return;
Expand Down Expand Up @@ -217,14 +217,15 @@ void C013_Receive(struct EventStruct *event) {
# ifndef BUILD_NO_DEBUG

if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
if ((event->Data[1] > 1) && (event->Data[1] < 6))
if ((event->Data != nullptr) &&
(event->Data[1] > 1) && (event->Data[1] < 6))
{
String log = (F("C013 : msg "));

for (uint8_t x = 1; x < 6; x++)
{
log += ' ';
log += (int)event->Data[x];
log += static_cast<int>(event->Data[x]);
}
addLog(LOG_LEVEL_DEBUG_MORE, log);
}
Expand All @@ -245,7 +246,7 @@ void C013_Receive(struct EventStruct *event) {

if (event->Par2 < count) { count = event->Par2; }

memcpy((uint8_t *)&infoReply, (uint8_t *)event->Data, count);
memcpy(reinterpret_cast<uint8_t *>(&infoReply), event->Data, count);

if (infoReply.isValid()) {
// to prevent flash wear out (bugs in communication?) we can only write to an empty task
Expand Down Expand Up @@ -286,7 +287,7 @@ void C013_Receive(struct EventStruct *event) {
int count = sizeof(C013_SensorDataStruct);

if (event->Par2 < count) { count = event->Par2; }
memcpy((uint8_t *)&dataReply, (uint8_t *)event->Data, count);
memcpy(reinterpret_cast<uint8_t *>(&dataReply), event->Data, count);

if (dataReply.isValid()) {
// only if this task has a remote feed, update values
Expand Down
12 changes: 6 additions & 6 deletions src/_C015.ino
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ bool CPlugin_015(CPlugin::Function function, struct EventStruct *event, String&
# ifdef CPLUGIN_015_SSL
case CPlugin::Function::CPLUGIN_WEBFORM_LOAD:
{
char thumbprint[60];
LoadCustomControllerSettings(event->ControllerIndex, (uint8_t *)&thumbprint, sizeof(thumbprint));
char thumbprint[60] = {0};
LoadCustomControllerSettings(event->ControllerIndex, reinterpret_cast<const uint8_t *>(&thumbprint), sizeof(thumbprint));

if (strlen(thumbprint) != 59) {
strcpy(thumbprint, CPLUGIN_015_DEFAULT_THUMBPRINT);
Expand Down Expand Up @@ -142,13 +142,13 @@ bool CPlugin_015(CPlugin::Function function, struct EventStruct *event, String&
_C015_LastConnectAttempt[event->ControllerIndex] = 0;

# ifdef CPLUGIN_015_SSL
char thumbprint[60];
char thumbprint[60] = {0};
String error = F("Specify server thumbprint with exactly 59 symbols string like " CPLUGIN_015_DEFAULT_THUMBPRINT);

if (!safe_strncpy(thumbprint, webArg("c015_thumbprint"), 60) || (strlen(thumbprint) != 59)) {
addHtmlError(error);
}
SaveCustomControllerSettings(event->ControllerIndex, (uint8_t *)&thumbprint, sizeof(thumbprint));
SaveCustomControllerSettings(event->ControllerIndex, reinterpret_cast<const uint8_t *>(&thumbprint), sizeof(thumbprint));
# endif // ifdef CPLUGIN_015_SSL
}
break;
Expand Down Expand Up @@ -286,8 +286,8 @@ boolean Blynk_keep_connection_c015(int controllerIndex, ControllerSettingsStruct
_C015_LastConnectAttempt[controllerIndex] = millis();

# ifdef CPLUGIN_015_SSL
char thumbprint[60];
LoadCustomControllerSettings(controllerIndex, (uint8_t *)&thumbprint, sizeof(thumbprint));
char thumbprint[60] = {0};
LoadCustomControllerSettings(controllerIndex, reinterpret_cast<uint8_t *>(&thumbprint), sizeof(thumbprint));

if (strlen(thumbprint) != 59) {
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
Expand Down
2 changes: 1 addition & 1 deletion src/_C016.ino
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ bool CPlugin_016(CPlugin::Function function, struct EventStruct *event, String&
event,
valueCount,
C016_allowLocalSystemTime ? node_time.now() : node_time.getUnixTime());
success = ControllerCache.write((uint8_t *)&element, sizeof(element));
success = ControllerCache.write(reinterpret_cast<const uint8_t *>(&element), sizeof(element));

/*
if (C016_DelayHandler == nullptr) {
Expand Down
2 changes: 1 addition & 1 deletion src/_C017.ino
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ bool do_process_c017_delay_queue(int controller_number, const C017_queue_element
// addLog(LOG_LEVEL_INFO, String(F("ZBX: ")) + JSON_packet_content);
// Send the packet
client.write(packet_header, sizeof(packet_header) - 1);
client.write((char *)&payload_len, sizeof(payload_len));
client.write(reinterpret_cast<const char *>(&payload_len), sizeof(payload_len));
client.write(JSON_packet_content.c_str(), payload_len);

client.stop();
Expand Down
6 changes: 3 additions & 3 deletions src/_C018.ino
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ bool CPlugin_018(CPlugin::Function function, struct EventStruct *event, String&
if (!customConfig) {
break;
}
LoadCustomControllerSettings(event->ControllerIndex, (uint8_t *)customConfig.get(), sizeof(C018_ConfigStruct));
LoadCustomControllerSettings(event->ControllerIndex, reinterpret_cast<uint8_t *>(customConfig.get()), sizeof(C018_ConfigStruct));
customConfig->validate();
baudrate = customConfig->baudrate;
rxpin = customConfig->rxpin;
Expand Down Expand Up @@ -729,7 +729,7 @@ bool CPlugin_018(CPlugin::Function function, struct EventStruct *event, String&
customConfig->stackVersion = getFormItemInt(F("ttnstack"), customConfig->stackVersion);
customConfig->adr = isFormItemChecked(F("adr"));
serialHelper_webformSave(customConfig->serialPort, customConfig->rxpin, customConfig->txpin);
SaveCustomControllerSettings(event->ControllerIndex, (uint8_t *)customConfig.get(), sizeof(C018_ConfigStruct));
SaveCustomControllerSettings(event->ControllerIndex, reinterpret_cast<const uint8_t *>(customConfig.get()), sizeof(C018_ConfigStruct));
}
break;
}
Expand Down Expand Up @@ -854,7 +854,7 @@ bool C018_init(struct EventStruct *event) {
if (!customConfig) {
return false;
}
LoadCustomControllerSettings(event->ControllerIndex, (uint8_t *)customConfig.get(), sizeof(C018_ConfigStruct));
LoadCustomControllerSettings(event->ControllerIndex, reinterpret_cast<uint8_t *>(customConfig.get()), sizeof(C018_ConfigStruct));
customConfig->validate();

if (!C018_data->init(customConfig->serialPort, customConfig->rxpin, customConfig->txpin, customConfig->baudrate,
Expand Down
2 changes: 1 addition & 1 deletion src/_P006_BMP085.ino
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ boolean Plugin_006(uint8_t function, struct EventStruct *event, String& string)
{
UserVar[event->BaseVarIndex] = P006_data->readTemperature();
int elev = PCONFIG(1);
float pressure = (float)P006_data->readPressure() / 100.0f;
float pressure = static_cast<float>(P006_data->readPressure()) / 100.0f;

if (elev != 0)
{
Expand Down
2 changes: 1 addition & 1 deletion src/_P007_PCF8591.ino
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ boolean Plugin_007(uint8_t function, struct EventStruct *event, String& string)
if (Wire.available())
{
Wire.read(); // Read older value first (stored in chip)
UserVar[event->BaseVarIndex] = (float)Wire.read(); // now read actual value and store into Nodo var
UserVar[event->BaseVarIndex] = Wire.read(); // now read actual value and store into Nodo var

if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log;
Expand Down
4 changes: 2 additions & 2 deletions src/_P012_LCD.ino
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ boolean Plugin_012(uint8_t function, struct EventStruct *event, String& string)
if (error.length() > 0) {
addHtmlError(error);
}
SaveCustomTaskSettings(event->TaskIndex, (uint8_t *)&deviceTemplate, sizeof(deviceTemplate));
SaveCustomTaskSettings(event->TaskIndex, reinterpret_cast<const uint8_t *>(&deviceTemplate), sizeof(deviceTemplate));
success = true;
break;
}
Expand Down Expand Up @@ -198,7 +198,7 @@ boolean Plugin_012(uint8_t function, struct EventStruct *event, String& string)
if (nullptr != P012_data) {
// FIXME TD-er: This is a huge stack allocated object.
char deviceTemplate[P12_Nlines][P12_Nchars];
LoadCustomTaskSettings(event->TaskIndex, (uint8_t *)&deviceTemplate, sizeof(deviceTemplate));
LoadCustomTaskSettings(event->TaskIndex, reinterpret_cast<uint8_t *>(&deviceTemplate), sizeof(deviceTemplate));

for (uint8_t x = 0; x < P012_data->Plugin_012_rows; x++)
{
Expand Down
4 changes: 2 additions & 2 deletions src/_P013_HCSR04.ino
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ boolean Plugin_013(uint8_t function, struct EventStruct *event, String& string)

int8_t Plugin_013_TRIG_Pin = CONFIG_PIN1;
int8_t Plugin_013_IRQ_Pin = CONFIG_PIN2;
int16_t max_distance_cm = (measuringUnit == UNIT_CM) ? max_distance : (float)max_distance * 2.54f;
int16_t max_distance_cm = (measuringUnit == UNIT_CM) ? max_distance : static_cast<float>(max_distance) * 2.54f;

// create sensor instance and add to std::map
P_013_sensordefs.erase(event->TaskIndex);
Expand Down Expand Up @@ -309,7 +309,7 @@ float Plugin_013_read(taskIndex_t taskIndex)
int16_t measuringUnit = Settings.TaskDevicePluginConfig[taskIndex][3];
int16_t filterType = Settings.TaskDevicePluginConfig[taskIndex][4];
int16_t filterSize = Settings.TaskDevicePluginConfig[taskIndex][5];
int16_t max_distance_cm = (measuringUnit == UNIT_CM) ? max_distance : (float)max_distance * 2.54f;
int16_t max_distance_cm = (measuringUnit == UNIT_CM) ? max_distance : static_cast<float>(max_distance) * 2.54f;

unsigned int echoTime = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/_P016_IR.ino
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ boolean displayRawToReadableB32Hex(String& outputStr, decode_results results)
continue;
}
avg /= results.rawlen / 2;
float avgTms = (float)totTms / (results.rawlen / 2);
float avgTms = static_cast<float>(totTms) / (results.rawlen / 2);

if ((avgTms <= bstMul) && (avg < bstAvg))
{
Expand Down
2 changes: 1 addition & 1 deletion src/_P018_Dust.ino
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ boolean Plugin_018(uint8_t function, struct EventStruct *event, String& string)
delayMicroseconds(9680);
}
interrupts();
UserVar[event->BaseVarIndex] = (float)value;
UserVar[event->BaseVarIndex] = value;
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("GPY : Dust value: ");
log += value;
Expand Down
2 changes: 1 addition & 1 deletion src/_P023_OLED.ino
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ boolean Plugin_023(uint8_t function, struct EventStruct *event, String& string)
if (error.length() > 0) {
addHtmlError(error);
}
SaveCustomTaskSettings(event->TaskIndex, (uint8_t *)&deviceTemplate, sizeof(deviceTemplate));
SaveCustomTaskSettings(event->TaskIndex, reinterpret_cast<const uint8_t *>(&deviceTemplate), sizeof(deviceTemplate));
success = true;
break;
}
Expand Down
2 changes: 1 addition & 1 deletion src/_P024_MLX90614.ino
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ boolean Plugin_024(uint8_t function, struct EventStruct *event, String& string)
static_cast<P024_data_struct *>(getPluginTaskData(event->TaskIndex));

if (nullptr != P024_data) {
UserVar[event->BaseVarIndex] = (float)P024_data->readTemperature(PCONFIG(0));
UserVar[event->BaseVarIndex] = P024_data->readTemperature(PCONFIG(0));
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = F("MLX90614 : Temperature: ");
log += formatUserVarNoCheck(event->TaskIndex, 0);
Expand Down
4 changes: 2 additions & 2 deletions src/_P025_ADS1115.ino
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ boolean Plugin_025(uint8_t function, struct EventStruct *event, String& string)

if (nullptr != P025_data) {
const int16_t value = P025_data->read();
UserVar[event->BaseVarIndex] = (float)value;
UserVar[event->BaseVarIndex] = value;

String log;
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
Expand All @@ -186,7 +186,7 @@ boolean Plugin_025(uint8_t function, struct EventStruct *event, String& string)

if (adc1 != adc2)
{
float normalized = (float)(value - adc1) / (float)(adc2 - adc1);
const float normalized = static_cast<float>(value - adc1) / static_cast<float>(adc2 - adc1);
UserVar[event->BaseVarIndex] = normalized * (out2 - out1) + out1;
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
log += ' ';
Expand Down
6 changes: 3 additions & 3 deletions src/_P030_BMP280.ino
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,9 @@ boolean Plugin_030(uint8_t function, struct EventStruct *event, String& string)

if (elev)
{
UserVar[event->BaseVarIndex + 1] = Plugin_030_pressureElevation((float)Plugin_030_readPressure(idx) / 100, elev);
UserVar[event->BaseVarIndex + 1] = Plugin_030_pressureElevation(static_cast<float>(Plugin_030_readPressure(idx)) / 100.0f, elev);
} else {
UserVar[event->BaseVarIndex + 1] = ((float)Plugin_030_readPressure(idx)) / 100;
UserVar[event->BaseVarIndex + 1] = static_cast<float>(Plugin_030_readPressure(idx)) / 100.0f;
}

if (loglevelActiveFor(LOG_LEVEL_INFO)) {
Expand Down Expand Up @@ -315,7 +315,7 @@ float Plugin_030_readPressure(uint8_t idx) {
var2 = (((int64_t)_bmp280_calib[idx].dig_P8) * p) >> 19;

p = ((p + var1 + var2) >> 8) + (((int64_t)_bmp280_calib[idx].dig_P7) << 4);
return (float)p / 256;
return static_cast<float>(p) / 256.0f;
}

// **************************************************************************/
Expand Down
8 changes: 4 additions & 4 deletions src/_P036_FrameOLED.ino
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string)
if (error.length() > 0) {
addHtmlError(error);
}
SaveCustomTaskSettings(event->TaskIndex, (uint8_t *)&(P036_data->DisplayLinesV1), sizeof(P036_data->DisplayLinesV1));
SaveCustomTaskSettings(event->TaskIndex, reinterpret_cast<const uint8_t *>(&(P036_data->DisplayLinesV1)), sizeof(P036_data->DisplayLinesV1));

// Need to delete the allocated object here
delete P036_data;
Expand Down Expand Up @@ -945,8 +945,8 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string)

const int strlen = strnlen_P(P036_data->DisplayLinesV1[LineNo - 1].Content, sizeof(P036_data->DisplayLinesV1[LineNo - 1].Content));
if (strlen > 0) {
const float fAvgPixPerChar = ((float)PixLength) / strlen;
const int iCharToRemove = ceil(((float)(PixLength - 255)) / fAvgPixPerChar);
const float fAvgPixPerChar = static_cast<float>(PixLength) / strlen;
const int iCharToRemove = ceil((static_cast<float>(PixLength - 255)) / fAvgPixPerChar);

// shorten string because OLED controller can not handle such long strings
P036_data->DisplayLinesV1[LineNo - 1].Content[strlen - iCharToRemove] = 0;
Expand All @@ -972,7 +972,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string)
}

if (UserVar[event->BaseVarIndex] == 1) {
uint8_t nextFrame = ceil(((float)LineNo) / P036_data->ScrollingPages.linesPerFrame) - 1; // next frame shows the new content,
uint8_t nextFrame = ceil((static_cast<float>(LineNo)) / P036_data->ScrollingPages.linesPerFrame) - 1; // next frame shows the new content,
// 0-based
P036_data->P036_JumpToPage(event, nextFrame); // Start to display the selected page,
// function needs 65ms!
Expand Down
Loading

0 comments on commit e60b20d

Please sign in to comment.