diff --git a/cmd/tools/CMakeLists.txt b/cmd/tools/CMakeLists.txt index 412a386943be9c2dbc1130f3b81eb3e8dd627f40..94e79b8e07cc8e56f58ef3559b3cc8c4b99a19cd 100644 --- a/cmd/tools/CMakeLists.txt +++ b/cmd/tools/CMakeLists.txt @@ -294,6 +294,7 @@ target_include_directories(gxl2gv PRIVATE target_link_libraries(gxl2gv cgraph + gvc ingraphs ${EXPAT_LIBRARIES} ) diff --git a/cmd/tools/Makefile.am b/cmd/tools/Makefile.am index 5a293619b3f956264d39a61c9c30ec99356673a3..4978feafbe3f2a2a230cc23d5078dac5ca88664b 100644 --- a/cmd/tools/Makefile.am +++ b/cmd/tools/Makefile.am @@ -74,6 +74,7 @@ endif gxl2gv_SOURCES = cvtgxl.c gv2gxl.c gxl2gv.c gxl2gv_LDADD = \ + $(top_builddir)/lib/gvc/libgvc.la \ $(top_builddir)/lib/ingraphs/libingraphs_C.la \ $(top_builddir)/lib/cgraph/libcgraph.la \ $(top_builddir)/lib/cdt/libcdt.la @EXPAT_LIBS@ @@ -387,9 +388,6 @@ EXTRA_DIST = $(man_MANS) $(pdf) bcomps.vcxproj* \ gmlscan.c gmlparse.c gmlparse.h gml2gv.vcxproj* \ graphml2gv.vcxproj* gv2gml.vcxproj* -# FIXME - these are missing -# gv2gxl.vcxproj* - CLEANFILES = stamp.h DISTCLEANFILES = $(pdf) gmlparse.[ch] gmlscan.c \ diff --git a/cmd/tools/gv2gxl.c b/cmd/tools/gv2gxl.c index c19b6c80762c13bbc79cd9249dd56c6a777814fc..69f59e27af2990f1ce17a6ca6211aef096ae040e 100644 --- a/cmd/tools/gv2gxl.c +++ b/cmd/tools/gv2gxl.c @@ -8,9 +8,11 @@ * Contributors: Details at https://graphviz.org *************************************************************************/ - +#include +#include #include "convert.h" #include +#include #include #define SMALLBUF 128 @@ -41,7 +43,7 @@ typedef struct { Agrec_t h; int written; -} Agnodeinfo_t; +} Local_Agnodeinfo_t; static int Level; /* level of tabs */ static Agsym_t *Tailport, *Headport; @@ -148,112 +150,22 @@ static int legalGXLName(char *id) return 1; } -/* return true if *s points to &[A-Za-z]+; (e.g. Ç ) - * or &#[0-9]*; (e.g. & ) - * or &#x[0-9a-fA-F]*; (e.g. 水 ) - */ -static int xml_isentity(char *s) -{ - s++; /* already known to be '&' */ - if (*s == ';') { // '&;' is not a valid entity - return 0; - } - if (*s == '#') { - s++; - if (*s == 'x' || *s == 'X') { - s++; - while ((*s >= '0' && *s <= '9') - || (*s >= 'a' && *s <= 'f') - || (*s >= 'A' && *s <= 'F')) - s++; - } else { - while (*s >= '0' && *s <= '9') - s++; - } - } else { - while ((*s >= 'a' && *s <= 'z') - || (*s >= 'A' && *s <= 'Z')) - s++; - } - if (*s == ';') - return 1; - return 0; +// `fputs` wrapper to handle the difference in calling convention to what +// `xml_escape`’s `cb` expects +static inline int put(void *stream, const char *s) { + return fputs(s, stream); } -static char *_xml_string(char *s, int notURL) -{ - static char *buf = NULL; - static size_t bufsize = 0; - char *p, *sub, *prev = NULL; - size_t len, pos = 0; - - if (!buf) { - bufsize = 64; - buf = N_NEW(bufsize, char); - } - - p = buf; - while (s && *s) { - if (pos > (bufsize - 8)) { - bufsize *= 2; - buf = realloc(buf, bufsize); - p = buf + pos; - } - /* escape '&' only if not part of a legal entity sequence */ - if (*s == '&' && !(xml_isentity(s))) { - sub = "&"; - len = 5; - } - /* '<' '>' are safe to substitute even if string is already UTF-8 coded - * since UTF-8 strings won't contain '<' or '>' */ - else if (*s == '<') { - sub = "<"; - len = 4; - } - else if (*s == '>') { - sub = ">"; - len = 4; - } - else if ((*s == '-') && notURL) { /* can't be used in xml comment strings */ - sub = "-"; - len = 5; - } - else if (*s == ' ' && prev && *prev == ' ' && notURL) { - /* substitute 2nd and subsequent spaces with required_spaces */ - sub = " "; /* inkscape doesn't recognise   */ - len = 6; - } - else if (*s == '"') { - sub = """; - len = 6; - } - else if (*s == '\'') { - sub = "'"; - len = 5; - } - else { - sub = s; - len = 1; - } - while (len--) { - *p++ = *sub++; - pos++; - } - prev = s; - s++; - } - *p = '\0'; - return buf; +// write a string to the given file, XML-escaping the input +static inline int xml_puts(FILE *stream, const char *s) { + const xml_flags_t flags = {.dash = 1, .nbsp = 1}; + return xml_escape(s, flags, put, stream); } -static char *xml_string(char *s) -{ - return _xml_string(s,1); -} - -static char *xml_url_string(char *s) -{ - return _xml_string(s,0); +// wrapper around `xml_escape` to set flags for URL escaping +static int xml_url_puts(FILE *f, const char *s) { + const xml_flags_t flags = {0}; + return xml_escape(s, flags, put, f); } static int isGxlGrammar(char *name) @@ -373,11 +285,15 @@ static void graphAttrs(FILE * gxlFile, Agraph_t * g) val = agget(g, GXL_ROLE); if (!EMPTY(val)) { - fprintf(gxlFile, " role=\"%s\"", xml_string(val)); + fprintf(gxlFile, " role=\""); + xml_puts(gxlFile, val); + fprintf(gxlFile, "\""); } val = agget(g, GXL_HYPER); if (!EMPTY(val)) { - fprintf(gxlFile, " hypergraph=\"%s\"", xml_string(val)); + fprintf(gxlFile, " hypergraph=\""); + xml_puts(gxlFile, val); + fprintf(gxlFile, "\""); } } @@ -387,15 +303,21 @@ static void edgeAttrs(FILE * gxlFile, Agedge_t * e) val = agget(e, GXL_ID); if (!EMPTY(val)) { - fprintf(gxlFile, " id=\"%s\"", xml_string(val)); + fprintf(gxlFile, " id=\""); + xml_puts(gxlFile, val); + fprintf(gxlFile, "\""); } val = agget(e, GXL_FROM); if (!EMPTY(val)) { - fprintf(gxlFile, " fromorder=\"%s\"", xml_string(val)); + fprintf(gxlFile, " fromorder=\""); + xml_puts(gxlFile, val); + fprintf(gxlFile, "\""); } val = agget(e, GXL_TO); if (!EMPTY(val)) { - fprintf(gxlFile, " toorder=\"%s\"", xml_string(val)); + fprintf(gxlFile, " toorder=\""); + xml_puts(gxlFile, val); + fprintf(gxlFile, "\""); } } @@ -407,7 +329,9 @@ static void printHref(FILE * gxlFile, void *n) val = agget(n, GXL_TYPE); if (!EMPTY(val)) { tabover(gxlFile); - fprintf(gxlFile, "\t\n", xml_url_string(val)); + fprintf(gxlFile, "\t\n"); tabover(gxlFile); fprintf(gxlFile, "\t\n"); } @@ -441,25 +365,38 @@ writeDict(Agraph_t * g, FILE * gxlFile, char *name, Dict_t * dict, locatorVal += 13; tabover(gxlFile); - fprintf(gxlFile, "\t\n", xml_string(sym->name)); + fprintf(gxlFile, "\tname); + fprintf(gxlFile, "\">\n"); tabover(gxlFile); - fprintf(gxlFile, "\t\t\n", - xml_url_string(locatorVal)); + fprintf(gxlFile, "\t\t\n"); tabover(gxlFile); fprintf(gxlFile, "\t\n"); } else { tabover(gxlFile); if (isGraph) { - fprintf(gxlFile, "\tname)); - fprintf(gxlFile, "kind=\"%s\">\n", xml_string(name)); + fprintf(gxlFile, "\tname); + fprintf(gxlFile, "\" "); + fprintf(gxlFile, "kind=\""); + xml_puts(gxlFile, name); + fprintf(gxlFile, "\">\n"); } else { - fprintf(gxlFile, "\tname)); - fprintf(gxlFile, "%s\">\n", xml_string(name)); + fprintf(gxlFile, "\tname); + fprintf(gxlFile, "\" kind=\""); + xml_puts(gxlFile, name); + fprintf(gxlFile, "\">\n"); } tabover(gxlFile); - fprintf(gxlFile, "\t\t%s\n", xml_string(sym->defval)); + fprintf(gxlFile, "\t\t"); + xml_puts(gxlFile, sym->defval); + fprintf(gxlFile, "\n"); tabover(gxlFile); fprintf(gxlFile, "\t\n"); } @@ -475,10 +412,16 @@ writeDict(Agraph_t * g, FILE * gxlFile, char *name, Dict_t * dict, } tabover(gxlFile); - fprintf(gxlFile, "\tname) + GXL_COMP_LEN))); - fprintf(gxlFile, "kind=\"%s\">\n", xml_string(name)); + fprintf(gxlFile, "\tname + GXL_COMP_LEN); + fprintf(gxlFile, "\" "); + fprintf(gxlFile, "kind=\""); + xml_puts(gxlFile, name); + fprintf(gxlFile, "\">\n"); tabover(gxlFile); - fprintf(gxlFile, "\t\t%s\n", xml_string(sym->defval)); + fprintf(gxlFile, "\t\t"); + xml_puts(gxlFile, sym->defval); + fprintf(gxlFile, "\n"); tabover(gxlFile); fprintf(gxlFile, "\t\n"); } @@ -554,7 +497,9 @@ writeHdr(gxlstate_t * stp, Agraph_t * g, FILE * gxlFile, int top) tabover(gxlFile); fprintf(gxlFile, "\t\n"); tabover(gxlFile); - fprintf(gxlFile, "\t\t%s\n", xml_string(name)); + fprintf(gxlFile, "\t\t"); + xml_puts(gxlFile, name); + fprintf(gxlFile, "\n"); tabover(gxlFile); fprintf(gxlFile, "\t\n"); } @@ -607,7 +552,9 @@ static int writeEdgeName(Agedge_t * e, FILE * gxlFile) tabover(gxlFile); fprintf(gxlFile, "\t\n"); tabover(gxlFile); - fprintf(gxlFile, "\t\t%s\n", xml_string(p)); + fprintf(gxlFile, "\t\t"); + xml_puts(gxlFile, p); + fprintf(gxlFile, "\n"); tabover(gxlFile); fprintf(gxlFile, "\t\n"); rv = TRUE; @@ -650,24 +597,29 @@ writeNondefaultAttr(void *obj, FILE * gxlFile, Dict_t * defdict) locatorVal += 13; tabover(gxlFile); - fprintf(gxlFile, "\t\n", - xml_string(sym->name)); + fprintf(gxlFile, "\tname); + fprintf(gxlFile, "\">\n"); tabover(gxlFile); - fprintf(gxlFile, - "\t\t\n", - xml_url_string(locatorVal)); + fprintf(gxlFile, "\t\t\n"); tabover(gxlFile); fprintf(gxlFile, "\t\n"); } else { tabover(gxlFile); - fprintf(gxlFile, "\tname)); + fprintf(gxlFile, "\tname); + fprintf(gxlFile, "\""); if (aghtmlstr(data->str[sym->id])) { // This is a <…> string. Note this in the kind. fprintf(gxlFile, " kind=\"HTML-like string\""); } fprintf(gxlFile, ">\n"); tabover(gxlFile); - fprintf(gxlFile, "\t\t%s\n", xml_string(data->str[sym->id])); + fprintf(gxlFile, "\t\t"); + xml_puts(gxlFile, data->str[sym->id]); + fprintf(gxlFile, "\n"); tabover(gxlFile); fprintf(gxlFile, "\t\n"); } @@ -678,10 +630,13 @@ writeNondefaultAttr(void *obj, FILE * gxlFile, Dict_t * defdict) if (data->str[sym->id] != sym->defval) { tabover(gxlFile); - fprintf(gxlFile, "\t\n", - xml_string(((sym->name) + GXL_COMP_LEN))); + fprintf(gxlFile, "\tname + GXL_COMP_LEN); + fprintf(gxlFile, "\">\n"); tabover(gxlFile); - fprintf(gxlFile, "\t\t%s\n", xml_string(data->str[sym->id])); + fprintf(gxlFile, "\t\t"); + xml_puts(gxlFile, data->str[sym->id]); + fprintf(gxlFile, "\n"); tabover(gxlFile); fprintf(gxlFile, "\t\n"); } @@ -717,7 +672,9 @@ writeNode(gxlstate_t * stp, Agnode_t * n, FILE * gxlFile, Dict_t * d) tabover(gxlFile); fprintf(gxlFile, "\t\n"); tabover(gxlFile); - fprintf(gxlFile, "\t\t%s\n", xml_string(name)); + fprintf(gxlFile, "\t\t"); + xml_puts(gxlFile, name); + fprintf(gxlFile, "\n"); tabover(gxlFile); fprintf(gxlFile, "\t\n"); } @@ -736,9 +693,13 @@ static void writePort(Agedge_t * e, FILE * gxlFile, char *name) val = agget(e, name); if (val && val[0]) { tabover(gxlFile); - fprintf(gxlFile, "\t\n", xml_string(name)); + fprintf(gxlFile, "\t\n"); tabover(gxlFile); - fprintf(gxlFile, "\t\t%s\n", xml_string(val)); + fprintf(gxlFile, "\t\t"); + xml_puts(gxlFile, val); + fprintf(gxlFile, "\n"); tabover(gxlFile); fprintf(gxlFile, "\t\n"); } @@ -800,7 +761,7 @@ writeEdge(gxlstate_t * stp, Agedge_t * e, FILE * gxlFile, Dict_t * d) } -#define writeval(n) (((Agnodeinfo_t*)((n)->base.data))->written) +#define writeval(n) (((Local_Agnodeinfo_t*)((n)->base.data))->written) static void writeBody(gxlstate_t * stp, Agraph_t * g, FILE * gxlFile) { @@ -909,7 +870,7 @@ static void freeState(gxlstate_t * stp) void gv_to_gxl(Agraph_t * g, FILE * gxlFile) { gxlstate_t *stp = initState(g); - aginit(g, AGNODE, "node", sizeof(Agnodeinfo_t), TRUE); + aginit(g, AGNODE, "node", sizeof(Local_Agnodeinfo_t), TRUE); iterateHdr(stp, g); iterateBody(stp, g); diff --git a/cmd/tools/gxl2gv.vcxproj b/cmd/tools/gxl2gv.vcxproj index 84075525c0f7c24ab464ea63f0e0b2f5c0473357..835296c36e11da061e8dbadcedc2ffe2c779b839 100644 --- a/cmd/tools/gxl2gv.vcxproj +++ b/cmd/tools/gxl2gv.vcxproj @@ -53,7 +53,7 @@ Disabled - $(SolutionDir)windows\include;$(SolutionDir)windows\dependencies\libraries\vcpkg\installed\x86-windows\include;$(SolutionDir)windows\dependencies\libraries\x86\include;$(SolutionDir);$(SolutionDir)lib;$(SolutionDir)lib\cdt;$(SolutionDir)lib\cgraph;%(AdditionalIncludeDirectories) + $(SolutionDir)windows\include;$(SolutionDir)windows\dependencies\libraries\vcpkg\installed\x86-windows\include;$(SolutionDir)windows\dependencies\libraries\x86\include;$(SolutionDir);$(SolutionDir)lib;$(SolutionDir)lib\cdt;$(SolutionDir)lib\cgraph;$(SolutionDir)lib\pathplan;$(SolutionDir)lib\pack;$(SolutionDir)lib\gvc;$(SolutionDir)lib\common;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;WIN32_DLL;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL @@ -78,7 +78,7 @@ copy $(SolutionDir)windows\dependencies\libraries\vcpkg\installed\x86-windows\bi - $(SolutionDir)windows\include;$(SolutionDir)windows\dependencies\libraries\vcpkg\installed\x86-windows\include;$(SolutionDir)windows\dependencies\libraries\x86\include;$(SolutionDir);$(SolutionDir)lib;$(SolutionDir)lib\cdt;$(SolutionDir)lib\cgraph;%(AdditionalIncludeDirectories) + $(SolutionDir)windows\include;$(SolutionDir)windows\dependencies\libraries\vcpkg\installed\x86-windows\include;$(SolutionDir)windows\dependencies\libraries\x86\include;$(SolutionDir);$(SolutionDir)lib;$(SolutionDir)lib\cdt;$(SolutionDir)lib\cgraph;$(SolutionDir)lib\pathplan;$(SolutionDir)lib\pack;$(SolutionDir)lib\gvc;$(SolutionDir)lib\common;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;%(PreprocessorDefinitions) Level4 diff --git a/lib/common/xml.c b/lib/common/xml.c index 9a246447e9236327a8f4ed763595f964d5b382fd..18731015b8747bf44eae3c230ac72208036d5b51 100644 --- a/lib/common/xml.c +++ b/lib/common/xml.c @@ -16,11 +16,11 @@ static bool isalpha_no_locale(char c) { * or &#[0-9]*; (e.g. & ) * or &#x[0-9a-fA-F]*; (e.g. 水 ) */ -static int xml_isentity(const char *s) +static bool xml_isentity(const char *s) { s++; /* already known to be '&' */ if (*s == ';') { // '&;' is not a valid entity - return 0; + return false; } if (*s == '#') { s++; @@ -37,8 +37,8 @@ static int xml_isentity(const char *s) s++; } if (*s == ';') - return 1; - return 0; + return true; + return false; } /** XML-escape a character