Imported Upstream version 0.4.8
[platform/upstream/libsmi.git] / tools / dump-svg.c
1 /*
2  * dump-svg.c --
3  *
4  *      Operations to extract a SVG diagram from MIB modules.
5  *      This driver is based on the cm-driver by A. Mueller.
6  *      Mail comments and suggestions to sperner@ibr.cs.tu-bs.de
7  *
8  * Copyright (c) 2004-2005 K. Sperner, Technical University of Braunschweig.
9  *
10  * See the file "COPYING" for information on usage and redistribution
11  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12  *
13  * @(#) $Id: dump-svg.c 8090 2008-04-18 12:56:29Z strauss $
14  */
15
16
17
18 #include <config.h>
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <math.h>
25 #ifdef HAVE_WIN_H
26 #include "win.h"
27 #endif
28
29 #include "smi.h"
30 #include "smidump.h"
31 #include "rea.h"
32 #include "dump-svg-script.h"
33
34 #define URL "http://www.ibr.cs.tu-bs.de/projects/libsmi/svg/mib2svg.cgi?"
35
36
37
38 extern int smiAsprintf(char **strp, const char *format, ...);
39
40
41
42 /*
43  * Definitions used by the svg output driver (node layout).
44  */
45
46 /* FIXME int or float? */
47 static const float HEADFONTSIZETABLE   = (float)7;
48 static const float HEADSPACESIZETABLE  = (float)4;
49 static const float ATTRFONTSIZE        = (float)7;
50 static const float ATTRSPACESIZE       = (float)2;
51 static const float TABLEHEIGHT         = (float)20; /*headline of the table*/
52 static const float TABLEELEMHEIGHT     = (float)15; /*height of one attribute*/
53 static const float TABLEBOTTOMHEIGHT   = (float)5;  /*bottom of the table*/
54
55 static const int MODULE_INFO_WIDTH     =150;
56 /* The description of RowStatus is quite long... :-/ */
57 static const int DYN_TEXT              =470;
58
59 /* used by the springembedder */
60 static const int ITERATIONS            =100;
61
62 static char *link;
63 static const char *linkcolor = "blue";
64
65
66
67 /* ------ Misc. -----------------                                            */
68
69
70
71 static char *getTimeString(time_t t)
72 {
73     static char   *s = NULL;
74     struct tm     *tm;
75
76     if (s) xfree(s);
77     
78     tm = gmtime(&t);
79     if (tm->tm_hour == 0 && tm->tm_min == 0) {
80         smiAsprintf(&s, "%04d-%02d-%02d",
81                         tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
82     } else {
83         smiAsprintf(&s, "%04d-%02d-%02d %02d:%02d",
84                         tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
85                         tm->tm_hour, tm->tm_min);
86     }
87     return s;
88 }
89
90
91
92 /* -------------- main functions ------------------------------------------- */
93
94
95
96 /*
97  * Creates the graph nodes of the given module
98  */
99 static void algCreateNodes(SmiModule *module)
100 {
101     SmiNode   *node;
102     GraphNode *newNode;
103     
104     /* get tables and scalars from the MIB module */
105     for (node = smiGetFirstNode(module, SMI_NODEKIND_TABLE);
106          node;
107          node = smiGetNextNode(node, SMI_NODEKIND_TABLE)) {
108         if ((node->status == SMI_STATUS_DEPRECATED
109             && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
110             || (node->status == SMI_STATUS_OBSOLETE
111             && !SHOW_DEPR_OBSOLETE))
112             continue;
113         newNode = graphInsertNode(graph, node);
114         newNode->smiModule = module;
115     }
116     for (node = smiGetFirstNode(module, SMI_NODEKIND_SCALAR);
117          node;
118          node = smiGetNextNode(node, SMI_NODEKIND_SCALAR)) {
119         if ((node->status == SMI_STATUS_DEPRECATED
120             && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
121             || (node->status == SMI_STATUS_OBSOLETE
122             && !SHOW_DEPR_OBSOLETE))
123             continue;
124         newNode = graphInsertNode(graph, node);
125         newNode->smiModule = module;
126     }
127 }
128
129
130
131 /* ------ XML primitives ------                                              */
132
133
134
135 /*
136  * parseTooltip: Parse any input to output to make the text safe for the
137  * ShowTooltipMZ-functin in the ecma-script.
138  */
139 static void parseTooltip(char *input, char *output)
140 {
141     int i, j;
142
143     for (i = j = 0; input[i]; i++) {
144         switch (input[i]) {
145         case '\n':
146             output[j++] = '\\';
147             output[j++] = 'n';
148             break;
149         case '\\':
150             output[j++] = '\\';
151             output[j++] = '\\';
152             break;
153         case '\"':
154             /* quotes are not allowed in strings. */
155             /* See chapter 3.4.5 in "Understanding SNMP MIBs" */
156             break;
157         case '&':
158             output[j++] = '&';
159             output[j++] = 'a';
160             output[j++] = 'm';
161             output[j++] = 'p';
162             output[j++] = ';';
163             break;
164         case '<':
165             output[j++] = '&';
166             output[j++] = 'l';
167             output[j++] = 't';
168             output[j++] = ';';
169             break;
170         case '>':
171             output[j++] = '&';
172             output[j++] = 'g';
173             output[j++] = 't';
174             output[j++] = ';';
175             break;
176         case '\'':
177             /* It seems, &apos; doesn't work... */
178             output[j++] = '\\';
179             output[j++] = '\'';
180             break;
181         default:
182             output[j++] = input[i];
183         }
184     }
185     output[j] = '\0';
186 }
187
188 static int isObjectGroup(SmiNode *groupNode)
189 {
190     SmiNode     *smiNode;
191     SmiElement  *smiElement;
192     
193     for (smiElement = smiGetFirstElement(groupNode); smiElement;
194          smiElement = smiGetNextElement(smiElement)) {
195
196         smiNode = smiGetElementNode(smiElement);
197         
198         if (smiNode->nodekind != SMI_NODEKIND_SCALAR
199             && smiNode->nodekind != SMI_NODEKIND_COLUMN) {
200             return 0;
201         }
202     }
203
204     return 1;
205 }
206
207 static int isNotificationGroup(SmiNode *groupNode)
208 {
209     SmiNode     *smiNode;
210     SmiElement  *smiElement;
211     
212     for (smiElement = smiGetFirstElement(groupNode); smiElement;
213          smiElement = smiGetNextElement(smiElement)) {
214
215         smiNode = smiGetElementNode(smiElement);
216         
217         if (smiNode->nodekind != SMI_NODEKIND_NOTIFICATION) {
218             return 0;
219         }
220     }
221
222     return 1;
223 }
224
225 static char *printFillColor(SmiStatus status)
226 {
227     return
228         (status == SMI_STATUS_CURRENT)     ? "rgb(0%,0%,0%)" :
229         (status == SMI_STATUS_DEPRECATED)  ? "rgb(40%,40%,40%)" :
230         (status == SMI_STATUS_OBSOLETE)    ? "rgb(60%,60%,60%)" :
231         (status == SMI_STATUS_MANDATORY)   ? "rgb(0%,0%,0%)" :
232         (status == SMI_STATUS_OPTIONAL)    ? "rgb(20%,20%,20%)" :
233                                              "";
234 }
235
236 static char *getStatusString(SmiStatus status)
237 {
238     return
239         (status == SMI_STATUS_CURRENT)     ? "current" :
240         (status == SMI_STATUS_DEPRECATED)  ? "deprecated" :
241         (status == SMI_STATUS_OBSOLETE)    ? "obsolete" :
242         (status == SMI_STATUS_MANDATORY)   ? "mandatory" :
243         (status == SMI_STATUS_OPTIONAL)    ? "optional" :
244                                              "<unknown>";
245 }
246
247 /*
248  * Prints the footer of the SVG output file.
249  */
250 static void printSVGClose(float xMin, float yMin, float xMax, float yMax)
251 {
252     float scale;
253
254     scale = max((xMax-xMin)/CANVASWIDTH,(yMax-yMin)/CANVASHEIGHT);
255     /* enclose whole canvas in its bounding box */
256     /*
257     printf(" <rect x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\"\n",
258            xMin, yMin, xMax-xMin-1, yMax-yMin-1);
259     printf("       fill=\"none\" stroke=\"blue\" stroke-width=\"1\"/>\n");
260     */
261     if (!STATIC_OUTPUT) {
262         printf(" <g transform=\"translate(%.2f,%.2f) scale(%.2f)\">\n",
263                                                         xMin, yMin, scale);
264         printf(" <g id=\"tooltip\" style=\"visibility: hidden\">\n");
265         printf("   <rect id=\"ttr\" x=\"0\" y=\"0\" rx=\"5\" ry=\"5\"");
266         printf(" width=\"100\" height=\"16\"/>\n");
267         printf("   <line id=\"ttl\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"0\"/>\n");
268         printf("   <text class=\"tooltip\" xml:space=\"preserve\"");
269         printf(" id=\"ttt\" x=\"0\" y=\"0\" style=\"visibility: hidden\">");
270         printf("dyn. Text</text>\n");
271         printf("   <text class=\"tooltip\" xml:space=\"preserve\"");
272         printf(" x=\"-10\" y=\"-10\">dyn. Text</text>\n");
273         printf(" </g>\n");
274         printf(" </g>\n");
275     }
276     printf("</svg>\n");
277 }
278
279 /*
280  * FIXME stimmt das?
281  * index = 0 -> no index element
282  * index = 1 -> index element -> printed with "+"
283  */
284 static void printSVGAttribute(SmiNode *node, SmiNode *tableNode, int index,
285                               int modc, SmiModule **modv,
286                               float *textYOffset, float *textXOffset)
287 {
288     int         i, target_exists = 0;
289     size_t      length;
290     char        *tooltip, *tooltipDescription, *typeDescription;
291     const char  *baseTypeTooltipText = "This is a basetype.";
292     const char  *isDefined = " is defined in module ";
293
294     if ((node->status == SMI_STATUS_DEPRECATED
295         && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
296         || (node->status == SMI_STATUS_OBSOLETE
297         && !SHOW_DEPR_OBSOLETE))
298         return;
299
300     printf("  <text");
301     if (!index) {
302         printf(" id=\"%s\"", node->name);
303     }
304     printf(" fill=\"%s\"", printFillColor(node->status));
305     printf(" x=\"%.2f\" y=\"%.2f\">\n",
306                                 *textXOffset + ATTRSPACESIZE + 4, *textYOffset);
307
308     *textYOffset += TABLEELEMHEIGHT;
309
310     /* FIXME
311        rintf(" textLength=\"100\" lengthAdjust=\"spacingAndGlyphs\""); */
312
313     if (!index) {
314         if (node->access == SMI_ACCESS_NOT_ACCESSIBLE) {
315             printf("    <tspan style=\"text-anchor:middle\">-</tspan>");
316         } else {
317             printf("    <tspan style=\"text-anchor:middle\">+</tspan>");
318         }
319     }
320
321     printf("<tspan x=\"%.2f\"", *textXOffset + ATTRSPACESIZE + 8);
322     if (!STATIC_OUTPUT) {
323         if (node->description) {
324             tooltip = (char *)xmalloc(2*strlen(node->description));
325             parseTooltip(node->description, tooltip);
326             printf(" onmousemove=\"ShowTooltipMZ(evt,'%s')\"", tooltip);
327             printf(" onmouseout=\"HideTooltip(evt)\"");
328             xfree(tooltip);
329         }
330         if (index) {
331             for (i=0; i<modc; i++) {
332                 if (modv[i] == smiGetNodeModule(node)) {
333                     target_exists = 1;
334                 }
335             }
336             if (!target_exists) {
337                 printf(" fill=\"%s\">\n", linkcolor);
338                 printf("      <a xlink:href=\"%s", link);
339                 for (i=0; i<modc; i++) {
340                     printf("&amp;mibs=%s", modv[i]->name);
341                 }
342                 printf("&amp;mibs=%s\">\n", smiGetNodeModule(node)->name);
343                 printf("        %s:\n", node->name);
344                 printf("      </a></tspan>\n");
345             } else {
346                 printf(">%s:</tspan>\n", node->name);
347             }
348         } else {
349             printf(">%s:</tspan>\n", node->name);
350         }
351     } else {
352         printf(">%s:</tspan>\n", node->name);
353     }
354
355     printf("    <tspan");
356     if (!STATIC_OUTPUT) {
357         if ((typeDescription = algGetTypeDescription(node))) {
358             tooltipDescription = (char *)xmalloc(2*strlen(typeDescription));
359             parseTooltip(typeDescription, tooltipDescription);
360             if (algGetTypeModule(node)) {
361                 if ((smiGetNodeModule(node) != smiGetNodeModule(tableNode)) ||
362                     (smiGetNodeModule(node) != algGetTypeModule(node))) {
363                     length = strlen(tooltipDescription) + 150;
364                     tooltip = (char *)xmalloc(length);
365                     strcpy(tooltip, algGetTypeName(node));
366                     strcat(tooltip, isDefined);
367                     strcat(tooltip, algGetTypeModule(node)->name);
368                     strcat(tooltip, ":\\n\\n");
369                     strcat(tooltip, tooltipDescription);
370                 } else {
371                     length = strlen(tooltipDescription) + 150;
372                     tooltip = (char *)xmalloc(length);
373                     strcpy(tooltip, tooltipDescription);
374                 }
375             } else {
376                 length = strlen(tooltipDescription);
377                 tooltip = (char *)xmalloc(length);
378                 strcpy(tooltip, tooltipDescription);
379             }
380             xfree(tooltipDescription);
381             printf(" onmousemove=\"ShowTooltipMZ(evt,'%s')\"", tooltip);
382             printf(" onmouseout=\"HideTooltip(evt)\"");
383             xfree(tooltip);
384         } else if (isBaseType(node)) {
385             length = strlen(baseTypeTooltipText) + 1;
386             tooltip = (char *)xmalloc(length);
387             strcpy(tooltip, baseTypeTooltipText);
388             printf(" onmousemove=\"ShowTooltipMZ(evt,'%s')\"", tooltip);
389             printf(" onmouseout=\"HideTooltip(evt)\"");
390             xfree(tooltip);
391         }
392     }
393     printf(">%s</tspan>", algGetTypeName(node));
394     switch (node->status) {
395     case SMI_STATUS_DEPRECATED:
396     case SMI_STATUS_OBSOLETE:
397         printf(" (%s)", getStatusString(node->status));
398     case SMI_STATUS_MANDATORY:
399     case SMI_STATUS_OPTIONAL:
400     case SMI_STATUS_CURRENT:
401     case SMI_STATUS_UNKNOWN:
402         ;
403     }
404     printf("</text>\n");
405 }
406
407 /*
408  * prints the related scalars for a given table
409  */
410 static void printSVGRelatedScalars(GraphNode *node, SmiNode *tableNode,
411                                    int modc, SmiModule **modv,
412                                    float *textYOffset, float *textXOffset)
413 {
414     GraphEdge *tEdge;
415     
416     for (tEdge = graphGetFirstEdgeByNode(graph, node);
417          tEdge;
418          tEdge = graphGetNextEdgeByNode(graph, tEdge, node)) {
419         if (tEdge->startNode == node  &&
420             tEdge->endNode->smiNode->nodekind == SMI_NODEKIND_SCALAR) {
421
422             printSVGAttribute(tEdge->endNode->smiNode, tableNode, 0,
423                               modc, modv,
424                               textYOffset, textXOffset);
425         }
426     }
427 }
428
429 /*
430  * prints all columns objects of the given node
431  */
432 static void printSVGAllColumns(GraphNode *node, SmiNode *tableNode,
433                                int modc, SmiModule **modv,
434                                float *textYOffset, float *textXOffset)
435 {
436     SmiModule *module  = NULL;
437     SmiNode   *smiNode = NULL;
438     SmiNode   *ppNode;
439
440     module  = smiGetNodeModule(node->smiNode);
441
442     for (smiNode = smiGetFirstNode(module, SMI_NODEKIND_COLUMN);
443          smiNode;
444          smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_COLUMN)) {
445         ppNode = smiGetParentNode(smiNode);
446         ppNode = smiGetParentNode(ppNode);
447         
448         if (!algIsIndexElement(node->smiNode, smiNode) &&
449             cmpSmiNodes(node->smiNode, ppNode))
450             printSVGAttribute(smiNode, tableNode, 0,
451                               modc, modv,
452                               textYOffset, textXOffset);
453     }
454 }
455
456 /*
457  * adds the index to an augmenting table (row-element)
458  */
459 static void printSVGAugmentIndex(GraphNode *tNode, SmiNode *tableNode,
460                                  int modc, SmiModule **modv,
461                                  float *textYOffset, float *textXOffset)
462 {
463     GraphEdge  *tEdge;
464     SmiElement *smiElement;
465
466     for (tEdge = graphGetFirstEdgeByNode(graph, tNode);
467          tEdge;
468          tEdge = graphGetNextEdgeByNode(graph, tEdge, tNode)) {
469         if (tEdge->indexkind == SMI_INDEX_AUGMENT) {
470             for (smiElement = smiGetFirstElement(
471                 smiGetFirstChildNode(tEdge->startNode->smiNode));
472                  smiElement;
473                  smiElement = smiGetNextElement(smiElement)) {
474                 if (!cmpSmiNodes(tNode->smiNode, tEdge->startNode->smiNode)) {
475                     printSVGAttribute(smiGetElementNode(smiElement), tableNode,
476                                       1, modc, modv,
477                                       textYOffset, textXOffset);
478                 }
479             }
480         }
481     }
482 }
483
484 /*
485  * print "This module doesn't contain any objects"
486  */
487 static void printNoObjects()
488 {
489     printf(" <rect x=\"10\" y=\"10\" width=\"120\" height=\"40\"");
490     printf(" fill=\"white\" stroke=\"black\"/>\n");
491     printf("  <text x=\"15\" y=\"25\" fill=\"black\">\n");
492     printf("   This module doesn't\n");
493     printf("  </text>\n");
494     printf("  <text x=\"15\" y=\"40\" fill=\"black\">\n");
495     printf("   contain any objects.\n");
496     printf("  </text>\n");
497 }
498
499 /*
500  * print "This module only contains textual conventions"
501  */
502 static void printOnlyTCs()
503 {
504     printf(" <rect x=\"10\" y=\"10\" width=\"150\" height=\"40\"");
505     printf(" fill=\"white\" stroke=\"black\"/>\n");
506     printf("  <text x=\"15\" y=\"25\" fill=\"black\">\n");
507     printf("   This module only contains\n");
508     printf("  </text>\n");
509     printf("  <text x=\"15\" y=\"40\" fill=\"black\">\n");
510     printf("   textual conventions.\n");
511     printf("  </text>\n");
512 }
513
514 /*
515  * create svg-output for the given node
516  */
517 static void printSVGObject(GraphNode *node, int *classNr,
518                            int modc, SmiModule **modv)
519 {
520     SmiElement *smiElement;
521     float textXOffset, textYOffset, xOrigin, yOrigin;
522     size_t length = 1;
523     char *tooltip, *tooltipTable, *tooltipEntry;
524     const char *blankLine = "\\n-- -- --\\n";
525     
526     if (!node) return;
527
528     xOrigin = node->dia.w/-2;
529     yOrigin = node->dia.h/-2;
530     textYOffset = yOrigin + TABLEHEIGHT + TABLEELEMHEIGHT;
531     textXOffset = xOrigin;
532
533     printf("  <g transform=\"translate(%.2f,%.2f)\">\n",
534            node->dia.x + node->component->xOffset,
535            node->dia.y + node->component->yOffset);
536     printf("    <rect id=\"%s\"", node->smiNode->name);
537     printf(" x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\"\n",
538            xOrigin, yOrigin, node->dia.w, node->dia.h);
539     printf("          fill=\"white\" stroke=\"black\"/>\n");
540     if (!STATIC_OUTPUT) {
541         printf("    <rect x=\"%.2f\" y=\"%.2f\" width=\"16\" height=\"16\"",
542                xOrigin + 2, yOrigin + 2);
543         printf(" rx=\"4\" ry=\"4\"");
544         printf(" stroke-width=\"3\" stroke=\"gray\" fill=\"white\"\n");
545         printf("          onmousedown=\"ClickObj(evt)\"");
546         printf(" onclick=\"ClickObj(evt)\"");
547         printf(" onmousemove=\"MoveObj(evt)\"");
548         printf(" onmouseup=\"OutOfObj(evt)\"");
549         printf(" onmouseout=\"OutOfObj(evt)\"/>\n");
550     }
551     printf("    <polygon points=\"%.2f %.2f %.2f %.2f\"\n",
552            xOrigin, yOrigin + TABLEHEIGHT,
553            xOrigin + node->dia.w, yOrigin + TABLEHEIGHT);
554     printf("          fill=\"none\" stroke=\"black\"/>\n");
555     printf("    <text x=\"0\" y=\"%.2f\"", yOrigin + 15);
556     printf(" fill=\"%s\"", printFillColor(node->smiNode->status));
557     printf(" style=\"text-anchor:middle; font-weight:bold\"");
558
559     /* descriptions for the table and the entries */
560     if (!STATIC_OUTPUT) {
561         if (node->smiNode->description) {
562             tooltipTable=(char *)xmalloc(2*strlen(node->smiNode->description));
563             parseTooltip(node->smiNode->description, tooltipTable);
564         }
565         if (smiGetFirstChildNode(node->smiNode)->description) {
566             tooltipEntry=(char *)xmalloc(2*strlen(smiGetFirstChildNode(
567                                                 node->smiNode)->description));
568             parseTooltip(smiGetFirstChildNode(node->smiNode)->description,
569                                                                 tooltipEntry);
570         }
571
572         if (node->smiNode->description) {
573             length += strlen(tooltipTable);
574         }
575         if (node->smiNode->description
576                 && smiGetFirstChildNode(node->smiNode)->description) {
577             length += strlen(blankLine);
578         }
579         if (smiGetFirstChildNode(node->smiNode)->description) {
580             length += strlen(tooltipEntry);
581         }
582         tooltip = (char *)xmalloc(length);
583
584         strcpy(tooltip, "\0");
585         if (node->smiNode->description) {
586             strcat(tooltip, tooltipTable);
587         }
588         if (node->smiNode->description
589                 && smiGetFirstChildNode(node->smiNode)->description) {
590             strcat(tooltip, blankLine);
591         }
592         if (smiGetFirstChildNode(node->smiNode)->description) {
593             strcat(tooltip, tooltipEntry);
594         }
595
596         printf(" onmousemove=\"ShowTooltipMZ(evt,'%s')\"", tooltip);
597         printf(" onmouseout=\"HideTooltip(evt)\"");
598
599         if (node->smiNode->description) {
600             xfree(tooltipTable);
601         }
602         if (smiGetFirstChildNode(node->smiNode)->description) {
603             xfree(tooltipEntry);
604         }
605         xfree(tooltip);
606     }
607
608     printf(">\n");
609     printf("         %s",smiGetFirstChildNode(node->smiNode)->name);
610     switch (node->smiNode->status) {
611     case SMI_STATUS_DEPRECATED:
612     case SMI_STATUS_OBSOLETE:
613         printf(" (%s)", getStatusString(node->smiNode->status));
614     case SMI_STATUS_MANDATORY:
615     case SMI_STATUS_OPTIONAL:
616     case SMI_STATUS_CURRENT:
617     case SMI_STATUS_UNKNOWN:
618         ;
619     }
620     printf("</text>\n");
621
622     (*classNr)++;
623
624     if (node->smiNode->nodekind == SMI_NODEKIND_TABLE) {
625
626         if (node->dia.relatedScalars) {
627             /* A */
628             printSVGRelatedScalars(node, node->smiNode,
629                                    modc, modv,
630                                    &textYOffset, &textXOffset);
631
632             printf("    <polygon points=\"%.2f %.2f %.2f %.2f\"\n",
633                             xOrigin,
634                             textYOffset - TABLEELEMHEIGHT + TABLEBOTTOMHEIGHT,
635                             xOrigin + node->dia.w,
636                             textYOffset - TABLEELEMHEIGHT + TABLEBOTTOMHEIGHT);
637             printf("          fill=\"none\" stroke=\"black\"/>\n");
638             textYOffset += TABLEBOTTOMHEIGHT;
639         }
640
641         if (node->dia.indexObjects) {
642             /* B */
643             printSVGAugmentIndex(node, node->smiNode,
644                                  modc, modv,
645                                  &textYOffset, &textXOffset);
646             /* C */
647             for (smiElement = smiGetFirstElement(
648                 smiGetFirstChildNode(node->smiNode));
649                  smiElement;
650                  smiElement = smiGetNextElement(smiElement)) {
651                 printSVGAttribute(smiGetElementNode(smiElement), node->smiNode,
652                                   1, modc, modv,
653                                   &textYOffset, &textXOffset);
654             }
655
656             printf("    <polygon points=\"%.2f %.2f %.2f %.2f\"\n",
657                             xOrigin,
658                             textYOffset - TABLEELEMHEIGHT + TABLEBOTTOMHEIGHT,
659                             xOrigin + node->dia.w,
660                             textYOffset - TABLEELEMHEIGHT + TABLEBOTTOMHEIGHT);
661             printf("          fill=\"none\" stroke=\"black\"/>\n");
662             textYOffset += TABLEBOTTOMHEIGHT;
663         }
664
665         /* D */
666         if (PRINT_DETAILED_ATTR) {
667             printSVGAllColumns(node, node->smiNode,
668                                modc, modv,
669                                &textYOffset, &textXOffset);
670         }
671     }
672
673     printf("  </g>\n");
674 }
675
676 /*
677  * prints a group of scalars denoted by group
678  */
679 static void printSVGGroup(int group, int *classNr,
680                           int modc, SmiModule **modv)
681 {
682     GraphNode *tNode;
683     float textXOffset, textYOffset, xOrigin, yOrigin;
684
685     for (tNode = graph->nodes; tNode; tNode = tNode->nextPtr) {
686         if (tNode->group == group) break;
687     }
688
689     if (!tNode) return;
690
691     xOrigin = tNode->dia.w/-2;
692     yOrigin = tNode->dia.h/-2;
693     textYOffset = yOrigin + TABLEHEIGHT + TABLEELEMHEIGHT;
694     textXOffset = xOrigin;
695
696     printf("  <g transform=\"translate(%.2f,%.2f)\">\n",
697            tNode->dia.x + tNode->component->xOffset,
698            tNode->dia.y + tNode->component->yOffset);
699     printf("    <rect id=\"%s\"",
700            smiGetParentNode(tNode->smiNode)->name);
701     printf(" x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\"\n",
702            xOrigin, yOrigin, tNode->dia.w, tNode->dia.h);
703     printf("          fill=\"white\" stroke=\"black\"/>\n");
704     if (!STATIC_OUTPUT) {
705         printf("    <rect x=\"%.2f\" y=\"%.2f\" width=\"16\" height=\"16\"",
706                xOrigin + 2, yOrigin + 2);
707         printf(" rx=\"4\" ry=\"4\"");
708         printf(" stroke-width=\"3\" stroke=\"gray\" fill=\"white\"\n");
709         printf("          onmousedown=\"ClickObj(evt)\"");
710         printf(" onclick=\"ClickObj(evt)\"");
711         printf(" onmousemove=\"MoveObj(evt)\"");
712         printf(" onmouseup=\"OutOfObj(evt)\"");
713         printf(" onmouseout=\"OutOfObj(evt)\"/>\n");
714     }
715     printf("    <polygon points=\"%.2f %.2f %.2f %.2f\"\n",
716            xOrigin, yOrigin + TABLEHEIGHT,
717            xOrigin + tNode->dia.w, yOrigin + TABLEHEIGHT);
718     printf("          fill=\"none\" stroke=\"black\"/>\n");
719     printf("    <text x=\"0\" y=\"%.2f\"", yOrigin + 15);
720     printf(" fill=\"%s\"",
721                     printFillColor(smiGetParentNode(tNode->smiNode)->status));
722     printf(" style=\"text-anchor:middle; font-weight:bold\">\n");
723     /* groups don't seem to have a description. */
724     printf("         %s", smiGetParentNode(tNode->smiNode)->name);
725     switch (smiGetParentNode(tNode->smiNode)->status) {
726     case SMI_STATUS_DEPRECATED:
727     case SMI_STATUS_OBSOLETE:
728         printf(" (%s)",
729                     getStatusString(smiGetParentNode(tNode->smiNode)->status));
730     case SMI_STATUS_MANDATORY:
731     case SMI_STATUS_OPTIONAL:
732     case SMI_STATUS_CURRENT:
733     case SMI_STATUS_UNKNOWN:
734         ;
735     }
736     printf("</text>\n");
737
738     (*classNr)++;
739
740     for (tNode = graph->nodes; tNode; tNode = tNode->nextPtr) {
741         if (tNode->group == group) {
742             printSVGAttribute(tNode->smiNode, tNode->smiNode, 0,
743                               modc, modv,
744                               &textYOffset, &textXOffset);
745         }
746     }
747     
748     printf("  </g>\n");
749 }
750
751 static void calculateIntersectionPoints(GraphEdge *tEdge)
752 {
753     float alpha, beta;
754     const float PI = acos(-1);
755
756     /* calculate intersection of edge and startNode */
757     alpha = atan2(tEdge->startNode->dia.y-tEdge->endNode->dia.y,
758                   tEdge->startNode->dia.x-tEdge->endNode->dia.x);
759     beta = atan2(tEdge->startNode->dia.h, tEdge->startNode->dia.w);
760     if (alpha < 0)
761         alpha += PI;
762     if (alpha < beta
763         || (alpha > PI-beta && alpha < PI+beta)
764         || alpha > 2*PI-beta) {
765         /* intersection at left or right border */
766         if (tEdge->startNode->dia.x < tEdge->endNode->dia.x) {
767             tEdge->dia.startX = tEdge->startNode->dia.x +
768                                                     tEdge->startNode->dia.w/2;
769         } else {
770             tEdge->dia.startX = tEdge->startNode->dia.x -
771                                                     tEdge->startNode->dia.w/2;
772         }
773         if (tEdge->startNode->dia.y < tEdge->endNode->dia.y) {
774             tEdge->dia.startY = tEdge->startNode->dia.y +
775                                 fabsf(tEdge->startNode->dia.w*tan(alpha)/2);
776         } else {
777             tEdge->dia.startY = tEdge->startNode->dia.y -
778                                 fabsf(tEdge->startNode->dia.w*tan(alpha)/2);
779         }
780     } else {
781         /* intersection at top or bottom border */
782         if (tEdge->startNode->dia.y < tEdge->endNode->dia.y) {
783             tEdge->dia.startY = tEdge->startNode->dia.y +
784                                                     tEdge->startNode->dia.h/2;
785         } else {
786             tEdge->dia.startY = tEdge->startNode->dia.y -
787                                                     tEdge->startNode->dia.h/2;
788         }
789         if (tEdge->startNode->dia.x < tEdge->endNode->dia.x) {
790             tEdge->dia.startX = tEdge->startNode->dia.x +
791                                 fabsf(tEdge->startNode->dia.h/(2*tan(alpha)));
792         } else {
793             tEdge->dia.startX = tEdge->startNode->dia.x -
794                                 fabsf(tEdge->startNode->dia.h/(2*tan(alpha)));
795         }
796     }
797
798     /* calculate intersection of edge and endNode */
799     alpha = atan2(tEdge->startNode->dia.y-tEdge->endNode->dia.y,
800                   tEdge->startNode->dia.x-tEdge->endNode->dia.x);
801     beta = atan2(tEdge->endNode->dia.h, tEdge->endNode->dia.w);
802     if (alpha < 0)
803         alpha += PI;
804     if (alpha < beta
805         || (alpha > PI-beta && alpha < PI+beta)
806         || alpha > 2*PI-beta) {
807         /* intersection at left or right border */
808         if (tEdge->startNode->dia.x > tEdge->endNode->dia.x) {
809             tEdge->dia.endX = tEdge->endNode->dia.x + tEdge->endNode->dia.w/2;
810         } else {
811             tEdge->dia.endX = tEdge->endNode->dia.x - tEdge->endNode->dia.w/2;
812         }
813         if (tEdge->startNode->dia.y > tEdge->endNode->dia.y) {
814             tEdge->dia.endY = tEdge->endNode->dia.y +
815                                     fabsf(tEdge->endNode->dia.w*tan(alpha)/2);
816         } else {
817             tEdge->dia.endY = tEdge->endNode->dia.y -
818                                     fabsf(tEdge->endNode->dia.w*tan(alpha)/2);
819         }
820     } else {
821         /* intersection at top or bottom border */
822         if (tEdge->startNode->dia.y > tEdge->endNode->dia.y) {
823             tEdge->dia.endY = tEdge->endNode->dia.y + tEdge->endNode->dia.h/2;
824         } else {
825             tEdge->dia.endY = tEdge->endNode->dia.y - tEdge->endNode->dia.h/2;
826         }
827         if (tEdge->startNode->dia.x > tEdge->endNode->dia.x) {
828             tEdge->dia.endX = tEdge->endNode->dia.x +
829                                 fabsf(tEdge->endNode->dia.h/(2*tan(alpha)));
830         } else {
831             tEdge->dia.endX = tEdge->endNode->dia.x -
832                                 fabsf(tEdge->endNode->dia.h/(2*tan(alpha)));
833         }
834     }
835 }
836
837 static void printSVGDependency(GraphEdge *tEdge)
838 {
839     int revert = 0;
840
841     calculateIntersectionPoints(tEdge);
842
843     /* print text upside down, if angle is between 180° and 360° */
844     if (tEdge->startNode->dia.x > tEdge->endNode->dia.x)
845         revert = 1;
846
847     printf(" <path id=\"%s-%s\"\n",
848         tEdge->startNode->smiNode->name,
849         tEdge->endNode->smiNode->name);
850     if (!revert) {
851         printf("       d=\"M %.2f %.2f %.2f %.2f\"\n",
852             tEdge->dia.startX + tEdge->startNode->component->xOffset,
853             tEdge->dia.startY + tEdge->startNode->component->yOffset,
854             tEdge->dia.endX + tEdge->endNode->component->xOffset,
855             tEdge->dia.endY + tEdge->endNode->component->yOffset);
856     } else {
857         printf("       d=\"M %.2f %.2f %.2f %.2f\"\n",
858             tEdge->dia.endX + tEdge->endNode->component->xOffset,
859             tEdge->dia.endY + tEdge->endNode->component->yOffset,
860             tEdge->dia.startX + tEdge->startNode->component->xOffset,
861             tEdge->dia.startY + tEdge->startNode->component->yOffset);
862     }
863     printf("       stroke-dasharray=\"10, 10\" stroke=\"black\"");
864     if (!revert) {
865         printf(" marker-end=\"url(#arrowend)\"/>\n");
866     } else {
867         printf(" marker-start=\"url(#arrowstart)\"/>\n");
868     }
869 }
870
871 /*
872  * Aggregation is a special case of the association.
873  * If aggregate = 1 it is an aggregation if 0 it is an association.
874  */
875 static void printSVGAssociation(GraphEdge *tEdge, int aggregate)
876 {
877     int revert = 0;
878
879     if (aggregate > 1) aggregate = 1;
880     if (aggregate < 0) aggregate = 0;
881
882     calculateIntersectionPoints(tEdge);
883
884     /* expands should have cardinalities 1 * */
885     if (tEdge->indexkind==SMI_INDEX_EXPAND)
886         tEdge->cardinality = GRAPH_CARD_ONE_TO_MANY;
887
888     /* print text upside down, if angle is between 180° and 360° */
889     if (tEdge->startNode->dia.x > tEdge->endNode->dia.x)
890         revert = 1;
891
892     /* print edge */
893     printf(" <path id=\"%s-%s\"\n",
894         tEdge->startNode->smiNode->name,
895         tEdge->endNode->smiNode->name);
896     if (!revert) {
897         printf("       d=\"M %.2f %.2f %.2f %.2f\"\n",
898             tEdge->dia.startX + tEdge->startNode->component->xOffset,
899             tEdge->dia.startY + tEdge->startNode->component->yOffset,
900             tEdge->dia.endX + tEdge->endNode->component->xOffset,
901             tEdge->dia.endY + tEdge->endNode->component->yOffset);
902     } else {
903         printf("       d=\"M %.2f %.2f %.2f %.2f\"\n",
904             tEdge->dia.endX + tEdge->endNode->component->xOffset,
905             tEdge->dia.endY + tEdge->endNode->component->yOffset,
906             tEdge->dia.startX + tEdge->startNode->component->xOffset,
907             tEdge->dia.startY + tEdge->startNode->component->yOffset);
908     }
909     printf("       stroke=\"black\"");
910     if (tEdge->indexkind==SMI_INDEX_AUGMENT ||
911         tEdge->indexkind==SMI_INDEX_SPARSE ||
912         tEdge->indexkind==SMI_INDEX_EXPAND) {
913         if (!revert) {
914             printf(" marker-start=\"url(#arrowstart)\"");
915         } else {
916             printf(" marker-end=\"url(#arrowend)\"");
917         }
918     } else if (tEdge->indexkind==SMI_INDEX_REORDER) {
919         printf(" marker-start=\"url(#arrowstart)\"");
920         printf(" marker-end=\"url(#arrowend)\"");
921     }
922     printf("/>\n");
923
924     /* edges without labels are finished here */
925     if (tEdge->cardinality==GRAPH_CARD_UNKNOWN)
926         return;
927
928     /* print labels */
929     printf(" <text text-anchor=\"middle\">\n");
930     printf("    <textPath xlink:href=\"#%s-%s\"",
931                 tEdge->startNode->smiNode->name, tEdge->endNode->smiNode->name);
932     if (!revert) {
933         printf(" startOffset=\"10%%\">\n");
934     } else {
935         printf(" startOffset=\"90%%\">\n");
936     }
937     switch (tEdge->cardinality) {
938     case GRAPH_CARD_ZERO_TO_ONE:
939     case GRAPH_CARD_ZERO_TO_MANY:
940         printf("       0");
941         break;
942     case GRAPH_CARD_ONE_TO_ONE:
943     case GRAPH_CARD_ONE_TO_MANY:
944     case GRAPH_CARD_ONE_TO_ZERO_OR_ONE:
945         printf("       1");
946         break;
947     case GRAPH_CARD_UNKNOWN:
948         ;
949     }
950     printf("</textPath>\n");
951     printf(" </text>\n");
952
953     if (tEdge->indexkind==SMI_INDEX_AUGMENT ||
954         tEdge->indexkind==SMI_INDEX_SPARSE ||
955         tEdge->indexkind==SMI_INDEX_REORDER ||
956         tEdge->indexkind==SMI_INDEX_EXPAND) {
957         printf(" <text text-anchor=\"middle\">\n");
958         printf("    <textPath xlink:href=\"#%s-%s\" startOffset=\"50%%\">\n",
959                 tEdge->startNode->smiNode->name, tEdge->endNode->smiNode->name);
960     }
961     switch(tEdge->indexkind) {
962     case SMI_INDEX_AUGMENT:
963         printf("       augments");
964         break;
965     case SMI_INDEX_SPARSE:
966         printf("       sparsly augments");
967         break;
968     case SMI_INDEX_REORDER:
969         printf("       reorders");
970         break;
971     case SMI_INDEX_EXPAND:
972         printf("       expands");
973         break;
974     case SMI_INDEX_UNKNOWN:
975     case SMI_INDEX_INDEX:
976         ;
977     }
978     if (tEdge->indexkind==SMI_INDEX_AUGMENT ||
979         tEdge->indexkind==SMI_INDEX_SPARSE ||
980         tEdge->indexkind==SMI_INDEX_REORDER ||
981         tEdge->indexkind==SMI_INDEX_EXPAND) {
982         printf("</textPath>\n");
983         printf(" </text>\n");
984     }
985
986     printf(" <text text-anchor=\"middle\">\n");
987     printf("    <textPath xlink:href=\"#%s-%s\"",
988                 tEdge->startNode->smiNode->name, tEdge->endNode->smiNode->name);
989     if (!revert) {
990         printf(" startOffset=\"90%%\">\n");
991     } else {
992         printf(" startOffset=\"10%%\">\n");
993     }
994     switch (tEdge->cardinality) {
995     case GRAPH_CARD_ONE_TO_ONE:
996     case GRAPH_CARD_ZERO_TO_ONE:
997         printf("       1");
998         break;
999     case GRAPH_CARD_ONE_TO_MANY:
1000     case GRAPH_CARD_ZERO_TO_MANY:
1001         printf("       *");
1002         break;
1003     case GRAPH_CARD_ONE_TO_ZERO_OR_ONE:
1004         printf("       0..1");
1005         break;
1006     case GRAPH_CARD_UNKNOWN:
1007         ;
1008     }
1009     printf("</textPath>\n");
1010     printf(" </text>\n");
1011 }
1012
1013 static void printSVGConnection(GraphEdge *tEdge)
1014 {
1015     switch (tEdge->connection) {
1016     case GRAPH_CON_UNKNOWN:
1017         break;
1018     case GRAPH_CON_AGGREGATION :        /* never used??? */
1019         printSVGAssociation(tEdge,1);
1020         break;
1021     case GRAPH_CON_DEPENDENCY :
1022         printSVGDependency(tEdge);
1023         break;
1024     case GRAPH_CON_ASSOCIATION :
1025         printSVGAssociation(tEdge,0);
1026         break;      
1027     }
1028 }
1029
1030 /*
1031  * Prints the title of the SVG output file (Modulename and smidump version).
1032  * TODO
1033  * Print title somewhere into the SVG.
1034  * Make size of SVG configurable.
1035  */
1036 static void printSVGHeaderAndTitle(int modc, SmiModule **modv,
1037                                    int miCount, int idCount,
1038                                    float xMin, float yMin,
1039                                    float xMax, float yMax)
1040 {
1041     size_t  length1;
1042     char   *note1;
1043     int     i;
1044     const char *s11 = "Conceptual model of ";
1045     const char *s12 = "- generated by smidump " SMI_VERSION_STRING;
1046     float scale;
1047
1048     scale = max((xMax-xMin)/CANVASWIDTH,(yMax-yMin)/CANVASHEIGHT);
1049
1050     /*
1051      * Calculate the length of the string...
1052      */
1053
1054     length1 = strlen(s11) + strlen(s12) + 1;
1055     for (i = 0; i < modc; i++) {
1056         length1 += strlen(modv[i]->name) + 1;
1057     }
1058
1059     /*
1060      * ... before allocating a buffer and putting the string together.
1061      */
1062
1063     note1 = xmalloc(length1);
1064     strcpy(note1, s11);
1065     for (i = 0; i < modc; i++) {
1066         strcat(note1, modv[i]->name);
1067         strcat(note1, " ");
1068     }
1069     strcat(note1, s12);
1070
1071     printf("<?xml version=\"1.0\"?>\n");
1072     printf("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n");
1073     printf("  \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
1074     printf("<svg preserveAspectRatio=\"xMinYMin meet\"\n");
1075     printf("     width=\"%i\" height=\"%i\" viewBox=\"%.2f %.2f %.2f %.2f\"\n",
1076            CANVASWIDTH, CANVASHEIGHT, xMin, yMin, xMax-xMin, yMax-yMin);
1077     printf("     version=\"1.1\"\n");
1078     printf("     xmlns=\"http://www.w3.org/2000/svg\"\n");
1079     printf("     xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
1080     if (!STATIC_OUTPUT)
1081         printf("\n     onload=\"init(evt)\" onzoom=\"ZoomControl()\"");
1082     printf(">\n\n");
1083
1084     if (!STATIC_OUTPUT) {
1085         /* css-stylesheet for the tooltip-text */
1086         printf("<style type=\"text/css\">\n<![CDATA[\ntext.tooltip {\n");
1087         printf("    font-family: \"Courier New\", Courier, monospace;\n}\n");
1088         printf("]]>\n</style>\n\n");
1089
1090         /* the ecma-script for the tooltip */
1091         /* and the folding of the module information */
1092         /* and the colorizing of the text */
1093         printf("<script type=\"text/ecmascript\">\n<![CDATA[\n");
1094         /* print the script from the included file */
1095         /* FIXME calculate things dynamically: */
1096         /*       * maximal number of lines for the tooltip. */
1097         printf(code, idCount, idCount, idCount, idCount,
1098                         scale, xMin, scale, yMin, DYN_TEXT, DYN_TEXT,
1099                         miCount, miCount, miCount,
1100                         idCount, idCount, idCount, idCount, idCount, idCount);
1101         printf("// ]]>\n</script>\n\n");
1102     }
1103
1104     printf(" <title>%s</title>\n", note1);
1105
1106     /* definitions for the arrowheads */
1107     printf(" <defs>\n");
1108     printf("   <marker id=\"arrowstart\" markerWidth=\"12\"");
1109     printf(" markerHeight=\"8\" refX=\"0\" refY=\"4\" orient=\"auto\">\n");
1110     printf("     <line x1=\"12\" y1=\"0\" x2=\"0\" y2=\"4\"");
1111     printf(" fill=\"none\" stroke=\"black\"/>\n");
1112     printf("     <line x1=\"0\" y1=\"4\" x2=\"12\" y2=\"8\"");
1113     printf(" fill=\"none\" stroke=\"black\"/>\n");
1114     printf("   </marker>\n");
1115     printf("   <marker id=\"arrowend\" markerWidth=\"12\"");
1116     printf(" markerHeight=\"8\" refX=\"12\" refY=\"4\" orient=\"auto\">\n");
1117     printf("     <line x1=\"0\" y1=\"0\" x2=\"12\" y2=\"4\"");
1118     printf(" fill=\"none\" stroke=\"black\"/>\n");
1119     printf("     <line x1=\"12\" y1=\"4\" x2=\"0\" y2=\"8\"");
1120     printf(" fill=\"none\" stroke=\"black\"/>\n");
1121     printf("   </marker>\n");
1122     printf(" </defs>\n\n");
1123
1124     xfree(note1);
1125 }
1126
1127 /*
1128  * Calculates the size of a given node for the UML representation.
1129  *
1130  * FIXME this algorithm may work good for a monospace-font. we have some
1131  * problems with the proportional-font. :-(
1132  */
1133 static GraphNode *calcNodeSize(GraphNode *node, int *idCount)
1134 {
1135     GraphEdge  *tEdge;
1136     SmiNode    *tNode,*ppNode;
1137     SmiElement *smiElement;
1138     SmiModule  *module;
1139     float      lastHeight;
1140     int        stringlen;
1141
1142     if (node->smiNode->nodekind == SMI_NODEKIND_SCALAR) return node;
1143
1144     node->use = 1;
1145     node->dia.x = (float) rand();
1146     node->dia.y = (float) rand();
1147     node->dia.x /= (float) RAND_MAX;
1148     node->dia.y /= (float) RAND_MAX;
1149     node->dia.w = strlen(node->smiNode->name) * HEADFONTSIZETABLE
1150         + HEADSPACESIZETABLE;
1151     node->dia.h = TABLEHEIGHT + TABLEBOTTOMHEIGHT;
1152
1153     lastHeight = node->dia.h;
1154     /* A */
1155     for (tEdge = graphGetFirstEdgeByNode(graph,node);
1156          tEdge;
1157          tEdge = graphGetNextEdgeByNode(graph, tEdge, node)) {
1158         if (tEdge->startNode == node &&
1159             tEdge->endNode->smiNode->nodekind == SMI_NODEKIND_SCALAR) {
1160
1161             tNode = tEdge->endNode->smiNode;
1162
1163             if ((tNode->status == SMI_STATUS_DEPRECATED
1164                 && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
1165                 || (tNode->status == SMI_STATUS_OBSOLETE
1166                 && !SHOW_DEPR_OBSOLETE))
1167                 continue;
1168
1169             stringlen = strlen(tNode->name) + strlen(algGetTypeName(tNode)) +2;
1170             switch (tNode->status) {
1171             case SMI_STATUS_DEPRECATED:
1172             case SMI_STATUS_OBSOLETE:
1173                 stringlen += strlen(getStatusString(tNode->status)) +3;
1174             case SMI_STATUS_MANDATORY:
1175             case SMI_STATUS_OPTIONAL:
1176             case SMI_STATUS_CURRENT:
1177             case SMI_STATUS_UNKNOWN:
1178                 ;
1179             }
1180             node->dia.w = max(node->dia.w, stringlen
1181                           * ATTRFONTSIZE
1182                           + ATTRSPACESIZE + 5);         
1183             node->dia.h += TABLEELEMHEIGHT;
1184             (*idCount)++;
1185         }
1186     }
1187     if (node->dia.h > lastHeight) {
1188         node->dia.relatedScalars = 1;
1189         node->dia.h += TABLEBOTTOMHEIGHT;
1190     }
1191
1192     lastHeight = node->dia.h;
1193     /* B */
1194     for (tEdge = graphGetFirstEdgeByNode(graph,node);
1195          tEdge;
1196          tEdge = graphGetNextEdgeByNode(graph, tEdge, node)) {
1197         if (tEdge->indexkind == SMI_INDEX_AUGMENT) {
1198             for (smiElement = smiGetFirstElement(
1199                 smiGetFirstChildNode(tEdge->startNode->smiNode));
1200                  smiElement;
1201                  smiElement = smiGetNextElement(smiElement)) {
1202                 if (!cmpSmiNodes(node->smiNode, tEdge->startNode->smiNode)) {
1203
1204                     tNode = smiGetElementNode(smiElement);
1205
1206                     if ((tNode->status == SMI_STATUS_DEPRECATED
1207                         && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
1208                         || (tNode->status == SMI_STATUS_OBSOLETE
1209                         && !SHOW_DEPR_OBSOLETE))
1210                         continue;
1211
1212                     stringlen = strlen(tNode->name)
1213                                             + strlen(algGetTypeName(tNode)) +3;
1214                     switch (tNode->status) {
1215                     case SMI_STATUS_DEPRECATED:
1216                     case SMI_STATUS_OBSOLETE:
1217                         stringlen += strlen(getStatusString(tNode->status)) +3;
1218                     case SMI_STATUS_MANDATORY:
1219                     case SMI_STATUS_OPTIONAL:
1220                     case SMI_STATUS_CURRENT:
1221                     case SMI_STATUS_UNKNOWN:
1222                         ;
1223                     }
1224                     node->dia.w = max(node->dia.w, stringlen
1225                                                     * ATTRFONTSIZE
1226                                                     + ATTRSPACESIZE + 5);
1227                     node->dia.h += TABLEELEMHEIGHT;
1228                 }
1229             }
1230         }
1231     }
1232
1233     /* C */
1234     for (smiElement = smiGetFirstElement(
1235         smiGetFirstChildNode(node->smiNode));
1236          smiElement;
1237          smiElement = smiGetNextElement(smiElement)) {
1238         
1239         tNode = smiGetElementNode(smiElement);
1240
1241         if ((tNode->status == SMI_STATUS_DEPRECATED
1242             && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
1243             || (tNode->status == SMI_STATUS_OBSOLETE
1244             && !SHOW_DEPR_OBSOLETE))
1245             continue;
1246
1247         stringlen = strlen(tNode->name) + strlen(algGetTypeName(tNode)) +3;
1248         switch (tNode->status) {
1249         case SMI_STATUS_DEPRECATED:
1250         case SMI_STATUS_OBSOLETE:
1251             stringlen += strlen(getStatusString(tNode->status)) +3;
1252         case SMI_STATUS_MANDATORY:
1253         case SMI_STATUS_OPTIONAL:
1254         case SMI_STATUS_CURRENT:
1255         case SMI_STATUS_UNKNOWN:
1256             ;
1257         }
1258         node->dia.w = max(node->dia.w, stringlen
1259                       * ATTRFONTSIZE
1260                       + ATTRSPACESIZE + 5);
1261         node->dia.h += TABLEELEMHEIGHT;
1262     }
1263     if (node->dia.h > lastHeight) {
1264         node->dia.indexObjects = 1;
1265         node->dia.h += TABLEBOTTOMHEIGHT;
1266     }
1267
1268     /* D */
1269     if (PRINT_DETAILED_ATTR && node->smiNode->nodekind == SMI_NODEKIND_TABLE) {
1270         module  = smiGetNodeModule(node->smiNode);
1271
1272         for (tNode = smiGetFirstNode(module, SMI_NODEKIND_COLUMN);
1273              tNode;
1274              tNode = smiGetNextNode(tNode, SMI_NODEKIND_COLUMN)) {
1275             ppNode = smiGetParentNode(tNode);
1276             ppNode = smiGetParentNode(ppNode);
1277
1278             if (cmpSmiNodes(node->smiNode, ppNode)) {
1279                 int len;
1280                 char *typeName;
1281
1282                 if ((tNode->status == SMI_STATUS_DEPRECATED
1283                     && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
1284                     || (tNode->status == SMI_STATUS_OBSOLETE
1285                     && !SHOW_DEPR_OBSOLETE))
1286                     continue;
1287
1288                 typeName = algGetTypeName(tNode);
1289                 len = strlen(tNode->name) + (typeName ? strlen(typeName)+2 : 1);
1290                 switch (tNode->status) {
1291                 case SMI_STATUS_DEPRECATED:
1292                 case SMI_STATUS_OBSOLETE:
1293                     len += strlen(getStatusString(tNode->status)) +3;
1294                 case SMI_STATUS_MANDATORY:
1295                 case SMI_STATUS_OPTIONAL:
1296                 case SMI_STATUS_CURRENT:
1297                 case SMI_STATUS_UNKNOWN:
1298                     ;
1299                 }
1300                 node->dia.w = max(node->dia.w, len
1301                     * ATTRFONTSIZE
1302                     + ATTRSPACESIZE + 5);
1303                 node->dia.h += TABLEELEMHEIGHT;
1304                 (*idCount)++;
1305             }
1306         }
1307     }
1308
1309     return node;
1310 }
1311
1312 /*
1313  * Calculates the size of a group-node for the UML representation.
1314  */
1315 static GraphNode *calcGroupSize(int group, int *idCount)
1316 {
1317     GraphNode *calcNode, *node;
1318     SmiNode   *tNode;
1319     int       stringlen;
1320
1321     for (calcNode = graph->nodes; calcNode; calcNode = calcNode->nextPtr) {
1322         if (calcNode->group == group) break;
1323     }
1324
1325     if (!calcNode) return NULL;
1326
1327     calcNode->use = 1;
1328     calcNode->dia.w = strlen(calcNode->smiNode->name) * HEADFONTSIZETABLE
1329         + HEADSPACESIZETABLE;
1330     calcNode->dia.h = TABLEHEIGHT + TABLEBOTTOMHEIGHT;
1331
1332     for (node = graph->nodes; node; node = node->nextPtr) {
1333         if (node->group == group) {
1334             tNode = node->smiNode;
1335
1336             if ((tNode->status == SMI_STATUS_DEPRECATED
1337                 && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
1338                 || (tNode->status == SMI_STATUS_OBSOLETE
1339                 && !SHOW_DEPR_OBSOLETE))
1340                 continue;
1341
1342             stringlen = strlen(tNode->name) + strlen(algGetTypeName(tNode)) +2;
1343             switch (tNode->status) {
1344             case SMI_STATUS_DEPRECATED:
1345             case SMI_STATUS_OBSOLETE:
1346                 stringlen += strlen(getStatusString(tNode->status)) +3;
1347             case SMI_STATUS_MANDATORY:
1348             case SMI_STATUS_OPTIONAL:
1349             case SMI_STATUS_CURRENT:
1350             case SMI_STATUS_UNKNOWN:
1351                 ;
1352             }
1353             calcNode->dia.w = max(calcNode->dia.w, stringlen
1354                             * ATTRFONTSIZE
1355                             + ATTRSPACESIZE + 5);
1356             calcNode->dia.h += TABLEELEMHEIGHT;
1357             (*idCount)++;
1358         }
1359     }
1360
1361     return calcNode;
1362 }
1363
1364
1365 /* ------------------------------------------------------------------------- */
1366
1367
1368
1369 static int invalidType(SmiBasetype basetype)
1370 {
1371     return (basetype == SMI_BASETYPE_FLOAT32)
1372         || (basetype == SMI_BASETYPE_FLOAT64)
1373         || (basetype == SMI_BASETYPE_FLOAT128);
1374 }
1375
1376 static int countTCs(int modc, SmiModule **modv)
1377 {
1378     SmiType *smiType;
1379     int     i, invalid, j = 0;
1380
1381     for (i=0; i<modc; i++) {
1382         for(smiType = smiGetFirstType(modv[i]);
1383             smiType; smiType = smiGetNextType(smiType)) {
1384             if (smiType->status != SMI_STATUS_UNKNOWN) {
1385                 invalid = invalidType(smiType->basetype);
1386                 if (!invalid) {
1387                     j++;
1388                 }
1389             }
1390         }
1391     }
1392
1393     return j;
1394 }
1395
1396
1397 static void calcModuleIdentityCount(int modc, SmiModule **modv,
1398                                     int *miCount, int modId[])
1399 {
1400     int         i;
1401     SmiNode     *smiNode;
1402     SmiRevision *smiRevision;
1403
1404     /* MODULE-IDENTITY */
1405     (*miCount)++;
1406     for (i = 0; i < modc; i++) {
1407         modId[i] = 0;
1408         smiNode = smiGetModuleIdentityNode(modv[i]);
1409         if (smiNode) {
1410             /* name of the module */
1411             (*miCount)++;
1412             modId[i] = 1;
1413             /* revision history of the module */
1414             smiRevision = smiGetFirstRevision(modv[i]);
1415             if (!smiRevision) {
1416                 (*miCount)++;
1417             } else {
1418                 for(; smiRevision;
1419                                 smiRevision = smiGetNextRevision(smiRevision)) {
1420                     (*miCount)++;
1421                 }
1422             }
1423         }
1424     }
1425 }
1426
1427 static void calcNotificationTypeCount(int modc, SmiModule **modv,
1428                                       int *miCount, int nType[])
1429 {
1430     int     i;
1431     SmiNode *smiNode;
1432
1433     /* NOTIFICATION-TYPE */
1434     (*miCount)++;
1435     for (i = 0; i < modc; i++) {
1436         nType[i] = 0;
1437         smiNode = smiGetModuleIdentityNode(modv[i]);
1438         if (smiNode) {
1439             /* name of the module */
1440             (*miCount)++;
1441             /* name of the notification */
1442             for (smiNode = smiGetFirstNode(modv[i], SMI_NODEKIND_NOTIFICATION);
1443                 smiNode;
1444                 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_NOTIFICATION)) {
1445                 if ((smiNode->status == SMI_STATUS_DEPRECATED
1446                     && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
1447                     || (smiNode->status == SMI_STATUS_OBSOLETE
1448                     && !SHOW_DEPR_OBSOLETE))
1449                     continue;
1450                 (*miCount)++;
1451                 nType[i] = 1;
1452             }
1453         }
1454     }
1455 }
1456
1457 static void calcObjectGroupCount(int modc, SmiModule **modv,
1458                                  int *miCount, int oGroup[])
1459 {
1460     int     i;
1461     SmiNode *smiNode;
1462
1463     /* OBJECT-GROUP */
1464     (*miCount)++;
1465     for (i = 0; i < modc; i++) {
1466         oGroup[i] = 0;
1467         smiNode = smiGetModuleIdentityNode(modv[i]);
1468         if (smiNode) {
1469             /* name of the module */
1470             (*miCount)++;
1471             /* name of the group */
1472             for (smiNode = smiGetFirstNode(modv[i], SMI_NODEKIND_GROUP);
1473                 smiNode;
1474                 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_GROUP)) {
1475                 if (!isObjectGroup(smiNode))
1476                     continue;
1477                 if ((smiNode->status == SMI_STATUS_DEPRECATED
1478                     && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
1479                     || (smiNode->status == SMI_STATUS_OBSOLETE
1480                     && !SHOW_DEPR_OBSOLETE))
1481                     continue;
1482                 (*miCount)++;
1483                 oGroup[i] = 1;
1484             }
1485         }
1486     }
1487 }
1488
1489 static void calcNotificationGroupCount(int modc, SmiModule **modv,
1490                                        int *miCount, int nGroup[])
1491 {
1492     int     i;
1493     SmiNode *smiNode;
1494
1495     /* NOTIFICATION-GROUP */
1496     (*miCount)++;
1497     for (i = 0; i < modc; i++) {
1498         nGroup[i] = 0;
1499         smiNode = smiGetModuleIdentityNode(modv[i]);
1500         if (smiNode) {
1501             /* name of the module */
1502             (*miCount)++;
1503             /* name of the group */
1504             for (smiNode = smiGetFirstNode(modv[i], SMI_NODEKIND_GROUP);
1505                 smiNode;
1506                 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_GROUP)) {
1507                 if (!isNotificationGroup(smiNode))
1508                     continue;
1509                 if ((smiNode->status == SMI_STATUS_DEPRECATED
1510                     && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
1511                     || (smiNode->status == SMI_STATUS_OBSOLETE
1512                     && !SHOW_DEPR_OBSOLETE))
1513                     continue;
1514                 (*miCount)++;
1515                 nGroup[i] = 1;
1516             }
1517         }
1518     }
1519 }
1520
1521 static void calcModuleComplianceCount(int modc, SmiModule **modv,
1522                                       int *miCount, int mCompl[])
1523 {
1524     int           i;
1525     char          *done = NULL;
1526     char          s[1024];
1527     char          *module;
1528     SmiNode       *smiNode, *smiNode2;
1529     SmiModule     *smiModule2;
1530     SmiElement    *smiElement;
1531     SmiOption     *smiOption;
1532     SmiRefinement *smiRefinement;
1533
1534     /* MODULE-COMPLIANCE */
1535     (*miCount)++;
1536     for (i = 0; i < modc; i++) {
1537         mCompl[i] = 0;
1538         smiNode = smiGetModuleIdentityNode(modv[i]);
1539         if (smiNode) {
1540             /* name of the module */
1541             (*miCount)++;
1542             /* name of the compliance */
1543             for (smiNode = smiGetFirstNode(modv[i], SMI_NODEKIND_COMPLIANCE);
1544                 smiNode;
1545                 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_COMPLIANCE)) {
1546                 if ((smiNode->status == SMI_STATUS_DEPRECATED
1547                     && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
1548                     || (smiNode->status == SMI_STATUS_OBSOLETE
1549                     && !SHOW_DEPR_OBSOLETE))
1550                     continue;
1551                 (*miCount)++;
1552                 mCompl[i] = 1;
1553                 /* modules for the compliance */
1554                 done = xstrdup("+");
1555                 for (module = modv[i]->name; module; ) {
1556                     /* name of the module */
1557                     (*miCount)++;
1558                     /* mandatory groups */
1559                     (*miCount)++;
1560                     /* groups */
1561                     for (smiOption = smiGetFirstOption(smiNode); smiOption;
1562                                     smiOption = smiGetNextOption(smiOption)) {
1563                         smiNode2 = smiGetOptionNode(smiOption);
1564                         smiModule2 = smiGetNodeModule(smiNode2);
1565                         if (!strcmp(smiModule2->name, module)) {
1566                             (*miCount)++;
1567                         }
1568                     }
1569                     /* objects */
1570                     for (smiRefinement = smiGetFirstRefinement(smiNode);
1571                         smiRefinement;
1572                         smiRefinement = smiGetNextRefinement(smiRefinement)) {
1573                         smiNode2 = smiGetRefinementNode(smiRefinement);
1574                         smiModule2 = smiGetNodeModule(smiNode2);
1575                         if (!strcmp(smiModule2->name, module)) {
1576                             (*miCount)++;
1577                         }
1578                     }
1579                     /* find next module */
1580                     done = xrealloc(done,
1581                                 strlen(done)+strlen(module)+2*sizeof(char));
1582                     strcat(done, module);
1583                     strcat(done, "+");
1584                     module = NULL;
1585                     for (smiElement = smiGetFirstElement(smiNode);
1586                          smiElement;
1587                          smiElement = smiGetNextElement(smiElement)) {
1588                         sprintf(s, "+%s+", smiGetNodeModule(smiGetElementNode(
1589                                                         smiElement))->name);
1590                         if ((!strstr(done, s))) {
1591                             module = smiGetNodeModule(smiGetElementNode(
1592                                                         smiElement))->name;
1593                             break;
1594                         }
1595                     }
1596                 }
1597                 xfree(done);
1598             }
1599         }
1600     }
1601 }
1602
1603 /*
1604  * calculate the number of entries in the module-information-section.
1605  * headings for empty sections are counted here, but they are omitted
1606  * in the svg, so the calculated number is an upper bound. the maximal
1607  * size of this gap is 4*(modc+1). this may be considered as a bug.
1608  */
1609 static void prepareModInfo(int modc, SmiModule **modv, int *miCount,
1610             int modId[], int nType[], int oGroup[], int nGroup[], int mCompl[])
1611 {
1612     calcModuleIdentityCount(modc, modv, miCount, modId);
1613     calcNotificationTypeCount(modc, modv, miCount, nType);
1614     calcObjectGroupCount(modc, modv, miCount, oGroup);
1615     calcNotificationGroupCount(modc, modv, miCount, nGroup);
1616     calcModuleComplianceCount(modc, modv, miCount, mCompl);
1617 }
1618
1619
1620 /* ------------------------------------------------------------------------- */
1621
1622 static void populateMarkupList(SmiNode *smiNode, int *miNr,
1623                                StringListElem markupList[], int miCount)
1624 {
1625     int            i;
1626     SmiElement     *smiElement;
1627     StringListElem *lastElem;
1628     StringListElem *tElem;
1629     StringListElem *newElem;
1630     StringListElem *cloneElem;
1631
1632     markupList[*miNr].miElem = smiNode->name;
1633     markupList[*miNr].status = smiNode->status;
1634     markupList[*miNr].nextPtr = NULL;
1635
1636     for (smiElement = smiGetFirstElement(smiNode); smiElement;
1637         smiElement = smiGetNextElement(smiElement)) {
1638
1639         newElem = xmalloc(sizeof(StringListElem));
1640         memset(newElem, 0, sizeof(StringListElem));
1641         newElem->miElem = smiGetElementNode(smiElement)->name;
1642         newElem->status = smiGetElementNode(smiElement)->status;
1643
1644         if (markupList[*miNr].nextPtr == NULL) {
1645             markupList[*miNr].nextPtr = newElem;
1646         } else {
1647             for (tElem = markupList[*miNr].nextPtr;
1648                 tElem; tElem = tElem->nextPtr) {
1649                 lastElem = tElem;
1650             }
1651             lastElem->nextPtr = newElem;
1652         }
1653         if (isNotificationGroup(smiNode)) {
1654             for (i=0; i<miCount; i++) {
1655                 if (markupList[i].miElem == NULL)
1656                     continue;
1657                 if (markupList[i].miElem != newElem->miElem)
1658                     continue;
1659                 for (tElem = markupList[i].nextPtr;
1660                     tElem; tElem = tElem->nextPtr) {
1661                     cloneElem = xmalloc(sizeof(StringListElem));
1662                     memcpy(cloneElem, tElem, sizeof(StringListElem));
1663                     newElem->nextPtr = cloneElem;
1664                     newElem = newElem->nextPtr;
1665                 }
1666             }
1667         }
1668     }
1669 }
1670
1671 static void printInformationNode(SmiNode *smiNode,
1672                                  float *x, float *y, int *miNr,
1673                                  StringListElem markupList[], int miCount)
1674 {
1675     int            j, k;
1676     char           *tooltip;
1677     SmiElement     *smiElement;
1678     StringListElem *tElem;
1679
1680     printf(" <g id=\"MI%i\" transform=\"translate", *miNr);
1681     printf("(%.2f,%.2f)\">\n", *x, *y);
1682     printf("  <text id=\"%s\"", smiNode->name);
1683     printf(" fill=\"%s\"", printFillColor(smiNode->status));
1684
1685     if (!STATIC_OUTPUT) {
1686         smiElement = smiGetFirstElement(smiNode);
1687         if (smiElement || smiNode->description) {
1688             printf(" onmousemove=\"");
1689         }
1690         if (smiNode->description) {
1691             tooltip = (char *)xmalloc(2*strlen(smiNode->description));
1692             parseTooltip(smiNode->description, tooltip);
1693             printf("ShowTooltipMZ(evt,'%s')", tooltip);
1694             xfree(tooltip);
1695         }
1696         if (smiElement && smiNode->description) {
1697             printf(";");
1698         }
1699         for (j = 0; smiElement;
1700             j++, smiElement = smiGetNextElement(smiElement)) {
1701             if (j) {
1702                 printf(";");
1703             }
1704             printf("colorText('%s','red')",
1705                                         smiGetElementNode(smiElement)->name);
1706             if (isNotificationGroup(smiNode)) {
1707                 /* parse markupList */
1708                 for (k=0; k<miCount; k++) {
1709                     if (markupList[k].miElem == NULL)
1710                         continue;
1711                     if (markupList[k].miElem !=
1712                                         smiGetElementNode(smiElement)->name)
1713                         continue;
1714                     for (tElem = markupList[k].nextPtr;
1715                         tElem; tElem = tElem->nextPtr) {
1716                         printf(";colorText('%s','red')", tElem->miElem);
1717                     }
1718                 }
1719             }
1720         }
1721         if (j || smiNode->description) {
1722             printf("\"");
1723         }
1724
1725         smiElement = smiGetFirstElement(smiNode);
1726         if (smiElement) {
1727             printf(" onclick=\"setStatus(evt,'red','%s')",
1728                                             printFillColor(smiNode->status));
1729         }
1730         for (j = 0; smiElement;
1731             j++, smiElement = smiGetNextElement(smiElement)) {
1732             printf(";changeColor(evt,'%s','red','%s')",
1733                         smiGetElementNode(smiElement)->name,
1734                         printFillColor(smiGetElementNode(smiElement)->status));
1735             if (isNotificationGroup(smiNode)) {
1736                 /* parse markupList */
1737                 for (k=0; k<miCount; k++) {
1738                     if (markupList[k].miElem == NULL)
1739                         continue;
1740                     if (markupList[k].miElem !=
1741                                         smiGetElementNode(smiElement)->name)
1742                         continue;
1743                     for (tElem = markupList[k].nextPtr;
1744                         tElem; tElem = tElem->nextPtr) {
1745                         printf(";changeColor(evt,'%s','red','%s')",
1746                                 tElem->miElem, printFillColor(tElem->status));
1747                     }
1748                 }
1749             }
1750         }
1751         if (j) {
1752             printf("\"");
1753         }
1754
1755         smiElement = smiGetFirstElement(smiNode);
1756         if (smiElement || smiNode->description) {
1757             printf(" onmouseout=\"");
1758         }
1759         if (smiNode->description) {
1760             printf("HideTooltip(evt)");
1761         }
1762         if (smiElement && smiNode->description) {
1763             printf(";");
1764         }
1765         for (j = 0; smiElement;
1766             j++, smiElement = smiGetNextElement(smiElement)) {
1767             if (j) {
1768                 printf(";");
1769             }
1770             printf("colorText('%s',", smiGetElementNode(smiElement)->name);
1771             printf("'%s')",
1772                         printFillColor(smiGetElementNode(smiElement)->status));
1773             if (isNotificationGroup(smiNode)) {
1774                 /* parse markupList */
1775                 for (k=0; k<miCount; k++) {
1776                     if (markupList[k].miElem == NULL)
1777                         continue;
1778                     if (markupList[k].miElem !=
1779                                     smiGetElementNode(smiElement)->name)
1780                         continue;
1781                     for (tElem = markupList[k].nextPtr;
1782                         tElem; tElem = tElem->nextPtr) {
1783                         printf(";colorText('%s',", tElem->miElem);
1784                         printf("'%s')", printFillColor(tElem->status));
1785                     }
1786                 }
1787             }
1788         }
1789         if (j || smiNode->description) {
1790             printf("\"");
1791         }
1792     }
1793
1794     printf(">%s", smiNode->name);
1795     switch (smiNode->status) {
1796     case SMI_STATUS_DEPRECATED:
1797     case SMI_STATUS_OBSOLETE:
1798         printf(" (%s)", getStatusString(smiNode->status));
1799     case SMI_STATUS_MANDATORY:
1800     case SMI_STATUS_OPTIONAL:
1801     case SMI_STATUS_CURRENT:
1802     case SMI_STATUS_UNKNOWN:
1803         ;
1804     }
1805     printf("</text>\n");
1806     printf(" </g>\n");
1807     *y += TABLEELEMHEIGHT;
1808     (*miNr)++;
1809 }
1810
1811 static void printComplianceNode(SmiNode *smiNode, int modc, SmiModule **modv,
1812                                 float *x, float *y, int *miNr, int i,
1813                                 StringListElem markupList[], int miCount)
1814 {
1815     int            j, k, foreign_exists, textColor = 0;
1816     char           *tooltip;
1817     char           *done = NULL;
1818     char           s[1024];
1819     char           *module;
1820     SmiNode        *smiNode2;
1821     SmiModule      *smiModule2;
1822     SmiElement     *smiElement;
1823     /* SmiRevision    *smiRevision; */
1824     SmiOption      *smiOption;
1825     SmiRefinement  *smiRefinement;
1826     StringListElem *tElem;
1827
1828     printf(" <g id=\"MI%i\" transform=\"translate", *miNr);
1829     printf("(%.2f,%.2f)\">\n", *x, *y);
1830     printf("  <text");
1831     switch (smiNode->status) {
1832     case SMI_STATUS_DEPRECATED:
1833         printf(" fill=\"rgb(40%%,40%%,40%%)\"");
1834         textColor = 40;
1835         break;
1836     case SMI_STATUS_OBSOLETE:
1837         printf(" fill=\"rgb(60%%,60%%,60%%)\"");
1838         textColor = 60;
1839         break;
1840     case SMI_STATUS_CURRENT:
1841     case SMI_STATUS_MANDATORY:
1842         printf(" fill=\"rgb(0%%,0%%,0%%)\"");
1843         textColor = 0;
1844         break;
1845     case SMI_STATUS_OPTIONAL:
1846         printf(" fill=\"rgb(20%%,20%%,20%%)\"");
1847         textColor = 20;
1848         break;
1849     case SMI_STATUS_UNKNOWN:
1850         ;
1851     }
1852     printf(">\n");
1853
1854     if (!STATIC_OUTPUT) {
1855         printf("   <tspan style=\"text-anchor:middle\"");
1856         printf(" onclick=\"collapse(evt)\">--</tspan>\n");
1857     }
1858     printf("   <tspan x=\"5\"");
1859
1860     if (!STATIC_OUTPUT && smiNode->description) {
1861         tooltip = (char *)xmalloc(2*strlen(smiNode->description));
1862         parseTooltip(smiNode->description, tooltip);
1863         printf(" onmousemove=\"ShowTooltipMZ(evt,'%s')", tooltip);
1864         xfree(tooltip);
1865         printf("\" onmouseout=\"HideTooltip(evt)\"");
1866     }
1867     printf(">%s", smiNode->name);
1868     switch (smiNode->status) {
1869     case SMI_STATUS_DEPRECATED:
1870     case SMI_STATUS_OBSOLETE:
1871         printf(" (%s)", getStatusString(smiNode->status));
1872     case SMI_STATUS_MANDATORY:
1873     case SMI_STATUS_OPTIONAL:
1874     case SMI_STATUS_CURRENT:
1875     case SMI_STATUS_UNKNOWN:
1876         ;
1877     }
1878     printf("</tspan>\n");
1879     printf("  </text>\n");
1880     printf(" </g>\n");
1881     (*miNr)++;
1882     *y += TABLEELEMHEIGHT;
1883
1884     /* modules for the compliance */
1885     *x += TABLEELEMHEIGHT;
1886     done = xstrdup("+");
1887     for (module = modv[i]->name; module; ) {
1888         foreign_exists = 0;
1889         if (module == modv[i]->name) {
1890             foreign_exists = 1;
1891         } else {
1892             for (j = 0; j < modc; j++) {
1893                 if (module == modv[j]->name) {
1894                     foreign_exists = 1;
1895                     break;
1896                 }
1897             }
1898         }
1899         printf(" <g id=\"MI%i\" transform=\"translate", *miNr);
1900         printf("(%.2f,%.2f)\">\n", *x, *y);
1901         printf("  <text fill=\"rgb(%i%%,%i%%,%i%%)\">\n",
1902                                             textColor, textColor, textColor);
1903         if (!STATIC_OUTPUT) {
1904             printf("   <tspan style=\"text-anchor:middle\"");
1905             printf(" onclick=\"collapse(evt)\">--</tspan>\n");
1906         }
1907         if (!foreign_exists && !STATIC_OUTPUT) {
1908             printf("   <tspan fill=\"%s\" x=\"5\">\n", linkcolor);
1909             printf("    <a xlink:href=\"%s", link);
1910             for (k=0; k<modc; k++) {
1911                 printf("&amp;mibs=%s", modv[k]->name);
1912             }
1913             printf("&amp;mibs=%s\">", module);
1914             printf("%s", module);
1915             printf("</a>\n");
1916             printf("   </tspan>\n");
1917         } else {
1918             printf("    <tspan x=\"5\">%s</tspan>\n", module);
1919         }
1920         printf("  </text>\n");
1921         printf(" </g>\n");
1922         (*miNr)++;
1923         *y += TABLEELEMHEIGHT;
1924
1925         /* mandatory groups */
1926         *x += TABLEELEMHEIGHT;
1927         *x += TABLEBOTTOMHEIGHT;
1928         printf(" <g id=\"MI%i\" transform=\"translate", *miNr);
1929         printf("(%.2f,%.2f)\">\n", *x, *y);
1930         printf("  <text id=\"mandatorygroups%s%s\"", smiNode->name, module);
1931         printf(" fill=\"rgb(%i%%,%i%%,%i%%)\"",
1932                                             textColor, textColor, textColor);
1933         if (!STATIC_OUTPUT && foreign_exists) {
1934             smiElement = smiGetFirstElement(smiNode);
1935             if (smiElement) {
1936                 printf(" onmousemove=\"");
1937             }
1938             for (j = 0; smiElement;
1939                 j++, smiElement = smiGetNextElement(smiElement)) {
1940                 if (!strcmp(smiGetNodeModule(smiGetElementNode(
1941                                                 smiElement))->name, module)) {
1942                     if (j) {
1943                         printf(";");
1944                     }
1945                     printf("colorText('%s','red')",
1946                                         smiGetElementNode(smiElement)->name);
1947                     /* parse markupList */
1948                     for (k=0; k<miCount; k++) {
1949                         if (markupList[k].miElem == NULL)
1950                             continue;
1951                         if (markupList[k].miElem !=
1952                                         smiGetElementNode(smiElement)->name)
1953                             continue;
1954                         for (tElem = markupList[k].nextPtr;
1955                             tElem; tElem = tElem->nextPtr) {
1956                             printf(";colorText('%s','red')", tElem->miElem);
1957                         }
1958                     }
1959                 }
1960             }
1961             if (j) {
1962                 printf("\"");
1963             }
1964
1965             smiElement = smiGetFirstElement(smiNode);
1966             if (smiElement) {
1967                 printf(" onclick=\"setStatus(evt,'red','%s')",
1968                                             printFillColor(smiNode->status));
1969             }
1970             for (j = 0; smiElement;
1971                 j++, smiElement = smiGetNextElement(smiElement)) {
1972                 if (!strcmp(smiGetNodeModule(smiGetElementNode(
1973                                                 smiElement))->name, module)) {
1974                     printf(";changeColor(evt,'%s','red','%s')",
1975                         smiGetElementNode(smiElement)->name,
1976                         printFillColor(smiGetElementNode(smiElement)->status));
1977                     /* parse markupList */
1978                     for (k=0; k<miCount; k++) {
1979                         if (markupList[k].miElem == NULL)
1980                             continue;
1981                         if (markupList[k].miElem !=
1982                                             smiGetElementNode(smiElement)->name)
1983                             continue;
1984                         for (tElem = markupList[k].nextPtr;
1985                             tElem; tElem = tElem->nextPtr) {
1986                             printf(";changeColor(evt,'%s','red','%s')",
1987                                 tElem->miElem, printFillColor(tElem->status));
1988                         }
1989                     }
1990                 }
1991             }
1992             if (j) {
1993                 printf("\"");
1994             }
1995
1996             smiElement = smiGetFirstElement(smiNode);
1997             if (smiElement) {
1998                 printf(" onmouseout=\"");
1999             }
2000             for (j = 0; smiElement;
2001                 j++, smiElement = smiGetNextElement(smiElement)) {
2002                 if (!strcmp(smiGetNodeModule(smiGetElementNode(
2003                                                 smiElement))->name, module)) {
2004                     if (j) {
2005                         printf(";");
2006                     }
2007                     printf("colorText('%s',",
2008                                         smiGetElementNode(smiElement)->name);
2009                     printf("'%s')",
2010                         printFillColor(smiGetElementNode(smiElement)->status));
2011                     /* parse markupList */
2012                     for (k=0; k<miCount; k++) {
2013                         if (markupList[k].miElem == NULL)
2014                             continue;
2015                         if (markupList[k].miElem !=
2016                                         smiGetElementNode(smiElement)->name)
2017                             continue;
2018                         for (tElem = markupList[k].nextPtr;
2019                             tElem; tElem = tElem->nextPtr) {
2020                             printf(";colorText('%s',", tElem->miElem);
2021                             printf("'%s')", printFillColor(tElem->status));
2022                         }
2023                     }
2024                 }
2025             }
2026             if (j) {
2027                 printf("\"");
2028             }
2029         }
2030         printf(">Mandatory Groups</text>\n");
2031         printf(" </g>\n");
2032         *y += TABLEELEMHEIGHT;
2033         (*miNr)++;
2034
2035         /* groups */
2036         for (smiOption = smiGetFirstOption(smiNode); smiOption;
2037                                     smiOption = smiGetNextOption(smiOption)) {
2038             smiNode2 = smiGetOptionNode(smiOption);
2039             smiModule2 = smiGetNodeModule(smiNode2);
2040             if (!strcmp(smiModule2->name, module)) {
2041                 printf(" <g id=\"MI%i\" transform=", *miNr);
2042                 printf("\"translate(%.2f,%.2f)\">\n", *x, *y);
2043                 printf("  <text id=\"group%s%s%s\"",
2044                                         smiNode->name, smiNode2->name, module);
2045                 printf(" fill=\"rgb(%i%%,%i%%,%i%%)\"",
2046                                             textColor, textColor, textColor);
2047                 if (!STATIC_OUTPUT) {
2048                     printf(" onmousemove=\"");
2049                     if (smiOption->description) {
2050                         tooltip = (char *)xmalloc(2*strlen(
2051                                                     smiOption->description));
2052                         parseTooltip(smiOption->description, tooltip);
2053                         printf("ShowTooltipMZ(evt,'%s')", tooltip);
2054                         xfree(tooltip);
2055                     }
2056                     if (smiOption->description && foreign_exists)
2057                         printf(";");
2058                     if (foreign_exists) {
2059                         printf("colorText('%s','salmon')", smiNode2->name);
2060                         /* parse markupList */
2061                         for (j=0; j<miCount; j++) {
2062                             if (markupList[j].miElem == NULL)
2063                                 continue;
2064                             if (markupList[j].miElem != smiNode2->name)
2065                                 continue;
2066                             for (tElem = markupList[j].nextPtr;
2067                                 tElem; tElem = tElem->nextPtr) {
2068                                 printf(";colorText('%s','salmon')",
2069                                                                 tElem->miElem);
2070                             }
2071                         }
2072                     }
2073
2074                     if (foreign_exists) {
2075                         printf("\" onclick=\"setStatus(evt,'salmon','%s')",
2076                                             printFillColor(smiNode2->status));
2077                         printf(";changeColor(evt,'%s','salmon','%s')",
2078                             smiNode2->name, printFillColor(smiNode2->status));
2079                         /* parse markupList */
2080                         for (j=0; j<miCount; j++) {
2081                             if (markupList[j].miElem == NULL)
2082                                 continue;
2083                             if (markupList[j].miElem != smiNode2->name)
2084                                 continue;
2085                             for (tElem = markupList[j].nextPtr;
2086                                 tElem; tElem = tElem->nextPtr) {
2087                                 printf(";changeColor(evt,'%s','salmon','%s')",
2088                                                 tElem->miElem,
2089                                                 printFillColor(tElem->status));
2090                             }
2091                         }
2092                     }
2093
2094                     printf("\" onmouseout=\"");
2095                     if (smiOption->description) {
2096                         printf("HideTooltip(evt)");
2097                     }
2098                     if (smiOption->description && foreign_exists)
2099                         printf(";");
2100                     if (foreign_exists) {
2101                         printf("colorText('%s',", smiNode2->name);
2102                         printf("'%s')", printFillColor(smiNode2->status));
2103                         /* parse markupList */
2104                         for (j=0; j<miCount; j++) {
2105                             if (markupList[j].miElem == NULL)
2106                                 continue;
2107                             if (markupList[j].miElem != smiNode2->name)
2108                                 continue;
2109                             for (tElem = markupList[j].nextPtr;
2110                                 tElem; tElem = tElem->nextPtr) {
2111                                 printf(";colorText('%s',", tElem->miElem);
2112                                 printf("'%s')", printFillColor(tElem->status));
2113                             }
2114                         }
2115                     }
2116                     printf("\"");
2117                 }
2118                 printf(">Group %s</text>\n", smiNode2->name);
2119                 printf(" </g>\n");
2120                 *y += TABLEELEMHEIGHT;
2121                 (*miNr)++;
2122             }
2123         }
2124
2125         /* objects */
2126         for (smiRefinement = smiGetFirstRefinement(smiNode); smiRefinement;
2127                         smiRefinement = smiGetNextRefinement(smiRefinement)) {
2128             smiNode2 = smiGetRefinementNode(smiRefinement);
2129             smiModule2 = smiGetNodeModule(smiNode2);
2130             if (!strcmp(smiModule2->name, module)) {
2131                 printf(" <g id=\"MI%i\" transform=", *miNr);
2132                 printf("\"translate(%.2f,%.2f)\">\n", *x, *y);
2133                 printf("  <text id=\"object%s%s%s\"",
2134                                         smiNode->name, smiNode2->name, module);
2135                 printf(" fill=\"rgb(%i%%,%i%%,%i%%)\"",
2136                                             textColor, textColor, textColor);
2137                 if (!STATIC_OUTPUT) {
2138                     printf(" onmousemove=\"");
2139                     if (smiRefinement->description) {
2140                         tooltip = (char *)xmalloc(2*strlen(
2141                                                 smiRefinement->description));
2142                         parseTooltip(smiRefinement->description, tooltip);
2143                         printf("ShowTooltipMZ(evt,'%s')", tooltip);
2144                         xfree(tooltip);
2145                     }
2146                     if (smiRefinement->description && foreign_exists)
2147                         printf(";");
2148                     if (foreign_exists)
2149                         printf("colorText('%s','salmon')", smiNode2->name);
2150
2151                     if (foreign_exists) {
2152                         printf("\" onclick=\"setStatus(evt,'salmon','%s')",
2153                                             printFillColor(smiNode2->status));
2154                         printf(";changeColor(evt,'%s','salmon','%s')",
2155                             smiNode2->name, printFillColor(smiNode2->status));
2156                     }
2157
2158                     printf("\" onmouseout=\"");
2159                     if (smiRefinement->description) {
2160                         printf("HideTooltip(evt)");
2161                     }
2162                     if (smiRefinement->description && foreign_exists)
2163                         printf(";");
2164                     if (foreign_exists) {
2165                         printf("colorText('%s',", smiNode2->name);
2166                         printf("'%s')", printFillColor(smiNode2->status));
2167                     }
2168                     printf("\"");
2169                 }
2170                 printf(">Object %s</text>\n", smiNode2->name);
2171                 printf(" </g>\n");
2172                 *y += TABLEELEMHEIGHT;
2173                 (*miNr)++;
2174             }
2175         }
2176         *x -= TABLEELEMHEIGHT;
2177         *x -= TABLEBOTTOMHEIGHT;
2178
2179         /* find next module */
2180         done = xrealloc(done, strlen(done)+strlen(module)+2*sizeof(char));
2181         strcat(done, module);
2182         strcat(done, "+");
2183         module = NULL;
2184         for (smiElement = smiGetFirstElement(smiNode); smiElement;
2185                                 smiElement = smiGetNextElement(smiElement)) {
2186             sprintf(s, "+%s+", smiGetNodeModule(smiGetElementNode(
2187                                                         smiElement))->name);
2188             if ((!strstr(done, s))) {
2189                 module = smiGetNodeModule(smiGetElementNode(smiElement))->name;
2190                 break;
2191             }
2192         }
2193     }
2194     xfree(done);
2195     *x -= TABLEELEMHEIGHT;
2196 }
2197
2198 static void printModuleIdentity(int modc, SmiModule **modv,
2199                                 float *x, float *y, int *miNr)
2200 {
2201     int         i, j;
2202     char        *tooltip;
2203     SmiNode     *smiNode;
2204     /* SmiElement  *smiElement; */
2205     SmiRevision *smiRevision;
2206     GraphNode   *tNode;
2207
2208     printf(" <g id=\"MI%i\" transform=\"translate(%.2f,%.2f)\">\n",
2209                                                                 *miNr, *x, *y);
2210     printf("  <text>\n");
2211     if (!STATIC_OUTPUT) {
2212         printf("   <tspan style=\"text-anchor:middle\"");
2213         printf(" onclick=\"collapse(evt)\">--</tspan>\n");
2214     }
2215     printf("   <tspan x=\"5\">Modules</tspan>\n");
2216     printf("  </text>\n");
2217     printf(" </g>\n");
2218     (*miNr)++;
2219     *y += TABLEELEMHEIGHT;
2220     for (i = 0; i < modc; i++) {
2221         smiNode = smiGetModuleIdentityNode(modv[i]);
2222         if (smiNode) {
2223
2224             /* name and description of the module. */
2225             *x += TABLEELEMHEIGHT;
2226             printf(" <g id=\"MI%i\" transform=\"translate(%.2f,%.2f)\">\n",
2227                                                                 *miNr, *x, *y);
2228             printf("  <text>\n");
2229             if (!STATIC_OUTPUT) {
2230                 printf("   <tspan style=\"text-anchor:middle\"");
2231                 printf(" onclick=\"collapse(evt)\">--</tspan>\n");
2232             }
2233             printf("   <tspan x=\"5\"");
2234             if (!STATIC_OUTPUT) {
2235                 if (modv[i]->description || modc > 1) {
2236                     printf(" onmousemove=\"");
2237                 }
2238                 if (modv[i]->description) {
2239                     tooltip = (char *)xmalloc(2*strlen(modv[i]->description));
2240                     parseTooltip(modv[i]->description, tooltip);
2241                     printf("ShowTooltipMZ(evt,'%s')", tooltip);
2242                     xfree(tooltip);
2243                 }
2244                 if (modv[i]->description && modc > 1) {
2245                     printf(";");
2246                 }
2247                 if (modc > 1) {
2248                     j = 0;
2249                     for (tNode = graph->nodes; tNode; tNode = tNode->nextPtr) {
2250                         if (tNode->smiModule == modv[i] && tNode->use) {
2251                             if (j) {
2252                                 printf(";");
2253                             }
2254                             if (tNode->group == 0) {
2255                                 printf("colorText('%s','blanchedalmond')",
2256                                     tNode->smiNode->name);
2257                             } else {
2258                                 printf("colorText('%s','blanchedalmond')",
2259                                     smiGetParentNode(tNode->smiNode)->name);
2260                             }
2261                             j++;
2262                         }
2263                     }
2264                 }
2265                 if (modv[i]->description || modc > 1) {
2266                     printf("\" onmouseout=\"");
2267                 }
2268                 if (modv[i]->description) {
2269                     printf("HideTooltip(evt)");
2270                 }
2271                 if (modv[i]->description && modc > 1) {
2272                     printf(";");
2273                 }
2274                 if (modc > 1) {
2275                     j = 0;
2276                     for (tNode = graph->nodes; tNode; tNode = tNode->nextPtr) {
2277                         if (tNode->smiModule == modv[i] && tNode->use) {
2278                             if (j) {
2279                                 printf(";");
2280                             }
2281                             if (tNode->group == 0) {
2282                                 printf("colorText('%s','white')",
2283                                     tNode->smiNode->name);
2284                             } else {
2285                                 printf("colorText('%s','white')",
2286                                     smiGetParentNode(tNode->smiNode)->name);
2287                             }
2288                             j++;
2289                         }
2290                     }
2291                 }
2292                 if (modv[i]->description || modc > 1) {
2293                     printf("\"");
2294                 }
2295             }
2296             printf(">%s</tspan>\n", modv[i]->name);
2297             printf("  </text>\n");
2298             printf(" </g>\n");
2299             (*miNr)++;
2300             *y += TABLEELEMHEIGHT;
2301             *x -= TABLEELEMHEIGHT;
2302
2303             /* revision history of the module. */
2304             *x += 2*TABLEELEMHEIGHT;
2305             *x += TABLEBOTTOMHEIGHT;
2306             smiRevision = smiGetFirstRevision(modv[i]);
2307             if (!smiRevision) {
2308                 printf(" <g id=\"MI%i\" transform=\"translate(%.2f,%.2f)\">\n",
2309                                                                 *miNr, *x, *y);
2310                 printf("  <text>1970-01-01</text>\n");
2311                 printf(" </g>\n");
2312                 (*miNr)++;
2313                 *y += TABLEELEMHEIGHT;
2314             } else {
2315                 for(; smiRevision;
2316                                 smiRevision = smiGetNextRevision(smiRevision)) {
2317                     printf(" <g id=\"MI%i\" transform=\"translate", *miNr);
2318                     printf("(%.2f,%.2f)\">\n", *x, *y);
2319                     printf("  <text");
2320                     if (!STATIC_OUTPUT && smiRevision->description && strcmp(
2321                 smiRevision->description,
2322                 "[Revision added by libsmi due to a LAST-UPDATED clause.]")) {
2323                         tooltip = (char *)xmalloc(2*
2324                                         strlen(smiRevision->description));
2325                         parseTooltip(smiRevision->description, tooltip);
2326                         printf(" onmousemove=\"ShowTooltipMZ(evt,'%s')\"",
2327                                                                 tooltip);
2328                         printf(" onmouseout=\"HideTooltip(evt)\"");
2329                         xfree(tooltip);
2330                     }
2331                     printf(">%s</text>\n", getTimeString(smiRevision->date));
2332                     printf(" </g>\n");
2333                     *y += TABLEELEMHEIGHT;
2334                     (*miNr)++;
2335                 }
2336             }
2337             *x -= 2*TABLEELEMHEIGHT;
2338             *x -= TABLEBOTTOMHEIGHT;
2339         }
2340     }
2341     *y += TABLEELEMHEIGHT;
2342 }
2343
2344 static void printNotificationType(int modc, SmiModule **modv,
2345                                   float *x, float *y, int *miNr, int nType[],
2346                                   StringListElem markupList[], int miCount)
2347 {
2348     int         i, j;
2349     SmiNode     *smiNode;
2350     int         statusOrder[5] = {
2351                     SMI_STATUS_CURRENT,
2352                     SMI_STATUS_MANDATORY,
2353                     SMI_STATUS_OPTIONAL,
2354                     SMI_STATUS_DEPRECATED,
2355                     SMI_STATUS_OBSOLETE
2356                 };
2357
2358     printf(" <g id=\"MI%i\" transform=\"translate(%.2f,%.2f)\">\n",
2359                                                                 *miNr, *x, *y);
2360     printf("  <text>\n");
2361     if (!STATIC_OUTPUT) {
2362         printf("   <tspan style=\"text-anchor:middle\"");
2363         printf(" onclick=\"collapse(evt)\">--</tspan>\n");
2364     }
2365     printf("   <tspan x=\"5\">Notifications</tspan>\n");
2366     printf("  </text>\n");
2367     printf(" </g>\n");
2368     (*miNr)++;
2369     *y += TABLEELEMHEIGHT;
2370     for (i = 0; i < modc; i++) {
2371         if (!nType[i])
2372             continue;
2373         smiNode = smiGetModuleIdentityNode(modv[i]);
2374         if (smiNode) {
2375
2376             /* name of the module */
2377             *x += TABLEELEMHEIGHT;
2378             printf(" <g id=\"MI%i\" transform=\"translate(%.2f,%.2f)\">\n",
2379                                                                 *miNr, *x, *y);
2380             printf("  <text>\n");
2381             if (!STATIC_OUTPUT) {
2382                 printf("   <tspan style=\"text-anchor:middle\"");
2383                 printf(" onclick=\"collapse(evt)\">--</tspan>\n");
2384             }
2385             printf("   <tspan x=\"5\">%s</tspan>\n", modv[i]->name);
2386             printf("  </text>\n");
2387             printf(" </g>\n");
2388             (*miNr)++;
2389             *y += TABLEELEMHEIGHT;
2390             *x -= TABLEELEMHEIGHT;
2391
2392             /* name, status and description of the notification */
2393             *x += 2*TABLEELEMHEIGHT;
2394             *x += TABLEBOTTOMHEIGHT;
2395             for (j=0; j<5; j++) {
2396                 for (smiNode = smiGetFirstNode(modv[i],
2397                                                     SMI_NODEKIND_NOTIFICATION);
2398                     smiNode;
2399                     smiNode = smiGetNextNode(smiNode,
2400                                                 SMI_NODEKIND_NOTIFICATION)) {
2401                     if (smiNode->status != statusOrder[j])
2402                         continue;
2403                     if ((smiNode->status == SMI_STATUS_DEPRECATED
2404                         && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
2405                         || (smiNode->status == SMI_STATUS_OBSOLETE
2406                         && !SHOW_DEPR_OBSOLETE))
2407                         continue;
2408                     if (!STATIC_OUTPUT)
2409                         populateMarkupList(smiNode, miNr, markupList, miCount);
2410                     printInformationNode(smiNode, x, y, miNr,
2411                                                         markupList, miCount);
2412                 }
2413             }
2414             *x -= 2*TABLEELEMHEIGHT;
2415             *x -= TABLEBOTTOMHEIGHT;
2416         }
2417     }
2418     *y += TABLEELEMHEIGHT;
2419 }
2420
2421 static void printObjectGroup(int modc, SmiModule **modv,
2422                              float *x, float *y, int *miNr, int oGroup[],
2423                              StringListElem markupList[], int miCount)
2424 {
2425     int         i, j;
2426     SmiNode     *smiNode;
2427     int         statusOrder[5] = {
2428                     SMI_STATUS_CURRENT,
2429                     SMI_STATUS_MANDATORY,
2430                     SMI_STATUS_OPTIONAL,
2431                     SMI_STATUS_DEPRECATED,
2432                     SMI_STATUS_OBSOLETE
2433                 };
2434
2435     printf(" <g id=\"MI%i\" transform=\"translate(%.2f,%.2f)\">\n",
2436                                                                 *miNr, *x, *y);
2437     printf("  <text>\n");
2438     if (!STATIC_OUTPUT) {
2439         printf("   <tspan style=\"text-anchor:middle\"");
2440         printf(" onclick=\"collapse(evt)\">--</tspan>\n");
2441     }
2442     printf("   <tspan x=\"5\">Object Groups</tspan>\n");
2443     printf("  </text>\n");
2444     printf(" </g>\n");
2445     (*miNr)++;
2446     *y += TABLEELEMHEIGHT;
2447     for (i = 0; i < modc; i++) {
2448         if (!oGroup[i])
2449             continue;
2450         smiNode = smiGetModuleIdentityNode(modv[i]);
2451         if (smiNode) {
2452
2453             /* name of the module */
2454             *x += TABLEELEMHEIGHT;
2455             printf(" <g id=\"MI%i\" transform=\"translate(%.2f,%.2f)\">\n",
2456                                                                 *miNr, *x, *y);
2457             printf("  <text>\n");
2458             if (!STATIC_OUTPUT) {
2459                 printf("   <tspan style=\"text-anchor:middle\"");
2460                 printf(" onclick=\"collapse(evt)\">--</tspan>\n");
2461             }
2462             printf("   <tspan x=\"5\">%s</tspan>\n", modv[i]->name);
2463             printf("  </text>\n");
2464             printf(" </g>\n");
2465             (*miNr)++;
2466             *y += TABLEELEMHEIGHT;
2467             *x -= TABLEELEMHEIGHT;
2468
2469             /* name, status and description of the group */
2470             *x += 2*TABLEELEMHEIGHT;
2471             *x += TABLEBOTTOMHEIGHT;
2472             for (j=0; j<5; j++) {
2473                 for (smiNode = smiGetFirstNode(modv[i], SMI_NODEKIND_GROUP);
2474                     smiNode;
2475                     smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_GROUP)) {
2476                     if (!isObjectGroup(smiNode))
2477                         continue;
2478                     if (smiNode->status != statusOrder[j])
2479                         continue;
2480                     if ((smiNode->status == SMI_STATUS_DEPRECATED
2481                         && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
2482                         || (smiNode->status == SMI_STATUS_OBSOLETE
2483                         && !SHOW_DEPR_OBSOLETE))
2484                         continue;
2485                     if (!STATIC_OUTPUT)
2486                         populateMarkupList(smiNode, miNr, markupList, miCount);
2487                     printInformationNode(smiNode, x, y, miNr,
2488                                                         markupList, miCount);
2489                 }
2490             }
2491             *x -= 2*TABLEELEMHEIGHT;
2492             *x -= TABLEBOTTOMHEIGHT;
2493         }
2494     }
2495     *y += TABLEELEMHEIGHT;
2496 }
2497
2498 static void printNotificationGroup(int modc, SmiModule **modv,
2499                                    float *x, float *y, int *miNr, int nGroup[],
2500                                    StringListElem markupList[], int miCount)
2501 {
2502     int         i, j;
2503     SmiNode     *smiNode;
2504     int         statusOrder[5] = {
2505                     SMI_STATUS_CURRENT,
2506                     SMI_STATUS_MANDATORY,
2507                     SMI_STATUS_OPTIONAL,
2508                     SMI_STATUS_DEPRECATED,
2509                     SMI_STATUS_OBSOLETE
2510                 };
2511
2512     printf(" <g id=\"MI%i\" transform=\"translate(%.2f,%.2f)\">\n",
2513                                                                 *miNr, *x, *y);
2514     printf("  <text>\n");
2515     if (!STATIC_OUTPUT) {
2516         printf("   <tspan style=\"text-anchor:middle\"");
2517         printf(" onclick=\"collapse(evt)\">--</tspan>\n");
2518     }
2519     printf("   <tspan x=\"5\">Notification Groups</tspan>\n");
2520     printf("  </text>\n");
2521     printf(" </g>\n");
2522     (*miNr)++;
2523     *y += TABLEELEMHEIGHT;
2524     for (i = 0; i < modc; i++) {
2525         if (!nGroup[i])
2526             continue;
2527         smiNode = smiGetModuleIdentityNode(modv[i]);
2528         if (smiNode) {
2529
2530             /* name of the module */
2531             *x += TABLEELEMHEIGHT;
2532             printf(" <g id=\"MI%i\" transform=\"translate(%.2f,%.2f)\">\n",
2533                                                                 *miNr, *x, *y);
2534             printf("  <text>\n");
2535             if (!STATIC_OUTPUT) {
2536                 printf("   <tspan style=\"text-anchor:middle\"");
2537                 printf(" onclick=\"collapse(evt)\">--</tspan>\n");
2538             }
2539             printf("   <tspan x=\"5\">%s</tspan>\n", modv[i]->name);
2540             printf("  </text>\n");
2541             printf(" </g>\n");
2542             (*miNr)++;
2543             *y += TABLEELEMHEIGHT;
2544             *x -= TABLEELEMHEIGHT;
2545
2546             /* name, status and description of the group */
2547             *x += 2*TABLEELEMHEIGHT;
2548             *x += TABLEBOTTOMHEIGHT;
2549             for (j=0; j<5; j++) {
2550                 for (smiNode = smiGetFirstNode(modv[i], SMI_NODEKIND_GROUP);
2551                     smiNode;
2552                     smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_GROUP)) {
2553                     if (!isNotificationGroup(smiNode))
2554                         continue;
2555                     if (smiNode->status != statusOrder[j])
2556                         continue;
2557                     if ((smiNode->status == SMI_STATUS_DEPRECATED
2558                         && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
2559                         || (smiNode->status == SMI_STATUS_OBSOLETE
2560                         && !SHOW_DEPR_OBSOLETE))
2561                         continue;
2562                     if (!STATIC_OUTPUT)
2563                         populateMarkupList(smiNode, miNr, markupList, miCount);
2564                     printInformationNode(smiNode, x, y, miNr,
2565                                                         markupList, miCount);
2566                 }
2567             }
2568             *x -= 2*TABLEELEMHEIGHT;
2569             *x -= TABLEBOTTOMHEIGHT;
2570         }
2571     }
2572     *y += TABLEELEMHEIGHT;
2573 }
2574
2575 static void printModuleCompliance(int modc, SmiModule **modv,
2576                                   float *x, float *y, int *miNr, int mCompl[],
2577                                   StringListElem markupList[], int miCount)
2578 {
2579     int         i, j;
2580     SmiNode     *smiNode;
2581     int         statusOrder[5] = {
2582                     SMI_STATUS_CURRENT,
2583                     SMI_STATUS_MANDATORY,
2584                     SMI_STATUS_OPTIONAL,
2585                     SMI_STATUS_DEPRECATED,
2586                     SMI_STATUS_OBSOLETE
2587                 };
2588
2589     printf(" <g id=\"MI%i\" transform=\"translate(%.2f,%.2f)\">\n",
2590                                                                 *miNr, *x, *y);
2591     printf("  <text>\n");
2592     if (!STATIC_OUTPUT) {
2593         printf("   <tspan style=\"text-anchor:middle\"");
2594         printf(" onclick=\"collapse(evt)\">--</tspan>\n");
2595     }
2596     printf("   <tspan x=\"5\">Compliance Statements</tspan>\n");
2597     printf("  </text>\n");
2598     printf(" </g>\n");
2599     (*miNr)++;
2600     *y += TABLEELEMHEIGHT;
2601     for (i = 0; i < modc; i++) {
2602         if (!mCompl[i])
2603             continue;
2604         smiNode = smiGetModuleIdentityNode(modv[i]);
2605         if (smiNode) {
2606
2607             /* name of the module */
2608             *x += TABLEELEMHEIGHT;
2609             printf(" <g id=\"MI%i\" transform=\"translate(%.2f,%.2f)\">\n",
2610                                                                 *miNr, *x, *y);
2611             printf("  <text>\n");
2612             if (!STATIC_OUTPUT) {
2613                 printf("   <tspan style=\"text-anchor:middle\"");
2614                 printf(" onclick=\"collapse(evt)\">--</tspan>\n");
2615             }
2616             printf("   <tspan x=\"5\">%s</tspan>\n", modv[i]->name);
2617             printf("  </text>\n");
2618             printf(" </g>\n");
2619             (*miNr)++;
2620             *y += TABLEELEMHEIGHT;
2621             *x -= TABLEELEMHEIGHT;
2622
2623             /* name, status and description of the compliance */
2624             *x += 2*TABLEELEMHEIGHT;
2625             for (j=0; j<5; j++) {
2626                 for (smiNode = smiGetFirstNode(modv[i],
2627                                                     SMI_NODEKIND_COMPLIANCE);
2628                     smiNode;
2629                     smiNode = smiGetNextNode(smiNode,
2630                                                     SMI_NODEKIND_COMPLIANCE)) {
2631                     if (smiNode->status != statusOrder[j])
2632                         continue;
2633                     if ((smiNode->status == SMI_STATUS_DEPRECATED
2634                         && !SHOW_DEPRECATED && !SHOW_DEPR_OBSOLETE)
2635                         || (smiNode->status == SMI_STATUS_OBSOLETE
2636                         && !SHOW_DEPR_OBSOLETE))
2637                         continue;
2638                     printComplianceNode(smiNode, modc, modv, x, y, miNr, i,
2639                                                         markupList, miCount);
2640                 }
2641             }
2642             *x -= 2*TABLEELEMHEIGHT;
2643         }
2644     }
2645     *y += TABLEELEMHEIGHT;
2646 }
2647
2648 static void printModuleInformation(int modc, SmiModule **modv,
2649                                    float x, float y, float maxHeight,
2650                                    int modId[], int nType[], int oGroup[],
2651                                    int nGroup[], int mCompl[], int miCount)
2652 {
2653     int i, j, miNr = 0;
2654     float scale = 1, miHeight;
2655     int modIdPrint = 0;
2656     int nTypePrint = 0, oGroupPrint = 0, nGroupPrint = 0, mComplPrint = 0;
2657
2658     StringListElem *markupList = xcalloc(miCount,sizeof(StringListElem));
2659     
2660     /* only print sections containig information */
2661     for (i = 0; i < modc; i++) {
2662         modIdPrint |= modId[i];
2663         nTypePrint |= nType[i];
2664         oGroupPrint |= oGroup[i];
2665         nGroupPrint |= nGroup[i];
2666         mComplPrint |= mCompl[i];
2667     }
2668
2669     /* count blank lines */
2670     i = 0;
2671     j = 0;
2672     if (modIdPrint) {
2673         i++;
2674     } else {
2675         j++;
2676     }
2677     if (nTypePrint) {
2678         i++;
2679     } else {
2680         j++;
2681     }
2682     if (oGroupPrint) {
2683         i++;
2684     } else {
2685         j++;
2686     }
2687     if (nGroupPrint) {
2688         i++;
2689     } else {
2690         j++;
2691     }
2692     if (mComplPrint) {
2693         i++;
2694     } else {
2695         j++;
2696     }
2697     if (i>1)
2698         i--;
2699
2700     /* test if we must shrink moduleInformation to fit it into canvas */
2701     miHeight = ((miCount + i - (2 * j)) * 15 + 10);
2702     if (miHeight > maxHeight)
2703         scale *= maxHeight/miHeight;
2704
2705     printf(" <g transform=\"translate(%.2f,%.2f) scale(%.2f)\">\n",
2706                                                                 x, y, scale);
2707
2708     /* now use x and y as relative coordinates. */
2709     x = 0;
2710     y = 10;
2711
2712     if (modIdPrint)
2713         printModuleIdentity(modc, modv, &x, &y, &miNr);
2714     if (nTypePrint)
2715         printNotificationType(modc, modv, &x, &y, &miNr, nType, markupList,
2716                                                                     miCount);
2717     if (oGroupPrint)
2718         printObjectGroup(modc, modv, &x, &y, &miNr, oGroup, markupList,
2719                                                                     miCount);
2720     if (nGroupPrint)
2721         printNotificationGroup(modc, modv, &x, &y, &miNr, nGroup, markupList,
2722                                                                     miCount);
2723     if (mComplPrint)
2724         printModuleCompliance(modc, modv, &x, &y, &miNr, mCompl, markupList,
2725                                                                     miCount);
2726
2727     printf(" </g>\n");
2728
2729     if (markupList) xfree(markupList);
2730 }
2731
2732
2733 /* ------------------------------------------------------------------------- */
2734
2735
2736 static float fa(float d, float k)
2737 {
2738     return (float) (d*d/k);
2739 }
2740
2741 static float fr(float d, float k)
2742 {
2743     return (float) (k*k/d);
2744 }
2745
2746 static int overlap(GraphNode *vNode, GraphNode *uNode)
2747 {
2748     if (vNode->dia.x+vNode->dia.w/2>=uNode->dia.x-uNode->dia.w/2 &&
2749         vNode->dia.x-vNode->dia.w/2<=uNode->dia.x+uNode->dia.w/2 &&
2750         vNode->dia.y+vNode->dia.h/2>=uNode->dia.y-uNode->dia.h/2 &&
2751         vNode->dia.y-vNode->dia.h/2<=uNode->dia.y+uNode->dia.h/2) {
2752         return 1;
2753     } else {
2754         return 0;
2755     }
2756 }
2757
2758 /*
2759  * test if node and edge intersect
2760  */
2761 static float intersect(GraphNode *node, GraphEdge *edge)
2762 {
2763     float a, b, intersect = 0;
2764
2765     /* handle case in which edge is parallel to y-axis */
2766     if (edge->endNode->dia.x == edge->startNode->dia.x) {
2767         if ((node->dia.x-node->dia.w/2 < edge->endNode->dia.x &&
2768             node->dia.x+node->dia.w/2 < edge->endNode->dia.x) ||
2769             (node->dia.x-node->dia.w/2 > edge->endNode->dia.x &&
2770             node->dia.x+node->dia.w/2 > edge->endNode->dia.x))
2771             return intersect;
2772         intersect = node->dia.x - edge->startNode->dia.x;
2773     } else {
2774         /* calculate a and b for y=ax+b */
2775         a = (edge->endNode->dia.y - edge->startNode->dia.y) /
2776             (edge->endNode->dia.x - edge->startNode->dia.x);
2777         b = edge->startNode->dia.y - (a * edge->startNode->dia.x);
2778         /* test if entire node is above or under edge */
2779         if ((node->dia.y-node->dia.h/2 - (a * node->dia.x-node->dia.w/2) > b &&
2780             node->dia.y+node->dia.h/2 - (a * node->dia.x-node->dia.w/2) > b &&
2781             node->dia.y-node->dia.h/2 - (a * node->dia.x+node->dia.w/2) > b &&
2782             node->dia.y+node->dia.h/2 - (a * node->dia.x+node->dia.w/2) > b) ||
2783             (node->dia.y-node->dia.h/2 - (a * node->dia.x-node->dia.w/2) < b &&
2784             node->dia.y+node->dia.h/2 - (a * node->dia.x-node->dia.w/2) < b &&
2785             node->dia.y-node->dia.h/2 - (a * node->dia.x+node->dia.w/2) < b &&
2786             node->dia.y+node->dia.h/2 - (a * node->dia.x+node->dia.w/2) < b))
2787             return intersect;
2788         intersect = (a * node->dia.x - node->dia.y + b) /
2789                     (float)(sqrt(a*a+1));
2790     }
2791     /* test if node is over upper end of edge or under lower end of edge */
2792     if (node->dia.y+node->dia.h/2 <
2793                 min(edge->startNode->dia.y,edge->endNode->dia.y) ||
2794         node->dia.y-node->dia.h/2 >
2795                 max(edge->startNode->dia.y,edge->endNode->dia.y)) {
2796         intersect = 0;
2797         return intersect;
2798     }
2799     /* node and edge intersect */
2800     return intersect;
2801 }
2802
2803 /*
2804  * Implements the springembedder. Look at LNCS 2025, pp. 71-86.
2805  * and: http://citeseer.ist.psu.edu/fruchterman91graph.html
2806  * Input: Graph with known width and height of nodes.
2807  * Output: Coordinates (x,y) for the nodes.
2808  * Only the nodes and edges with use==1 are considered.
2809  */
2810 static void layoutComponent(GraphComponent *component,
2811                         int nodeoverlap, int edgeoverlap)
2812 {
2813     int i;
2814     float k, xDelta, yDelta, absDelta, absDisp, t, dist;
2815     GraphNode *vNode, *uNode;
2816     GraphEdge *eEdge;
2817
2818     k = 400;
2819     t = 200;
2820
2821     for (i=0; i<ITERATIONS; i++) {
2822         /* calculate repulsive forces */
2823         for (vNode = component->firstComponentNode; vNode;
2824                                         vNode = vNode->nextComponentNode) {
2825             vNode->dia.xDisp = 0;
2826             vNode->dia.yDisp = 0;
2827             for (uNode = component->firstComponentNode; uNode;
2828                                         uNode = uNode->nextComponentNode) {
2829                 if (vNode==uNode)
2830                     continue;
2831                 xDelta = vNode->dia.x - uNode->dia.x;
2832                 yDelta = vNode->dia.y - uNode->dia.y;
2833                 absDelta = (float) (sqrt(xDelta*xDelta + yDelta*yDelta));
2834                 vNode->dia.xDisp += (xDelta/absDelta)*fr(absDelta, k);
2835                 vNode->dia.yDisp += (yDelta/absDelta)*fr(absDelta, k);
2836                 /* add another repulsive force if the nodes overlap */
2837                 if (nodeoverlap && overlap(vNode, uNode)) {
2838                     vNode->dia.xDisp += 4*(xDelta/absDelta)*fr(1/absDelta, k);
2839                     vNode->dia.yDisp += 4*(yDelta/absDelta)*fr(1/absDelta, k);
2840                 }
2841             }
2842         }
2843         for (eEdge = graph->edges; eEdge; eEdge = eEdge->nextPtr) {
2844             if (!eEdge->use || eEdge->startNode->component != component)
2845                 continue;
2846             /* add another repulsive force if edge and any node overlap */
2847             if (edgeoverlap) {
2848                 for (vNode = component->firstComponentNode; vNode;
2849                                         vNode = vNode->nextComponentNode) {
2850                     if (eEdge->startNode == vNode ||
2851                         eEdge->endNode == vNode ||
2852                         overlap(eEdge->startNode, vNode) ||
2853                         overlap(eEdge->endNode, vNode))
2854                         continue;
2855                     if ((dist = intersect(vNode, eEdge))) {
2856                         if (eEdge->startNode->dia.x == eEdge->endNode->dia.x) {
2857                             eEdge->startNode->dia.xDisp -=
2858                                 8*(dist/fabsf(dist))*fr(1/dist, k);
2859                             eEdge->endNode->dia.xDisp -=
2860                                 8*(dist/fabsf(dist))*fr(1/dist, k);
2861                             vNode->dia.xDisp +=
2862                                 8*(dist/fabsf(dist))*fr(1/dist, k);
2863                         } else {
2864                             xDelta = -1*(eEdge->endNode->dia.y
2865                                          -eEdge->startNode->dia.y)
2866                                        /(eEdge->endNode->dia.x
2867                                          -eEdge->startNode->dia.x);
2868                             yDelta = 1;
2869                             absDelta = (float) (sqrt(xDelta*xDelta
2870                                                      + yDelta*yDelta));
2871                             eEdge->startNode->dia.xDisp +=
2872                                 8*(xDelta/absDelta)*fr(1/dist, k);
2873                             eEdge->startNode->dia.yDisp +=
2874                                 8*(yDelta/absDelta)*fr(1/dist, k);
2875                             eEdge->endNode->dia.xDisp +=
2876                                 8*(xDelta/absDelta)*fr(1/dist, k);
2877                             eEdge->endNode->dia.yDisp +=
2878                                 8*(yDelta/absDelta)*fr(1/dist, k);
2879                             vNode->dia.xDisp -=
2880                                 8*(xDelta/absDelta)*fr(1/dist, k);
2881                             vNode->dia.yDisp -=
2882                                 8*(yDelta/absDelta)*fr(1/dist, k);
2883                         }
2884                     }
2885                 }
2886             }
2887             /* calculate attractive forces */
2888             xDelta = eEdge->startNode->dia.x - eEdge->endNode->dia.x;
2889             yDelta = eEdge->startNode->dia.y - eEdge->endNode->dia.y;
2890             absDelta = (float) (sqrt(xDelta*xDelta + yDelta*yDelta));
2891             eEdge->startNode->dia.xDisp -= (xDelta/absDelta)*fa(absDelta, k);
2892             eEdge->startNode->dia.yDisp -= (yDelta/absDelta)*fa(absDelta, k);
2893             eEdge->endNode->dia.xDisp += (xDelta/absDelta)*fa(absDelta, k);
2894             eEdge->endNode->dia.yDisp += (yDelta/absDelta)*fa(absDelta, k);
2895         }
2896         /* limit the maximum displacement to the temperature t */
2897         for (vNode = component->firstComponentNode; vNode;
2898                                         vNode = vNode->nextComponentNode) {
2899             absDisp = (float) (sqrt(vNode->dia.xDisp*vNode->dia.xDisp
2900                                         + vNode->dia.yDisp*vNode->dia.yDisp));
2901             vNode->dia.x += (vNode->dia.xDisp/absDisp)*min(absDisp, t);
2902             vNode->dia.y += (vNode->dia.yDisp/absDisp)*min(absDisp, t);
2903         }
2904         /* reduce the temperature as the layout approaches a better configuration */
2905         t *= 0.9;
2906     }
2907 }
2908
2909
2910 /* ------------------------------------------------------------------------- */
2911
2912
2913 static void addNodeToComponent(GraphNode *tNode, GraphComponent *tComponent)
2914 {
2915     GraphEdge *tEdge;
2916
2917     tNode->component = tComponent;
2918     for (tEdge = graph->edges; tEdge; tEdge = tEdge->nextPtr) {
2919         if (!tEdge->use)
2920             continue;
2921         if (tEdge->startNode == tNode && tEdge->endNode->component == NULL) {
2922             tEdge->endNode->nextComponentNode = tNode->nextComponentNode;
2923             tNode->nextComponentNode = tEdge->endNode;
2924             addNodeToComponent(tEdge->endNode, tComponent);
2925         }
2926         if (tEdge->endNode == tNode && tEdge->startNode->component == NULL) {
2927             tEdge->startNode->nextComponentNode = tNode->nextComponentNode;
2928             tNode->nextComponentNode = tEdge->startNode;
2929             addNodeToComponent(tEdge->startNode, tComponent);
2930         }
2931     }
2932 }
2933
2934
2935 /* split the graph into components */
2936 static void splitGraphIntoComponents()
2937 {
2938     GraphNode      *tNode;
2939     GraphComponent *tComponent;
2940     for (tNode = graph->nodes; tNode; tNode = tNode->nextPtr) {
2941         if (!tNode->use)
2942             continue;
2943         if (tNode->component == NULL) {
2944             tComponent = graphInsertComponent(graph);
2945             tComponent->firstComponentNode = tNode;
2946             addNodeToComponent(tNode, tComponent);
2947         }
2948     }
2949 }
2950
2951
2952 /* layout components (except first) and calculate bounding boxes and offsets */
2953 static void layoutComponents(float *yMin, float *yMax, float *x)
2954 {
2955     GraphNode      *tNode;
2956     GraphComponent *tComponent;
2957
2958     *x=10;
2959     for (tComponent = graph->components->nextPtr; tComponent;
2960                                             tComponent = tComponent->nextPtr) {
2961         layoutComponent(tComponent, 0, 0);
2962         /* FIXME do we need a stage with nodeoverlap and without edgeoverlap? */
2963         layoutComponent(tComponent, 1, 0);
2964         layoutComponent(tComponent, 1, 1);
2965
2966         for (tNode = tComponent->firstComponentNode; tNode;
2967                                         tNode = tNode->nextComponentNode) {
2968             if (tNode->dia.x - tNode->dia.w/2 < tComponent->xMin)
2969                 tComponent->xMin = tNode->dia.x - tNode->dia.w/2;
2970             if (tNode->dia.x + tNode->dia.w/2 > tComponent->xMax)
2971                 tComponent->xMax = tNode->dia.x + tNode->dia.w/2;
2972             if (tNode->dia.y - tNode->dia.h/2 < tComponent->yMin)
2973                 tComponent->yMin = tNode->dia.y - tNode->dia.h/2;
2974             if (tNode->dia.y + tNode->dia.h/2 > tComponent->yMax)
2975                 tComponent->yMax = tNode->dia.y + tNode->dia.h/2;
2976         }
2977
2978         tComponent->xOffset = *x - tComponent->xMin;
2979         *x += 10 + tComponent->xMax - tComponent->xMin;
2980         tComponent->yOffset = -0.5*(tComponent->yMin+tComponent->yMax);
2981         if (tComponent->yMin + tComponent->yOffset < *yMin)
2982             *yMin = tComponent->yMin + tComponent->yOffset;
2983         if (tComponent->yMax + tComponent->yOffset > *yMax)
2984             *yMax = tComponent->yMax + tComponent->yOffset;
2985     }
2986 }
2987
2988
2989 /* Print SVG to stdout */
2990 static void printSVG(int modc, SmiModule **modv, int miCount, int idCount,
2991                      float xMin, float yMin, float xMax, float yMax,
2992                      int nodecount, int TCcount,
2993                      int modId[], int nType[], int oGroup[],
2994                      int nGroup[], int mCompl[])
2995 {
2996     GraphComponent *tComponent;
2997     GraphNode      *tNode;
2998     GraphEdge      *tEdge;
2999     int            classNr=0;
3000
3001     /* output of svg to stdout begins here */
3002     printSVGHeaderAndTitle(modc, modv, miCount, idCount,
3003                                                         xMin, yMin, xMax, yMax);
3004
3005     /* module doesn't contain any objects. */
3006     if (nodecount == 0) {
3007         if (TCcount > 0) {
3008             printOnlyTCs();
3009         } else {
3010             printNoObjects();
3011         }
3012     }
3013
3014     /* loop through components (except first) to print edges and nodes */
3015     for (tComponent = graph->components->nextPtr; tComponent;
3016                                             tComponent = tComponent->nextPtr) {
3017         for (tEdge = graph->edges; tEdge; tEdge = tEdge->nextPtr) {
3018             if (!tEdge->use || tEdge->startNode->component != tComponent)
3019                 continue;
3020             printSVGConnection(tEdge);
3021         }
3022         for (tNode = tComponent->firstComponentNode; tNode;
3023                                         tNode = tNode->nextComponentNode) {
3024             printSVGObject(tNode, &classNr, modc, modv);
3025         }
3026         /* enclose component in its bounding box */
3027         /*
3028         printf(" <rect x=\"%.2f\" y=\"%.2f\" width=\"%.2f\" height=\"%.2f\"\n",
3029                 tComponent->xMin + tComponent->xOffset,
3030                 tComponent->yMin + tComponent->yOffset,
3031                 tComponent->xMax - tComponent->xMin,
3032                 tComponent->yMax - tComponent->yMin);
3033         printf("       fill=\"none\" stroke=\"green\" stroke-width=\"1\"/>\n");
3034         */
3035     }
3036
3037     /* print single nodes */
3038     for (tNode = graph->components->firstComponentNode; tNode;
3039                                         tNode = tNode->nextComponentNode) {
3040         if (tNode->group == 0) {
3041             printSVGObject(tNode, &classNr, modc, modv);
3042         } else {
3043             printSVGGroup(tNode->group, &classNr, modc, modv);
3044         }
3045     }
3046
3047     /* print Module-Information */
3048     printModuleInformation(modc, modv, xMax-MODULE_INFO_WIDTH, yMin+10,
3049                                 yMax-yMin,
3050                                 modId, nType, oGroup, nGroup, mCompl, miCount);
3051
3052     /* output of svg to stdout ends here */
3053     printSVGClose(xMin, yMin, xMax, yMax);
3054 }
3055
3056
3057 /* prepare nodes and edges for drawing */
3058 static void prepareNodesAndEdges(int *idCount, float *xMax, int *nodecount,
3059                                  int *singleNodes, float *maxHeight)
3060 {
3061     GraphNode      *tNode, *lastNode = NULL;
3062     GraphEdge      *tEdge;
3063     GraphComponent *tComponent;
3064     float          x=10;
3065     int            group;
3066
3067     /* find edges which are supposed to be drawn */
3068     for (tEdge = graph->edges; tEdge; tEdge = tEdge->nextPtr) {
3069         if (tEdge->connection != GRAPH_CON_UNKNOWN
3070             && tEdge->startNode->smiNode->nodekind != SMI_NODEKIND_SCALAR
3071             && tEdge->endNode->smiNode->nodekind != SMI_NODEKIND_SCALAR
3072             && tEdge->startNode != tEdge->endNode) {
3073             tEdge->use = 1;
3074             tEdge->startNode->degree++;
3075             tEdge->endNode->degree++;
3076         }
3077     }
3078
3079     tComponent = graphInsertComponent(graph);
3080
3081     /* prepare nodes which are supposed to be drawn */
3082     for (tNode = graph->nodes; tNode; tNode = tNode->nextPtr) {
3083         tNode = calcNodeSize(tNode, idCount);
3084         if (tNode->smiNode->nodekind != SMI_NODEKIND_SCALAR) {
3085             (*nodecount)++;
3086             if (tNode->degree == 0) {
3087                 /* single nodes are members of the first component. */
3088                 if (tComponent->firstComponentNode == NULL) {
3089                     tComponent->firstComponentNode = tNode;
3090                 } else {
3091                     lastNode->nextComponentNode = tNode;
3092                 }
3093                 lastNode = tNode;
3094                 tNode->component = tComponent;
3095                 tNode->dia.x = x + tNode->dia.w/2;
3096                 x += 10 + tNode->dia.w;
3097                 tNode->dia.y = 0;
3098                 if (tNode->dia.h > *maxHeight)
3099                     *maxHeight = tNode->dia.h;
3100             }
3101         }
3102     }
3103     for (group = 1; group <= algGetNumberOfGroups(); group++) {
3104         tNode = calcGroupSize(group, idCount);
3105         (*nodecount)++;
3106         /* groupnodes are members of the first component. */
3107         if (tComponent->firstComponentNode == NULL) {
3108             tComponent->firstComponentNode = tNode;
3109         } else {
3110             lastNode->nextComponentNode = tNode;
3111         }
3112         lastNode = tNode;
3113         tNode->component = tComponent;
3114         tNode->dia.x = x + tNode->dia.w/2;
3115         x += 10 + tNode->dia.w;
3116         tNode->dia.y = 0;
3117         if (tNode->dia.h > *maxHeight)
3118             *maxHeight = tNode->dia.h;
3119     }
3120     *xMax = x;
3121     if (tComponent->firstComponentNode == NULL)
3122         *singleNodes = 0;
3123 }
3124
3125
3126
3127 /*
3128  * generate SVG diagram and print it to stdout:
3129  * - identify and prepare nodes and egdes
3130  * - split graph into its components
3131  * - layout components
3132  * - prepare module information
3133  * - print header
3134  * - print components
3135  * - print module information
3136  * - print footer
3137  */
3138 static void generateSVG(int modc, SmiModule **modv)
3139 {
3140     int            nodecount=0, singleNodes=1, miCount=0;
3141     int            i, idCount=0, TCcount=0, miPrint=0;
3142     float          x=10, xMin=0, yMin=0, xMax=0, yMax=0, maxHeight=0;
3143
3144     int            *modId = xcalloc(modc,sizeof(int));
3145     int            *nType = xcalloc(modc,sizeof(int));
3146     int            *oGroup = xcalloc(modc,sizeof(int));
3147     int            *nGroup = xcalloc(modc,sizeof(int));
3148     int            *mCompl = xcalloc(modc,sizeof(int));
3149
3150     /* prepare nodes and edges for drawing */
3151     prepareNodesAndEdges(&idCount, &xMax, &nodecount, &singleNodes, &maxHeight);
3152
3153     /* split the graph into components */
3154     splitGraphIntoComponents();
3155
3156     /* layout components (except first) and calculate bounding boxes and offsets */
3157     layoutComponents(&yMin, &yMax, &x);
3158
3159     if (graph->components->nextPtr)
3160         yMin -= 10;
3161     yMax += 10;
3162     if (x > xMax)
3163         xMax = x;
3164
3165     /* adjust values for the first component (component of single nodes) */
3166     graph->components->yOffset = yMax + maxHeight/2;
3167     if (singleNodes)
3168         yMax += maxHeight + 10;
3169
3170     /* module doesn't contain any objects. */
3171     if (nodecount == 0) {
3172         TCcount = countTCs(modc, modv);
3173         if (TCcount > 0) {
3174             xMax += 160;
3175         } else {
3176             xMax += 130;
3177         }
3178         yMax += 40;
3179     }
3180
3181     /* count entries in the ModuleInformation-Section */
3182     prepareModInfo(modc, modv, &miCount, modId, nType, oGroup, nGroup, mCompl);
3183     idCount += miCount;
3184
3185     /* enlarge canvas for ModuleInformation if it is supposed to be printed */
3186     for (i = 0; i < modc; i++) {
3187         miPrint |= modId[i];
3188         miPrint |= nType[i];
3189         miPrint |= oGroup[i];
3190         miPrint |= nGroup[i];
3191         miPrint |= mCompl[i];
3192     }
3193     if (miPrint) {
3194         xMax += MODULE_INFO_WIDTH;
3195     }
3196
3197     printSVG(modc, modv, miCount, idCount, xMin, yMin, xMax, yMax, nodecount,
3198              TCcount, modId, nType, oGroup, nGroup, mCompl);
3199     
3200     xfree(mCompl);
3201     xfree(nGroup);
3202     xfree(oGroup);
3203     xfree(nType);
3204     xfree(modId);
3205 }
3206
3207
3208
3209 static void buildLink(int modc, SmiModule **modv)
3210 {
3211     size_t length;
3212     const char *url = URL;
3213     /* note: first string, so no &amp; required */
3214     const char *widthstr = "width=";
3215     const char *heightstr = "&amp;height=";
3216     const char *deprstr = "&amp;deprobs=deprecated";
3217     const char *deprobsstr = "&amp;deprobs=obsolete";
3218     char width[15];
3219     char height[15];
3220
3221     length = strlen(url);
3222     sprintf(width, "%i", CANVASWIDTH);
3223     sprintf(height, "%i", CANVASHEIGHT);
3224     length += strlen(widthstr) + strlen(width);
3225     length += strlen(heightstr) + strlen(height);
3226     if (SHOW_DEPRECATED) {
3227         length += strlen(deprstr);
3228     }
3229     if (SHOW_DEPR_OBSOLETE) {
3230         length += strlen(deprobsstr);
3231     }
3232     link = xmalloc(length + 1);
3233     strcpy(link, url);
3234     strcat(link, widthstr);
3235     strcat(link, width);
3236     strcat(link, heightstr);
3237     strcat(link, height);
3238     if (SHOW_DEPRECATED) {
3239         strcat(link, deprstr);
3240     }
3241     if (SHOW_DEPR_OBSOLETE) {
3242         strcat(link, deprobsstr);
3243     }
3244 }
3245
3246
3247 static void calcConceptualModel()
3248 {
3249     algLinkTables();
3250     algCheckLinksByName();
3251     algConnectLonelyNodes();
3252     algCheckForDependency();
3253     algCheckForPointerRels();
3254 }
3255
3256
3257 /* ------------------------------------------------------------------------- */
3258
3259
3260
3261 static void dumpSvg(int modc, SmiModule **modv, int flags, char *output)
3262 {
3263     int       i;
3264
3265     buildLink(modc, modv);
3266
3267     if (flags & SMIDUMP_FLAG_UNITE) {
3268         if (! graph) {
3269             graph = xmalloc(sizeof(Graph));
3270             graph->nodes = NULL;
3271             graph->edges = NULL;
3272             graph->components = NULL;
3273         }
3274         
3275         for (i = 0; i < modc; i++) {
3276             algCreateNodes(modv[i]);
3277         }
3278         
3279         calcConceptualModel();
3280         
3281         generateSVG(modc, modv);
3282         
3283         graphExit(graph);
3284         graph = NULL;
3285     } else {
3286         for (i = 0; i < modc; i++) {
3287             if (! graph) {
3288                 graph = xmalloc(sizeof(Graph));
3289                 graph->nodes = NULL;
3290                 graph->edges = NULL;
3291                 graph->components = NULL;
3292             }
3293             
3294             algCreateNodes(modv[i]);
3295         
3296             calcConceptualModel();
3297             
3298             generateSVG(1, &(modv[i]));
3299         
3300             graphExit(graph);
3301             graph = NULL;
3302         }
3303     }
3304
3305     if (fflush(stdout) || ferror(stdout)) {
3306         perror("smidump: write error");
3307         exit(1);
3308     }
3309
3310     xfree(link);
3311 }
3312
3313
3314
3315 void initSvg()
3316 {
3317     static SmidumpDriverOption opt[] = {
3318         { "width", OPT_INT, &CANVASWIDTH, 0,
3319           "width of the svg output (default=1100)"},
3320         { "height", OPT_INT, &CANVASHEIGHT, 0,
3321           "height of the svg output (default=700)"},
3322         { "show-deprecated", OPT_FLAG, &SHOW_DEPRECATED, 0,
3323           "show deprecated items"},
3324         { "show-depr-obsolete", OPT_FLAG, &SHOW_DEPR_OBSOLETE, 0,
3325           "show deprecated and obsolete items"},
3326         { "static-output", OPT_FLAG, &STATIC_OUTPUT, 0,
3327           "disable all interactivity (e.g. for printing)"},
3328         { 0, OPT_END, 0, 0 }
3329     };
3330
3331     static SmidumpDriver driver = {
3332         "svg",
3333         dumpSvg,
3334         0,
3335         SMIDUMP_DRIVER_CANT_OUTPUT,
3336         "SVG diagram",
3337         opt,
3338         NULL
3339     };
3340
3341     smidumpRegisterDriver(&driver);
3342 }