1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 #include <config.h>
21
22 #include <stdio.h>
23 #ifdef HAVE_STDLIB_H
24 #include <stdlib.h>
25 #endif
26
27 #include "libgeda_priv.h"
28
29 #ifdef HAVE_LIBDMALLOC
30 #include <dmalloc.h>
31 #endif
32
33 /*! \brief */
34 static int page_control_counter=0;
35
36 /*! \todo Finish function documentation!!!
37 * \brief Search for schematic associated source files and load them.
38 * \par Function Description
39 * This function searches the associated source file refered by the
40 * <B>filename</B> and loads it. If the <B>flag</B> is set to
41 * <B>HIERARCHY_NORMAL_LOAD</B> and the page is allready in the list of
42 * pages it will return the <B>pid</B> of that page.
43 * If the <B>flag</B> is set to <B>HIERARCHY_FORCE_LOAD</B> then this
44 * function will load the page again with a new page id. The second case
45 * is mainly used by gnetlist where pushed down schematics MUST be unique.
46 *
47 * \param [in] toplevel The TOPLEVEL object.
48 * \param [in] filename Schematic file name.
49 * \param [in] parent The parent page of the schematic.
50 * \param [in] page_control
51 * \param [in] flag
52 * \return The page loaded, or NULL if failed.
53 *
54 * \note
55 * This function goes and finds the associated source files and
56 * loads all up
57 * It only works for schematic files though
58 * this is basically push
59 * flag can either be HIERARCHY_NORMAL_LOAD or HIERARCHY_FORCE_LOAD
60 * flag is mainly used by gnetlist where pushed down schematics MUST be unique
61 */
62 PAGE *
s_hierarchy_down_schematic_single(TOPLEVEL * toplevel,const gchar * filename,PAGE * parent,int page_control,int flag)63 s_hierarchy_down_schematic_single(TOPLEVEL *toplevel, const gchar *filename,
64 PAGE *parent, int page_control, int flag)
65 {
66 gchar *string;
67 PAGE *found = NULL;
68 PAGE *forbear;
69
70 g_return_val_if_fail ((toplevel != NULL), NULL);
71 g_return_val_if_fail ((filename != NULL), NULL);
72 g_return_val_if_fail ((parent != NULL), NULL);
73
74 string = s_slib_search_single(filename);
75 if (string == NULL) {
76 return NULL;
77 }
78
79 switch (flag) {
80 case HIERARCHY_NORMAL_LOAD:
81 {
82 gchar *filename = f_normalize_filename (string, NULL);
83 found = s_page_search (toplevel, filename);
84 g_free (filename);
85
86 if (found) {
87 /* check whether this page is in the parents list */
88 for (forbear = parent;
89 forbear != NULL && found->pid != forbear->pid && forbear->up >= 0;
90 forbear = s_page_search_by_page_id (toplevel->pages, forbear->up))
91 ; /* void */
92
93 if (forbear != NULL && found->pid == forbear->pid) {
94 s_log_message(_("hierarchy loop detected while visiting page:\n"
95 " \"%s\"\n"), found->page_filename);
96 return NULL; /* error signal */
97 }
98 s_page_goto (toplevel, found);
99 if (page_control != 0) {
100 found->page_control = page_control;
101 }
102 found->up = parent->pid;
103 g_free (string);
104 return found;
105 }
106
107 found = s_page_new (toplevel, string);
108
109 f_open (toplevel, found, found->page_filename, NULL);
110 }
111 break;
112
113 case HIERARCHY_FORCE_LOAD:
114 {
115 found = s_page_new (toplevel, string);
116 f_open (toplevel, found, found->page_filename, NULL);
117 }
118 break;
119
120 default:
121 g_return_val_if_reached (NULL);
122 }
123
124 if (page_control == 0) {
125 page_control_counter++;
126 found->page_control = page_control_counter;
127 } else {
128 found->page_control = page_control;
129 }
130
131 found->up = parent->pid;
132
133 g_free (string);
134
135 return found;
136 }
137
138 /*! \todo Finish function documentation!!!
139 * \brief
140 * \par Function Description
141 *
142 */
143 void
s_hierarchy_down_symbol(TOPLEVEL * toplevel,const CLibSymbol * symbol,PAGE * parent)144 s_hierarchy_down_symbol (TOPLEVEL *toplevel, const CLibSymbol *symbol,
145 PAGE *parent)
146 {
147 PAGE *page;
148 gchar *filename;
149
150 filename = s_clib_symbol_get_filename (symbol);
151
152 page = s_page_search (toplevel, filename);
153 if (page) {
154 /* change link to parent page since we
155 * can come here from any parent and must
156 * come back to the same page */
157 page->up = parent->pid;
158 s_page_goto (toplevel, page);
159 g_free (filename);
160 return;
161 }
162
163 page = s_page_new (toplevel, filename);
164 g_free(filename);
165
166 s_page_goto (toplevel, page);
167
168 f_open(toplevel, page, page->page_filename, NULL);
169
170 page->up = parent->pid;
171 page_control_counter++;
172 page->page_control = page_control_counter;
173
174 }
175
176 /*! \brief Search for the parent page of a page in hierarchy.
177 * \par Function Description
178 * This function searches the parent page of page \a page in the
179 * hierarchy. It checks all the pages in the list \a page_list.
180 *
181 * It returns a pointer on the page if found, NULL otherwise.
182 *
183 * \note
184 * The page \a current_page must be in the list \a page_list.
185 *
186 * \param [in] page_list The list of pages in which to search.
187 * \param [in] current_page The reference page for the search.
188 * \returns A pointer on the page found or NULL if not found.
189 */
190 PAGE *
s_hierarchy_find_up_page(GedaPageList * page_list,PAGE * current_page)191 s_hierarchy_find_up_page (GedaPageList *page_list, PAGE *current_page)
192 {
193 if (current_page->up < 0) {
194 s_log_message(_("There are no schematics above the current one!\n"));
195 return NULL;
196 }
197
198 return s_page_search_by_page_id (page_list, current_page->up);
199 }
200
201 /*! \brief Find page hierarchy below a page.
202 * \par Function Description
203 * This function traverses the hierarchy tree of pages and returns a
204 * flat list of pages that are below \a p_current. There are two \a
205 * flags that can be used to control the way that the return value is
206 * constructed: <B>HIERARCHY_NODUPS</B> returns a list without
207 * duplicate pages, and <B>HIERARCHY_POSTORDER</B> traverses the
208 * hierarchy tree and returns a postorder list instead of preorder.
209 *
210 * \param toplevel The TOPLEVEL structure.
211 * \param p_current The PAGE to traverse hierarchy for.
212 * \param flags Flags controlling form of return value.
213 * \return A GList of PAGE pointers.
214 *
215 * \warning
216 * Caller must destroy returned GList with g_list_free().
217 */
218 GList *
s_hierarchy_traversepages(TOPLEVEL * toplevel,PAGE * p_current,gint flags)219 s_hierarchy_traversepages (TOPLEVEL *toplevel, PAGE *p_current, gint flags)
220 {
221 OBJECT *o_current;
222 PAGE *child_page;
223 char *filename = NULL;
224 static GList *pages = NULL;
225 const GList *iter;
226
227 g_return_val_if_fail ((toplevel != NULL), NULL);
228 g_return_val_if_fail ((p_current != NULL), NULL);
229
230 /* init static variables the first time*/
231 if (!(flags & HIERARCHY_INNERLOOP)) {
232 pages = NULL;
233 }
234
235 /* preorder traversing */
236 if (!(flags & HIERARCHY_POSTORDER)) {
237 /* check whether we already visited this page */
238 if ((flags & HIERARCHY_NODUPS)
239 && (g_list_find (pages, p_current) != NULL)) {
240 return pages; /* drop the page subtree */
241 }
242 pages = g_list_append (pages, p_current);
243 }
244
245 /* walk throught the page objects and search for underlaying schematics */
246 for (iter = s_page_objects (p_current);
247 iter != NULL ;
248 iter = g_list_next (iter)) {
249 o_current = (OBJECT *)iter->data;
250
251 /* only complex things like symbols can contain attributes */
252 if (o_current->type != OBJ_COMPLEX) continue;
253
254 filename =
255 o_attrib_search_attached_attribs_by_name (o_current, "source", 0);
256
257 /* if above is NULL, then look inside symbol */
258 if (filename == NULL) {
259 filename =
260 o_attrib_search_inherited_attribs_by_name (o_current, "source", 0);
261 }
262
263 if (filename == NULL) continue;
264
265 /* we got a schematic source attribute
266 lets load the page and dive into it */
267 child_page =
268 s_hierarchy_down_schematic_single (toplevel, filename, p_current, 0,
269 HIERARCHY_NORMAL_LOAD);
270 if (child_page != NULL) {
271 /* call the recursive function */
272 s_hierarchy_traversepages (toplevel, child_page, flags | HIERARCHY_INNERLOOP);
273 } else {
274 s_log_message (_("ERROR in s_hierarchy_traverse: "
275 "schematic not found: %s\n"),
276 filename);
277 }
278
279 g_free (filename);
280 filename = NULL;
281 }
282
283 /* postorder traversing */
284 if (flags & HIERARCHY_POSTORDER) {
285 /* check whether we already visited this page */
286 if ((flags & HIERARCHY_NODUPS)
287 && (g_list_find (pages, p_current) != NULL)) {
288 return pages; /* don't append it */
289 }
290 pages = g_list_append (pages, p_current);
291 }
292
293 return pages;
294 }
295
296 /*! \todo Finish function documentation!!!
297 * \brief
298 * \par Function Description
299 *
300 * \note
301 * Test function which only prints the name of a page and it's number.
302 */
303 gint
s_hierarchy_print_page(PAGE * p_current,void * data)304 s_hierarchy_print_page (PAGE *p_current, void * data)
305 {
306 printf("pagefilename: %s pageid: %d\n",
307 p_current->page_filename, p_current->pid);
308 return 0;
309 }
310
311 /*! \brief Search for a page preceding a given page in hierarchy.
312 * \par Function Description
313 * This function searches the previous sibling of page \a page in the
314 * hierarchy. It checks all the pages preceding \a page in the list
315 * \a page_list.
316 *
317 * It returns a pointer on the page if found, NULL otherwise.
318 *
319 * \note
320 * The page \a current_page must be in the list \a page_list.
321 *
322 * \param [in] page_list The list of pages in which to search.
323 * \param [in] current_page The reference page for the search.
324 * \returns A pointer on the page found or NULL if not found.
325 */
326 PAGE *
s_hierarchy_find_prev_page(GedaPageList * page_list,PAGE * current_page)327 s_hierarchy_find_prev_page (GedaPageList *page_list, PAGE *current_page)
328 {
329 const GList *iter;
330
331 iter = g_list_find (geda_list_get_glist (page_list), current_page);
332 for (iter = g_list_previous (iter);
333 iter != NULL;
334 iter = g_list_previous (iter)) {
335
336 PAGE *page = (PAGE *)iter->data;
337 if (page->page_control == current_page->page_control) {
338 return page;
339 }
340 }
341
342 return NULL;
343 }
344
345 /*! \brief Search for a page following a given page in hierarchy.
346 * \par Function Description
347 * This function searches the next sibling of page \a page in the
348 * hierarchy. It checks all the pages following \a page in the list
349 * \a page_list.
350 *
351 * It returns a pointer on the page if found, NULL otherwise.
352 *
353 * \note
354 * The page \a current_page must be in the list \a page_list.
355 *
356 * \param [in] page_list The list of pages in which to search.
357 * \param [in] current_page The reference page for the search.
358 * \returns A pointer on the page found or NULL if not found.
359 */
360 PAGE *
s_hierarchy_find_next_page(GedaPageList * page_list,PAGE * current_page)361 s_hierarchy_find_next_page (GedaPageList *page_list, PAGE *current_page)
362 {
363 const GList *iter;
364
365 iter = g_list_find (geda_list_get_glist (page_list), current_page);
366 for (iter = g_list_next (iter);
367 iter != NULL;
368 iter = g_list_next (iter)) {
369
370 PAGE *page = (PAGE *)iter->data;
371 if (page->page_control == current_page->page_control) {
372 return page;
373 }
374 }
375
376 return NULL;
377 }
378