gv2gml.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. /**
  2. * @file
  3. * @brief DOT-GML converter
  4. */
  5. /**********************************************************
  6. * This software is part of the graphviz package *
  7. * https://graphviz.org *
  8. * *
  9. * Copyright (c) 1994-2004 AT&T Corp. *
  10. * and is licensed under the *
  11. * Common Public License, Version 1.0 *
  12. * by AT&T Corp. *
  13. * *
  14. * Information and Software Systems Research *
  15. * AT&T Research, Florham Park NJ *
  16. **********************************************************/
  17. #include <stdbool.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <inttypes.h>
  22. #include <getopt.h>
  23. #include <cgraph/cgraph.h>
  24. #include <cgraph/gv_ctype.h>
  25. #include <cgraph/ingraphs.h>
  26. #include <cgraph/strview.h>
  27. #include <cgraph/tokenize.h>
  28. #include <common/types.h>
  29. #include <common/utils.h>
  30. #include <util/exit.h>
  31. #include <util/streq.h>
  32. #include <util/unreachable.h>
  33. #include "openFile.h"
  34. static FILE *outFile;
  35. static char *CmdName;
  36. static char **Files;
  37. static uint64_t id;
  38. static bool yworks; ///< use yWorks.com variant of GML?
  39. #define POS_SET (1<<0)
  40. #define W_SET (1<<1)
  41. #define H_SET (1<<2)
  42. #define INVIS (1<<3)
  43. #define FILL (1<<4)
  44. #define LINE (1<<5)
  45. #define DASH (1<<6)
  46. #define DOT (1<<7)
  47. #define BOLD (1<<8)
  48. typedef struct {
  49. unsigned int flags;
  50. /* graphics */
  51. double x;
  52. double y;
  53. double w;
  54. double h;
  55. char* type; /* shape */
  56. char* image;
  57. char* fill; /* fillcolor */
  58. char* outline; /* pencolor */
  59. char* width; /* penwidth */
  60. char* outlineStyle;
  61. /* label graphics */
  62. char* fontColor;
  63. char* fontSize;
  64. char* fontName;
  65. } node_attrs;
  66. typedef struct {
  67. unsigned int flags;
  68. /* graphics */
  69. char* width; /* penwidth */
  70. char* fill; /* pencolor */
  71. char* arrow; /* dir */
  72. char* arrowhead; /* arrowhead */
  73. char* arrowtail; /* arrowtail */
  74. char* pos;
  75. /* label graphics */
  76. char* fontColor;
  77. char* fontSize;
  78. char* fontName;
  79. } edge_attrs;
  80. typedef struct {
  81. Agrec_t h;
  82. uint64_t id;
  83. } Local_Agnodeinfo_t;
  84. #define ID(n) (((Local_Agnodeinfo_t*)(n->base.data))->id)
  85. static void indent(int ix) {
  86. while (ix--)
  87. fprintf (outFile, " ");
  88. }
  89. /// Return true if input string is number
  90. static bool isNumber(char* s) {
  91. char* ep = s;
  92. strtod(s, &ep);
  93. if (s != ep) {
  94. while (gv_isspace(*ep)) ep++;
  95. if (*ep) return false;
  96. return true;
  97. }
  98. return false;
  99. }
  100. /// Simple implementation for parsing style attribute
  101. static int
  102. parseStyle (char* s)
  103. {
  104. int flags = 0;
  105. char* sep = " \t,";
  106. for (tok_t t = tok(s, sep); !tok_end(&t); tok_next(&t)) {
  107. strview_t ip = tok_get(&t);
  108. if (strview_str_eq(ip, "invis")) flags |= INVIS;
  109. else if (strview_str_eq(ip, "filled")) flags |= FILL;
  110. else if (strview_str_eq(ip, "dashed")) flags |= DASH;
  111. else if (strview_str_eq(ip, "dotted")) flags |= DOT;
  112. else if (strview_str_eq(ip, "solid")) flags |= LINE;
  113. else if (strview_str_eq(ip, "bold")) flags |= BOLD;
  114. }
  115. return flags;
  116. }
  117. static void emitInt(char *name, int value, int ix) {
  118. indent(ix);
  119. fprintf (outFile, "%s %d\n", name, value);
  120. }
  121. static void emitReal(char *name, double value, int ix) {
  122. indent(ix);
  123. fprintf (outFile, "%s %g\n", name, value);
  124. }
  125. static void emitPoint(double x, double y, int ix) {
  126. indent(ix);
  127. fprintf (outFile, "point [ x %g y %g ]\n", x, y);
  128. }
  129. static char*
  130. skipWS (char* s)
  131. {
  132. while (gv_isspace(*s)) s++;
  133. return s;
  134. }
  135. /* Return NULL if unsuccessful
  136. */
  137. static char*
  138. readPoint (char* s, double* xp, double* yp)
  139. {
  140. char* endp;
  141. s = skipWS(s);
  142. *xp = strtod (s, &endp);
  143. if (s == endp) return NULL;
  144. endp++; /* skip comma */
  145. s = endp;
  146. *yp = strtod (s, &endp);
  147. if (s == endp) return NULL;
  148. else return endp;
  149. }
  150. static char*
  151. arrowEnd (char* s0, char* pfx, int* fp, double* xp, double* yp)
  152. {
  153. char* s = skipWS(s0);
  154. if (strncmp(s,pfx,2)) return s;
  155. s += 2; /* skip prefix */
  156. s = readPoint (s, xp, yp);
  157. if (s == NULL) {
  158. fprintf (stderr, "Illegal spline end: %s\n", s0);
  159. graphviz_exit(1);
  160. }
  161. *fp = 1;
  162. return s;
  163. }
  164. static void emitSpline(char *s, int ix) {
  165. double sx, sy, ex, ey;
  166. int sarrow = 0;
  167. int earrow = 0;
  168. s = arrowEnd (s, "e,", &earrow, &ex, &ey);
  169. s = arrowEnd (s, "s,", &sarrow, &sx, &sy);
  170. indent(ix);
  171. fprintf (outFile, "Line [\n");
  172. if (sarrow)
  173. emitPoint(sx, sy, ix+1);
  174. while ((s = readPoint (s, &sx, &sy)))
  175. emitPoint(sx, sy, ix+1);
  176. if (earrow)
  177. emitPoint(ex, ey, ix+1);
  178. indent(ix);
  179. fprintf (outFile, "]\n");
  180. }
  181. // `fputs` wrapper to handle the difference in calling convention to what
  182. // `xml_escape`’s `cb` expects
  183. static inline int put(void *stream, const char *s) {
  184. return fputs(s, stream);
  185. }
  186. // write a string to the given file, XML-escaping the input
  187. static inline int xml_puts(FILE *stream, const char *s) {
  188. const xml_flags_t flags = {.dash = 1, .nbsp = 1};
  189. return xml_escape(s, flags, put, stream);
  190. }
  191. static void emitAttr(char *name, char *value, int ix) {
  192. indent(ix);
  193. if (isNumber (value))
  194. fprintf (outFile, "%s %s\n", name, value);
  195. else {
  196. fprintf(outFile, "%s \"", name);
  197. xml_puts(outFile, value);
  198. fputs("\"\n", outFile);
  199. }
  200. }
  201. /* node attributes:
  202. * label
  203. * graphics
  204. * LabelGraphics
  205. */
  206. static void emitNodeAttrs(Agraph_t *G, Agnode_t *np, int ix) {
  207. Agsym_t* s;
  208. char* v;
  209. node_attrs attrs;
  210. int doGraphics = 0;
  211. int doLabelGraphics = 0;
  212. char* label = 0;
  213. int style;
  214. double x, y;
  215. /* First, process the attributes, saving the graphics attributes */
  216. memset(&attrs,0, sizeof(attrs));
  217. for (s = agnxtattr (G, AGNODE, NULL); s; s = agnxtattr (G, AGNODE, s)) {
  218. if (streq(s->name, "style")) { /* hasFill outlineStyle invis */
  219. if (*(v = agxget (np, s))) {
  220. style = parseStyle (v);
  221. if (style & INVIS)
  222. attrs.flags |= INVIS;
  223. if (style & FILL)
  224. attrs.flags |= FILL;
  225. if (style & LINE)
  226. attrs.outlineStyle = "line";
  227. if (style & DASH)
  228. attrs.outlineStyle = "dashed";
  229. if (style & DOT)
  230. attrs.outlineStyle = "dotted";
  231. doGraphics = 1;
  232. }
  233. }
  234. else if (streq(s->name, "label")) {
  235. v = agxget (np, s);
  236. if (streq("\\N", v)) {
  237. label = agnameof(np);
  238. emitAttr(s->name, label, ix);
  239. doLabelGraphics = 1;
  240. }
  241. else if (*v) {
  242. label = v;
  243. emitAttr(s->name, label, ix);
  244. doLabelGraphics = 1;
  245. }
  246. }
  247. else if (streq(s->name, "penwidth")) {
  248. if (*(v = agxget (np, s))) {
  249. attrs.width = v;
  250. doGraphics = 1;
  251. }
  252. }
  253. else if (streq(s->name, "width")) {
  254. v = agxget (np, s);
  255. if (*v) {
  256. attrs.w = 72.0*atof (v);
  257. attrs.flags |= W_SET;
  258. doGraphics = 1;
  259. }
  260. }
  261. else if (streq(s->name, "height")) {
  262. v = agxget (np, s);
  263. if (*v) {
  264. attrs.h = 72.0*atof (v);
  265. attrs.flags |= H_SET;
  266. doGraphics = 1;
  267. }
  268. }
  269. else if (streq(s->name, "pos")) {
  270. v = agxget (np, s);
  271. if (sscanf (v, "%lf,%lf", &x, &y) == 2) {
  272. doGraphics = 1;
  273. attrs.x = x;
  274. attrs.y = y;
  275. attrs.flags |= POS_SET;
  276. }
  277. }
  278. else if (streq(s->name, "shape")) { /* type */
  279. if (*(v = agxget (np, s))) {
  280. attrs.type = v;
  281. doGraphics = 1;
  282. }
  283. }
  284. else if (streq(s->name, "color")) {
  285. if (*(v = agxget (np, s))) {
  286. attrs.fill = v;
  287. attrs.outline = v;
  288. doGraphics = 1;
  289. }
  290. }
  291. else if (streq(s->name, "fillcolor")) {
  292. if (*(v = agxget (np, s))) {
  293. attrs.fill = v;
  294. doGraphics = 1;
  295. }
  296. }
  297. else if (streq(s->name, "pencolor")) {
  298. if (*(v = agxget (np, s))) {
  299. attrs.outline = v;
  300. doGraphics = 1;
  301. }
  302. }
  303. else if (streq(s->name, "fontname")) { /* fontName */
  304. if (*(v = agxget (np, s))) {
  305. attrs.fontName = v;
  306. doLabelGraphics = 1;
  307. }
  308. }
  309. else if (streq(s->name, "fontsize")) { /* fontSize */
  310. if (*(v = agxget (np, s))) {
  311. attrs.fontSize = v;
  312. doLabelGraphics = 1;
  313. }
  314. }
  315. else if (streq(s->name, "fontcolor")) { /* fontColor */
  316. if (*(v = agxget (np, s))) {
  317. attrs.fontColor = v;
  318. doLabelGraphics = 1;
  319. }
  320. }
  321. else {
  322. v = agxget (np, s);
  323. emitAttr(s->name, v, ix);
  324. }
  325. }
  326. /* Then, print them, if any */
  327. if (doGraphics) {
  328. fprintf (outFile, " graphics [\n");
  329. if (attrs.flags & POS_SET) {
  330. emitReal("x", attrs.x, ix+1);
  331. emitReal("y", attrs.y, ix+1);
  332. }
  333. if (attrs.flags & W_SET) {
  334. emitReal("w", attrs.w, ix+1);
  335. }
  336. if (attrs.flags & H_SET) {
  337. emitReal("H", attrs.h, ix+1);
  338. }
  339. if (attrs.flags & INVIS) {
  340. emitInt("visible", 0, ix+1);
  341. }
  342. if (attrs.flags & FILL) {
  343. emitInt("hasFill", 1, ix+1);
  344. }
  345. if (attrs.type) {
  346. emitAttr("type", attrs.type, ix+1);
  347. }
  348. if (attrs.image) {
  349. emitAttr("image", attrs.image, ix+1);
  350. }
  351. if (attrs.fill) {
  352. emitAttr("fill", attrs.fill, ix+1);
  353. }
  354. if (attrs.outline) {
  355. emitAttr("outline", attrs.outline, ix+1);
  356. }
  357. if (attrs.width) {
  358. emitAttr("width", attrs.width, ix+1);
  359. }
  360. if (attrs.outlineStyle) {
  361. emitAttr("outlineStyle", attrs.outlineStyle, ix+1);
  362. }
  363. fprintf (outFile, " ]\n");
  364. }
  365. if (doLabelGraphics) {
  366. fprintf (outFile, " LabelGraphics [\n");
  367. if (label) emitAttr("text", label, ix+1);
  368. if (attrs.fontColor) {
  369. emitAttr(yworks ? "color" : "fontColor", attrs.fontColor, ix+1);
  370. }
  371. if (attrs.fontSize) {
  372. emitAttr("fontSize", attrs.fontSize, ix+1);
  373. }
  374. if (attrs.fontName) {
  375. emitAttr("fontName", attrs.fontName, ix+1);
  376. }
  377. fprintf (outFile, " ]\n");
  378. }
  379. }
  380. /* emitNode:
  381. * set node id
  382. * label, width height, x, y, type fillcolor
  383. */
  384. static void emitNode(Agraph_t *G, Agnode_t *n) {
  385. agbindrec(n, "nodeinfo", sizeof(Local_Agnodeinfo_t), true);
  386. fprintf(outFile, " node [\n id %" PRIu64 "\n name \"%s\"\n", id,
  387. agnameof(n));
  388. ID(n) = id++;
  389. emitNodeAttrs(G, n, 2);
  390. fprintf (outFile, " ]\n");
  391. }
  392. /* edge attributes:
  393. * label
  394. * graphics
  395. * LabelGraphics
  396. */
  397. static void emitEdgeAttrs(Agraph_t *G, Agedge_t *ep, int ix) {
  398. Agsym_t* s;
  399. char* v;
  400. edge_attrs attrs;
  401. int doGraphics = 0;
  402. int doLabelGraphics = 0;
  403. char* label = 0;
  404. int style;
  405. /* First, process the attributes, saving the graphics attributes */
  406. memset(&attrs,0, sizeof(attrs));
  407. for (s = agnxtattr (G, AGEDGE, NULL); s; s = agnxtattr (G, AGEDGE, s)) {
  408. if (streq(s->name, "style")) { /* hasFill outlineStyle invis */
  409. if (*(v = agxget (ep, s))) {
  410. style = parseStyle (v);
  411. if (style & INVIS)
  412. attrs.flags |= INVIS;
  413. if (style & LINE)
  414. attrs.flags |= LINE;
  415. if (style & DASH)
  416. attrs.flags |= DASH;
  417. if (style & DOT)
  418. attrs.flags |= DOT;
  419. if (style & BOLD)
  420. attrs.width = "2";
  421. doGraphics = 1;
  422. }
  423. }
  424. else if (streq(s->name, "label")) {
  425. if (*(v = agxget (ep, s))) {
  426. label = v;
  427. emitAttr(s->name, label, ix);
  428. doLabelGraphics = 1;
  429. }
  430. }
  431. else if (streq(s->name, "penwidth")) {
  432. if (*(v = agxget (ep, s))) {
  433. attrs.width = v;
  434. doGraphics = 1;
  435. }
  436. }
  437. else if (streq(s->name, "pos")) {
  438. if (*(v = agxget (ep, s))) {
  439. doGraphics = 1;
  440. attrs.pos = v;
  441. }
  442. }
  443. else if (streq(s->name, "dir")) {
  444. if (*(v = agxget (ep, s))) {
  445. doGraphics = 1;
  446. attrs.arrow = v;
  447. }
  448. }
  449. else if (streq(s->name, "color")) {
  450. if (*(v = agxget (ep, s))) {
  451. attrs.fill = v;
  452. doGraphics = 1;
  453. }
  454. }
  455. else if (streq(s->name, "pencolor")) {
  456. if (*(v = agxget (ep, s))) {
  457. attrs.fill = v;
  458. doGraphics = 1;
  459. }
  460. }
  461. else if (streq(s->name, "arrowhead")) {
  462. if (*(v = agxget (ep, s))) {
  463. attrs.arrowhead = v;
  464. doGraphics = 1;
  465. }
  466. }
  467. else if (streq(s->name, "arrowtail")) {
  468. if (*(v = agxget (ep, s))) {
  469. attrs.arrowtail = v;
  470. doGraphics = 1;
  471. }
  472. }
  473. else if (streq(s->name, "fontname")) { /* fontName */
  474. if (*(v = agxget (ep, s))) {
  475. attrs.fontName = v;
  476. doLabelGraphics = 1;
  477. }
  478. }
  479. else if (streq(s->name, "fontsize")) { /* fontSize */
  480. if (*(v = agxget (ep, s))) {
  481. attrs.fontSize = v;
  482. doLabelGraphics = 1;
  483. }
  484. }
  485. else if (streq(s->name, "fontcolor")) { /* fontColor */
  486. if (*(v = agxget (ep, s))) {
  487. attrs.fontColor = v;
  488. doLabelGraphics = 1;
  489. }
  490. }
  491. else {
  492. v = agxget (ep, s);
  493. emitAttr(s->name, v, ix);
  494. }
  495. }
  496. /* Then, print them, if any */
  497. if (doGraphics) {
  498. fprintf (outFile, " graphics [\n");
  499. if (attrs.pos) {
  500. emitSpline(attrs.pos, ix+1);
  501. }
  502. if (attrs.flags & INVIS) {
  503. emitInt("visible", 0, ix+1);
  504. }
  505. if (attrs.fill) {
  506. emitAttr("fill", attrs.fill, ix+1);
  507. }
  508. if (attrs.width) {
  509. emitAttr("width", attrs.width, ix+1);
  510. }
  511. if (attrs.arrowhead) {
  512. emitAttr("targetArrow", attrs.arrowhead, ix+1);
  513. }
  514. if (attrs.arrowtail) {
  515. emitAttr("sourceArrow", attrs.arrowtail, ix+1);
  516. }
  517. if (attrs.flags & DASH) {
  518. emitAttr("style", "dashed", ix+1);
  519. }
  520. else if (attrs.flags & DOT) {
  521. emitAttr("style", "dotted", ix+1);
  522. }
  523. else if (attrs.flags & LINE) {
  524. emitAttr("style", "line", ix+1);
  525. }
  526. if (attrs.arrow) {
  527. if (streq(attrs.arrow,"forward"))
  528. emitAttr("arrow", "first", ix+1);
  529. else if (streq(attrs.arrow,"back"))
  530. emitAttr("arrow", "last", ix+1);
  531. else if (streq(attrs.arrow,"both"))
  532. emitAttr("arrow", "both", ix+1);
  533. else if (streq(attrs.arrow,"none"))
  534. emitAttr("arrow", "none", ix+1);
  535. }
  536. fprintf (outFile, " ]\n");
  537. }
  538. if (doLabelGraphics) {
  539. fprintf (outFile, " LabelGraphics [\n");
  540. if (label) emitAttr("text", label, ix+1);
  541. if (attrs.fontColor) {
  542. emitAttr(yworks ? "color" : "fontColor", attrs.fontColor, ix+1);
  543. }
  544. if (attrs.fontSize) {
  545. emitAttr("fontSize", attrs.fontSize, ix+1);
  546. }
  547. if (attrs.fontName) {
  548. emitAttr("fontName", attrs.fontName, ix+1);
  549. }
  550. fprintf (outFile, " ]\n");
  551. }
  552. }
  553. static void emitEdge(Agraph_t *G, Agedge_t *e) {
  554. fprintf(outFile, " edge [\n id %" PRIu64 "\n", (uint64_t)AGSEQ(e));
  555. fprintf(outFile, " source %" PRIu64 "\n", ID(agtail(e)));
  556. fprintf(outFile, " target %" PRIu64 "\n", ID(aghead(e)));
  557. emitEdgeAttrs(G, e, 2);
  558. fprintf (outFile, " ]\n");
  559. }
  560. static void emitGraphAttrs(Agraph_t *G) {
  561. Agsym_t* s;
  562. char* v;
  563. for (s = agnxtattr (G, AGRAPH, NULL); s; s = agnxtattr (G, AGRAPH, s)) {
  564. if (*(v = agxget (G, s))) {
  565. emitAttr(s->name, v, 1);
  566. }
  567. }
  568. }
  569. static void gv_to_gml(Agraph_t *G) {
  570. Agnode_t* n;
  571. Agedge_t* e;
  572. fprintf (outFile, "graph [\n version 2\n");
  573. if (agisdirected(G))
  574. fprintf (outFile, " directed 1\n");
  575. else
  576. fprintf (outFile, " directed 0\n");
  577. emitGraphAttrs(G);
  578. /* FIX: Not sure how to handle default attributes or subgraphs */
  579. for (n = agfstnode(G); n; n = agnxtnode (G, n)) {
  580. emitNode(G, n);
  581. }
  582. for (n = agfstnode(G); n; n = agnxtnode (G, n)) {
  583. for (e = agfstout(G, n); e; e = agnxtout (G, e)) {
  584. emitEdge(G, e);
  585. }
  586. }
  587. fprintf (outFile, "]\n");
  588. }
  589. static char *useString = "Usage: %s [-y] [-?] <files>\n\
  590. -o<file> : output to <file> (stdout)\n\
  591. -y : output yWorks.com GML variant\n\
  592. -? - print usage\n\
  593. If no files are specified, stdin is used\n";
  594. static void usage(int v)
  595. {
  596. printf(useString, CmdName);
  597. graphviz_exit(v);
  598. }
  599. static char *cmdName(char *cmd)
  600. {
  601. char *sp;
  602. sp = strrchr(cmd, '/');
  603. if (sp)
  604. sp++;
  605. else
  606. sp = cmd;
  607. return sp;
  608. }
  609. static void initargs(int argc, char **argv)
  610. {
  611. int c;
  612. CmdName = cmdName(argv[0]);
  613. opterr = 0;
  614. while ((c = getopt(argc, argv, ":o:y")) != -1) {
  615. switch (c) {
  616. case 'o':
  617. if (outFile != NULL)
  618. fclose(outFile);
  619. outFile = openFile(CmdName, optarg, "w");
  620. break;
  621. case 'y':
  622. yworks = true;
  623. break;
  624. case ':':
  625. fprintf(stderr, "%s: option -%c missing parameter\n", CmdName, optopt);
  626. usage(1);
  627. break;
  628. case '?':
  629. if (optopt == '?')
  630. usage(0);
  631. else {
  632. fprintf(stderr, "%s: option -%c unrecognized\n", CmdName,
  633. optopt);
  634. usage(1);
  635. }
  636. break;
  637. default:
  638. UNREACHABLE();
  639. }
  640. }
  641. argv += optind;
  642. argc -= optind;
  643. if (argc)
  644. Files = argv;
  645. if (!outFile)
  646. outFile = stdout;
  647. }
  648. int main(int argc, char **argv)
  649. {
  650. Agraph_t *G;
  651. Agraph_t *prev = 0;
  652. int rv;
  653. ingraph_state ig;
  654. rv = 0;
  655. initargs(argc, argv);
  656. newIngraph(&ig, Files);
  657. while ((G = nextGraph(&ig))) {
  658. if (prev) {
  659. id = 0;
  660. agclose(prev);
  661. }
  662. prev = G;
  663. gv_to_gml(G);
  664. fflush(outFile);
  665. }
  666. graphviz_exit(rv);
  667. }