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_zalloc (sizeof (*context
));
87 context
->prev
= current_context
;
88 current_context
= context
;
90 /* Copy exported variables. */
91 for (i
= 0; i
< HASHSZ
; i
++)
93 struct grub_env_var
*var
;
95 for (var
= context
->prev
->vars
[i
]; var
; var
= var
->next
)
97 if (export
&& var
->type
== GRUB_ENV_VAR_GLOBAL
)
99 if (grub_env_set (var
->name
, var
->value
) != GRUB_ERR_NONE
)
101 grub_env_context_close ();
104 grub_register_variable_hook (var
->name
, var
->read_hook
, var
->write_hook
);
109 return GRUB_ERR_NONE
;
113 grub_env_context_close (void)
115 struct grub_env_context
*context
;
118 if (! current_context
->prev
)
119 grub_fatal ("cannot close the initial context");
121 /* Free the variables associated with this context. */
122 for (i
= 0; i
< HASHSZ
; i
++)
124 struct grub_env_var
*p
, *q
;
126 for (p
= current_context
->vars
[i
]; p
; p
= q
)
130 if (p
->type
!= GRUB_ENV_VAR_DATA
)
131 grub_free (p
->value
);
136 /* Restore the previous context. */
137 context
= current_context
->prev
;
138 grub_free (current_context
);
139 current_context
= context
;
141 return GRUB_ERR_NONE
;
145 grub_env_insert (struct grub_env_context
*context
,
146 struct grub_env_var
*var
)
148 int idx
= grub_env_hashval (var
->name
);
150 /* Insert the variable into the hashtable. */
151 var
->prevp
= &context
->vars
[idx
];
152 var
->next
= context
->vars
[idx
];
154 var
->next
->prevp
= &(var
->next
);
155 context
->vars
[idx
] = var
;
159 grub_env_remove (struct grub_env_var
*var
)
161 /* Remove the entry from the variable table. */
162 *var
->prevp
= var
->next
;
164 var
->next
->prevp
= var
->prevp
;
168 grub_env_export (const char *name
)
170 struct grub_env_var
*var
;
172 var
= grub_env_find (name
);
174 var
->type
= GRUB_ENV_VAR_GLOBAL
;
176 return GRUB_ERR_NONE
;
180 grub_env_set (const char *name
, const char *val
)
182 struct grub_env_var
*var
;
184 /* If the variable does already exist, just update the variable. */
185 var
= grub_env_find (name
);
188 char *old
= var
->value
;
191 var
->value
= var
->write_hook (var
, val
);
193 var
->value
= grub_strdup (val
);
202 return GRUB_ERR_NONE
;
205 /* The variable does not exist, so create a new one. */
206 var
= grub_zalloc (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_zalloc (sizeof (*var
));
383 var
->type
= GRUB_ENV_VAR_DATA
;
384 var
->name
= mangled_name
;
385 var
->value
= (char *) ptr
;
387 grub_env_insert (current_context
, var
);
389 return GRUB_ERR_NONE
;
393 grub_free (mangled_name
);
398 grub_env_get_data_slot (const char *name
)
403 mangled_name
= mangle_data_slot_name (name
);
407 ptr
= grub_env_get (mangled_name
);
408 grub_free (mangled_name
);
416 grub_env_unset_data_slot (const char *name
)
420 mangled_name
= mangle_data_slot_name (name
);
424 grub_env_unset (mangled_name
);
425 grub_free (mangled_name
);