1 /* env.c - Environment variables */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2005,2006,2007,2008 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 (void)
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 (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
)
134 /* Restore the previous context. */
135 context
= current_context
->prev
;
136 grub_free (current_context
);
137 current_context
= context
;
139 return GRUB_ERR_NONE
;
143 grub_env_insert (struct grub_env_context
*context
,
144 struct grub_env_var
*var
)
146 int idx
= grub_env_hashval (var
->name
);
148 /* Insert the variable into the hashtable. */
149 var
->prevp
= &context
->vars
[idx
];
150 var
->next
= context
->vars
[idx
];
152 var
->next
->prevp
= &(var
->next
);
153 context
->vars
[idx
] = var
;
157 grub_env_remove (struct grub_env_var
*var
)
159 /* Remove the entry from the variable table. */
160 *var
->prevp
= var
->next
;
162 var
->next
->prevp
= var
->prevp
;
166 grub_env_export (const char *name
)
168 struct grub_env_var
*var
;
170 var
= grub_env_find (name
);
172 var
->type
= GRUB_ENV_VAR_GLOBAL
;
174 return GRUB_ERR_NONE
;
178 grub_env_set (const char *name
, const char *val
)
180 struct grub_env_var
*var
;
182 /* If the variable does already exist, just update the variable. */
183 var
= grub_env_find (name
);
186 char *old
= var
->value
;
189 var
->value
= var
->write_hook (var
, val
);
191 var
->value
= grub_strdup (val
);
200 return GRUB_ERR_NONE
;
203 /* The variable does not exist, so create a new one. */
204 var
= grub_malloc (sizeof (*var
));
208 grub_memset (var
, 0, sizeof (*var
));
210 /* This is not necessary, because GRUB_ENV_VAR_LOCAL == 0. But leave
211 this for readability. */
212 var
->type
= GRUB_ENV_VAR_LOCAL
;
214 var
->name
= grub_strdup (name
);
218 var
->value
= grub_strdup (val
);
222 grub_env_insert (current_context
, var
);
224 return GRUB_ERR_NONE
;
227 grub_free (var
->name
);
228 grub_free (var
->value
);
235 grub_env_get (const char *name
)
237 struct grub_env_var
*var
;
239 var
= grub_env_find (name
);
244 return var
->read_hook (var
, var
->value
);
250 grub_env_unset (const char *name
)
252 struct grub_env_var
*var
;
254 var
= grub_env_find (name
);
258 /* XXX: It is not possible to unset variables with a read or write
260 if (var
->read_hook
|| var
->write_hook
)
263 grub_env_remove (var
);
265 grub_free (var
->name
);
266 if (var
->type
!= GRUB_ENV_VAR_DATA
)
267 grub_free (var
->value
);
272 grub_env_iterate (int (*func
) (struct grub_env_var
*var
))
274 struct grub_env_sorted_var
*sorted_list
= 0;
275 struct grub_env_sorted_var
*sorted_var
;
278 /* Add variables associated with this context into a sorted list. */
279 for (i
= 0; i
< HASHSZ
; i
++)
281 struct grub_env_var
*var
;
283 for (var
= current_context
->vars
[i
]; var
; var
= var
->next
)
285 struct grub_env_sorted_var
*p
, **q
;
287 /* Ignore data slots. */
288 if (var
->type
== GRUB_ENV_VAR_DATA
)
291 sorted_var
= grub_malloc (sizeof (*sorted_var
));
295 sorted_var
->var
= var
;
297 for (q
= &sorted_list
, p
= *q
; p
; q
= &((*q
)->next
), p
= *q
)
299 if (grub_strcmp (p
->var
->name
, var
->name
) > 0)
303 sorted_var
->next
= *q
;
308 /* Iterate FUNC on the sorted list. */
309 for (sorted_var
= sorted_list
; sorted_var
; sorted_var
= sorted_var
->next
)
310 if (func (sorted_var
->var
))
315 /* Free the sorted list. */
316 for (sorted_var
= sorted_list
; sorted_var
; )
318 struct grub_env_sorted_var
*tmp
= sorted_var
->next
;
320 grub_free (sorted_var
);
326 grub_register_variable_hook (const char *name
,
327 grub_env_read_hook_t read_hook
,
328 grub_env_write_hook_t write_hook
)
330 struct grub_env_var
*var
= grub_env_find (name
);
334 if (grub_env_set (name
, "") != GRUB_ERR_NONE
)
337 var
= grub_env_find (name
);
338 /* XXX Insert an assertion? */
341 var
->read_hook
= read_hook
;
342 var
->write_hook
= write_hook
;
344 return GRUB_ERR_NONE
;
348 mangle_data_slot_name (const char *name
)
352 mangled_name
= grub_malloc (grub_strlen (name
) + 2);
356 grub_sprintf (mangled_name
, "\e%s", name
);
361 grub_env_set_data_slot (const char *name
, const void *ptr
)
364 struct grub_env_var
*var
;
366 mangled_name
= mangle_data_slot_name (name
);
370 /* If the variable does already exist, just update the variable. */
371 var
= grub_env_find (mangled_name
);
374 var
->value
= (char *) ptr
;
375 return GRUB_ERR_NONE
;
378 /* The variable does not exist, so create a new one. */
379 var
= grub_malloc (sizeof (*var
));
383 grub_memset (var
, 0, sizeof (*var
));
385 var
->type
= GRUB_ENV_VAR_DATA
;
386 var
->name
= mangled_name
;
387 var
->value
= (char *) ptr
;
389 grub_env_insert (current_context
, var
);
391 return GRUB_ERR_NONE
;
395 grub_free (mangled_name
);
400 grub_env_get_data_slot (const char *name
)
405 mangled_name
= mangle_data_slot_name (name
);
409 ptr
= grub_env_get (mangled_name
);
410 grub_free (mangled_name
);
418 grub_env_unset_data_slot (const char *name
)
422 mangled_name
= mangle_data_slot_name (name
);
426 grub_env_unset (mangled_name
);
427 grub_free (mangled_name
);