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_visit_none(OBJECT
*o_current
,
50 void (*fn
)(OBJECT
*, void *),
52 static void s_basic_grips_foreach_none(OBJECT
*o
,
53 gboolean (*fn
)(OBJECT
*o
,
54 int grip_x
, int grip_y
,
58 static int s_basic_grips_move_none(OBJECT
*o
, int whichone
, int x
, int y
);
60 /*! \todo Finish function documentation!!!
62 * \par Function Description
65 void error_if_called(void)
67 fprintf(stderr
, "Somebody called error_if_called!\n");
71 /*! \todo Finish function documentation!!!
73 * \par Function Description
76 void exit_if_null(void *ptr
)
79 fprintf(stderr
, "gEDA: Got NULL ptr!, please e-mail maintainer\n");
85 /*! \brief Return toplevel->page_current if toplevel is non-NULL
86 * \par Function Description
87 * \param [in] toplevel The TOPLEVEL object, possibly NULL
88 * \return A pointer to the current page, or NULL if toplevel was NULL.
90 PAGE
*safe_page_current(TOPLEVEL
*toplevel
)
95 page
= toplevel
->page_current
;
101 /*! \todo Finish function documentation!!!
103 * \par Function Description
106 /* hack rename this to be s_return_tail */
107 /* update object_tail or any list of that matter */
108 OBJECT
*return_tail(OBJECT
*head
)
110 OBJECT
*o_current
=NULL
;
111 OBJECT
*ret_struct
=NULL
;
114 while ( o_current
!= NULL
) { /* goto end of list */
115 ret_struct
= o_current
;
116 o_current
= o_current
->next
;
122 /*! \todo Finish function documentation!!!
124 * \par Function Description
127 /* hack rename this to be s_return_head */
128 /* update object_tail or any list of that matter */
129 OBJECT
*return_head(OBJECT
*tail
)
131 OBJECT
*o_current
=NULL
;
132 OBJECT
*ret_struct
=NULL
;
135 while ( o_current
!= NULL
) { /* goto end of list */
136 ret_struct
= o_current
;
137 o_current
= o_current
->prev
;
143 /*! \brief Initialize an already-allocated object.
144 * \par Function Description
145 * Initializes the members of the OBJECT base class.
147 * \param [in] new_node A pointer to an allocated OBJECT
148 * \return A pointer to the initialized object.
150 OBJECT
*s_basic_init_object(OBJECT
*new_node
)
153 new_node
->sid
= global_sid
++;
155 /* Start without indirection or back-annotation. */
156 new_node
->vpad_to_pad
= NULL
;
157 new_node
->pad_to_pin
= NULL
;
159 /* Figure out who owns this OBJECT later. */
160 new_node
->owning_slot
= NULL
;
161 new_node
->uuid
= NULL
;
163 /* Setup the bounding box */
165 new_node
->w_left
= 0;
166 new_node
->w_right
= 0;
167 new_node
->w_bottom
= 0;
168 new_node
->w_bounds_valid
= FALSE
;
170 /* Setup line/circle structs */
171 new_node
->line
= NULL
;
172 new_node
->path
= NULL
;
173 new_node
->circle
= NULL
;
174 new_node
->arc
= NULL
;
175 new_node
->box
= NULL
;
176 new_node
->picture
= NULL
;
177 new_node
->text
= NULL
;
178 new_node
->complex = NULL
;
179 new_node
->type_state
= NULL
;
181 new_node
->conn_list
= NULL
;
183 new_node
->visited
= 0;
185 new_node
->complex_basename
= NULL
;
186 new_node
->complex_parent
= NULL
;
188 /* Setup the color */
189 new_node
->color
= WHITE
;
190 new_node
->saved_color
= -1;
191 new_node
->selected
= FALSE
;
192 new_node
->dont_redraw
= FALSE
;
193 new_node
->locked_color
= -1;
194 new_node
->draw_grips
= FALSE
;
196 new_node
->bus_ripper_direction
= 0;
198 new_node
->copy_func
= &s_basic_copy_error
;
199 new_node
->action_func
= error_if_called
;
200 new_node
->bounds_recalc_func
= &s_basic_bounds_recalc_error
;
201 new_node
->sel_func
= error_if_called
;
202 new_node
->draw_func
= error_if_called
;
203 new_node
->embed_func
= NULL
; /* FIXME: Sticking out like a sore thumb... */
204 new_node
->visit_func
= &s_basic_visit_none
;
205 new_node
->grip_foreach_func
= &s_basic_grips_foreach_none
;
206 new_node
->grip_move_func
= &s_basic_grips_move_none
;
207 new_node
->destroy_func
= NULL
;
209 new_node
->line_end
= END_NONE
;
210 new_node
->line_type
= TYPE_SOLID
;
211 new_node
->line_width
= 0;
212 new_node
->line_space
= 0;
213 new_node
->line_length
= 0;
214 new_node
->fill_width
= 0;
215 new_node
->fill_angle1
= 0;
216 new_node
->fill_angle2
= 0;
217 new_node
->fill_pitch1
= 0;
218 new_node
->fill_pitch2
= 0;
220 new_node
->attribs
= NULL
;
221 new_node
->attached_to
= NULL
;
222 new_node
->copied_to
= NULL
;
223 new_node
->show_name_value
= SHOW_NAME_VALUE
;
224 new_node
->visibility
= VISIBLE
;
226 new_node
->pin_type
= PIN_TYPE_NET
;
227 new_node
->whichend
= -1;
229 /* Setup link list stuff */
230 new_node
->prev
= NULL
;
231 new_node
->next
= NULL
;
236 OBJECT
*s_basic_link_object( OBJECT
*new_node
, OBJECT
*ptr
)
238 /* should never happen, but could */
239 if (new_node
== NULL
) {
240 fprintf(stderr
, "Got a null new_node in link_object\n");
244 new_node
->prev
= ptr
; /* setup previous link */
247 ptr
->next
= new_node
;
253 OBJECT
*s_basic_unlink_object(OBJECT
*o_current
)
255 OBJECT
*prev
= o_current
->prev
;
258 o_current
->next
->prev
= o_current
->prev
;
260 o_current
->prev
->next
= o_current
->next
;
261 o_current
->next
= o_current
->prev
= NULL
;
266 void s_basic_splice(OBJECT
*before
, OBJECT
*first
, OBJECT
*last
)
268 g_assert(first
->prev
== NULL
);
269 g_assert(last
->next
== NULL
);
270 first
->prev
= before
->prev
;
272 before
->prev
->next
= first
;
276 /*! \todo Finish function documentation!!!
278 * \par Function Description
281 void print_struct_forw(OBJECT
const *ptr
)
283 OBJECT
const *o_current
=NULL
;
287 if (o_current
== NULL
) {
289 printf("AGGGGGGGGGGG NULLLLL PRINT\n");
291 printf("TRYING to PRINT\n");
292 while (o_current
!= NULL
) {
293 printf("Name: %s\n", o_current
->name
);
294 printf("Type: %d\n", o_current
->type
);
295 printf("Sid: %d\n", o_current
->sid
);
297 if (o_current
->type
== OBJ_COMPLEX
|| o_current
->type
== OBJ_PLACEHOLDER
) {
298 print_struct_forw(o_current
->complex->prim_objs
);
301 o_attrib_print (o_current
->attribs
);
304 o_current
= o_current
->next
;
308 /*! \todo Finish function documentation!!!
310 * \par Function Description
313 void print_struct(OBJECT
const *ptr
)
315 OBJECT
const *o_current
=NULL
;
319 if (o_current
!= NULL
) {
320 printf("Name: %s\n", o_current
->name
);
321 printf("Type: %d\n", o_current
->type
);
322 printf("Sid: %d\n", o_current
->sid
);
323 if (o_current
->line
!= NULL
) {
326 s_basic_get_grip(o_current
, GRIP_1
, &x1
, &y1
);
327 s_basic_get_grip(o_current
, GRIP_2
, &x2
, &y2
);
328 printf("Line points.x1: %d\n", x1
);
329 printf("Line points.y1: %d\n", y1
);
330 printf("Line points.x2: %d\n", x2
);
331 printf("Line points.y2: %d\n", y2
);
334 o_attrib_print (o_current
->attribs
);
340 /*! \todo Finish function documentation!!!
342 * \par Function Description
346 s_delete_object(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
348 if (o_current
!= NULL
) {
349 g_signal_emit_by_name(o_current
, "remove");
350 s_conn_remove(o_current
);
352 if (o_current
->attached_to
!= NULL
) {
353 /* do the actual remove */
354 o_attrib_remove(&o_current
->attached_to
->attribs
, o_current
);
357 if (toplevel
->page_current
->object_lastplace
== o_current
) {
358 toplevel
->page_current
->object_lastplace
= NULL
;
361 if (o_current
->destroy_func
) {
362 (*o_current
->destroy_func
)(o_current
);
364 if (o_current
->line
) {
365 /* printf("sdeleting line\n");*/
366 g_free(o_current
->line
);
368 o_current
->line
= NULL
;
370 if (o_current
->path
) {
371 g_free(o_current
->path
);
373 o_current
->path
= NULL
;
375 /* printf("sdeleting circle\n");*/
376 g_free(o_current
->circle
);
377 o_current
->circle
= NULL
;
379 /* printf("sdeleting arc\n");*/
380 g_free(o_current
->arc
);
381 o_current
->arc
= NULL
;
383 /* printf("sdeleting box\n");*/
384 g_free(o_current
->box
);
385 o_current
->box
= NULL
;
387 if (o_current
->picture
) {
388 /* printf("sdeleting picture\n");*/
389 o_current
->picture
= NULL
;
392 o_current
->text
= NULL
;
394 /* printf("sdeleting name\n");*/
395 g_free(o_current
->name
);
396 o_current
->name
= NULL
;
398 g_free(o_current
->uuid
);
399 o_current
->uuid
= NULL
;
401 /* printf("sdeleting complex_basename\n");*/
402 g_free(o_current
->complex_basename
);
403 o_current
->complex_basename
= NULL
;
405 if (o_current
->complex) {
406 if (o_current
->complex->prim_objs
) {
407 /* printf("sdeleting complex->primitive_objects\n");*/
408 s_delete_list_fromstart(toplevel
,
409 o_current
->complex->prim_objs
);
411 o_current
->complex->prim_objs
= NULL
;
413 g_free(o_current
->complex);
414 o_current
->complex = NULL
;
417 if (o_current
->attribs
) {
418 /* This is the only real use of toplevel that isn't just for page_current. */
419 o_attrib_detach_all(toplevel
, o_current
);
422 g_object_unref(o_current
);
424 o_current
=NULL
; /* misc clean up */
428 /*! \todo Finish function documentation!!!
430 * \par Function Description
433 OBJECT
*s_delete(TOPLEVEL
*toplevel
, OBJECT
*o_current
)
435 OBJECT
*o_prev
= NULL
;
436 if (o_current
!= NULL
) {
439 printf("sdel: %s\n", o_current
->name
);
440 printf("sdel: %d\n", o_current
->sid
);
443 o_prev
= s_basic_unlink_object(o_current
);
444 s_delete_object(toplevel
, o_current
);
450 /*! \todo Finish function documentation!!!
452 * \par Function Description
455 /* deletes everything include the head */
456 void s_delete_list_fromstart(TOPLEVEL
*toplevel
, OBJECT
*start
)
460 o_current
= return_tail(start
);
462 /* do the delete backwards */
463 while(o_current
!= NULL
) {
464 o_current
= s_delete(toplevel
, o_current
);
468 /*! \todo Finish function documentation!!!
470 * \par Function Description
473 /* deletes everything include the GList */
475 s_delete_object_glist(TOPLEVEL
*toplevel
, GList
*list
)
477 OBJECT
*o_current
=NULL
;
480 ptr
= g_list_last(list
);
482 /* do the delete backwards */
484 o_current
= ptr
->data
;
485 s_delete_object(toplevel
, o_current
);
486 ptr
= g_list_previous (ptr
);
491 OBJECT
*s_basic_copy(TOPLEVEL
*toplevel
, OBJECT
*prototype
)
495 retval
= (*prototype
->copy_func
)(toplevel
, prototype
);
497 /* make sure sid is the same! */
498 retval
->sid
= prototype
->sid
;
499 retval
->uuid
= g_strdup(prototype
->uuid
);
505 static OBJECT
*s_basic_copy_error(TOPLEVEL
*toplevel
, OBJECT
*prototype
)
507 g_return_val_if_reached(NULL
);
510 static void s_basic_bounds_recalc_error(OBJECT
*o_current G_GNUC_UNUSED
)
512 g_return_if_reached();
515 static void s_basic_visit_none(OBJECT
*o_current G_GNUC_UNUSED
,
516 void (*fn
)(OBJECT
*, void *) G_GNUC_UNUSED
,
517 void *context G_GNUC_UNUSED
)
521 static void s_basic_grips_foreach_none(OBJECT
*o G_GNUC_UNUSED
,
522 gboolean (*fn
)(OBJECT
*o
,
523 int grip_x
, int grip_y
,
524 enum grip_t whichone
,
525 void *userdata
) G_GNUC_UNUSED
,
526 void *userdata G_GNUC_UNUSED
)
528 /* Some objects have no grips at all; do nothing. */
531 static int s_basic_grips_move_none(OBJECT
*o
, int whichone
,
532 int x G_GNUC_UNUSED
, int y G_GNUC_UNUSED
)
534 s_log_message("Object #<object %s> has no grip_move_func\n", o
->name
);
539 /*! \brief Traverse a list of grip descriptors.
540 * \par Function Description
541 * Calls a user-supplied closure on each grip in the list, until any
542 * one of them returns TRUE. The grip list sentinel is the entry whose
543 * #whichone field is set to GRIP_NONE.
545 * \param [in] o The OBJECT containing the grips.
546 * \param [in] grips A pointer to a vector of grip descriptors.
547 * \param [in] fn A function to call on each grip.
548 * \param [in] userdata User-supplied context for \a fn.
550 void s_basic_grip_foreach_helper(OBJECT
*o
,
552 gboolean (*fn
)(OBJECT
*o
,
553 int grip_x
, int grip_y
,
554 enum grip_t whichone
,
560 for (i
= 0; grips
[i
].whichone
!= GRIP_NONE
; i
++) {
561 if ((*fn
)(o
, grips
[i
].x
, grips
[i
].y
, grips
[i
].whichone
, userdata
)) {
567 static gboolean
check_one_grip(OBJECT
*o G_GNUC_UNUSED
, int grip_x
, int grip_y
,
568 enum grip_t whichone
, void *userdata
)
570 struct grip_foreach_context
*ctx
= (struct grip_foreach_context
*) userdata
;
572 if (ctx
->whichone
== whichone
) {
582 /*! \brief Get a specific grip from an object.
583 * \par Function Description
584 * Gets a grip by a grip constant found earlier.
586 * \param [in] o The object to search.
587 * \param [in] whichone The grip constant.
588 * \param [in] x A pointer to the world X coordinate.
589 * \param [in] y A pointer to the world Y coordinate.
590 * \return TRUE if the grip exists, FALSE otherwise.
592 gboolean
s_basic_get_grip(OBJECT
const *o
, enum grip_t whichone
, int *x
, int *y
)
594 struct grip_foreach_context grip_finder
= {
595 .whichone
= whichone
,
601 if (o
->grip_foreach_func
) {
602 (*o
->grip_foreach_func
)((OBJECT
*) o
, &check_one_grip
, &grip_finder
);
605 return grip_finder
.found
;
608 static gboolean
grip_list_accumulator(OBJECT
*o G_GNUC_UNUSED
,
609 int grip_x
, int grip_y
,
610 enum grip_t whichone
,
613 GList
**grip_list
= userdata
;
619 g
->whichone
= whichone
;
621 *grip_list
= g_list_prepend(*grip_list
, g
);
626 /*! \brief Get a list of all of an OBJECT's grips.
627 * \par Function Description
628 * Gets a list of all grips in world coordinates on an object. Even coinciding
629 * and functionally duplicate grips are in the returned list.
631 * \param [in] o The object to query.
632 * \param [in] init An existing list, or NULL
633 * \return A GList holding GRIP objects, which should be g_free'd.
635 * \note Grips get added to the \em front of \a init.
637 GList
*s_basic_get_all_grips(OBJECT
const *o
, GList
*init
)
639 if (o
->grip_foreach_func
) {
640 (*o
->grip_foreach_func
)((OBJECT
*) o
, &grip_list_accumulator
, &init
);
646 /*! \brief Move one of an OBJECT's grips.
647 * \par Function Description
648 * Asks the OBJECT to move one of its grips while keeping others fixed.
650 * #whichone may be GRIP_NONE, which means to reset the object to being
651 * freshly placed at (#x,#y).
653 * \param [in] o The object to manipulate.
654 * \param [in] whichone The grip to move with the pointer.
655 * \param [in] x The world X coordinate of the pointer.
656 * \param [in] y The world Y coordinate of the pointer.
657 * \return A grip constant suggesting the next grip to move, or GRIP_NONE.
659 int s_basic_move_grip(OBJECT
*o
, int whichone
, int x
, int y
)
661 return ((*o
->grip_move_func
)(o
, whichone
, x
, y
));
664 static gboolean
search_one_grip(OBJECT
*o G_GNUC_UNUSED
, int grip_x
, int grip_y
,
665 enum grip_t whichone
, void *userdata
)
667 struct grip_foreach_context
*ctx
= (struct grip_foreach_context
*) userdata
;
669 if (grip_x
== *ctx
->x
&& grip_y
== *ctx
->y
) {
670 ctx
->whichone
= whichone
;
677 /*! \brief Find a grip whose position is (#x,#y).
678 * \par Function Description
679 * Searches an object's grips for one that is exactly at the given point.
681 * \param [in] o The object to search.
682 * \param [in] x The world X-coordinate of the point.
683 * \param [in] y The world Y-coordinate of the point.
684 * \return A grip constant identifying the found grip, or GRIP_NONE.
686 int s_basic_search_grips_exact(OBJECT
const *o
, int x
, int y
)
688 struct grip_foreach_context grip_searcher
= {
689 .whichone
= GRIP_NONE
,
694 if (o
->grip_foreach_func
) {
695 (*o
->grip_foreach_func
)((OBJECT
*) o
, &search_one_grip
, &grip_searcher
);
698 return (grip_searcher
.whichone
);
701 /** \brief Traverse an object, applying a closure to each sub-object.
702 * \par Function Description
703 * \param [in] object The object to traverse.
704 * \param [in] fn The closure function.
705 * \param [in] context The closure context to pass to fn.
706 * \param [in] order Whether to visit parents before or after children.
707 * \param [in] depth Maximum depth of traversal.
708 * \todo Decide if depth should be a limit or level selector.
709 * \todo Maybe apply the closure only to specific object types?
710 * \note This function modifies \a object only if \a fn does.
712 void s_visit(OBJECT
*object
, void (*fn
)(OBJECT
*, void *), void *context
,
713 enum visit_order order
, int depth
)
717 if (object
== NULL
) {
718 fprintf(stderr
, "s_visit() on NULL object. This is probably an error.");
722 if (order
== VISIT_PREFIX
|| order
== VISIT_UNORDERED
) {
723 (*fn
)(object
, context
);
726 /* Limit the traversal depth. Don't visit attributes */
727 if (depth
!= 1 && object
->attached_to
== NULL
) {
731 newdepth
= depth
- 1;
736 /* TODO: Just use s_visit_object */
738 switch (object
->type
) {
740 if (object
->complex->prim_objs
== NULL
) {
741 /* Component is already half deleted. */
742 fprintf(stderr
, "s_visit(): Aborting walk on half deleted complex\n");
745 for (list
= object
->complex->prim_objs
->next
; list
; list
= list
->next
) {
746 s_visit(list
, fn
, context
, order
, newdepth
);
752 case OBJ_LINE
: case OBJ_PIN
: /* There is no separate PIN type. */
755 /* No substructure. */
758 if (object
->text
->prim_objs
== NULL
) {
759 /* Text is invisible. */
762 for (list
= object
->text
->prim_objs
->next
; list
; list
= list
->next
) {
763 s_visit(list
, fn
, context
, order
, newdepth
);
767 fprintf(stderr
, "Can't walk structure of %s\n", object
->name
);
772 if (order
== VISIT_POSTFIX
) {
773 (*fn
)(object
, context
);
777 void s_visit_object(OBJECT
*o_current
,
778 void (*fn
)(OBJECT
*, void *),
781 (*o_current
->visit_func
)(o_current
, fn
, context
);
784 void s_visit_page(PAGE
*page
, void (*fn
)(OBJECT
*, void *), void *context
,
785 enum visit_order order
, int depth
)
789 for (o_current
= page
->object_head
->next
;
791 o_current
= o_current
->next
) {
792 s_visit(o_current
, fn
, context
, order
, depth
);
796 void s_visit_toplevel(TOPLEVEL
*toplevel
, void (*fn
)(OBJECT
*, void *),
797 void *context
, enum visit_order order
, int depth
)
802 for (iter
= geda_list_get_glist(toplevel
->pages
);
804 iter
= g_list_next(iter
)) {
805 page
= (PAGE
*) iter
->data
;
806 s_visit_page(page
, fn
, context
, order
, depth
);
810 /*! \todo Finish function documentation!!!
812 * \par Function Description
815 /* used by o_text_read */
816 char *remove_nl(char *string
)
824 while(string
[i
] != '\0' && string
[i
] != '\n' && string
[i
] != '\r') {
833 /*! \todo Finish function documentation!!!
835 * \par Function Description
838 /* used by o_text_read */
839 char *remove_last_nl(char *string
)
846 len
= strlen(string
);
847 if (string
[len
-1] == '\n' || string
[len
-1] == '\r')
848 string
[len
-1] = '\0';
853 /*! \brief Expand environment variables in string.
854 * \par Function Description
855 * This function returns the passed string with environment variables
858 * The invocations of environment variable MUST be in the form
859 * '${variable_name}', '$variable_name' is not valid here. Environment
860 * variable names consists solely of letters, digits and '_'. It is
861 * possible to escape a '$' character in the string by repeating it
864 * It outputs error messages to console and leaves the malformed and
865 * bad variable names in the returned string.
867 * \param [in] string The string with variables to expand.
868 * \return A newly-allocated string with variables expanded or NULL
869 * if input string was NULL.
872 s_expand_env_variables (const gchar
*string
)
877 if (string
== NULL
) {
881 gstring
= g_string_sized_new (strlen (string
));
887 /* look for end of string or possible variable name start */
888 while (string
[i
] != '\0' && string
[i
] != '$') i
++;
889 g_string_append_len (gstring
, string
+ start
, i
- start
);
890 if (string
[i
] == '\0') {
891 /* end of string, return built string */
892 return g_string_free (gstring
, FALSE
);
898 /* look for the end of the variable name */
900 while (string
[i
] != '\0' && string
[i
] != '}') i
++;
901 if (string
[i
] == '\0') {
902 /* problem: no closing '}' to variable */
904 "Found malformed environment variable in '%s'\n",
906 g_string_append (gstring
, "$");
907 g_string_append_len (gstring
, string
+ start
, i
- start
+ 1);
913 /* test characters of variable name */
915 j
< i
&& (g_ascii_isalnum (string
[j
]) || string
[j
] == '_');
918 /* illegal character detected in variable name */
920 "Found bad character [%c] in variable name.\n",
922 g_string_append (gstring
, "${");
923 g_string_append_len (gstring
, string
+ start
, i
- start
+ 1);
925 /* extract variable name from string and expand it */
926 gchar
*variable_name
= g_strndup (string
+ start
, i
- start
);
927 const gchar
*env
= g_getenv (variable_name
);
928 g_free (variable_name
);
929 g_string_append (gstring
, (env
== NULL
) ? "" : env
);
937 g_string_append_c (gstring
, string
[i
++]);
941 /* an isolated '$', put it in output */
942 g_string_append_c (gstring
, '$');