Skip to content

Commit

Permalink
Fix (un)escaping XML strings (OpenModelica#12537)
Browse files Browse the repository at this point in the history
* [OMEdit] Fix unparsing of non-escaping backslash

* [OMShell] Fix unparsing of non-escaping backslash

* [Codegen] Fix escaping strings in templates

* Fix unescaping XML strings in variable browser
  • Loading branch information
anotheruserofgithub committed Jun 12, 2024
1 parent aac2b10 commit 141ca09
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 85 deletions.
8 changes: 4 additions & 4 deletions OMCompiler/Compiler/Template/CodegenFMUCommon.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,8 @@ end StartString;
template ScalarVariableTypeRealAttribute(String unit, String displayUnit)
"Generates code for ScalarVariable Type Real file for FMU target."
::=
let unit_ = if unit then 'unit="<%unit%>"'
let displayUnit_ = if displayUnit then 'displayUnit="<%displayUnit%>"'
let unit_ = if unit then 'unit="<%Util.escapeModelicaStringToXmlString(unit)%>"'
let displayUnit_ = if displayUnit then 'displayUnit="<%Util.escapeModelicaStringToXmlString(displayUnit)%>"'
<<
<%unit_%> <%displayUnit_%>
>>
Expand Down Expand Up @@ -654,8 +654,8 @@ template UnitString2(SimVar simvar)
::=
match simvar
case SIMVAR(unit = unit, displayUnit = displayUnit) then
let unitString = if unit then ' unit="<%unit%>"'
let displayUnitString = if displayUnit then ' displayUnit="<%displayUnit%>"'
let unitString = if unit then ' unit="<%Util.escapeModelicaStringToXmlString(unit)%>"'
let displayUnitString = if displayUnit then ' displayUnit="<%Util.escapeModelicaStringToXmlString(displayUnit)%>"'
//'<%unitString%><%displayUnitString%>' skip displayUnit because FMI2XML fails for e.g. bar
'<%unitString%>'
end UnitString2;
Expand Down
4 changes: 2 additions & 2 deletions OMCompiler/Compiler/Template/CodegenUtil.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -345,13 +345,13 @@ end ScalarVariableTypeNominalAttribute;
template ScalarVariableTypeUnitAttribute(String unit)
"generates code for unit attribute"
::=
'<% if unit then ' unit="<%unit%>"' %>'
'<% if unit then ' unit="<%Util.escapeModelicaStringToXmlString(unit)%>"' %>'
end ScalarVariableTypeUnitAttribute;

template ScalarVariableTypeDisplayUnitAttribute(String displayUnit)
"generates code for displayUnit attribute"
::=
'<% if displayUnit then ' displayUnit="<%displayUnit%>"' %>'
'<% if displayUnit then ' displayUnit="<%Util.escapeModelicaStringToXmlString(displayUnit)%>"' %>'
end ScalarVariableTypeDisplayUnitAttribute;

template MinString(DAE.Exp exp)
Expand Down
4 changes: 2 additions & 2 deletions OMCompiler/Compiler/Template/CodegenXML.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,8 @@ end initValXml;
template ScalarVariableTypeRealAttributeXml(String unit, String displayUnit)
"Generates XML code for ScalarVariable Type Real file ."
::=
let unit_ = if unit then 'unit="<%unit%>"'
let displayUnit_ = if displayUnit then 'displayUnit="<%displayUnit%>"'
let unit_ = if unit then 'unit="<%Util.escapeModelicaStringToXmlString(unit)%>"'
let displayUnit_ = if displayUnit then 'displayUnit="<%Util.escapeModelicaStringToXmlString(displayUnit)%>"'
<<
<%unit_%> <%displayUnit_%>
>>
Expand Down
8 changes: 4 additions & 4 deletions OMEdit/OMEditLIB/Plotting/VariablesWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -904,12 +904,12 @@ bool VariablesTreeModel::insertVariablesItems(QString fileName, QString filePath
bool changeAble = false;
getVariableInformation(&matReader, variableToFind, &type, &value, &changeAble, &variability, &unit, &displayUnit, &description);
/* set the variable type and value */
variableData << type << StringHandler::unparse(QString("\"").append(value).append("\""));
variableData << type << value;
/* set the variable unit */
variableData << StringHandler::unparse(QString("\"").append(unit).append("\""));
variableData << unit;
unit = variableData.at(VariableItemData::UNIT).toString();
/* set the variable displayUnit */
variableData << StringHandler::unparse(QString("\"").append(displayUnit).append("\""));
variableData << displayUnit;
/* set the variable displayUnits */
if ((variableData.at(VariableItemData::TYPE).toString().compare(QStringLiteral("String")) != 0) && !unit.isEmpty()) {
QStringList displayUnits, displayUnitOptions;
Expand Down Expand Up @@ -937,7 +937,7 @@ bool VariablesTreeModel::insertVariablesItems(QString fileName, QString filePath
variableData << QStringList();
}
/* set the variable description */
variableData << StringHandler::unparse(QString("\"").append(description).append("\""));
variableData << description;
/* construct tooltip text */
if (simulationOptions.isInteractiveSimulation()) {
variableData << tr("Variable: %1\nVariability: %2").arg(variableToFind).arg(variability);
Expand Down
107 changes: 58 additions & 49 deletions OMEdit/OMEditLIB/Util/StringHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1154,79 +1154,88 @@ QString StringHandler::escapeTextAnnotationString(QString value)
return res;
}

#define CONSUME_CHAR(value,res,i) \
if (value.at(i) == '\\') { \
i++; \
switch (value[i].toAscii()) { \
case '\'': res.append('\''); break; \
case '"': res.append('\"'); break; \
case '?': res.append('\?'); break; \
case '\\': res.append('\\'); break; \
case 'a': res.append('\a'); break; \
case 'b': res.append('\b'); break; \
case 'f': res.append('\f'); break; \
case 'n': res.append('\n'); break; \
case 'r': res.append('\r'); break; \
case 't': res.append('\t'); break; \
case 'v': res.append('\v'); break; \
} \
#define CONSUME_CHAR(value, res, len, i) \
if (value[i] == '\\' && i + 1 < len) { \
i++; \
switch (value[i].toAscii()) { \
case '\'': res.append('\''); break; \
case '"': res.append('\"'); break; \
case '?': res.append('\?'); break; \
case '\\': res.append('\\'); break; \
case 'a': res.append('\a'); break; \
case 'b': res.append('\b'); break; \
case 'f': res.append('\f'); break; \
case 'n': res.append('\n'); break; \
case 'r': res.append('\r'); break; \
case 't': res.append('\t'); break; \
case 'v': res.append('\v'); break; \
default: res.append('\\') \
.append(value[i]); \
} \
} else { \
res.append(value[i]); \
res.append(value[i]); \
}

QString StringHandler::unparse(QString value)
{
QString res;
value = value.trimmed();
if (value.length() > 1 && value.at(0) == '\"' && value.at(value.length() - 1) == '\"') {
value = value.mid(1, (value.length() - 2));
for (int i=0; i < value.length(); i++) {
CONSUME_CHAR(value,res,i);
int len = value.length();
if (len > 2 && value.at(0) == '\"' && value.at(len - 1) == '\"') {
len -= 2;
value = value.mid(1, len);
for (int i = 0; i < len; i++) {
CONSUME_CHAR(value, res, len, i);
}
return res;
} else {
return "";
}
return res;
}

QStringList StringHandler::unparseStrings(QString value)
{
QStringList lst;
value = value.trimmed();
if (value[0] != '{') return lst; // ERROR?
int i=1;
QString res;
while (value[i] == '"') {
value = value.trimmed();
int len = value.length();
if (len < 2 || value[0] != '{') {
return lst; // ERROR?
}
if (value[1] == '}') {
return lst; // empty list
}
int i = 1;
while (i < len && value[i] == '"') {
i++;
while (value.at(i) != '"') {
CONSUME_CHAR(value,res,i);
i++;
while ((i < len && value[i] != '"')
/* if we have unexpected double quotes then, however omc should return \" */
/* remove this block once fixed in omc */
if (value[i] == '"' && value[i+1] != ',') {
if (value[i+1] != '}') {
CONSUME_CHAR(value,res,i);
i++;
}
}
/* remove this block once fixed in omc */
/* remove this condition once fixed in omc */
|| (i + 1 < len && value[i + 1] != ',' && value[i + 1] != '}')
/* remove this condition once fixed in omc */
) {
CONSUME_CHAR(value, res, len, i);
i++;
}
i++;
if (value[i] == '}') {
if (i < len) {
lst.append(res);
res = QString();
i++;
}
if (i == len) {
return lst; // ERROR?
}
if (value[i] == '}') {
return lst;
}
if (value[i] == ',') {
lst.append(res);
i++;
res = "";
while (value[i] == ' ') // if we have space before next value e.g {"x", "y", "z"}
while (i < len && value[i] == ' ') { // if we have space before next value e.g {"x", "y", "z"}
i++;
continue;
}
while (value[i] != '"' && !value[i].isNull()) {
i++;
fprintf(stderr, "error? malformed string-list. skipping: %c\n", value[i].toAscii());
}
} else {
while (i < len && value[i] != '"') {
fprintf(stderr, "error? malformed string-list. skipping: %c\n", value[i].toAscii());
i++;
}
}
}
return lst; // ERROR?
Expand Down
50 changes: 26 additions & 24 deletions OMShell/OMShell/OMShellGUI/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,39 +53,41 @@ extern "C" {
#include "meta/meta_modelica.h"
}

#define CONSUME_CHAR(value,res,i) \
if (value.at(i) == '\\') { \
i++; \
switch (value[i].toAscii()) { \
case '\'': res.append('\''); break; \
case '"': res.append('\"'); break; \
case '?': res.append('\?'); break; \
case '\\': res.append('\\'); break; \
case 'a': res.append('\a'); break; \
case 'b': res.append('\b'); break; \
case 'f': res.append('\f'); break; \
case 'n': res.append('\n'); break; \
case 'r': res.append('\r'); break; \
case 't': res.append('\t'); break; \
case 'v': res.append('\v'); break; \
} \
#define CONSUME_CHAR(value, res, len, i) \
if (value[i] == '\\' && i + 1 < len) { \
i++; \
switch (value[i].toAscii()) { \
case '\'': res.append('\''); break; \
case '"': res.append('\"'); break; \
case '?': res.append('\?'); break; \
case '\\': res.append('\\'); break; \
case 'a': res.append('\a'); break; \
case 'b': res.append('\b'); break; \
case 'f': res.append('\f'); break; \
case 'n': res.append('\n'); break; \
case 'r': res.append('\r'); break; \
case 't': res.append('\t'); break; \
case 'v': res.append('\v'); break; \
default: res.append('\\') \
.append(value[i]); \
} \
} else { \
res.append(value[i]); \
res.append(value[i]); \
}

QString unparse(QString value)
{
QString res;
value = value.trimmed();
if (value.length() > 1 && value.at(0) == '\"' && value.at(value.length() - 1) == '\"') {
value = value.mid(1, (value.length() - 2));
for (int i=0; i < value.length(); i++) {
CONSUME_CHAR(value,res,i);
int len = value.length();
if (len > 2 && value.at(0) == '\"' && value.at(len - 1) == '\"') {
len -= 2;
value = value.mid(1, len);
for (int i = 0; i < len; i++) {
CONSUME_CHAR(value, res, len, i);
}
return res;
} else {
return "";
}
return res;
}

int main(int argc, char *argv[])
Expand Down

0 comments on commit 141ca09

Please sign in to comment.