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
31 #include "libgeda_priv.h"
33 #ifdef HAVE_LIBDMALLOC
37 /*! this is modified here and in o_list.c */
40 struct grip_foreach_context
{
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
,
61 static int s_basic_grips_move_none(OBJECT
*o
, int whichone
, int x
, int y
);
63 /*! \todo Finish function documentation!!!
65 * \par Function Description
68 void error_if_called(void)
70 fprintf(stderr
, "Somebody called error_if_called!\n");
74 /*! \todo Finish function documentation!!!
76 * \par Function Description
79 void exit_if_null(void *ptr
)
82 fprintf(stderr
, "gEDA: Got NULL ptr!, please e-mail maintainer\n");
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
)
98 page
= toplevel
->page_current
;
104 /*! \todo Finish function documentation!!!
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
;
117 while ( o_current
!= NULL
) { /* goto end of list */
118 ret_struct
= o_current
;
119 o_current
= o_current
->next
;
125 /*! \todo Finish function documentation!!!
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
;
138 while ( o_current
!= NULL
) { /* goto end of list */
139 ret_struct
= o_current
;
140 o_current
= o_current
->prev
;
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
)
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 */
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
;
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");
247 new_node
->prev
= ptr
; /* setup previous link */
250 ptr
->next
= new_node
;
256 OBJECT
*s_basic_unlink_object(OBJECT
*o_current
)
258 OBJECT
*prev
= o_current
->prev
;
261 o_current
->next
->prev
= o_current
->prev
;
263 o_current
->prev
->next
= o_current
->next
;
264 o_current
->next
= o_current
->prev
= NULL
;
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
;
275 before
->prev
->next
= first
;
279 /*! \todo Finish function documentation!!!
281 * \par Function Description
284 void print_struct_forw(OBJECT
const *ptr
)
286 OBJECT
const *o_current
=NULL
;
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
);
307 o_current
= o_current
->next
;
311 /*! \todo Finish function documentation!!!
313 * \par Function Description
316 void print_struct(OBJECT
const *ptr
)
318 OBJECT
const *o_current
=NULL
;
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
) {
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
);
343 /*! \todo Finish function documentation!!!
345 * \par Function Description
349 s_delete_object(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
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
374 for (iter
= geda_list_get_glist(toplevel
->pages
);
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!!!
453 * \par Function Description
456 OBJECT
*s_delete(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
458 OBJECT
*o_prev
= NULL
;
459 if (o_current
!= NULL
) {
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
);
472 /*! \todo Finish function documentation!!!
474 * \par Function Description
477 /* deletes everything include the head */
478 void s_delete_list_fromstart(TOPLEVEL
*toplevel
, OBJECT
*start
)
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!!!
492 * \par Function Description
495 /* deletes everything include the GList */
497 s_delete_object_glist(TOPLEVEL
*toplevel
, GList
*list
)
499 OBJECT
*o_current
=NULL
;
502 ptr
= g_list_last(list
);
504 /* do the delete backwards */
506 o_current
= ptr
->data
;
507 s_delete_object(toplevel
, o_current
);
508 ptr
= g_list_previous (ptr
);
513 OBJECT
*s_basic_copy(TOPLEVEL
*toplevel
, OBJECT
*prototype
)
517 retval
= (*prototype
->copy_func
)(toplevel
, prototype
);
519 /* make sure sid is the same! */
520 retval
->sid
= prototype
->sid
;
521 retval
->uuid
= g_strdup(prototype
->uuid
);
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
)
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
);
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
,
585 gboolean (*fn
)(OBJECT
*o
,
586 int grip_x
, int grip_y
,
587 enum grip_t whichone
,
593 for (i
= 0; grips
[i
].whichone
!= GRIP_NONE
; i
++) {
594 if ((*fn
)(o
, grips
[i
].x
, grips
[i
].y
, grips
[i
].whichone
, userdata
)) {
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
) {
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
,
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
,
646 GList
**grip_list
= userdata
;
652 g
->whichone
= whichone
;
654 *grip_list
= g_list_prepend(*grip_list
, g
);
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
);
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
;
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
,
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
);
757 switch (res
= (*fn
)(object
, context
)) {
759 /* No special action; continue traversing. */
761 case VISIT_RES_NORECURSE
:
763 case VISIT_RES_EARLY
:
764 case VISIT_RES_ABORT
:
765 case VISIT_RES_RESTART
:
766 case VISIT_RES_ERROR
:
771 /* Visit the current object later. */
775 /* Limit the traversal depth. Don't visit attributes */
776 if (depth
!= 1 && object
->attached_to
== NULL
) {
780 newdepth
= depth
- 1;
785 switch (res
= s_visit_object(object
, fn
, context
, order
, newdepth
)) {
787 /* No special action; continue traversing. */
789 case VISIT_RES_NORECURSE
:
790 /* Only caller-supplied visitors may return VISIT_RES_NORECURSE. */
793 case VISIT_RES_EARLY
:
794 case VISIT_RES_ABORT
:
795 case VISIT_RES_RESTART
:
796 case VISIT_RES_ERROR
:
805 /* Current object has already been visited. */
808 switch (res
= (*fn
)(object
, context
)) {
811 case VISIT_RES_NORECURSE
:
812 /* Too late, the parent object has already been visited. */
815 case VISIT_RES_EARLY
:
816 case VISIT_RES_ABORT
:
817 case VISIT_RES_RESTART
:
818 case VISIT_RES_ERROR
:
827 enum visit_result
s_visit_object(OBJECT
*o_current
,
828 enum visit_result (*fn
)(OBJECT
*, void *),
829 void *context
, enum visit_order order
,
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
);
845 g_warn_if_fail(o_current
->type
== OBJ_HEAD
);
848 g_warn_if_fail(o_current
->type
!= OBJ_HEAD
);
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
)) {
859 /* No special action; continue traversing. */
861 case VISIT_RES_NORECURSE
:
862 /* Only caller-supplied visitors may return VISIT_RES_NORECURSE. */
865 case VISIT_RES_EARLY
:
866 case VISIT_RES_ABORT
:
867 case VISIT_RES_RESTART
:
868 case VISIT_RES_ERROR
:
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
,
889 enum visit_result res
;
893 for (iter
= geda_list_get_glist(toplevel
->pages
);
895 iter
= g_list_next(iter
)) {
896 page
= (PAGE
*) iter
->data
;
897 switch (res
= s_visit_page(page
, fn
, context
, order
, depth
)) {
899 /* No special action; continue traversing. */
901 case VISIT_RES_NORECURSE
:
902 /* Only caller-supplied visitors may return VISIT_RES_NORECURSE. */
905 case VISIT_RES_EARLY
:
906 case VISIT_RES_ABORT
:
907 case VISIT_RES_RESTART
:
908 case VISIT_RES_ERROR
:
916 /*! \todo Finish function documentation!!!
918 * \par Function Description
921 /* used by o_text_read */
922 char *remove_nl(char *string
)
930 while(string
[i
] != '\0' && string
[i
] != '\n' && string
[i
] != '\r') {
939 /*! \todo Finish function documentation!!!
941 * \par Function Description
944 /* used by o_text_read */
945 char *remove_last_nl(char *string
)
952 len
= strlen(string
);
953 if (string
[len
-1] == '\n' || string
[len
-1] == '\r')
954 string
[len
-1] = '\0';
959 /*! \brief Expand environment variables in string.
960 * \par Function Description
961 * This function returns the passed string with environment variables
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
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.
978 s_expand_env_variables (const gchar
*string
)
983 if (string
== NULL
) {
987 gstring
= g_string_sized_new (strlen (string
));
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
);
1002 switch (string
[i
]) {
1004 /* look for the end of the variable name */
1006 while (string
[i
] != '\0' && string
[i
] != '}') i
++;
1007 if (string
[i
] == '\0') {
1008 /* problem: no closing '}' to variable */
1010 "Found malformed environment variable in '%s'\n",
1012 g_string_append (gstring
, "$");
1013 g_string_append_len (gstring
, string
+ start
, i
- start
+ 1);
1019 /* test characters of variable name */
1021 j
< i
&& (g_ascii_isalnum (string
[j
]) || string
[j
] == '_');
1024 /* illegal character detected in variable name */
1026 "Found bad character [%c] in variable name.\n",
1028 g_string_append (gstring
, "${");
1029 g_string_append_len (gstring
, string
+ start
, i
- start
+ 1);
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
);
1042 /* an escaped '$' */
1043 g_string_append_c (gstring
, string
[i
++]);
1047 /* an isolated '$', put it in output */
1048 g_string_append_c (gstring
, '$');