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