1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3
4 /*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14 #include "common.h"
15 #include "mem.h"
16 #include "leftyio.h"
17 #include "code.h"
18 #include "tbl.h"
19 #include "dot2l.h"
20
21 extern void lex_begin (int);
22
23 char *gtype, *etype;
24 int yaccdone;
25 int attrclass;
26 int inattrstmt;
27
28 static graphframe_t *gstack, *topgframe;
29 static Tobj allgraphs, alledges, allnodes;
30 static Tobj gdict, edict, ndict, N;
31 static long newgid, neweid, newnid, gmark = -1, errflag;
32
33 static jmp_buf ljbuf;
34
35 static void filllabeltable (Tobj, int);
36 static void filllabelrect (Tobj);
37
38 static char *lsp, *rsp;
39
40 static void writesgraph (int, Tobj, int, int, char *);
41 static void writeattr (int, Tobj, char *);
42 static void quotestring (char *, Tobj);
43
D2Lparsegraphlabel(Tobj lo,Tobj ro)44 Tobj D2Lparsegraphlabel (Tobj lo, Tobj ro) {
45 volatile long lm;
46 volatile Tobj to;
47
48 lm = Mpushmark (lo);
49 Mpushmark (ro);
50 to = Ttable (4);
51 Mpushmark (to);
52 lsp = Tgetstring (lo);
53 if (ro && T_ISSTRING (ro))
54 rsp = Tgetstring (ro);
55 else
56 rsp = NULL;
57
58 if (setjmp (ljbuf)) {
59 to = NULL;
60 fprintf (stderr, "error in label >>%s<<\n", lsp);
61 } else
62 filllabeltable (to, TRUE);
63 Mpopmark (lm);
64 return to;
65 }
66
67 #define HASTEXT 1
68 #define HASPORT 2
69 #define HASTABLE 4
70 #define INTEXT 8
71 #define INPORT 16
72
73 #define ISCTRL(c) ( \
74 (c) == '{' || (c) == '}' || (c) == '|' || (c) == '<' || (c) == '>' \
75 )
76
filllabeltable(Tobj to,int flag)77 static void filllabeltable (Tobj to, int flag) {
78 Tobj cto, fo;
79 char *tsp, *psp, *hstsp, *hspsp;
80 char text[10240], port[256];
81 long cti;
82 int mode, wflag, ishardspace;
83
84 mode = 0;
85 cti = 0;
86 Tinsi (to, cti++, (cto = Ttable (2)));
87 hstsp = tsp = &text[0], hspsp = psp = &port[0];
88 wflag = TRUE;
89 ishardspace = FALSE;
90 while (wflag) {
91 switch (*lsp) {
92 case '<':
93 if (mode & (HASTABLE | HASPORT))
94 longjmp (ljbuf, 1); /* DOESN'T RETURN */
95 mode &= ~INTEXT;
96 mode |= (HASPORT | INPORT);
97 lsp++;
98 break;
99 case '>':
100 if (!(mode & INPORT))
101 longjmp (ljbuf, 1); /* DOESN'T RETURN */
102 mode &= ~INPORT;
103 lsp++;
104 break;
105 case '{':
106 lsp++;
107 if (mode != 0 || !*lsp)
108 longjmp (ljbuf, 1); /* DOESN'T RETURN */
109 Tinss (cto, "fields", (fo = Ttable (2)));
110 mode = HASTABLE;
111 filllabeltable (fo, FALSE);
112 break;
113 case '}':
114 case '|':
115 case '\000':
116 if ((!*lsp && !flag) || (mode & INPORT))
117 longjmp (ljbuf, 1); /* DOESN'T RETURN */
118 if (mode & HASPORT) {
119 if (psp > &port[0] + 1 && psp - 1 != hspsp && *(psp - 1) == ' ')
120 psp--;
121 *psp = '\000';
122 Tinss (cto, "port", Tstring (&port[0]));
123 hspsp = psp = &port[0];
124 }
125 if (!(mode & (HASTEXT | HASTABLE)))
126 mode |= HASTEXT, *tsp++ = ' ';
127 if (mode & HASTEXT) {
128 if (tsp > &text[0] + 1 && tsp - 1 != hstsp && *(tsp - 1) == ' ')
129 tsp--;
130 *tsp = '\000';
131 Tinss (cto, "text", Tstring (&text[0]));
132 hstsp = tsp = &text[0];
133 }
134 if (mode & (HASTEXT | HASPORT))
135 filllabelrect (cto);
136 if (*lsp) {
137 if (*lsp == '}') {
138 lsp++;
139 return;
140 }
141 Tinsi (to, cti++, (cto = Ttable (2)));
142 mode = 0;
143 lsp++;
144 } else
145 wflag = FALSE;
146 break;
147 case '\\':
148 if (*(lsp + 1)) {
149 if (ISCTRL (*(lsp + 1)))
150 lsp++;
151 else if (*(lsp + 1) == ' ')
152 ishardspace = TRUE, lsp++;
153 }
154 /* falling through ... */
155 default:
156 if ((mode & HASTABLE) && *lsp != ' ')
157 longjmp (ljbuf, 1); /* DOESN'T RETURN */
158 if (!(mode & (INTEXT | INPORT)) && (ishardspace || *lsp != ' '))
159 mode |= (INTEXT | HASTEXT);
160 if (mode & INTEXT) {
161 if (!(*lsp == ' ' && !ishardspace && *(tsp - 1) == ' '))
162 *tsp++ = *lsp;
163 if (ishardspace)
164 hstsp = tsp - 1;
165 } else if (mode & INPORT) {
166 if (
167 !(*lsp == ' ' && !ishardspace && (psp == &port[0] ||
168 *(psp - 1) == ' '))
169 )
170 *psp++ = *lsp;
171 if (ishardspace)
172 hspsp = psp - 1;
173 }
174 ishardspace = FALSE;
175 lsp++;
176 break;
177 }
178 }
179 return;
180 }
181
filllabelrect(Tobj to)182 static void filllabelrect (Tobj to) {
183 Tobj ro, p0o, p1o;
184 char *s2, *s3;
185 char c, c2;
186 int i;
187
188 if (!rsp)
189 return;
190 for (s2 = rsp; *s2 && *s2 != ' '; s2++)
191 ;
192 c = *s2, *s2 = 0;
193 Tinss (to, "rect", (ro = Ttable (2)));
194 Tinsi (ro, 0, (p0o = Ttable (2)));
195 Tinsi (ro, 1, (p1o = Ttable (2)));
196 for (i = 0; i < 4; i++) {
197 for (s3 = rsp; *s3 && *s3 != ','; s3++)
198 ;
199 c2 = *s3, *s3 = 0;
200 if (s3 == rsp)
201 longjmp (ljbuf, 1); /* DOESN'T RETURN */
202 switch (i) {
203 case 0: Tinss (p0o, "x", Tinteger ((long) atoi (rsp))); break;
204 case 1: Tinss (p0o, "y", Tinteger ((long) atoi (rsp))); break;
205 case 2: Tinss (p1o, "x", Tinteger ((long) atoi (rsp))); break;
206 case 3: Tinss (p1o, "y", Tinteger ((long) atoi (rsp))); break;
207 }
208 *s3 = c2;
209 rsp = s3;
210 if (*rsp == ',')
211 rsp++;
212 }
213 *s2 = c;
214 rsp = s2 + 1;
215 }
216
217 static Tobj nameo, attro, edgeso, hporto, tporto, heado, tailo, protogo;
218
D2Lreadgraph(int ioi,Tobj protograph)219 Tobj D2Lreadgraph (int ioi, Tobj protograph) {
220 graphframe_t *gframe, *tgframe;
221 edgeframe_t *eframe, *teframe;
222 Tobj graph;
223 long m;
224
225 protogo = protograph;
226 nameo = Tstring ("name");
227 m = Mpushmark (nameo);
228 attro = Tstring ("attr");
229 Mpushmark (attro);
230 edgeso = Tstring ("edges");
231 Mpushmark (edgeso);
232 hporto = Tstring ("hport");
233 Mpushmark (hporto);
234 tporto = Tstring ("tport");
235 Mpushmark (tporto);
236 heado = Tstring ("head");
237 Mpushmark (heado);
238 tailo = Tstring ("tail");
239 Mpushmark (tailo);
240 yaccdone = FALSE;
241 gstack = topgframe = NULL;
242 errflag = FALSE;
243 lex_begin (ioi);
244 yyparse ();
245 graph = NULL;
246 if (topgframe) {
247 graph = (errflag) ? NULL : topgframe->g;
248 for (gframe = gstack; gframe; gframe = tgframe) {
249 for (eframe = gframe->estack; eframe; eframe = teframe) {
250 teframe = eframe->next;
251 Mfree (eframe, M_BYTE2SIZE (sizeof (edgeframe_t)));
252 }
253 tgframe = gframe->next;
254 Mfree (gframe, M_BYTE2SIZE (sizeof (graphframe_t)));
255 }
256 goto done;
257 }
258 done:
259 Mpopmark (m);
260 return graph;
261 }
262
D2Lwritegraph(int ioi,Tobj graph,int flag)263 void D2Lwritegraph (int ioi, Tobj graph, int flag) {
264 Tobj nodes, node, sgraphs, sgraph, edges, edge, tail, head, tport, hport;
265 Tobj so, no, eo, to;
266 char buf[10240];
267 char *s;
268 int isdag, n, nn, i;
269
270 if (!(so = Tfinds (graph, "type")) || !T_ISSTRING (so))
271 s = "digraph";
272 else {
273 s = Tgetstring (so);
274 if (!*s)
275 s = "digraph";
276 }
277 strcpy (buf, s);
278 if (strcmp (s, "digraph") == 0 || strcmp (s, "strict digraph") == 0)
279 isdag = 1;
280 else
281 isdag = 0;
282 if (!(so = Tfinds (graph, "name")) || !T_ISSTRING (so))
283 s = "g";
284 else {
285 s = Tgetstring (so);
286 if (!*s)
287 s = "g";
288 }
289 strcat (buf, " ");
290 quotestring (buf, Tstring (s));
291 strcat (buf, " {");
292 IOwriteline (ioi, buf);
293 buf[0] = '\t', buf[1] = '\t', buf[2] = 0;
294 if ((to = Tfinds (graph, "graphattr")) && T_ISTABLE (to)) {
295 IOwriteline (ioi, "\tgraph [");
296 writeattr (ioi, to, buf);
297 IOwriteline (ioi, "\t]");
298 }
299 if ((to = Tfinds (graph, "nodeattr")) && T_ISTABLE (to)) {
300 IOwriteline (ioi, "\tnode [");
301 writeattr (ioi, to, buf);
302 IOwriteline (ioi, "\t]");
303 }
304 if ((to = Tfinds (graph, "edgeattr")) && T_ISTABLE (to)) {
305 IOwriteline (ioi, "\tedge [");
306 writeattr (ioi, to, buf);
307 IOwriteline (ioi, "\t]");
308 }
309
310 n = 0;
311 if ((nodes = Tfinds (graph, "nodes"))) {
312 if (!(no = Tfinds (graph, "maxnid")) || !T_ISNUMBER (no))
313 n = 100 * Tgettablen (nodes);
314 else
315 n = Tgetnumber (no);
316 for (i = 0; i < n; i++) {
317 if (!(node = Tfindi (nodes, i)))
318 continue;
319 buf[0] = '\t', buf[1] = 0;
320 quotestring (buf, Tfinds (node, "name"));
321 strcat (buf, " [");
322 IOwriteline (ioi, buf);
323 buf[0] = '\t', buf[1] = '\t', buf[2] = 0;
324 if ((to = Tfinds (node, "attr")))
325 writeattr (ioi, to, buf);
326 IOwriteline (ioi, "\t]");
327 }
328 }
329 nn = n;
330 if ((sgraphs = Tfinds (graph, "graphs"))) {
331 if (!(no = Tfinds (graph, "maxgid")) || !T_ISNUMBER (no))
332 n = 100 * Tgettablen (sgraphs);
333 else
334 n = Tgetnumber (no);
335 for (i = 0; i < n; i++) {
336 if (!(sgraph = Tfindi (sgraphs, i)) || Tfinds (sgraph, "wmark"))
337 continue;
338 buf[0] = '\t', buf[1] = 0;
339 writesgraph (ioi, sgraph, n, nn, buf);
340 }
341 for (i = 0; i < n; i++) {
342 if (!(sgraph = Tfindi (sgraphs, i)))
343 continue;
344 Tdels (sgraph, "wmark");
345 }
346 }
347 if ((edges = Tfinds (graph, "edges"))) {
348 if (!(eo = Tfinds (graph, "maxeid")) || !T_ISNUMBER (eo))
349 n = 100 * Tgettablen (edges);
350 else
351 n = Tgetnumber (eo);
352 for (i = 0; i < n; i++) {
353 if (!(edge = Tfindi (edges, i)))
354 continue;
355 if (!(tail = Tfinds (edge, "tail")))
356 continue;
357 if (!(head = Tfinds (edge, "head")))
358 continue;
359 tport = Tfinds (edge, "tport");
360 hport = Tfinds (edge, "hport");
361 buf[0] = '\t', buf[1] = 0;
362 quotestring (buf, Tfinds (tail, "name"));
363 if (tport && T_ISSTRING (tport)) {
364 strcat (buf, ":");
365 quotestring (buf, tport);
366 }
367 strcat (buf, isdag ? " -> " : " -- ");
368 quotestring (buf, Tfinds (head, "name"));
369 if (hport && T_ISSTRING (hport)) {
370 strcat (buf, ":");
371 quotestring (buf, hport);
372 }
373 strcat (buf, " [");
374 IOwriteline (ioi, buf);
375 buf[0] = '\t', buf[1] = '\t', buf[2] = 0;
376 if ((to = Tfinds (edge, "attr")))
377 writeattr (ioi, to, buf);
378 if (flag) {
379 sprintf (buf, "\t\tid = %d", i);
380 IOwriteline (ioi, buf);
381 }
382 IOwriteline (ioi, "\t]");
383 }
384 }
385 IOwriteline (ioi, "}");
386 }
387
writesgraph(int ioi,Tobj graph,int gn,int nn,char * buf)388 static void writesgraph (int ioi, Tobj graph, int gn, int nn, char *buf) {
389 Tobj nodes, node, sgraphs, sgraph, so, to;
390 char *s1, *s2, *s3;
391 int i;
392
393 Tinss (graph, "wmark", Tinteger (1));
394 s1 = buf + strlen (buf);
395 if (!(so = Tfinds (graph, "name")) || !T_ISSTRING (so))
396 sprintf (s1, "{");
397 else {
398 strcat (s1, "subgraph ");
399 quotestring (s1, so);
400 strcat (s1, " {");
401 }
402 IOwriteline (ioi, buf);
403 s2 = s1 + 1;
404 s3 = s2 + 1;
405 *s1 = '\t', *s2 = 0;
406 if ((to = Tfinds (graph, "graphattr")) && T_ISTABLE (to)) {
407 strcat (s1, "graph [");
408 IOwriteline (ioi, buf);
409 *s2 = '\t', *s3 = 0;
410 writeattr (ioi, to, buf);
411 *s2 = 0;
412 strcat (s1, "]");
413 IOwriteline (ioi, buf);
414 *s2 = 0;
415 }
416 if ((to = Tfinds (graph, "nodeattr")) && T_ISTABLE (to)) {
417 strcat (s1, "node [");
418 IOwriteline (ioi, buf);
419 *s2 = '\t', *s3 = 0;
420 writeattr (ioi, to, buf);
421 *s2 = 0;
422 strcat (s1, "]");
423 IOwriteline (ioi, buf);
424 *s2 = 0;
425 }
426 if ((to = Tfinds (graph, "edgeattr")) && T_ISTABLE (to)) {
427 strcat (s1, "edge [");
428 IOwriteline (ioi, buf);
429 *s2 = '\t', *s3 = 0;
430 writeattr (ioi, to, buf);
431 *s2 = 0;
432 strcat (s1, "]");
433 IOwriteline (ioi, buf);
434 *s2 = 0;
435 }
436 if ((nodes = Tfinds (graph, "nodes"))) {
437 for (i = 0; i < nn; i++) {
438 *s2 = 0;
439 if (!(node = Tfindi (nodes, i)))
440 continue;
441 quotestring (buf, Tfinds (node, "name"));
442 IOwriteline (ioi, buf);
443 }
444 }
445 if ((sgraphs = Tfinds (graph, "graphs"))) {
446 for (i = 0; i < gn; i++) {
447 if (!(sgraph = Tfindi (sgraphs, i)) || Tfinds (sgraph, "wmark"))
448 continue;
449 *s2 = 0;
450 writesgraph (ioi, sgraph, gn, nn, buf);
451 }
452 }
453 *s1 = '}', *s2 = 0;
454 IOwriteline (ioi, buf);
455 *s1 = 0;
456 }
457
writeattr(int ioi,Tobj to,char * buf)458 static void writeattr (int ioi, Tobj to, char *buf) {
459 Tkvindex_t tkvi;
460 char *s1, *s2, *s3;
461 int htmlflag;
462
463 s1 = buf + strlen (buf);
464 for (Tgetfirst (to, &tkvi); tkvi.kvp; Tgetnext (&tkvi)) {
465 switch (Tgettype (tkvi.kvp->ko)) {
466 case T_INTEGER:
467 sprintf (s1, "%ld = ", Tgetinteger (tkvi.kvp->ko));
468 break;
469 case T_REAL:
470 sprintf (s1, "%lf = ", Tgetreal (tkvi.kvp->ko));
471 break;
472 case T_STRING:
473 sprintf (s1, "%s = ", Tgetstring (tkvi.kvp->ko));
474 break;
475 }
476 s2 = buf + strlen (buf);
477 switch (Tgettype (tkvi.kvp->vo)) {
478 case T_INTEGER:
479 sprintf (s2, "\"%ld\"", Tgetinteger (tkvi.kvp->vo));
480 break;
481 case T_REAL:
482 sprintf (s2, "\"%lf\"", Tgetreal (tkvi.kvp->vo));
483 break;
484 case T_STRING:
485 *s2++ = '"', htmlflag = FALSE;
486 if (
487 *(s3 = Tgetstring (tkvi.kvp->vo)) == '>' &&
488 s3[strlen (s3) - 1] == '<'
489 )
490 *(s2 - 1) = '<', s3++, htmlflag = TRUE;
491 for ( ; *s3; s3++)
492 if (!htmlflag && *s3 == '"')
493 *s2++ = '\\', *s2++ = *s3;
494 else
495 *s2++ = *s3;
496 if (!htmlflag)
497 *s2++ = '"', *s2 = 0;
498 else
499 *(s2 - 1) = '>', *s2 = 0;
500 break;
501 default:
502 sprintf (s2, "\"\"");
503 break;
504 }
505 IOwriteline (ioi, buf);
506 }
507 *s1 = 0;
508 }
509
quotestring(char * buf,Tobj so)510 static void quotestring (char *buf, Tobj so) {
511 char *s1, *s2;
512
513 s1 = buf + strlen (buf);
514 *s1++ = '"';
515 if (so && T_ISSTRING (so))
516 for (s2 = Tgetstring (so); *s2; s2++) {
517 if (*s2 == '"')
518 *s1++ = '\\', *s1++ = *s2;
519 else
520 *s1++ = *s2;
521 }
522 *s1++ = '"', *s1 = 0;
523 }
524
anonname(char * buf)525 static void anonname(char* buf)
526 {
527 static int anon_id = 0;
528
529 sprintf(buf,"_anonymous_%d",anon_id++);
530 }
531
D2Lbegin(char * name)532 void D2Lbegin (char *name) {
533
534 char buf[BUFSIZ];
535 newgid = neweid = newnid = 0;
536 attrclass = GRAPH;
537
538 if (!(gstack = Mallocate (sizeof (graphframe_t))))
539 panic1 (POS, "D2Lbegingraph", "cannot allocate graph stack");
540 gstack->next = NULL;
541 gstack->estack = NULL;
542 topgframe = gstack;
543
544 gmark = Mpushmark ((gstack->g = Ttable (12)));
545 Tinss (gstack->g, "type", Tstring (gtype));
546 if (!name) {
547 anonname(buf);
548 name = buf;
549 }
550 Tinss (gstack->g, "name", Tstring (name));
551
552 /* the dictionaries */
553 Tinss (gstack->g, "graphdict", (gdict = Ttable (10)));
554 Tinss (gstack->g, "nodedict", (ndict = Ttable (10)));
555 Tinss (gstack->g, "edgedict", (edict = Ttable (10)));
556
557 /* this graph's nodes, edges, subgraphs */
558 Tinss (gstack->g, "nodes", (allnodes = gstack->nodes = Ttable (10)));
559 Tinss (gstack->g, "graphs", (allgraphs = gstack->graphs = Ttable (10)));
560 Tinss (gstack->g, "edges", (alledges = gstack->edges = Ttable (10)));
561
562 /* attributes */
563 gstack->gattr = gstack->nattr = gstack->eattr = NULL;
564 if (protogo) {
565 gstack->gattr = Tfinds (protogo, "graphattr");
566 gstack->nattr = Tfinds (protogo, "nodeattr");
567 gstack->eattr = Tfinds (protogo, "edgeattr");
568 }
569 gstack->gattr = (gstack->gattr ? Tcopy (gstack->gattr) : Ttable (10));
570 Tinss (gstack->g, "graphattr", gstack->gattr);
571 gstack->nattr = (gstack->nattr ? Tcopy (gstack->nattr) : Ttable (10));
572 Tinss (gstack->g, "nodeattr", gstack->nattr);
573 gstack->eattr = (gstack->eattr ? Tcopy (gstack->eattr) : Ttable (10));
574 Tinss (gstack->g, "edgeattr", gstack->eattr);
575 gstack->ecopy = gstack->eattr;
576 }
577
D2Lend(void)578 void D2Lend (void) {
579 if (gmark != -1)
580 Mpopmark (gmark);
581 gmark = -1;
582 yaccdone = TRUE;
583 }
584
D2Labort(void)585 void D2Labort (void) {
586 if (gmark != -1)
587 Mpopmark (gmark);
588 errflag = TRUE;
589 yaccdone = TRUE;
590 }
591
D2Lpushgraph(char * name)592 void D2Lpushgraph (char *name) {
593 graphframe_t *gframe;
594 Tobj g, idobj, nameobj;
595 long gid;
596
597 if (!(gframe = Mallocate (sizeof (graphframe_t))))
598 panic1 (POS, "D2Lpushgraph", "cannot allocate graph stack");
599 gframe->next = gstack, gstack = gframe;
600 gstack->estack = NULL;
601
602 if (name && (idobj = Tfinds (gdict, name))) {
603 gid = Tgetnumber (idobj), gstack->g = g = Tfindi (allgraphs, gid);
604 gstack->nodes = Tfinds (g, "nodes");
605 gstack->graphs = Tfinds (g, "graphs");
606 gstack->edges = Tfinds (g, "edges");
607 gstack->gattr = Tfinds (g, "graphattr");
608 gstack->nattr = Tfinds (g, "nodeattr");
609 gstack->ecopy = gstack->eattr = Tfinds (g, "edgeattr");
610 return;
611 }
612 if (!name) {
613 gid = newgid++;
614 nameobj = Tinteger (gid);
615 Tinso (gdict, nameobj, nameobj);
616 } else
617 Tinso (gdict, (nameobj = Tstring (name)), Tinteger ((gid = newgid++)));
618 Tinsi (allgraphs, gid, (gstack->g = g = Ttable (10)));
619 Tinss (g, "name", nameobj);
620 Tinss (g, "nodes", (gstack->nodes = Ttable (10)));
621 Tinss (g, "graphs", (gstack->graphs = Ttable (10)));
622 Tinss (g, "edges", (gstack->edges = Ttable (10)));
623 Tinss (g, "graphattr", (gstack->gattr = Tcopy (gstack->next->gattr)));
624 Tinss (g, "nodeattr", (gstack->nattr = Tcopy (gstack->next->nattr)));
625 Tinss (
626 g, "edgeattr",
627 (gstack->ecopy = gstack->eattr = Tcopy (gstack->next->eattr))
628 );
629 for (
630 gframe = gstack->next; gframe->graphs != allgraphs;
631 gframe = gframe->next
632 ) {
633 if (Tfindi (gframe->graphs, gid))
634 break;
635 Tinsi (gframe->graphs, gid, g);
636 }
637 return;
638 }
639
D2Lpopgraph(void)640 Tobj D2Lpopgraph (void) {
641 graphframe_t *gframe;
642 Tobj g;
643
644 gframe = gstack, gstack = gstack->next;
645 g = gframe->g;
646 Mfree (gframe, M_BYTE2SIZE (sizeof (graphframe_t)));
647 return g;
648 }
649
D2Linsertnode(char * name)650 Tobj D2Linsertnode (char *name) {
651 graphframe_t *gframe;
652 Tobj n, idobj, nameobj;
653 long nid, m;
654
655 if ((idobj = Tfinds (ndict, name))) {
656 nid = Tgetnumber (idobj), n = Tfindi (allnodes, nid);
657 } else {
658 m = Mpushmark ((nameobj = Tstring (name)));
659 Tinso (ndict, nameobj, Tinteger ((nid = newnid++)));
660 Mpopmark (m);
661 Tinsi (allnodes, nid, (n = Ttable (3)));
662 Tinso (n, nameo, nameobj);
663 Tinso (n, attro, Tcopy (gstack->nattr));
664 Tinso (n, edgeso, Ttable (2));
665 }
666 for (gframe = gstack; gframe->nodes != allnodes; gframe = gframe->next) {
667 if (Tfindi (gframe->nodes, nid))
668 break;
669 Tinsi (gframe->nodes, nid, n);
670 }
671 N = n;
672 return n;
673 }
674
D2Linsertedge(Tobj tail,char * tport,Tobj head,char * hport)675 void D2Linsertedge (Tobj tail, char *tport, Tobj head, char *hport) {
676 graphframe_t *gframe;
677 Tobj e;
678 long eid;
679
680 Tinsi (
681 alledges, (eid = neweid++),
682 (e = Ttable ((long) (3 + (tport ? 1 : 0) + (hport ? 1 : 0))))
683 );
684 Tinso (e, tailo, tail);
685 if (tport && tport[0])
686 Tinso (e, tporto, Tstring (tport));
687 Tinso (e, heado, head);
688 if (hport && hport[0])
689 Tinso (e, hporto, Tstring (hport));
690 Tinso (e, attro, Tcopy (gstack->ecopy));
691 Tinsi (Tfinds (head, "edges"), eid, e);
692 Tinsi (Tfinds (tail, "edges"), eid, e);
693 for (gframe = gstack; gframe->edges != alledges; gframe = gframe->next)
694 Tinsi (gframe->edges, eid, e);
695 }
696
D2Lbeginedge(int type,Tobj obj,char * port)697 void D2Lbeginedge (int type, Tobj obj, char *port) {
698 if (!(gstack->estack = Mallocate (sizeof (edgeframe_t))))
699 panic1 (POS, "D2Lbeginedge", "cannot allocate edge stack");
700 gstack->estack->next = NULL;
701 gstack->estack->type = type;
702 gstack->estack->obj = obj;
703 gstack->estack->port = strdup (port);
704 gstack->emark = Mpushmark ((gstack->ecopy = Tcopy (gstack->eattr)));
705 }
706
D2Lmidedge(int type,Tobj obj,char * port)707 void D2Lmidedge (int type, Tobj obj, char *port) {
708 edgeframe_t *eframe;
709
710 if (!(eframe = Mallocate (sizeof (edgeframe_t))))
711 panic1 (POS, "D2Lmidedge", "cannot allocate edge stack");
712 eframe->next = gstack->estack, gstack->estack = eframe;
713 gstack->estack->type = type;
714 gstack->estack->obj = obj;
715 gstack->estack->port = strdup (port);
716 }
717
D2Lendedge(void)718 void D2Lendedge (void) {
719 edgeframe_t *eframe, *hframe, *tframe;
720 Tobj tnodes, hnodes;
721 Tkvindex_t tkvi, hkvi;
722
723 for (eframe = gstack->estack; eframe->next; eframe = tframe) {
724 hframe = eframe, tframe = eframe->next;
725 if (hframe->type == NODE && tframe->type == NODE) {
726 D2Linsertedge (
727 tframe->obj, tframe->port, hframe->obj, hframe->port
728 );
729 } else if (hframe->type == NODE && tframe->type == GRAPH) {
730 tnodes = Tfinds (tframe->obj, "nodes");
731 for (Tgetfirst (tnodes, &tkvi); tkvi.kvp; Tgetnext (&tkvi))
732 D2Linsertedge (tkvi.kvp->vo, NULL, hframe->obj, hframe->port);
733 } else if (eframe->type == GRAPH && eframe->next->type == NODE) {
734 hnodes = Tfinds (hframe->obj, "nodes");
735 for (Tgetfirst (hnodes, &hkvi); hkvi.kvp; Tgetnext (&hkvi))
736 D2Linsertedge (tframe->obj, tframe->port, hkvi.kvp->vo, NULL);
737 } else {
738 tnodes = Tfinds (tframe->obj, "nodes");
739 hnodes = Tfinds (hframe->obj, "nodes");
740 for (Tgetfirst (tnodes, &tkvi); tkvi.kvp; Tgetnext (&tkvi))
741 for (Tgetfirst (hnodes, &hkvi); hkvi.kvp; Tgetnext (&hkvi))
742 D2Linsertedge (tkvi.kvp->vo, NULL, hkvi.kvp->vo, NULL);
743 }
744 free (eframe->port);
745 Mfree (eframe, M_BYTE2SIZE (sizeof (edgeframe_t)));
746 }
747 free (eframe->port);
748 Mfree (eframe, M_BYTE2SIZE (sizeof (edgeframe_t)));
749 Mpopmark (gstack->emark);
750 gstack->estack = NULL;
751 }
752
D2Lsetattr(char * name,char * value)753 void D2Lsetattr (char *name, char *value) {
754 if (inattrstmt) {
755 switch (attrclass) {
756 case NODE: Tinss (gstack->nattr, name, Tstring (value)); break;
757 case EDGE: Tinss (gstack->eattr, name, Tstring (value)); break;
758 case GRAPH: Tinss (gstack->gattr, name, Tstring (value)); break;
759 }
760 return;
761 }
762 switch (attrclass) {
763 case NODE: Tinss (Tfinds (N, "attr"), name, Tstring (value)); break;
764 case EDGE: Tinss (gstack->ecopy, name, Tstring (value)); break;
765 /* a subgraph cannot have optional attrs? */
766 case GRAPH: Tinss (gstack->gattr, name, Tstring (value)); break;
767 }
768 }
769