Skip to content

Commit

Permalink
GraphML writer now also uses igraph_real_fprintf_precise to avoid los…
Browse files Browse the repository at this point in the history
…ing precision for numeric attributes, fixes #796
  • Loading branch information
ntamas committed Dec 29, 2014
1 parent 1d2ae91 commit fdcaa14
Showing 1 changed file with 34 additions and 14 deletions.
48 changes: 34 additions & 14 deletions src/foreign-graphml.c
Original file line number Diff line number Diff line change
Expand Up @@ -1215,20 +1215,27 @@ static xmlSAXHandler igraph_i_graphml_sax_handler={

#endif

#define IS_FORBIDDEN_CONTROL_CHAR(x) ((x) < ' ' && (x) != '\t' && (x) != '\r' && (x) != '\n')

int igraph_i_xml_escape(char* src, char** dest) {
long int destlen=0;
char *s, *d;
unsigned char ch;

for (s=src; *s; s++, destlen++) {
if (*s == '&') destlen += 4;
else if (*s == '<') destlen += 3;
else if (*s == '>') destlen += 3;
else if (*s == '"') destlen += 5;
else if (*s == '\'') destlen += 5;
ch = (unsigned char)(*s);
if (ch == '&') destlen += 4;
else if (ch == '<') destlen += 3;
else if (ch == '>') destlen += 3;
else if (ch == '"') destlen += 5;
else if (ch == '\'') destlen += 5;
else if (IS_FORBIDDEN_CONTROL_CHAR(ch)) destlen += 5;
}
*dest=igraph_Calloc(destlen+1, char);
if (!*dest) IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM);
for (s=src, d=*dest; *s; s++, d++) {
switch (*s) {
ch = (unsigned char)(*s);
switch (ch) {
case '&':
strcpy(d, "&amp;"); d+=4; break;
case '<':
Expand All @@ -1240,7 +1247,11 @@ int igraph_i_xml_escape(char* src, char** dest) {
case '\'':
strcpy(d, "&apos;"); d+=5; break;
default:
*d = *s;
if (IS_FORBIDDEN_CONTROL_CHAR(ch)) {
d += sprintf(d, "&#x%02x;", (unsigned char)(ch))-1;
} else {
*d = ch;
}
}
}
*d=0;
Expand Down Expand Up @@ -1384,7 +1395,7 @@ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream,
const char *vprefix= prefixattr ? "v_" : "";
const char *eprefix= prefixattr ? "e_" : "";

ret=fprintf(outstream, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
ret=fprintf(outstream, "<?xml version=\"1.1\" encoding=\"UTF-8\"?>\n");
if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
ret=fprintf(outstream, "<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\"\n");
if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
Expand Down Expand Up @@ -1480,10 +1491,13 @@ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream,
IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv));
if (!isnan(VECTOR(numv)[0])) {
IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
ret=fprintf(outstream, " <data key=\"%s%s\">%g</data>\n",
gprefix, name_escaped, VECTOR(numv)[0]);
ret=fprintf(outstream, " <data key=\"%s%s\">", gprefix, name_escaped);
igraph_Free(name_escaped);
if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
ret=igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
ret=fprintf(outstream, "</data>");
if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
}
} else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) {
char *s, *s_escaped;
Expand Down Expand Up @@ -1526,10 +1540,13 @@ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream,
igraph_vss_1(l), &numv));
if (!isnan(VECTOR(numv)[0])) {
IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
ret=fprintf(outstream, " <data key=\"%s%s\">%g</data>\n",
vprefix, name_escaped, VECTOR(numv)[0]);
ret=fprintf(outstream, " <data key=\"%s%s\">", vprefix, name_escaped);
igraph_Free(name_escaped);
if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
ret=igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
ret=fprintf(outstream, "</data>\n");
if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
}
} else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_STRING) {
char *s, *s_escaped;
Expand Down Expand Up @@ -1582,10 +1599,13 @@ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream,
igraph_ess_1((igraph_integer_t) edge), &numv));
if (!isnan(VECTOR(numv)[0])) {
IGRAPH_CHECK(igraph_i_xml_escape(name, &name_escaped));
ret=fprintf(outstream, " <data key=\"%s%s\">%g</data>\n",
eprefix, name_escaped, VECTOR(numv)[0]);
ret=fprintf(outstream, " <data key=\"%s%s\">", eprefix, name_escaped);
igraph_Free(name_escaped);
if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
ret=igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]);
if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
ret=fprintf(outstream, "</data>\n");
if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE);
}
} else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_STRING) {
char *s, *s_escaped;
Expand Down

1 comment on commit fdcaa14

@ntamas
Copy link
Member Author

@ntamas ntamas commented on fdcaa14 Dec 29, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, actually this contains two unrelated fixes; one is for the escaping of control characters in GraphML files (#794), the other is for writing numeric attributes into GraphML files (#796).

Please sign in to comment.