Allow visitors to prevent recursion.
[geda-gaf/berndj.git] / libgeda / src / s_basic.c
blob2dd6b2c17054c0272ea10ea05cf9712fc3a5a54a
1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2007 Ales Hvezda
4 * Copyright (C) 1998-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA
20 #include <config.h>
22 #include <stdio.h>
23 #include <ctype.h>
24 #ifdef HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
27 #ifdef HAVE_STRING_H
28 #include <string.h>
29 #endif
31 #include "libgeda_priv.h"
33 #ifdef HAVE_LIBDMALLOC
34 #include <dmalloc.h>
35 #endif
37 /*! this is modified here and in o_list.c */
38 int global_sid=0;
40 struct grip_foreach_context {
41 enum grip_t whichone;
42 gboolean found;
43 int *x;
44 int *y;
47 static OBJECT *s_basic_copy_error(TOPLEVEL *toplevel, OBJECT *prototype);
48 static void s_basic_bounds_recalc_error(OBJECT *o_current);
49 static void s_basic_psprint_error(TOPLEVEL *toplevel, FILE *fp, OBJECT *o,
50 double scale, GArray *unicode_table);
51 static enum visit_result
52 s_basic_visit_none(OBJECT *o_current,
53 enum visit_result (*fn)(OBJECT *, void *), void *context,
54 enum visit_order order, int depth);
55 static void s_basic_grips_foreach_none(OBJECT *o,
56 gboolean (*fn)(OBJECT *o,
57 int grip_x, int grip_y,
58 enum grip_t whichone,
59 void *userdata),
60 void *userdata);
61 static int s_basic_grips_move_none(OBJECT *o, int whichone, int x, int y);
63 /*! \todo Finish function documentation!!!
64 * \brief
65 * \par Function Description
68 void error_if_called(void)
70 fprintf(stderr, "Somebody called error_if_called!\n");
71 g_assert(0);
74 /*! \todo Finish function documentation!!!
75 * \brief
76 * \par Function Description
79 void exit_if_null(void *ptr)
81 if (ptr == NULL) {
82 fprintf(stderr, "gEDA: Got NULL ptr!, please e-mail maintainer\n");
83 g_assert(0);
84 exit(-1);
88 /*! \brief Return toplevel->page_current if toplevel is non-NULL
89 * \par Function Description
90 * \param [in] toplevel The TOPLEVEL object, possibly NULL
91 * \return A pointer to the current page, or NULL if toplevel was NULL.
93 PAGE *safe_page_current(TOPLEVEL *toplevel)
95 PAGE *page = NULL;
97 if (toplevel) {
98 page = toplevel->page_current;
101 return page;
104 /*! \todo Finish function documentation!!!
105 * \brief
106 * \par Function Description
109 /* hack rename this to be s_return_tail */
110 /* update object_tail or any list of that matter */
111 OBJECT *return_tail(OBJECT *head)
113 OBJECT *o_current=NULL;
114 OBJECT *ret_struct=NULL;
116 o_current = head;
117 while ( o_current != NULL ) { /* goto end of list */
118 ret_struct = o_current;
119 o_current = o_current->next;
122 return(ret_struct);
125 /*! \todo Finish function documentation!!!
126 * \brief
127 * \par Function Description
130 /* hack rename this to be s_return_head */
131 /* update object_tail or any list of that matter */
132 OBJECT *return_head(OBJECT *tail)
134 OBJECT *o_current=NULL;
135 OBJECT *ret_struct=NULL;
137 o_current = tail;
138 while ( o_current != NULL ) { /* goto end of list */
139 ret_struct = o_current;
140 o_current = o_current->prev;
143 return(ret_struct);
146 /*! \brief Initialize an already-allocated object.
147 * \par Function Description
148 * Initializes the members of the OBJECT base class.
150 * \param [in] new_node A pointer to an allocated OBJECT
151 * \return A pointer to the initialized object.
153 OBJECT *s_basic_init_object(OBJECT *new_node)
155 /* setup sid */
156 new_node->sid = global_sid++;
158 /* Start without indirection or back-annotation. */
159 new_node->vpad_to_pad = NULL;
160 new_node->pad_to_pin = NULL;
162 /* Figure out who owns this OBJECT later. */
163 new_node->owning_slot = NULL;
164 new_node->uuid = NULL;
166 /* Setup the bounding box */
167 new_node->w_top = 0;
168 new_node->w_left = 0;
169 new_node->w_right = 0;
170 new_node->w_bottom = 0;
171 new_node->w_bounds_valid = FALSE;
173 /* Setup line/circle structs */
174 new_node->line = NULL;
175 new_node->path = NULL;
176 new_node->circle = NULL;
177 new_node->arc = NULL;
178 new_node->box = NULL;
179 new_node->picture = NULL;
180 new_node->text = NULL;
181 new_node->complex = NULL;
182 new_node->type_state = NULL;
184 new_node->conn_list = NULL;
186 new_node->visited = 0;
188 new_node->complex_basename = NULL;
189 new_node->complex_parent = NULL;
191 /* Setup the color */
192 new_node->color = WHITE;
193 new_node->saved_color = -1;
194 new_node->selectable = 1;
195 new_node->selected = FALSE;
196 new_node->dont_redraw = FALSE;
197 new_node->locked_color = -1;
198 new_node->draw_grips = FALSE;
200 new_node->bus_ripper_direction = 0;
202 new_node->copy_func = &s_basic_copy_error;
203 new_node->bounds_recalc_func = &s_basic_bounds_recalc_error;
204 new_node->draw_func = error_if_called;
205 new_node->psprint_func = &s_basic_psprint_error;
206 new_node->embed_func = NULL; /* FIXME: Sticking out like a sore thumb... */
207 new_node->visit_func = &s_basic_visit_none;
208 new_node->grip_foreach_func = &s_basic_grips_foreach_none;
209 new_node->grip_move_func = &s_basic_grips_move_none;
210 new_node->destroy_func = NULL;
212 new_node->line_end = END_NONE;
213 new_node->line_type = TYPE_SOLID;
214 new_node->line_width = 0;
215 new_node->line_space = 0;
216 new_node->line_length = 0;
217 new_node->fill_width = 0;
218 new_node->fill_angle1 = 0;
219 new_node->fill_angle2 = 0;
220 new_node->fill_pitch1 = 0;
221 new_node->fill_pitch2 = 0;
223 new_node->attribs = NULL;
224 new_node->attached_to = NULL;
225 new_node->copied_to = NULL;
226 new_node->show_name_value = SHOW_NAME_VALUE;
227 new_node->visibility = VISIBLE;
229 new_node->pin_type = PIN_TYPE_NET;
230 new_node->whichend = -1;
232 /* Setup link list stuff */
233 new_node->prev = NULL;
234 new_node->next = NULL;
236 return(new_node);
239 OBJECT *s_basic_link_object( OBJECT *new_node, OBJECT *ptr )
241 /* should never happen, but could */
242 if (new_node == NULL) {
243 fprintf(stderr, "Got a null new_node in link_object\n");
244 return(ptr);
247 new_node->prev = ptr; /* setup previous link */
249 if (ptr) {
250 ptr->next = new_node;
253 return new_node;
256 OBJECT *s_basic_unlink_object(OBJECT *o_current)
258 OBJECT *prev = o_current->prev;
260 if (o_current->next)
261 o_current->next->prev = o_current->prev;
262 if (o_current->prev)
263 o_current->prev->next = o_current->next;
264 o_current->next = o_current->prev = NULL;
266 return prev;
269 void s_basic_splice(OBJECT *before, OBJECT *first, OBJECT *last)
271 g_assert(first->prev == NULL);
272 g_assert(last->next == NULL);
273 first->prev = before->prev;
274 last->next = before;
275 before->prev->next = first;
276 before->prev = last;
279 /*! \todo Finish function documentation!!!
280 * \brief
281 * \par Function Description
284 void print_struct_forw(OBJECT const *ptr)
286 OBJECT const *o_current=NULL;
288 o_current = ptr;
290 if (o_current == NULL) {
292 printf("AGGGGGGGGGGG NULLLLL PRINT\n");
294 printf("TRYING to PRINT\n");
295 while (o_current != NULL) {
296 printf("Name: %s\n", o_current->name);
297 printf("Type: %d\n", o_current->type);
298 printf("Sid: %d\n", o_current->sid);
300 if (o_current->type == OBJ_COMPLEX || o_current->type == OBJ_PLACEHOLDER) {
301 print_struct_forw(o_current->complex->prim_objs);
304 o_attrib_print (o_current->attribs);
306 printf("----\n");
307 o_current = o_current->next;
311 /*! \todo Finish function documentation!!!
312 * \brief
313 * \par Function Description
316 void print_struct(OBJECT const *ptr)
318 OBJECT const *o_current=NULL;
320 o_current = ptr;
322 if (o_current != NULL) {
323 printf("Name: %s\n", o_current->name);
324 printf("Type: %d\n", o_current->type);
325 printf("Sid: %d\n", o_current->sid);
326 if (o_current->line != NULL) {
327 int x1, y1, x2, y2;
329 s_basic_get_grip(o_current, GRIP_1, &x1, &y1);
330 s_basic_get_grip(o_current, GRIP_2, &x2, &y2);
331 printf("Line points.x1: %d\n", x1);
332 printf("Line points.y1: %d\n", y1);
333 printf("Line points.x2: %d\n", x2);
334 printf("Line points.y2: %d\n", y2);
337 o_attrib_print (o_current->attribs);
339 printf("----\n");
343 /*! \todo Finish function documentation!!!
344 * \brief
345 * \par Function Description
348 void
349 s_delete_object(TOPLEVEL *toplevel, OBJECT *o_current)
351 GList *iter;
352 PAGE *page;
354 g_return_if_fail(o_current);
356 if (o_current != NULL) {
357 g_signal_emit_by_name(o_current, "remove");
358 s_conn_remove(o_current);
360 if (o_current->attached_to != NULL) {
361 /* do the actual remove */
362 o_attrib_remove(&o_current->attached_to->attribs, o_current);
366 * Check all pages, because reasoning about which pages hold the objects
367 * in object_buffer is hard. Getting it wrong and leaving object_lastfound
368 * as a dangling pointer is much worse than losing a few cycles to
369 * traversing the list of pages.
371 * Besides, hopefully object_lastfound should perhaps be managed with weak
372 * notifications.
374 for (iter = geda_list_get_glist(toplevel->pages);
375 iter != NULL;
376 iter = g_list_next(iter)) {
377 page = (PAGE *) iter->data;
378 if (page->object_lastfound == o_current) {
379 g_warn_if_fail(toplevel->page_current == page);
380 page->object_lastfound = NULL;
384 if (o_current->destroy_func) {
385 (*o_current->destroy_func)(o_current);
387 if (o_current->line) {
388 /* printf("sdeleting line\n");*/
389 g_free(o_current->line);
391 o_current->line = NULL;
393 if (o_current->path) {
394 g_free(o_current->path);
396 o_current->path = NULL;
398 /* printf("sdeleting circle\n");*/
399 g_free(o_current->circle);
400 o_current->circle = NULL;
402 /* printf("sdeleting arc\n");*/
403 g_free(o_current->arc);
404 o_current->arc = NULL;
406 /* printf("sdeleting box\n");*/
407 g_free(o_current->box);
408 o_current->box = NULL;
410 if (o_current->picture) {
411 /* printf("sdeleting picture\n");*/
412 o_current->picture = NULL;
415 o_current->text = NULL;
417 /* printf("sdeleting name\n");*/
418 g_free(o_current->name);
419 o_current->name = NULL;
421 g_free(o_current->uuid);
422 o_current->uuid = NULL;
424 /* printf("sdeleting complex_basename\n");*/
425 g_free(o_current->complex_basename);
426 o_current->complex_basename = NULL;
428 if (o_current->complex) {
429 if (o_current->complex->prim_objs) {
430 /* printf("sdeleting complex->primitive_objects\n");*/
431 s_delete_list_fromstart(toplevel,
432 o_current->complex->prim_objs);
434 o_current->complex->prim_objs = NULL;
436 g_free(o_current->complex);
437 o_current->complex = NULL;
440 if (o_current->attribs) {
441 /* This is the only real use of toplevel that isn't just for page_current. */
442 o_attrib_detach_all(toplevel, o_current);
445 g_object_unref(o_current);
447 o_current=NULL; /* misc clean up */
451 /*! \todo Finish function documentation!!!
452 * \brief
453 * \par Function Description
456 OBJECT *s_delete(TOPLEVEL *toplevel, OBJECT *o_current)
458 OBJECT *o_prev = NULL;
459 if (o_current != NULL) {
460 if (GEDA_DEBUG) {
461 printf("sdel: %s\n", o_current->name);
462 printf("sdel: %d\n", o_current->sid);
465 o_prev = s_basic_unlink_object(o_current);
466 s_delete_object(toplevel, o_current);
469 return o_prev;
472 /*! \todo Finish function documentation!!!
473 * \brief
474 * \par Function Description
477 /* deletes everything include the head */
478 void s_delete_list_fromstart(TOPLEVEL *toplevel, OBJECT *start)
480 OBJECT *o_current;
482 o_current = return_tail(start);
484 /* do the delete backwards */
485 while(o_current != NULL) {
486 o_current = s_delete(toplevel, o_current);
490 /*! \todo Finish function documentation!!!
491 * \brief
492 * \par Function Description
495 /* deletes everything include the GList */
496 void
497 s_delete_object_glist(TOPLEVEL *toplevel, GList *list)
499 OBJECT *o_current=NULL;
500 GList *ptr;
502 ptr = g_list_last(list);
504 /* do the delete backwards */
505 while(ptr != NULL) {
506 o_current = ptr->data;
507 s_delete_object(toplevel, o_current);
508 ptr = g_list_previous (ptr);
510 g_list_free(list);
513 OBJECT *s_basic_copy(TOPLEVEL *toplevel, OBJECT *prototype)
515 OBJECT *retval;
517 retval = (*prototype->copy_func)(toplevel, prototype);
518 if (retval) {
519 /* make sure sid is the same! */
520 retval->sid = prototype->sid;
521 retval->uuid = g_strdup(prototype->uuid);
524 return retval;
527 static OBJECT *s_basic_copy_error(TOPLEVEL *toplevel, OBJECT *prototype)
529 g_return_val_if_reached(NULL);
532 static void s_basic_bounds_recalc_error(OBJECT *o_current G_GNUC_UNUSED)
534 g_return_if_reached();
537 static void s_basic_psprint_error(TOPLEVEL *toplevel, FILE *fp, OBJECT *o,
538 double scale, GArray *unicode_table)
540 s_log_message("Object #<object %s> has no psprint_func\n", o->name);
541 g_return_if_reached();
544 static enum visit_result
545 s_basic_visit_none(OBJECT *o_current G_GNUC_UNUSED,
546 enum visit_result (*fn)(OBJECT *, void *) G_GNUC_UNUSED,
547 void *context G_GNUC_UNUSED,
548 enum visit_order order G_GNUC_UNUSED,
549 int depth G_GNUC_UNUSED)
551 return VISIT_RES_OK;
554 static void s_basic_grips_foreach_none(OBJECT *o G_GNUC_UNUSED,
555 gboolean (*fn)(OBJECT *o,
556 int grip_x, int grip_y,
557 enum grip_t whichone,
558 void *userdata) G_GNUC_UNUSED,
559 void *userdata G_GNUC_UNUSED)
561 /* Some objects have no grips at all; do nothing. */
564 static int s_basic_grips_move_none(OBJECT *o, int whichone,
565 int x G_GNUC_UNUSED, int y G_GNUC_UNUSED)
567 s_log_message("Object #<object %s> has no grip_move_func\n", o->name);
569 return whichone;
572 /*! \brief Traverse a list of grip descriptors.
573 * \par Function Description
574 * Calls a user-supplied closure on each grip in the list, until any
575 * one of them returns TRUE. The grip list sentinel is the entry whose
576 * #whichone field is set to GRIP_NONE.
578 * \param [in] o The OBJECT containing the grips.
579 * \param [in] grips A pointer to a vector of grip descriptors.
580 * \param [in] fn A function to call on each grip.
581 * \param [in] userdata User-supplied context for \a fn.
583 void s_basic_grip_foreach_helper(OBJECT *o,
584 const GRIP *grips,
585 gboolean (*fn)(OBJECT *o,
586 int grip_x, int grip_y,
587 enum grip_t whichone,
588 void *userdata),
589 void *userdata)
591 int i;
593 for (i = 0; grips[i].whichone != GRIP_NONE; i++) {
594 if ((*fn)(o, grips[i].x, grips[i].y, grips[i].whichone, userdata)) {
595 return;
600 static gboolean check_one_grip(OBJECT *o G_GNUC_UNUSED, int grip_x, int grip_y,
601 enum grip_t whichone, void *userdata)
603 struct grip_foreach_context *ctx = (struct grip_foreach_context *) userdata;
605 if (ctx->whichone == whichone) {
606 *ctx->x = grip_x;
607 *ctx->y = grip_y;
608 ctx->found = TRUE;
609 return TRUE;
612 return FALSE;
615 /*! \brief Get a specific grip from an object.
616 * \par Function Description
617 * Gets a grip by a grip constant found earlier.
619 * \param [in] o The object to search.
620 * \param [in] whichone The grip constant.
621 * \param [in] x A pointer to the world X coordinate.
622 * \param [in] y A pointer to the world Y coordinate.
623 * \return TRUE if the grip exists, FALSE otherwise.
625 gboolean s_basic_get_grip(OBJECT const *o, enum grip_t whichone, int *x, int *y)
627 struct grip_foreach_context grip_finder = {
628 .whichone = whichone,
629 .found = FALSE,
630 .x = x,
631 .y = y,
634 if (o->grip_foreach_func) {
635 (*o->grip_foreach_func)((OBJECT *) o, &check_one_grip, &grip_finder);
638 return grip_finder.found;
641 static gboolean grip_list_accumulator(OBJECT *o G_GNUC_UNUSED,
642 int grip_x, int grip_y,
643 enum grip_t whichone,
644 void *userdata)
646 GList **grip_list = userdata;
647 GRIP *g;
649 g = g_new(GRIP, 1);
650 g->x = grip_x;
651 g->y = grip_y;
652 g->whichone = whichone;
654 *grip_list = g_list_prepend(*grip_list, g);
656 return FALSE;
659 /*! \brief Get a list of all of an OBJECT's grips.
660 * \par Function Description
661 * Gets a list of all grips in world coordinates on an object. Even coinciding
662 * and functionally duplicate grips are in the returned list.
664 * \param [in] o The object to query.
665 * \param [in] init An existing list, or NULL
666 * \return A GList holding GRIP objects, which should be g_free'd.
668 * \note Grips get added to the \em front of \a init.
670 GList *s_basic_get_all_grips(OBJECT const *o, GList *init)
672 if (o->grip_foreach_func) {
673 (*o->grip_foreach_func)((OBJECT *) o, &grip_list_accumulator, &init);
676 return init;
679 /*! \brief Move one of an OBJECT's grips.
680 * \par Function Description
681 * Asks the OBJECT to move one of its grips while keeping others fixed.
683 * #whichone may be GRIP_NONE, which means to reset the object to being
684 * freshly placed at (#x,#y).
686 * \param [in] o The object to manipulate.
687 * \param [in] whichone The grip to move with the pointer.
688 * \param [in] x The world X coordinate of the pointer.
689 * \param [in] y The world Y coordinate of the pointer.
690 * \return A grip constant suggesting the next grip to move, or GRIP_NONE.
692 int s_basic_move_grip(OBJECT *o, int whichone, int x, int y)
694 return ((*o->grip_move_func)(o, whichone, x, y));
697 static gboolean search_one_grip(OBJECT *o G_GNUC_UNUSED, int grip_x, int grip_y,
698 enum grip_t whichone, void *userdata)
700 struct grip_foreach_context *ctx = (struct grip_foreach_context *) userdata;
702 if (grip_x == *ctx->x && grip_y == *ctx->y) {
703 ctx->whichone = whichone;
704 return TRUE;
707 return FALSE;
710 /*! \brief Find a grip whose position is (#x,#y).
711 * \par Function Description
712 * Searches an object's grips for one that is exactly at the given point.
714 * \param [in] o The object to search.
715 * \param [in] x The world X-coordinate of the point.
716 * \param [in] y The world Y-coordinate of the point.
717 * \return A grip constant identifying the found grip, or GRIP_NONE.
719 int s_basic_search_grips_exact(OBJECT const *o, int x, int y)
721 struct grip_foreach_context grip_searcher = {
722 .whichone = GRIP_NONE,
723 .x = &x,
724 .y = &y,
727 if (o->grip_foreach_func) {
728 (*o->grip_foreach_func)((OBJECT *) o, &search_one_grip, &grip_searcher);
731 return (grip_searcher.whichone);
734 /** \brief Traverse an object, applying a closure to each sub-object.
735 * \par Function Description
736 * \param [in] object The object to traverse.
737 * \param [in] fn The closure function.
738 * \param [in] context The closure context to pass to fn.
739 * \param [in] order Whether to visit parents before or after children.
740 * \param [in] depth Maximum depth of traversal.
741 * \todo Decide if depth should be a limit or level selector.
742 * \todo Maybe apply the closure only to specific object types?
743 * \note This function modifies \a object only if \a fn does.
745 enum visit_result s_visit(OBJECT *object,
746 enum visit_result (*fn)(OBJECT *, void *),
747 void *context, enum visit_order order, int depth)
749 enum visit_result res;
751 g_return_val_if_fail(object, VISIT_RES_ERROR);
753 switch (order) {
754 case VISIT_ANY:
755 case VISIT_LINEAR:
756 case VISIT_PREFIX:
757 switch (res = (*fn)(object, context)) {
758 case VISIT_RES_OK:
759 /* No special action; continue traversing. */
760 break;
761 case VISIT_RES_NORECURSE:
762 return VISIT_RES_OK;
763 case VISIT_RES_EARLY:
764 case VISIT_RES_ABORT:
765 case VISIT_RES_RESTART:
766 case VISIT_RES_ERROR:
767 return res;
769 break;
770 case VISIT_POSTFIX:
771 /* Visit the current object later. */
772 break;
775 /* Limit the traversal depth. Don't visit attributes */
776 if (depth != 1 && object->attached_to == NULL) {
777 int newdepth;
779 if (depth != 0) {
780 newdepth = depth - 1;
781 } else {
782 newdepth = 0;
785 switch (res = s_visit_object(object, fn, context, order, newdepth)) {
786 case VISIT_RES_OK:
787 /* No special action; continue traversing. */
788 break;
789 case VISIT_RES_NORECURSE:
790 /* Only caller-supplied visitors may return VISIT_RES_NORECURSE. */
791 g_warn_if_reached();
792 break;
793 case VISIT_RES_EARLY:
794 case VISIT_RES_ABORT:
795 case VISIT_RES_RESTART:
796 case VISIT_RES_ERROR:
797 return res;
801 switch (order) {
802 case VISIT_ANY:
803 case VISIT_LINEAR:
804 case VISIT_PREFIX:
805 /* Current object has already been visited. */
806 break;
807 case VISIT_POSTFIX:
808 switch (res = (*fn)(object, context)) {
809 case VISIT_RES_OK:
810 break;
811 case VISIT_RES_NORECURSE:
812 /* Too late, the parent object has already been visited. */
813 g_warn_if_reached();
814 break;
815 case VISIT_RES_EARLY:
816 case VISIT_RES_ABORT:
817 case VISIT_RES_RESTART:
818 case VISIT_RES_ERROR:
819 return res;
821 break;
824 return VISIT_RES_OK;
827 enum visit_result s_visit_object(OBJECT *o_current,
828 enum visit_result (*fn)(OBJECT *, void *),
829 void *context, enum visit_order order,
830 int depth)
832 return ((*o_current->visit_func)(o_current, fn, context, order, depth));
835 enum visit_result s_visit_list(OBJECT *o_current, enum list_kind kind,
836 enum visit_result (*fn)(OBJECT *, void *),
837 void *context, enum visit_order order, int depth)
839 enum visit_result res;
841 g_return_if_fail(o_current);
843 switch (kind) {
844 case LIST_KIND_HEAD:
845 g_warn_if_fail(o_current->type == OBJ_HEAD);
846 break;
847 case LIST_KIND_REST:
848 g_warn_if_fail(o_current->type != OBJ_HEAD);
849 break;
852 if (o_current->type == OBJ_HEAD) {
853 o_current = o_current->next;
856 for (; o_current != NULL; o_current = o_current->next) {
857 switch (res = s_visit(o_current, fn, context, order, depth)) {
858 case VISIT_RES_OK:
859 /* No special action; continue traversing. */
860 break;
861 case VISIT_RES_NORECURSE:
862 /* Only caller-supplied visitors may return VISIT_RES_NORECURSE. */
863 g_warn_if_reached();
864 break;
865 case VISIT_RES_EARLY:
866 case VISIT_RES_ABORT:
867 case VISIT_RES_RESTART:
868 case VISIT_RES_ERROR:
869 return res;
873 return VISIT_RES_OK;
876 enum visit_result s_visit_page(PAGE const *page,
877 enum visit_result (*fn)(OBJECT *, void *),
878 void *context, enum visit_order order, int depth)
880 return (s_visit_list(page->object_head, LIST_KIND_HEAD, fn,
881 context, order, depth));
884 enum visit_result s_visit_toplevel(TOPLEVEL const *toplevel,
885 enum visit_result (*fn)(OBJECT *, void *),
886 void *context, enum visit_order order,
887 int depth)
889 enum visit_result res;
890 const GList *iter;
891 PAGE *page;
893 for (iter = geda_list_get_glist(toplevel->pages);
894 iter != NULL;
895 iter = g_list_next(iter)) {
896 page = (PAGE *) iter->data;
897 switch (res = s_visit_page(page, fn, context, order, depth)) {
898 case VISIT_RES_OK:
899 /* No special action; continue traversing. */
900 break;
901 case VISIT_RES_NORECURSE:
902 /* Only caller-supplied visitors may return VISIT_RES_NORECURSE. */
903 g_warn_if_reached();
904 break;
905 case VISIT_RES_EARLY:
906 case VISIT_RES_ABORT:
907 case VISIT_RES_RESTART:
908 case VISIT_RES_ERROR:
909 return res;
913 return VISIT_RES_OK;
916 /*! \todo Finish function documentation!!!
917 * \brief
918 * \par Function Description
921 /* used by o_text_read */
922 char *remove_nl(char *string)
924 int i;
926 if (!string)
927 return NULL;
929 i = 0;
930 while(string[i] != '\0' && string[i] != '\n' && string[i] != '\r') {
931 i++;
934 string[i] = '\0';
936 return(string);
939 /*! \todo Finish function documentation!!!
940 * \brief
941 * \par Function Description
944 /* used by o_text_read */
945 char *remove_last_nl(char *string)
947 int len;
949 if (!string)
950 return NULL;
952 len = strlen(string);
953 if (string[len-1] == '\n' || string[len-1] == '\r')
954 string[len-1] = '\0';
956 return(string);
959 /*! \brief Expand environment variables in string.
960 * \par Function Description
961 * This function returns the passed string with environment variables
962 * expanded.
964 * The invocations of environment variable MUST be in the form
965 * '${variable_name}', '$variable_name' is not valid here. Environment
966 * variable names consists solely of letters, digits and '_'. It is
967 * possible to escape a '$' character in the string by repeating it
968 * twice.
970 * It outputs error messages to console and leaves the malformed and
971 * bad variable names in the returned string.
973 * \param [in] string The string with variables to expand.
974 * \return A newly-allocated string with variables expanded or NULL
975 * if input string was NULL.
977 gchar*
978 s_expand_env_variables (const gchar *string)
980 GString *gstring;
981 gint i;
983 if (string == NULL) {
984 return NULL;
987 gstring = g_string_sized_new (strlen (string));
988 i = 0;
989 while (TRUE) {
990 gint start;
992 start = i;
993 /* look for end of string or possible variable name start */
994 while (string[i] != '\0' && string[i] != '$') i++;
995 g_string_append_len (gstring, string + start, i - start);
996 if (string[i] == '\0') {
997 /* end of string, return built string */
998 return g_string_free (gstring, FALSE);
1001 i++;
1002 switch (string[i]) {
1003 case ('{'):
1004 /* look for the end of the variable name */
1005 start = i;
1006 while (string[i] != '\0' && string[i] != '}') i++;
1007 if (string[i] == '\0') {
1008 /* problem: no closing '}' to variable */
1009 fprintf (stderr,
1010 "Found malformed environment variable in '%s'\n",
1011 string);
1012 g_string_append (gstring, "$");
1013 g_string_append_len (gstring, string + start, i - start + 1);
1014 } else {
1015 gint j;
1017 /* discard "${" */
1018 start = start + 1;
1019 /* test characters of variable name */
1020 for (j = start;
1021 j < i && (g_ascii_isalnum (string[j]) || string[j] == '_');
1022 j++);
1023 if (i != j) {
1024 /* illegal character detected in variable name */
1025 fprintf (stderr,
1026 "Found bad character [%c] in variable name.\n",
1027 string[j]);
1028 g_string_append (gstring, "${");
1029 g_string_append_len (gstring, string + start, i - start + 1);
1030 } else {
1031 /* extract variable name from string and expand it */
1032 gchar *variable_name = g_strndup (string + start, i - start);
1033 const gchar *env = g_getenv (variable_name);
1034 g_free (variable_name);
1035 g_string_append (gstring, (env == NULL) ? "" : env);
1037 i++;
1039 break;
1041 case ('$'):
1042 /* an escaped '$' */
1043 g_string_append_c (gstring, string[i++]);
1044 break;
1046 default:
1047 /* an isolated '$', put it in output */
1048 g_string_append_c (gstring, '$');
1052 /* never reached */
1053 return NULL;