1 /* env.c - Environment variables */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
6 * GRUB 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 3 of the License, or
9 * (at your option) any later version.
11 * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
21 #include <grub/misc.h>
24 /* The size of the hash table. */
27 /* A hashtable for quick lookup of variables. */
28 struct grub_env_context
30 /* A hash table for variables. */
31 struct grub_env_var
*vars
[HASHSZ
];
33 /* One level deeper on the stack. */
34 struct grub_env_context
*prev
;
37 /* This is used for sorting only. */
38 struct grub_env_sorted_var
40 struct grub_env_var
*var
;
41 struct grub_env_sorted_var
*next
;
44 /* The initial context. */
45 static struct grub_env_context initial_context
;
47 /* The current context. */
48 static struct grub_env_context
*current_context
= &initial_context
;
50 /* Return the hash representation of the string S. */
52 grub_env_hashval (const char *s
)
56 /* XXX: This can be done much more efficiently. */
63 static struct grub_env_var
*
64 grub_env_find (const char *name
)
66 struct grub_env_var
*var
;
67 int idx
= grub_env_hashval (name
);
69 /* Look for the variable in the current context. */
70 for (var
= current_context
->vars
[idx
]; var
; var
= var
->next
)
71 if (grub_strcmp (var
->name
, name
) == 0)
78 grub_env_context_open (int export
)
80 struct grub_env_context
*context
;
83 context
= grub_malloc (sizeof (*context
));
87 grub_memset (context
, 0, sizeof (*context
));
88 context
->prev
= current_context
;
89 current_context
= context
;
91 /* Copy exported variables. */
92 for (i
= 0; i
< HASHSZ
; i
++)
94 struct grub_env_var
*var
;
96 for (var
= context
->prev
->vars
[i
]; var
; var
= var
->next
)
98 if (export
&& var
->type
== GRUB_ENV_VAR_GLOBAL
)
100 if (grub_env_set (var
->name
, var
->value
) != GRUB_ERR_NONE
)
102 grub_env_context_close ();
105 grub_register_variable_hook (var
->name
, var
->read_hook
, var
->write_hook
);
110 return GRUB_ERR_NONE
;
114 grub_env_context_close (void)
116 struct grub_env_context
*context
;
119 if (! current_context
->prev
)
120 grub_fatal ("cannot close the initial context");
122 /* Free the variables associated with this context. */
123 for (i
= 0; i
< HASHSZ
; i
++)
125 struct grub_env_var
*p
, *q
;
127 for (p
= current_context
->vars
[i
]; p
; p
= q
)
131 if (p
->type
!= GRUB_ENV_VAR_DATA
)
132 grub_free (p
->value
);
137 /* Restore the previous context. */
138 context
= current_context
->prev
;
139 grub_free (current_context
);
140 current_context
= context
;
142 return GRUB_ERR_NONE
;
146 grub_env_insert (struct grub_env_context
*context
,
147 struct grub_env_var
*var
)
149 int idx
= grub_env_hashval (var
->name
);
151 /* Insert the variable into the hashtable. */
152 var
->prevp
= &context
->vars
[idx
];
153 var
->next
= context
->vars
[idx
];
155 var
->next
->prevp
= &(var
->next
);
156 context
->vars
[idx
] = var
;
160 grub_env_remove (struct grub_env_var
*var
)
162 /* Remove the entry from the variable table. */
163 *var
->prevp
= var
->next
;
165 var
->next
->prevp
= var
->prevp
;
169 grub_env_export (const char *name
)
171 struct grub_env_var
*var
;
173 var
= grub_env_find (name
);
175 var
->type
= GRUB_ENV_VAR_GLOBAL
;
177 return GRUB_ERR_NONE
;
181 grub_env_set (const char *name
, const char *val
)
183 struct grub_env_var
*var
;
185 /* If the variable does already exist, just update the variable. */
186 var
= grub_env_find (name
);
189 char *old
= var
->value
;
192 var
->value
= var
->write_hook (var
, val
);
194 var
->value
= grub_strdup (val
);
203 return GRUB_ERR_NONE
;
206 /* The variable does not exist, so create a new one. */
207 var
= grub_malloc (sizeof (*var
));
211 grub_memset (var
, 0, sizeof (*var
));
213 /* This is not necessary, because GRUB_ENV_VAR_LOCAL == 0. But leave
214 this for readability. */
215 var
->type
= GRUB_ENV_VAR_LOCAL
;
217 var
->name
= grub_strdup (name
);
221 var
->value
= grub_strdup (val
);
225 grub_env_insert (current_context
, var
);
227 return GRUB_ERR_NONE
;
230 grub_free (var
->name
);
231 grub_free (var
->value
);
238 grub_env_get (const char *name
)
240 struct grub_env_var
*var
;
242 var
= grub_env_find (name
);
247 return var
->read_hook (var
, var
->value
);
253 grub_env_unset (const char *name
)
255 struct grub_env_var
*var
;
257 var
= grub_env_find (name
);
261 /* XXX: It is not possible to unset variables with a read or write
263 if (var
->read_hook
|| var
->write_hook
)
266 grub_env_remove (var
);
268 grub_free (var
->name
);
269 if (var
->type
!= GRUB_ENV_VAR_DATA
)
270 grub_free (var
->value
);
275 grub_env_iterate (int (*func
) (struct grub_env_var
*var
))
277 struct grub_env_sorted_var
*sorted_list
= 0;
278 struct grub_env_sorted_var
*sorted_var
;
281 /* Add variables associated with this context into a sorted list. */
282 for (i
= 0; i
< HASHSZ
; i
++)
284 struct grub_env_var
*var
;
286 for (var
= current_context
->vars
[i
]; var
; var
= var
->next
)
288 struct grub_env_sorted_var
*p
, **q
;
290 /* Ignore data slots. */
291 if (var
->type
== GRUB_ENV_VAR_DATA
)
294 sorted_var
= grub_malloc (sizeof (*sorted_var
));
298 sorted_var
->var
= var
;
300 for (q
= &sorted_list
, p
= *q
; p
; q
= &((*q
)->next
), p
= *q
)
302 if (grub_strcmp (p
->var
->name
, var
->name
) > 0)
306 sorted_var
->next
= *q
;
311 /* Iterate FUNC on the sorted list. */
312 for (sorted_var
= sorted_list
; sorted_var
; sorted_var
= sorted_var
->next
)
313 if (func (sorted_var
->var
))
318 /* Free the sorted list. */
319 for (sorted_var
= sorted_list
; sorted_var
; )
321 struct grub_env_sorted_var
*tmp
= sorted_var
->next
;
323 grub_free (sorted_var
);
329 grub_register_variable_hook (const char *name
,
330 grub_env_read_hook_t read_hook
,
331 grub_env_write_hook_t write_hook
)
333 struct grub_env_var
*var
= grub_env_find (name
);
337 if (grub_env_set (name
, "") != GRUB_ERR_NONE
)
340 var
= grub_env_find (name
);
341 /* XXX Insert an assertion? */
344 var
->read_hook
= read_hook
;
345 var
->write_hook
= write_hook
;
347 return GRUB_ERR_NONE
;
351 mangle_data_slot_name (const char *name
)
355 mangled_name
= grub_malloc (grub_strlen (name
) + 2);
359 grub_sprintf (mangled_name
, "\e%s", name
);
364 grub_env_set_data_slot (const char *name
, const void *ptr
)
367 struct grub_env_var
*var
;
369 mangled_name
= mangle_data_slot_name (name
);
373 /* If the variable does already exist, just update the variable. */
374 var
= grub_env_find (mangled_name
);
377 var
->value
= (char *) ptr
;
378 return GRUB_ERR_NONE
;
381 /* The variable does not exist, so create a new one. */
382 var
= grub_malloc (sizeof (*var
));
386 grub_memset (var
, 0, sizeof (*var
));
388 var
->type
= GRUB_ENV_VAR_DATA
;
389 var
->name
= mangled_name
;
390 var
->value
= (char *) ptr
;
392 grub_env_insert (current_context
, var
);
394 return GRUB_ERR_NONE
;
398 grub_free (mangled_name
);
403 grub_env_get_data_slot (const char *name
)
408 mangled_name
= mangle_data_slot_name (name
);
412 ptr
= grub_env_get (mangled_name
);
413 grub_free (mangled_name
);
421 grub_env_unset_data_slot (const char *name
)
425 mangled_name
= mangle_data_slot_name (name
);
429 grub_env_unset (mangled_name
);
430 grub_free (mangled_name
);