libgeda: Remove some exit() calls and assertions.
[geda-gaf/peter-b.git] / libgeda / src / s_hierarchy.c
blob25d277016cebf0f83249e01cdf53167e73662c9b
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)
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.
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.
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
20 #include <config.h>
22 #include <stdio.h>
23 #ifdef HAVE_STDLIB_H
24 #include <stdlib.h>
25 #endif
27 #include "libgeda_priv.h"
29 #ifdef HAVE_LIBDMALLOC
30 #include <dmalloc.h>
31 #endif
33 /*! \brief */
34 static int page_control_counter=0;
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.
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.
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
62 PAGE *
63 s_hierarchy_down_schematic_single(TOPLEVEL *toplevel, const gchar *filename,
64 PAGE *parent, int page_control, int flag)
66 gchar *string;
67 PAGE *found = NULL;
68 PAGE *forbear;
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);
74 string = s_slib_search_single(filename);
75 if (string == NULL) {
76 return NULL;
79 switch (flag) {
80 case HIERARCHY_NORMAL_LOAD:
82 gchar *filename = f_normalize_filename (string, NULL);
83 found = s_page_search (toplevel, filename);
84 g_free (filename);
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 */
93 if (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 */
98 s_page_goto (toplevel, found);
99 if (page_control != 0) {
100 found->page_control = page_control;
102 found->up = parent->pid;
103 g_free (string);
104 return found;
107 found = s_page_new (toplevel, string);
109 f_open (toplevel, found, found->page_filename, NULL);
111 break;
113 case HIERARCHY_FORCE_LOAD:
115 found = s_page_new (toplevel, string);
116 f_open (toplevel, found, found->page_filename, NULL);
118 break;
121 if (page_control == 0) {
122 page_control_counter++;
123 found->page_control = page_control_counter;
124 } else {
125 found->page_control = page_control;
128 found->up = parent->pid;
130 g_free (string);
132 return found;
135 /*! \todo Finish function documentation!!!
136 * \brief
137 * \par Function Description
140 void
141 s_hierarchy_down_symbol (TOPLEVEL *toplevel, const CLibSymbol *symbol,
142 PAGE *parent)
144 PAGE *page;
145 gchar *filename;
147 filename = s_clib_symbol_get_filename (symbol);
149 page = s_page_search (toplevel, filename);
150 if (page) {
151 s_page_goto (toplevel, page);
152 g_free (filename);
153 return;
156 page = s_page_new (toplevel, filename);
157 g_free(filename);
159 s_page_goto (toplevel, page);
161 f_open(toplevel, page, page->page_filename, NULL);
163 page->up = parent->pid;
164 page_control_counter++;
165 page->page_control = page_control_counter;
169 /*! \brief Search for the parent page of a page in hierarchy.
170 * \par Function Description
171 * This function searches the parent page of page \a page in the
172 * hierarchy. It checks all the pages in the list \a page_list.
174 * It returns a pointer on the page if found, NULL otherwise.
176 * \note
177 * The page \a current_page must be in the list \a page_list.
179 * \param [in] page_list The list of pages in which to search.
180 * \param [in] current_page The reference page for the search.
181 * \returns A pointer on the page found or NULL if not found.
183 PAGE *
184 s_hierarchy_find_up_page (GedaPageList *page_list, PAGE *current_page)
186 if (current_page->up < 0) {
187 s_log_message(_("There are no schematics above the current one!\n"));
188 return NULL;
191 return s_page_search_by_page_id (page_list, current_page->up);
194 /*! \brief Find page hierarchy below a page.
195 * \par Function Description
196 * This function traverses the hierarchy tree of pages and returns a
197 * flat list of pages that are below \a p_current. There are two \a
198 * flags that can be used to control the way that the return value is
199 * constructed: <B>HIERARCHY_NODUPS</B> returns a list without
200 * duplicate pages, and <B>HIERARCHY_POSTORDER</B> traverses the
201 * hierarchy tree and returns a postorder list instead of preorder.
203 * \param toplevel The TOPLEVEL structure.
204 * \param p_current The PAGE to traverse hierarchy for.
205 * \param flags Flags controlling form of return value.
206 * \return A GList of PAGE pointers.
208 * \warning
209 * Caller must destroy returned GList with g_list_free().
211 GList *
212 s_hierarchy_traversepages (TOPLEVEL *toplevel, PAGE *p_current, gint flags)
214 OBJECT *o_current;
215 PAGE *child_page;
216 char *filename = NULL;
217 static GList *pages = NULL;
218 const GList *iter;
220 g_return_val_if_fail ((toplevel != NULL), NULL);
221 g_return_val_if_fail ((p_current != NULL), NULL);
223 /* init static variables the first time*/
224 if (!(flags & HIERARCHY_INNERLOOP)) {
225 pages = NULL;
228 /* preorder traversing */
229 if (!(flags & HIERARCHY_POSTORDER)) {
230 /* check whether we already visited this page */
231 if ((flags & HIERARCHY_NODUPS)
232 && (g_list_find (pages, p_current) != NULL)) {
233 return pages; /* drop the page subtree */
235 pages = g_list_append (pages, p_current);
238 /* walk throught the page objects and search for underlaying schematics */
239 for (iter = s_page_objects (p_current);
240 iter != NULL ;
241 iter = g_list_next (iter)) {
242 o_current = (OBJECT *)iter->data;
244 /* only complex things like symbols can contain attributes */
245 if (o_current->type != OBJ_COMPLEX) continue;
247 filename =
248 o_attrib_search_attached_attribs_by_name (o_current, "source", 0);
250 /* if above is NULL, then look inside symbol */
251 if (filename == NULL) {
252 filename =
253 o_attrib_search_inherited_attribs_by_name (o_current, "source", 0);
256 if (filename == NULL) continue;
258 /* we got a schematic source attribute
259 lets load the page and dive into it */
260 child_page =
261 s_hierarchy_down_schematic_single (toplevel, filename, p_current, 0,
262 HIERARCHY_NORMAL_LOAD);
263 if (child_page != NULL) {
264 /* call the recursive function */
265 s_hierarchy_traversepages (toplevel, child_page, flags | HIERARCHY_INNERLOOP);
266 } else {
267 s_log_message (_("ERROR in s_hierarchy_traverse: "
268 "schematic not found: %s\n"),
269 filename);
272 g_free (filename);
273 filename = NULL;
276 /* postorder traversing */
277 if (flags & HIERARCHY_POSTORDER) {
278 /* check whether we already visited this page */
279 if ((flags & HIERARCHY_NODUPS)
280 && (g_list_find (pages, p_current) != NULL)) {
281 return pages; /* don't append it */
283 pages = g_list_append (pages, p_current);
286 return pages;
289 /*! \todo Finish function documentation!!!
290 * \brief
291 * \par Function Description
293 * \note
294 * Test function which only prints the name of a page and it's number.
296 gint
297 s_hierarchy_print_page (PAGE *p_current, void * data)
299 printf("pagefilename: %s pageid: %d\n",
300 p_current->page_filename, p_current->pid);
301 return 0;
304 /*! \brief Search for a page preceding a given page in hierarchy.
305 * \par Function Description
306 * This function searches the previous sibling of page \a page in the
307 * hierarchy. It checks all the pages preceding \a page in the list
308 * \a page_list.
310 * It returns a pointer on the page if found, NULL otherwise.
312 * \note
313 * The page \a current_page must be in the list \a page_list.
315 * \param [in] page_list The list of pages in which to search.
316 * \param [in] current_page The reference page for the search.
317 * \returns A pointer on the page found or NULL if not found.
319 PAGE *
320 s_hierarchy_find_prev_page (GedaPageList *page_list, PAGE *current_page)
322 const GList *iter;
324 iter = g_list_find (geda_list_get_glist (page_list), current_page);
325 for (iter = g_list_previous (iter);
326 iter != NULL;
327 iter = g_list_previous (iter)) {
329 PAGE *page = (PAGE *)iter->data;
330 if (page->page_control == current_page->page_control) {
331 return page;
335 return NULL;
338 /*! \brief Search for a page following a given page in hierarchy.
339 * \par Function Description
340 * This function searches the next sibling of page \a page in the
341 * hierarchy. It checks all the pages following \a page in the list
342 * \a page_list.
344 * It returns a pointer on the page if found, NULL otherwise.
346 * \note
347 * The page \a current_page must be in the list \a page_list.
349 * \param [in] page_list The list of pages in which to search.
350 * \param [in] current_page The reference page for the search.
351 * \returns A pointer on the page found or NULL if not found.
353 PAGE *
354 s_hierarchy_find_next_page (GedaPageList *page_list, PAGE *current_page)
356 const GList *iter;
358 iter = g_list_find (geda_list_get_glist (page_list), current_page);
359 for (iter = g_list_next (iter);
360 iter != NULL;
361 iter = g_list_next (iter)) {
363 PAGE *page = (PAGE *)iter->data;
364 if (page->page_control == current_page->page_control) {
365 return page;
369 return NULL;