2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2002,2007 Free Software Foundation, Inc.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <config.h>
2N/A
2N/A#include <stdio.h>
2N/A#include <string.h>
2N/A#include <stdlib.h>
2N/A#include <ctype.h>
2N/A
2N/A#include <grub/emu/misc.h>
2N/A#include <grub/util/misc.h>
2N/A#include <grub/util/resolve.h>
2N/A#include <grub/i18n.h>
2N/A
2N/A/* Module. */
2N/Astruct mod_list
2N/A{
2N/A const char *name;
2N/A struct mod_list *next;
2N/A};
2N/A
2N/A/* Dependency. */
2N/Astruct dep_list
2N/A{
2N/A const char *name;
2N/A struct mod_list *list;
2N/A struct dep_list *next;
2N/A};
2N/A
2N/Astatic char buf[1024];
2N/A
2N/Astatic void
2N/Afree_mod_list (struct mod_list *head)
2N/A{
2N/A while (head)
2N/A {
2N/A struct mod_list *next;
2N/A
2N/A next = head->next;
2N/A free ((void *) head->name);
2N/A free (head);
2N/A head = next;
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Afree_dep_list (struct dep_list *head)
2N/A{
2N/A while (head)
2N/A {
2N/A struct dep_list *next;
2N/A
2N/A next = head->next;
2N/A free ((void *) head->name);
2N/A free_mod_list (head->list);
2N/A free (head);
2N/A head = next;
2N/A }
2N/A}
2N/A
2N/A/* Read the list of dependencies. */
2N/Astatic struct dep_list *
2N/Aread_dep_list (FILE *fp)
2N/A{
2N/A struct dep_list *dep_list = 0;
2N/A
2N/A while (fgets (buf, sizeof (buf), fp))
2N/A {
2N/A char *p;
2N/A struct dep_list *dep;
2N/A
2N/A /* Get the target name. */
2N/A p = strchr (buf, ':');
2N/A if (! p)
2N/A grub_util_error (_("invalid line format: %s"), buf);
2N/A
2N/A *p++ = '\0';
2N/A
2N/A dep = xmalloc (sizeof (*dep));
2N/A dep->name = xstrdup (buf);
2N/A dep->list = 0;
2N/A
2N/A dep->next = dep_list;
2N/A dep_list = dep;
2N/A
2N/A /* Add dependencies. */
2N/A while (*p)
2N/A {
2N/A struct mod_list *mod;
2N/A char *name;
2N/A
2N/A /* Skip whitespace. */
2N/A while (*p && isspace (*p))
2N/A p++;
2N/A
2N/A if (! *p)
2N/A break;
2N/A
2N/A name = p;
2N/A
2N/A /* Skip non-whitespace. */
2N/A while (*p && ! isspace (*p))
2N/A p++;
2N/A
2N/A *p++ = '\0';
2N/A
2N/A mod = (struct mod_list *) xmalloc (sizeof (*mod));
2N/A mod->name = xstrdup (name);
2N/A mod->next = dep->list;
2N/A dep->list = mod;
2N/A }
2N/A }
2N/A
2N/A return dep_list;
2N/A}
2N/A
2N/Astatic char *
2N/Aget_module_name (const char *str)
2N/A{
2N/A char *base;
2N/A char *ext;
2N/A
2N/A base = strrchr (str, '/');
2N/A if (! base)
2N/A base = (char *) str;
2N/A else
2N/A base++;
2N/A
2N/A ext = strrchr (base, '.');
2N/A if (ext && strcmp (ext, ".mod") == 0)
2N/A {
2N/A char *name;
2N/A
2N/A name = xmalloc (ext - base + 1);
2N/A memcpy (name, base, ext - base);
2N/A name[ext - base] = '\0';
2N/A return name;
2N/A }
2N/A
2N/A return xstrdup (base);
2N/A}
2N/A
2N/Astatic char *
2N/Aget_module_path (const char *prefix, const char *str)
2N/A{
2N/A char *dir;
2N/A char *base;
2N/A char *ext;
2N/A char *ret;
2N/A
2N/A ext = strrchr (str, '.');
2N/A if (ext && strcmp (ext, ".mod") == 0)
2N/A base = xstrdup (str);
2N/A else
2N/A {
2N/A base = xmalloc (strlen (str) + 4 + 1);
2N/A sprintf (base, "%s.mod", str);
2N/A }
2N/A
2N/A dir = strchr (str, '/');
2N/A if (dir)
2N/A return base;
2N/A
2N/A ret = grub_util_get_path (prefix, base);
2N/A free (base);
2N/A return ret;
2N/A}
2N/A
2N/Astatic void
2N/Aadd_module (const char *dir,
2N/A struct dep_list *dep_list,
2N/A struct mod_list **mod_head,
2N/A struct grub_util_path_list **path_head,
2N/A const char *name)
2N/A{
2N/A char *mod_name;
2N/A struct grub_util_path_list *path;
2N/A struct mod_list *mod;
2N/A struct dep_list *dep;
2N/A
2N/A mod_name = get_module_name (name);
2N/A
2N/A /* Check if the module has already been added. */
2N/A for (mod = *mod_head; mod; mod = mod->next)
2N/A if (strcmp (mod->name, mod_name) == 0)
2N/A {
2N/A free (mod_name);
2N/A return;
2N/A }
2N/A
2N/A /* Resolve dependencies. */
2N/A for (dep = dep_list; dep; dep = dep->next)
2N/A if (strcmp (dep->name, mod_name) == 0)
2N/A {
2N/A for (mod = dep->list; mod; mod = mod->next)
2N/A add_module (dir, dep_list, mod_head, path_head, mod->name);
2N/A
2N/A break;
2N/A }
2N/A
2N/A /* Add this module. */
2N/A mod = (struct mod_list *) xmalloc (sizeof (*mod));
2N/A mod->name = mod_name;
2N/A mod->next = *mod_head;
2N/A *mod_head = mod;
2N/A
2N/A /* Add this path. */
2N/A path = (struct grub_util_path_list *) xmalloc (sizeof (*path));
2N/A path->name = get_module_path (dir, name);
2N/A path->next = *path_head;
2N/A *path_head = path;
2N/A}
2N/A
2N/Astruct grub_util_path_list *
2N/Agrub_util_resolve_dependencies (const char *prefix,
2N/A const char *dep_list_file,
2N/A char *modules[])
2N/A{
2N/A char *path;
2N/A FILE *fp;
2N/A struct dep_list *dep_list;
2N/A struct mod_list *mod_list = 0;
2N/A struct grub_util_path_list *path_list = 0;
2N/A
2N/A path = grub_util_get_path (prefix, dep_list_file);
2N/A fp = fopen (path, "r");
2N/A if (! fp)
2N/A grub_util_error (_("cannot open %s"), path);
2N/A
2N/A free (path);
2N/A dep_list = read_dep_list (fp);
2N/A fclose (fp);
2N/A
2N/A while (*modules)
2N/A {
2N/A add_module (prefix, dep_list, &mod_list, &path_list, *modules);
2N/A modules++;
2N/A }
2N/A
2N/A free_dep_list (dep_list);
2N/A free_mod_list (mod_list);
2N/A
2N/A { /* Reverse the path_list */
2N/A struct grub_util_path_list *p, *prev, *next;
2N/A
2N/A for (p = path_list, prev = NULL; p; p = next)
2N/A {
2N/A next = p->next;
2N/A p->next = prev;
2N/A prev = p;
2N/A }
2N/A
2N/A return prev;
2N/A }
2N/A}