Hunt down some unused variables.
[geda-gaf/berndj.git] / libgeda / src / s_basic.c
blob5feb691b6f6df8b70f232e63fe088cf6253dd3c6
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_visit_none(OBJECT *o_current,
50 void (*fn)(OBJECT *, void *),
51 void *context);
52 static void s_basic_grips_foreach_none(OBJECT *o,
53 gboolean (*fn)(OBJECT *o,
54 int grip_x, int grip_y,
55 enum grip_t whichone,
56 void *userdata),
57 void *userdata);
58 static int s_basic_grips_move_none(OBJECT *o, int whichone, int x, int y);
60 /*! \todo Finish function documentation!!!
61 * \brief
62 * \par Function Description
65 void error_if_called(void)
67 fprintf(stderr, "Somebody called error_if_called!\n");
68 g_assert(0);
71 /*! \todo Finish function documentation!!!
72 * \brief
73 * \par Function Description
76 void exit_if_null(void *ptr)
78 if (ptr == NULL) {
79 fprintf(stderr, "gEDA: Got NULL ptr!, please e-mail maintainer\n");
80 g_assert(0);
81 exit(-1);
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)
92 PAGE *page = NULL;
94 if (toplevel) {
95 page = toplevel->page_current;
98 return page;
101 /*! \todo Finish function documentation!!!
102 * \brief
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;
113 o_current = head;
114 while ( o_current != NULL ) { /* goto end of list */
115 ret_struct = o_current;
116 o_current = o_current->next;
119 return(ret_struct);
122 /*! \todo Finish function documentation!!!
123 * \brief
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;
134 o_current = tail;
135 while ( o_current != NULL ) { /* goto end of list */
136 ret_struct = o_current;
137 o_current = o_current->prev;
140 return(ret_struct);
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)
152 /* setup sid */
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 */
164 new_node->w_top = 0;
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;
233 return(new_node);
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");
241 return(ptr);
244 new_node->prev = ptr; /* setup previous link */
246 if (ptr) {
247 ptr->next = new_node;
250 return new_node;
253 OBJECT *s_basic_unlink_object(OBJECT *o_current)
255 OBJECT *prev = o_current->prev;
257 if (o_current->next)
258 o_current->next->prev = o_current->prev;
259 if (o_current->prev)
260 o_current->prev->next = o_current->next;
261 o_current->next = o_current->prev = NULL;
263 return prev;
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;
271 last->next = before;
272 before->prev->next = first;
273 before->prev = last;
276 /*! \todo Finish function documentation!!!
277 * \brief
278 * \par Function Description
281 void print_struct_forw(OBJECT const *ptr)
283 OBJECT const *o_current=NULL;
285 o_current = ptr;
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);
303 printf("----\n");
304 o_current = o_current->next;
308 /*! \todo Finish function documentation!!!
309 * \brief
310 * \par Function Description
313 void print_struct(OBJECT const *ptr)
315 OBJECT const *o_current=NULL;
317 o_current = ptr;
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) {
324 int x1, y1, x2, y2;
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);
336 printf("----\n");
340 /*! \todo Finish function documentation!!!
341 * \brief
342 * \par Function Description
345 void
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!!!
429 * \brief
430 * \par Function Description
433 OBJECT *s_delete(TOPLEVEL *toplevel, OBJECT *o_current)
435 OBJECT *o_prev = NULL;
436 if (o_current != NULL) {
438 #if DEBUG
439 printf("sdel: %s\n", o_current->name);
440 printf("sdel: %d\n", o_current->sid);
441 #endif
443 o_prev = s_basic_unlink_object(o_current);
444 s_delete_object(toplevel, o_current);
447 return o_prev;
450 /*! \todo Finish function documentation!!!
451 * \brief
452 * \par Function Description
455 /* deletes everything include the head */
456 void s_delete_list_fromstart(TOPLEVEL *toplevel, OBJECT *start)
458 OBJECT *o_current;
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!!!
469 * \brief
470 * \par Function Description
473 /* deletes everything include the GList */
474 void
475 s_delete_object_glist(TOPLEVEL *toplevel, GList *list)
477 OBJECT *o_current=NULL;
478 GList *ptr;
480 ptr = g_list_last(list);
482 /* do the delete backwards */
483 while(ptr != NULL) {
484 o_current = ptr->data;
485 s_delete_object(toplevel, o_current);
486 ptr = g_list_previous (ptr);
488 g_list_free(list);
491 OBJECT *s_basic_copy(TOPLEVEL *toplevel, OBJECT *prototype)
493 OBJECT *retval;
495 retval = (*prototype->copy_func)(toplevel, prototype);
496 if (retval) {
497 /* make sure sid is the same! */
498 retval->sid = prototype->sid;
499 retval->uuid = g_strdup(prototype->uuid);
502 return retval;
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);
536 return whichone;
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,
551 const GRIP *grips,
552 gboolean (*fn)(OBJECT *o,
553 int grip_x, int grip_y,
554 enum grip_t whichone,
555 void *userdata),
556 void *userdata)
558 int i;
560 for (i = 0; grips[i].whichone != GRIP_NONE; i++) {
561 if ((*fn)(o, grips[i].x, grips[i].y, grips[i].whichone, userdata)) {
562 return;
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) {
573 *ctx->x = grip_x;
574 *ctx->y = grip_y;
575 ctx->found = TRUE;
576 return TRUE;
579 return FALSE;
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,
596 .found = FALSE,
597 .x = x,
598 .y = y,
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,
611 void *userdata)
613 GList **grip_list = userdata;
614 GRIP *g;
616 g = g_new(GRIP, 1);
617 g->x = grip_x;
618 g->y = grip_y;
619 g->whichone = whichone;
621 *grip_list = g_list_prepend(*grip_list, g);
623 return FALSE;
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);
643 return 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;
671 return TRUE;
674 return FALSE;
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,
690 .x = &x,
691 .y = &y,
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)
715 OBJECT *list;
717 if (object == NULL) {
718 fprintf(stderr, "s_visit() on NULL object. This is probably an error.");
719 return;
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) {
728 int newdepth;
730 if (depth != 0) {
731 newdepth = depth - 1;
732 } else {
733 newdepth = 0;
736 /* TODO: Just use s_visit_object */
738 switch (object->type) {
739 case OBJ_COMPLEX:
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");
743 break;
745 for (list = object->complex->prim_objs->next; list; list = list->next) {
746 s_visit(list, fn, context, order, newdepth);
748 break;
749 case OBJ_ARC:
750 case OBJ_BOX:
751 case OBJ_CIRCLE:
752 case OBJ_LINE: case OBJ_PIN: /* There is no separate PIN type. */
753 case OBJ_PICTURE:
754 case OBJ_SLOT:
755 /* No substructure. */
756 break;
757 case OBJ_TEXT:
758 if (object->text->prim_objs == NULL) {
759 /* Text is invisible. */
760 break;
762 for (list = object->text->prim_objs->next; list; list = list->next) {
763 s_visit(list, fn, context, order, newdepth);
765 break;
766 default:
767 fprintf(stderr, "Can't walk structure of %s\n", object->name);
768 break;
772 if (order == VISIT_POSTFIX) {
773 (*fn)(object, context);
777 void s_visit_object(OBJECT *o_current,
778 void (*fn)(OBJECT *, void *),
779 void *context)
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)
787 OBJECT *o_current;
789 for (o_current = page->object_head->next;
790 o_current != NULL;
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)
799 const GList *iter;
800 PAGE *page;
802 for (iter = geda_list_get_glist(toplevel->pages);
803 iter != NULL;
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!!!
811 * \brief
812 * \par Function Description
815 /* used by o_text_read */
816 char *remove_nl(char *string)
818 int i;
820 if (!string)
821 return NULL;
823 i = 0;
824 while(string[i] != '\0' && string[i] != '\n' && string[i] != '\r') {
825 i++;
828 string[i] = '\0';
830 return(string);
833 /*! \todo Finish function documentation!!!
834 * \brief
835 * \par Function Description
838 /* used by o_text_read */
839 char *remove_last_nl(char *string)
841 int len;
843 if (!string)
844 return NULL;
846 len = strlen(string);
847 if (string[len-1] == '\n' || string[len-1] == '\r')
848 string[len-1] = '\0';
850 return(string);
853 /*! \brief Expand environment variables in string.
854 * \par Function Description
855 * This function returns the passed string with environment variables
856 * expanded.
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
862 * twice.
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.
871 gchar*
872 s_expand_env_variables (const gchar *string)
874 GString *gstring;
875 gint i;
877 if (string == NULL) {
878 return NULL;
881 gstring = g_string_sized_new (strlen (string));
882 i = 0;
883 while (TRUE) {
884 gint start;
886 start = i;
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);
895 i++;
896 switch (string[i]) {
897 case ('{'):
898 /* look for the end of the variable name */
899 start = i;
900 while (string[i] != '\0' && string[i] != '}') i++;
901 if (string[i] == '\0') {
902 /* problem: no closing '}' to variable */
903 fprintf (stderr,
904 "Found malformed environment variable in '%s'\n",
905 string);
906 g_string_append (gstring, "$");
907 g_string_append_len (gstring, string + start, i - start + 1);
908 } else {
909 gint j;
911 /* discard "${" */
912 start = start + 1;
913 /* test characters of variable name */
914 for (j = start;
915 j < i && (g_ascii_isalnum (string[j]) || string[j] == '_');
916 j++);
917 if (i != j) {
918 /* illegal character detected in variable name */
919 fprintf (stderr,
920 "Found bad character [%c] in variable name.\n",
921 string[j]);
922 g_string_append (gstring, "${");
923 g_string_append_len (gstring, string + start, i - start + 1);
924 } else {
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);
931 i++;
933 break;
935 case ('$'):
936 /* an escaped '$' */
937 g_string_append_c (gstring, string[i++]);
938 break;
940 default:
941 /* an isolated '$', put it in output */
942 g_string_append_c (gstring, '$');
946 /* never reached */
947 return NULL;