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