1 /**********
2 Copyright 1990 Regents of the University of California.  All rights reserved.
3 **********/
4 
5 /*
6   Manage graph data structure.
7 */
8 
9 #include "ngspice/ngspice.h"
10 #include "ngspice/graph.h"
11 #include "ngspice/ftedebug.h"
12 #include "ngspice/fteext.h"
13 
14 #include "ngspice/ftedbgra.h"
15 #include "graphdb.h"
16 #include "../breakp2.h"
17 #include "../display.h"
18 
19 
20 /* invariant:  currentgraph contains the current graph */
21 GRAPH *currentgraph;
22 
23 /*
24  *  We use a linked list rather than a circular one because we
25  *    expect few links per list and we don't need to add at the
26  *    end of a list (we can add at the beginning).
27  */
28 
29 /* linked list of graphs */
30 typedef struct listgraph {
31     /* we use GRAPH here instead of a pointer to save a tmalloc */
32     GRAPH graph;
33     struct listgraph *next;
34 } LISTGRAPH;
35 
36 #define NUMGBUCKETS 16
37 
38 typedef struct gbucket {
39     LISTGRAPH *list;
40 } GBUCKET;
41 
42 static GBUCKET GBucket[NUMGBUCKETS];
43 
44 /* note: Zero is not a valid id.  This is used in plot() in graf.c. */
45 static int RunningId = 1;
46 
47 /* Initialize graph structure */
setgraph(GRAPH * pgraph,int id)48 static inline void setgraph(GRAPH *pgraph, int id)
49 {
50     pgraph->graphid = id;
51     pgraph->degree = 1;
52     pgraph->linestyle = -1;
53 } /* end of function setgraph */
54 
55 
56 
57 /* Creates a new graph. Returns NULL on error */
NewGraph(void)58 GRAPH *NewGraph(void)
59 {
60     LISTGRAPH *list;
61     const int BucketId = RunningId % NUMGBUCKETS;
62 
63     /* allocate memory for graph via LISTGRAPH */
64     if ((list = TMALLOC(LISTGRAPH, 1)) == NULL) {
65         internalerror("can't allocate a listgraph");
66         return (GRAPH *) NULL;
67     }
68 
69     GRAPH * const pgraph = &list->graph;
70     setgraph(pgraph, RunningId);
71     GBUCKET *p_bucket = GBucket + BucketId;
72 
73     /* Add to the appropriate bucket at the front of the linked list */
74     if (!p_bucket->list) { /* no list yet */
75         p_bucket->list = list;
76     }
77     else {
78         /* insert at front of current list */
79         list->next = p_bucket->list;
80         p_bucket->list = list;
81     }
82 
83     RunningId++;
84 
85     return pgraph;
86 } /* end of function NewGraph */
87 
88 
89 
90 /* Given graph id, return graph */
FindGraph(int id)91 GRAPH *FindGraph(int id)
92 {
93     LISTGRAPH *list;
94 
95     /* Step through list of graphs until found or list ends */
96     for (list = GBucket[id % NUMGBUCKETS].list;
97             list && list->graph.graphid != id;
98             list = list->next) {
99         ;
100     }
101 
102     if (list) { /* found */
103         return &list->graph;
104     }
105     else {
106         return (GRAPH *) NULL;
107     }
108 } /* end of function FindGraph */
109 
110 
111 
CopyGraph(GRAPH * graph)112 GRAPH *CopyGraph(GRAPH *graph)
113 {
114     GRAPH *ret;
115     struct dveclist *link = NULL, *newlink = NULL;
116 
117     if (!graph) {
118         return NULL;
119     }
120 
121     ret = NewGraph();
122 
123     {
124         const int id = ret->graphid; /* save ID of the new graph */
125         memcpy(ret, graph, sizeof(GRAPH)); /* copy graph info (inc. ID) */
126         ret->graphid = id;   /* restore ID */
127     }
128 
129     /* copy keyed */
130     {
131         struct _keyed *k;
132         for (ret->keyed = NULL, k = graph->keyed; k; k = k->next) {
133             SaveText(ret, k->text, k->x, k->y);
134         }
135     }
136 
137     /* copy dvecs or reuse if "borrowed" already */
138     {
139         struct dveclist *new_plotdata = (struct dveclist *) NULL;
140         for (link = graph->plotdata; link; link = link->next) {
141             if (link->f_own_vector) {
142                 struct dvec * const old_vector = link->vector;
143                 struct dvec * const new_vector = vec_copy(old_vector);
144                 /* vec_copy doesn't set v_color or v_linestyle */
145                 new_vector->v_color = old_vector->v_color;
146                 new_vector->v_linestyle = old_vector->v_linestyle;
147                 new_vector->v_flags |= VF_PERMANENT;
148                 newlink = TMALLOC(struct dveclist, 1);
149                 newlink->next = new_plotdata;
150                 newlink->f_own_vector = TRUE;
151                 newlink->vector = new_vector;
152 
153                 /* If the link owns the vector, it also owns its scale
154                  * vector, if present */
155                 struct dvec *old_scale = old_vector->v_scale;
156                 if (old_scale != (struct dvec *) NULL) {
157                     new_plotdata = newlink; /* put in front */
158                     struct dvec * const new_scale = vec_copy(old_scale);
159                     new_scale->v_flags |= VF_PERMANENT;
160                     newlink->vector->v_scale = new_scale;
161                 }
162             }
163             else {
164                 newlink->vector = link->vector;
165                 newlink->f_own_vector = FALSE;
166             }
167            new_plotdata = newlink; /* put in front */
168         }
169 
170         ret->plotdata = new_plotdata; /* give vector list to plot */
171     } /* end of block copying or reusing dvecs */
172 
173     ret->commandline = copy(graph->commandline);
174     ret->plotname = copy(graph->plotname);
175 
176     {
177         const char * const lbl = graph->grid.xlabel;
178         if (lbl) {
179             ret->grid.xlabel = copy(lbl);
180         }
181     }
182 
183     {
184         const char * const lbl = graph->grid.ylabel;
185         if (lbl) {
186             ret->grid.ylabel = copy(lbl);
187         }
188     }
189 
190     /* Copy devdep information and size if present */
191     {
192         const void * const p = graph->devdep;
193         if (p != NULL) {
194             const size_t n = ret->n_byte_devdep = graph->n_byte_devdep;
195             void * const dst = ret->devdep = tmalloc(n);
196             (void) memcpy(dst, graph->devdep, n);
197         }
198     }
199 
200     return ret;
201 } /* end of function CopyGraph */
202 
203 
204 
DestroyGraph(int id)205 int DestroyGraph(int id)
206 {
207     /* Locate hash bucket for this graph */
208     const int index = id % NUMGBUCKETS;
209     LISTGRAPH *list = GBucket[index].list;
210 
211     /* Pointer before current one. Allows fixing list when the current
212      * node is deleted. Init to NULL to indicate that at head of list */
213     LISTGRAPH *lastlist = (LISTGRAPH *) NULL;
214 
215     /* Step through graphs in the bucket until the one with id is found */
216     while (list) {
217         if (list->graph.graphid == id) {  /* found it */
218             struct _keyed *k, *nextk;
219             struct dbcomm *db;
220 
221             /* Fix the iplot/trace dbs list */
222             for (db = dbs; db && db->db_graphid != id; db = db->db_next) {
223                 ;
224             }
225 
226             if (db && (db->db_type == DB_IPLOT ||
227                     db->db_type == DB_IPLOTALL)) {
228                 db->db_type = DB_DEADIPLOT;
229                 /* Delete this later */
230                 return 0;
231             }
232 
233             /* Adjust bucket pointers to remove the current node */
234             if (lastlist) { /* not at front */
235                 lastlist->next = list->next;
236             }
237             else { /* at front */
238                 GBucket[index].list = list->next;
239             }
240 
241             /* Run through and de-allocate dynamically allocated keyed list */
242             k = list->graph.keyed;
243             while (k) {
244                 nextk = k->next;
245                 txfree(k->text);
246                 txfree(k);
247                 k = nextk;
248             }
249 
250             /* Free vectors owned by this graph and free the list */
251             {
252                 struct dveclist *d = list->graph.plotdata;
253                 struct dveclist *nextd;
254                 while (d != (struct dveclist *) NULL) {
255                     nextd = d->next;
256                     if (d->f_own_vector) {
257                         /* list responsible for freeing this vector */
258                         if (d->vector->v_scale) {
259                             dvec_free(d->vector->v_scale);
260                         }
261                         dvec_free(d->vector);
262                     }
263                     txfree(d);
264                     d = nextd;
265                 }
266             }
267 
268             txfree(list->graph.commandline);
269             txfree(list->graph.plotname);
270             txfree(list->graph.grid.xlabel);
271             txfree(list->graph.grid.ylabel);
272 
273             /* If device-dependent space was allocated, free it. */
274             {
275                 void * const p = list->graph.devdep;
276                 if (p) {
277                     txfree(p);
278                 }
279             }
280 
281             txfree(list);
282 
283             return 1;
284         } /* end of case that graph ID was found */
285 
286         lastlist = list; /* update previous node */
287         list = list->next; /* step to next node */
288     } /* end of loop over graphs in the current bucket */
289 
290     /* The graph with ID id was not found */
291     internalerror("tried to destroy non-existent graph");
292     return 0;
293 } /* end of function DestroyGraph */
294 
295 
296 
297 /* Free up all dynamically allocated data structures */
FreeGraphs(void)298 void FreeGraphs(void)
299 {
300     /* Iterate over all hash buckets */
301     GBUCKET *gbucket;
302     for (gbucket = GBucket; gbucket < &GBucket[NUMGBUCKETS]; gbucket++) {
303         LISTGRAPH * list = gbucket->list; /* linked list of graphs here */
304         while (list) { /* Free each until end of list */
305             LISTGRAPH *deadl = list;
306             list = list->next;
307             txfree(deadl);
308         }
309     }
310 } /* end of functdion FreeGraphs */
311 
312 
313 
314 /* This function sets global varial currentgraph based on graphid */
SetGraphContext(int graphid)315 void SetGraphContext(int graphid)
316 {
317     currentgraph = FindGraph(graphid);
318 } /* end of function SetGraphContext */
319 
320 
321 
322 /* Stack of graph objects implemented as a linked list */
323 typedef struct gcstack {
324     GRAPH *pgraph;
325     struct gcstack *next;
326 } GCSTACK;
327 
328 static GCSTACK *gcstacktop; /* top of the stack of graphs */
329 
330 
331 /* note: This Push and Pop has tricky semantics.
332    Push(graph) will push the currentgraph onto the stack
333    and set currentgraph to graph.
334    Pop() simply sets currentgraph to previous value at the top of the stack
335    and pops stack.
336 */
PushGraphContext(GRAPH * graph)337 void PushGraphContext(GRAPH *graph)
338 {
339     GCSTACK *gcstack = TMALLOC(GCSTACK, 1);
340 
341     if (!gcstacktop) {
342         gcstacktop = gcstack;
343     }
344     else {
345         gcstack->next = gcstacktop;
346         gcstacktop = gcstack;
347     }
348     gcstacktop->pgraph = currentgraph;
349     currentgraph = graph;
350 } /* end of function PushGraphContext */
351 
352 
353 
PopGraphContext(void)354 void PopGraphContext(void)
355 {
356     currentgraph = gcstacktop->pgraph; /* pop from stack, making current */
357     GCSTACK *dead = gcstacktop; /* remove from stack */
358     gcstacktop = gcstacktop->next;
359     txfree(dead); /* free allocation */
360 } /* end of function PopGraphContext */
361 
362 
363 
364