/*
 *  Make GRUB rescue image
 *
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010  Free Software Foundation, Inc.
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <config.h>

#include <grub/util/install.h>
#include <grub/util/misc.h>
#include <grub/emu/exec.h>
#include <grub/emu/config.h>
#include <grub/emu/hostdisk.h>
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
#pragma GCC diagnostic ignored "-Wmissing-declarations"
#include <argp.h>
#pragma GCC diagnostic error "-Wmissing-prototypes"
#pragma GCC diagnostic error "-Wmissing-declarations"

#include <sys/types.h>
#include <sys/wait.h>

#include <string.h>
#include <time.h>

static char *source_dirs[GRUB_INSTALL_PLATFORM_MAX];
static char *rom_directory;
static char *label_font;
static char *label_color;
static char *label_bgcolor;
static char *product_name;
static char *product_version;
static char *output_image;
static char *xorriso;
static char *boot_grub;
static int xorriso_argc;
static int xorriso_arg_alloc;
static char **xorriso_argv;
static char *iso_uuid;
static char *iso9660_dir;

static void
xorriso_push (const char *val)
{
  if (xorriso_arg_alloc <= xorriso_argc + 1)
    {
      xorriso_arg_alloc = 2 * (4 + xorriso_argc);
      xorriso_argv = xrealloc (xorriso_argv,
			       sizeof (xorriso_argv[0])
			       * xorriso_arg_alloc);
    }
  xorriso_argv[xorriso_argc++] = xstrdup (val);
}

static void
xorriso_link (const char *from, const char *to)
{
  char *tof = grub_util_path_concat (2, iso9660_dir, to);
  char *val = xasprintf ("%s=%s", from, tof);
  xorriso_push (val);
  free (val);
  free (tof);
}

enum
  {
    OPTION_OUTPUT = 'o',
    OPTION_ROM_DIRECTORY = 0x301,
    OPTION_XORRISO,
    OPTION_GLUE_EFI,
    OPTION_RENDER_LABEL,
    OPTION_LABEL_FONT,
    OPTION_LABEL_COLOR,
    OPTION_LABEL_BGCOLOR,
    OPTION_PRODUCT_NAME,
    OPTION_PRODUCT_VERSION,
    OPTION_SPARC_BOOT,
    OPTION_ARCS_BOOT
  };

static struct argp_option options[] = {
  GRUB_INSTALL_OPTIONS,
  {"output", 'o', N_("FILE"),
   0, N_("save output in FILE [required]"), 2},
  {"rom-directory", OPTION_ROM_DIRECTORY, N_("DIR"),
   0, N_("save ROM images in DIR [optional]"), 2},
  {"xorriso", OPTION_XORRISO, N_("FILE"),
   /* TRANSLATORS: xorriso is a program for creating ISOs and burning CDs.  */
   0, N_("use FILE as xorriso [optional]"), 2},
  {"grub-glue-efi", OPTION_GLUE_EFI, N_("FILE"), OPTION_HIDDEN, 0, 2},
  {"grub-render-label", OPTION_RENDER_LABEL, N_("FILE"), OPTION_HIDDEN, 0, 2},
  {"label-font", OPTION_LABEL_FONT, N_("FILE"), 0, N_("use FILE as font for label"), 2},
  {"label-color", OPTION_LABEL_COLOR, N_("COLOR"), 0, N_("use COLOR for label"), 2},
  {"label-bgcolor", OPTION_LABEL_BGCOLOR, N_("COLOR"), 0, N_("use COLOR for label background"), 2},
  {"product-name", OPTION_PRODUCT_NAME, N_("STRING"), 0, N_("use STRING as product name"), 2},
  {"product-version", OPTION_PRODUCT_VERSION, N_("STRING"), 0, N_("use STRING as product version"), 2},
  {"sparc-boot", OPTION_SPARC_BOOT, 0, 0, N_("enable sparc boot. Disables HFS+, APM, ARCS and boot as disk image for i386-pc"), 2},
  {"arcs-boot", OPTION_ARCS_BOOT, 0, 0, N_("enable ARCS (big-endian mips machines, mostly SGI) boot. Disables HFS+, APM, sparc64 and boot as disk image for i386-pc"), 2},
  {0, 0, 0, 0, 0, 0}
};

#pragma GCC diagnostic ignored "-Wformat-nonliteral"

static char *
help_filter (int key, const char *text, void *input __attribute__ ((unused)))
{
  switch (key)
    {
    case ARGP_KEY_HELP_PRE_DOC:
      /* TRANSLATORS: it generates one single image which is bootable through any method. */
      return strdup (_("Make GRUB CD-ROM, disk, pendrive and floppy bootable image."));
    case ARGP_KEY_HELP_POST_DOC:
      {
	char *p1, *out;

	p1 = xasprintf (_("Generates a bootable CD/USB/floppy image.  Arguments other than options to this program"
      " are passed to xorriso, and indicate source files, source directories, or any of the "
      "mkisofs options listed by the output of `%s'."), "xorriso -as mkisofs -help");
	out = xasprintf ("%s\n\n%s\n\n%s", p1,
	  _("Option -- switches to native xorriso command mode."),
	  _("Mail xorriso support requests to <bug-xorriso@gnu.org>."));
	free (p1);
	return out;
      }
    default:
      return grub_install_help_filter (key, text, input);
    }
}

#pragma GCC diagnostic error "-Wformat-nonliteral"

enum {
  SYS_AREA_AUTO,
  SYS_AREA_COMMON,
  SYS_AREA_SPARC,
  SYS_AREA_ARCS
} system_area = SYS_AREA_AUTO;

static error_t 
argp_parser (int key, char *arg, struct argp_state *state)
{
  if (grub_install_parse (key, arg))
    return 0;
  switch (key)
    {
    case OPTION_OUTPUT:
      free (output_image);
      output_image = xstrdup (arg);
      return 0;
    case OPTION_ROM_DIRECTORY:
      free (rom_directory);
      rom_directory = xstrdup (arg);
      return 0;

      /*
       FIXME:
    # Intentionally undocumented
    --grub-mkimage-extra)
	mkimage_extra_arg="$mkimage_extra_arg `argument $option "$@"`"; shift ;;
    --grub-mkimage-extra=*)
	mkimage_extra_arg="$mkimage_extra_arg `echo "$option" | sed 's/--grub-mkimage-extra=//'`" ;;
      */
    case OPTION_SPARC_BOOT:
      system_area = SYS_AREA_SPARC;
      return 0;
    case OPTION_ARCS_BOOT:
      system_area = SYS_AREA_ARCS;
      return 0;
    case OPTION_PRODUCT_NAME:
      free (product_name);
      product_name = xstrdup (arg);
      return 0;
    case OPTION_PRODUCT_VERSION:
      free (product_version);
      product_version = xstrdup (arg);
      return 0;
      /* Accept and ignore for compatibility.  */
    case OPTION_GLUE_EFI:
    case OPTION_RENDER_LABEL:
      return 0;
    case OPTION_LABEL_FONT:
      free (label_font);
      label_font = xstrdup (arg);
      return 0;

    case OPTION_LABEL_COLOR:
      free (label_color);
      label_color = xstrdup (arg);
      return 0;

    case OPTION_LABEL_BGCOLOR:
      free (label_bgcolor);
      label_bgcolor = xstrdup (arg);
      return 0;

    case OPTION_XORRISO:
      free (xorriso);
      xorriso = xstrdup (arg);
      return 0;

    default:
      return ARGP_ERR_UNKNOWN;
    }
}

struct argp argp = {
  options, argp_parser, N_("[OPTION] SOURCE..."),
  NULL, NULL, help_filter, NULL
};

static void
write_part (FILE *f, const char *srcdir)
{
  FILE *in;
  char *inname = grub_util_path_concat (2, srcdir, "partmap.lst");
  char buf[260];
  in = grub_util_fopen (inname, "rb");
  if (!in)
    return;
  while (fgets (buf, 256, in))
    {
      char *ptr;
      for (ptr = buf + strlen (buf) - 1;
	   ptr >= buf && (*ptr == '\n' || *ptr == '\r');
	   ptr--);
      ptr[1] = '\0';
      fprintf (f, "insmod %s\n", buf);
    }
  fclose (in);
}

static void
make_image_abs (enum grub_install_plat plat,
		const char *mkimage_target,
		const char *output)
{
  char *load_cfg;
  FILE *load_cfg_f;

  if (!source_dirs[plat])
    return;

  grub_util_info (N_("enabling %s support ..."),
		  mkimage_target);

  load_cfg = grub_util_make_temporary_file ();

  load_cfg_f = grub_util_fopen (load_cfg, "wb");
  fprintf (load_cfg_f, "search --fs-uuid --set=root %s\n", iso_uuid);
  fprintf (load_cfg_f, "set prefix=(${root})/boot/grub\n");

  write_part (load_cfg_f, source_dirs[plat]);
  fclose (load_cfg_f);

  grub_install_push_module ("search");
  grub_install_push_module ("iso9660");
  grub_install_make_image_wrap (source_dirs[plat], "/boot/grub", output,
				0, load_cfg,
				mkimage_target, 0);
  grub_install_pop_module ();
  grub_install_pop_module ();
  grub_util_unlink (load_cfg);
}

static void
make_image (enum grub_install_plat plat,
	    const char *mkimage_target,
	    const char *output_sub)
{
  char *out = grub_util_path_concat (2, boot_grub, output_sub);
  make_image_abs (plat, mkimage_target, out);
  free (out);
}

static void
make_image_fwdisk_abs (enum grub_install_plat plat,
		       const char *mkimage_target,
		       const char *output)
{
  char *load_cfg;
  FILE *load_cfg_f;

  if (!source_dirs[plat])
    return;

  grub_util_info (N_("enabling %s support ..."),
		  mkimage_target);

  load_cfg = grub_util_make_temporary_file ();

  load_cfg_f = grub_util_fopen (load_cfg, "wb");
  write_part (load_cfg_f, source_dirs[plat]);
  fclose (load_cfg_f);

  grub_install_push_module ("iso9660");
  grub_install_make_image_wrap (source_dirs[plat], "()/boot/grub", output,
				0, load_cfg, mkimage_target, 0);
  grub_install_pop_module ();
  grub_util_unlink (load_cfg);
}

static int
check_xorriso (const char *val)
{
  const char *argv[5];
  int fd;
  pid_t pid;
  FILE *mdadm;
  char *buf = NULL;
  size_t len = 0;
  int ret = 0;

  argv[0] = xorriso;
  argv[1] = "-as";
  argv[2] = "mkisofs";
  argv[3] = "-help";
  argv[4] = NULL;

  pid = grub_util_exec_pipe_stderr (argv, &fd);

  if (!pid)
    return 0;

  /* Parent.  Read mdadm's output.  */
  mdadm = fdopen (fd, "r");
  if (! mdadm)
    return 0;

  while (getline (&buf, &len, mdadm) > 0)
    {
      if (grub_strstr (buf, val))
	ret = 1;
    }

  close (fd);
  waitpid (pid, NULL, 0);
  free (buf);
  return ret;
}

static void
make_image_fwdisk (enum grub_install_plat plat,
		   const char *mkimage_target,
		   const char *output_sub)
{
  char *out = grub_util_path_concat (2, boot_grub, output_sub);
  make_image_fwdisk_abs (plat, mkimage_target, out);
  free (out);
}

static int
option_is_end (const struct argp_option *opt)
{
  return !opt->key && !opt->name && !opt->doc && !opt->group;
}


static int
args_to_eat (const char *arg)
{
  int j;

  if (arg[0] != '-')
    return 0;

  if (arg[1] == '-')
    {
      for (j = 0; !option_is_end(&options[j]); j++)
	{
	  size_t len = strlen (options[j].name);
	  if (strncmp (arg + 2, options[j].name, len) == 0)
	    {
	      if (arg[2 + len] == '=')
		return 1;
	      if (arg[2 + len] == '\0' && options[j].arg)
		return 2;
	      if (arg[2 + len] == '\0')
		return 1;
	    }
	}
      if (strcmp (arg, "--help") == 0)
	return 1;
      if (strcmp (arg, "--usage") == 0)
	return 1;
      if (strcmp (arg, "--version") == 0)
	return 1;
      return 0;
    }
  if (arg[2] && arg[3])
    return 0;
  for (j = 0; !option_is_end(&options[j]); j++)
    {
      if (options[j].key > 0 && options[j].key < 128 && arg[1] == options[j].key)
	{
	  if (options[j].arg)
	    return 2;
	  return 1;
	}
      if (arg[1] == '?')
	return 1;
    }
  return 0;
}

int
main (int argc, char *argv[])
{
  char *romdir;
  char *sysarea_img = NULL;
  const char *pkgdatadir;
  int argp_argc;
  char **argp_argv;
  int xorriso_tail_argc;
  char **xorriso_tail_argv;

  grub_util_host_init (&argc, &argv);
  grub_util_disable_fd_syncs ();

  pkgdatadir = grub_util_get_pkgdatadir ();

  product_name = xstrdup (PACKAGE_NAME);
  product_version = xstrdup (PACKAGE_VERSION);
  xorriso = xstrdup ("xorriso");
  label_font = grub_util_path_concat (2, pkgdatadir, "unicode.pf2");

  argp_argv = xmalloc (sizeof (argp_argv[0]) * argc);
  xorriso_tail_argv = xmalloc (sizeof (argp_argv[0]) * argc);

  xorriso_tail_argc = 0;
  /* Program name */
  argp_argv[0] = argv[0];
  argp_argc = 1;

  /* argp doesn't allow us to catch unknwon arguments,
     so catch them before passing to argp
   */
  {
    int i;
    for (i = 1; i < argc; i++)
      {
	if (strcmp (argv[i], "-output") == 0) {
	  argp_argv[argp_argc++] = (char *) "--output";
	  i++;
	  argp_argv[argp_argc++] = argv[i];
	  continue;
	}
	switch (args_to_eat (argv[i]))
	  {
	  case 2:
	    argp_argv[argp_argc++] = argv[i++];
	    /* Fallthrough  */
	  case 1:
	    argp_argv[argp_argc++] = argv[i];
	    break;
	  case 0:
	    xorriso_tail_argv[xorriso_tail_argc++] = argv[i];
	    break;
	  }
      }
  }

  argp_parse (&argp, argp_argc, argp_argv, 0, 0, 0);

  if (!output_image)
    grub_util_error ("%s", _("output file must be specified"));

  grub_init_all ();
  grub_hostfs_init ();
  grub_host_init ();

  xorriso_push (xorriso);
  xorriso_push ("-as");
  xorriso_push ("mkisofs");
  xorriso_push ("-graft-points");
  
  iso9660_dir = grub_util_make_temporary_dir ();
  grub_util_info ("temporary iso9660 dir is `%s'", iso9660_dir);
  boot_grub = grub_util_path_concat (3, iso9660_dir, "boot", "grub");
  grub_install_mkdir_p (boot_grub);
  romdir = grub_util_path_concat (2, boot_grub, "roms");
  grub_util_mkdir (romdir);

  if (!grub_install_source_directory)
    {
      const char *pkglibdir = grub_util_get_pkglibdir ();
      enum grub_install_plat plat;

      for (plat = 0; plat < GRUB_INSTALL_PLATFORM_MAX; plat++)
	{
	  char *platdir = grub_util_path_concat (2, pkglibdir,
						 grub_install_get_platform_name (plat));

	  if (!grub_util_is_directory (platdir))
	    {
	      free (platdir);
	      continue;
	    }
	  source_dirs[plat] = platdir;
	  grub_install_copy_files (platdir,
				   boot_grub, plat);
	}
    }
  else
    {
      enum grub_install_plat plat;
      plat = grub_install_get_target (grub_install_source_directory);
      grub_install_copy_files (grub_install_source_directory,
			       boot_grub, plat);
      source_dirs[plat] = xstrdup (grub_install_source_directory);
    }
  if (system_area == SYS_AREA_AUTO || grub_install_source_directory)
    {
      if (source_dirs[GRUB_INSTALL_PLATFORM_I386_PC]
	  || source_dirs[GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275]
	  || source_dirs[GRUB_INSTALL_PLATFORM_I386_EFI]
	  || source_dirs[GRUB_INSTALL_PLATFORM_IA64_EFI]
	  || source_dirs[GRUB_INSTALL_PLATFORM_ARM_EFI]
	  || source_dirs[GRUB_INSTALL_PLATFORM_ARM64_EFI]
	  || source_dirs[GRUB_INSTALL_PLATFORM_X86_64_EFI])
	system_area = SYS_AREA_COMMON;
      else if (source_dirs[GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275])
	system_area = SYS_AREA_SPARC;
      else if (source_dirs[GRUB_INSTALL_PLATFORM_MIPS_ARC])
	system_area = SYS_AREA_ARCS;
    }

  /* obtain date-based UUID.  */
  {
    time_t tim;
    struct tm *tmm;
    tim = time (NULL);
    tmm = gmtime (&tim);
    iso_uuid = xmalloc (55);
    grub_snprintf (iso_uuid, 50,
		   "%04d-%02d-%02d-%02d-%02d-%02d-00",
		   tmm->tm_year + 1900,
		   tmm->tm_mon + 1,
		   tmm->tm_mday,
		   tmm->tm_hour,
		   tmm->tm_min,
		   tmm->tm_sec);
  }
  {
    char *uuid_out = xmalloc (strlen (iso_uuid) + 1 + 40);
    char *optr;
    const char *iptr;
    optr = grub_stpcpy (uuid_out, "--modification-date=");
    for (iptr = iso_uuid; *iptr; iptr++)
      if (*iptr != '-')
	*optr++ = *iptr;
    *optr = '\0';
    xorriso_push (uuid_out);
    free (uuid_out);
  }

  /* build BIOS core.img.  */
  if (source_dirs[GRUB_INSTALL_PLATFORM_I386_PC])
    {
      char *load_cfg;
      FILE *load_cfg_f;
      char *output = grub_util_path_concat (3, boot_grub, "i386-pc", "eltorito.img");
      load_cfg = grub_util_make_temporary_file ();

      grub_util_info (N_("enabling %s support ..."), "BIOS");
      load_cfg_f = grub_util_fopen (load_cfg, "wb");
      write_part (load_cfg_f, source_dirs[GRUB_INSTALL_PLATFORM_I386_PC]);
      fclose (load_cfg_f);

      grub_install_push_module ("biosdisk");
      grub_install_push_module ("iso9660");
      grub_install_make_image_wrap (source_dirs[GRUB_INSTALL_PLATFORM_I386_PC],
				    "/boot/grub", output,
				    0, load_cfg,
				    "i386-pc-eltorito", 0);

      xorriso_push ("-b");
      xorriso_push ("boot/grub/i386-pc/eltorito.img");
      xorriso_push ("-no-emul-boot");
      xorriso_push ("-boot-load-size");
      xorriso_push ("4");
      xorriso_push ("-boot-info-table");
      if (system_area == SYS_AREA_COMMON)
	{
	  if (check_xorriso ("grub2-boot-info"))
	    {
	      char *boot_hybrid = grub_util_path_concat (2, source_dirs[GRUB_INSTALL_PLATFORM_I386_PC],
							 "boot_hybrid.img");
	      xorriso_push ("--grub2-boot-info");
	      xorriso_push ("--grub2-mbr");
	      xorriso_push (boot_hybrid);
	    }
	  else
	    {
	      FILE *sa, *bi;
	      size_t sz;
	      char buf[512];
	      char *bin = grub_util_path_concat (2, source_dirs[GRUB_INSTALL_PLATFORM_I386_PC],
						 "boot.img");
	      grub_util_warn ("%s", _("Your xorriso doesn't support `--grub2-boot-info'. Some features are disabled. Please use xorriso 1.2.9 or later."));
	      sysarea_img = grub_util_make_temporary_file ();
	      sa = grub_util_fopen (sysarea_img, "wb");
	      if (!sa)
		grub_util_error (_("cannot open `%s': %s"), sysarea_img,
				 strerror (errno));
	      bi = grub_util_fopen (bin, "rb");
	      if (!bi)
		grub_util_error (_("cannot open `%s': %s"), bin,
				 strerror (errno));
	      if (fread (buf, 1, 512, bi) != 512)
		grub_util_error (_("cannot read `%s': %s"), bin,
				 strerror (errno));
	      fclose (bi);
	      fwrite (buf, 1, 512, sa);
	      
	      grub_install_make_image_wrap_file (source_dirs[GRUB_INSTALL_PLATFORM_I386_PC],
						 "/boot/grub", sa, sysarea_img,
						 0, load_cfg,
						 "i386-pc", 0);
	      sz = ftello (sa);
	      fflush (sa);
	      grub_util_fd_sync (fileno (sa));
	      fclose (sa);
	      
	      if (sz > 32768)
		{
		  grub_util_warn ("%s", _("Your xorriso doesn't support `--grub2-boot-info'. Your core image is too big. Boot as disk is disabled. Please use xorriso 1.2.9 or later."));
		}
	      else
		{
		  xorriso_push ("-G");
		  xorriso_push (sysarea_img);
		}
	    }
	}
      grub_install_pop_module ();
      grub_install_pop_module ();
      grub_util_unlink (load_cfg);
    }

  /** build multiboot core.img */
  grub_install_push_module ("pata");
  grub_install_push_module ("ahci");
  grub_install_push_module ("at_keyboard");
  make_image (GRUB_INSTALL_PLATFORM_I386_MULTIBOOT, "i386-multiboot", "i386-multiboot/core.elf");
  grub_install_pop_module ();
  grub_install_pop_module ();
  grub_install_pop_module ();

  make_image_fwdisk (GRUB_INSTALL_PLATFORM_I386_IEEE1275, "i386-ieee1275", "ofwx86.elf");

  char *core_services = NULL;

  if (source_dirs[GRUB_INSTALL_PLATFORM_I386_EFI]
      || source_dirs[GRUB_INSTALL_PLATFORM_X86_64_EFI]
      || source_dirs[GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275])
    {
      char *mach_ker, *sv, *label, *label_text;
      FILE *f;
      core_services = grub_util_path_concat (4, iso9660_dir, "System", "Library", "CoreServices");
      grub_install_mkdir_p (core_services);

      mach_ker = grub_util_path_concat (2, iso9660_dir, "mach_kernel");
      f = grub_util_fopen (mach_ker, "wb");
      fclose (f);
      free (mach_ker);

      sv = grub_util_path_concat (2, core_services, "SystemVersion.plist");
      f = grub_util_fopen (sv, "wb");
      fprintf (f, "<plist version=\"1.0\">\n"
	       "<dict>\n"
	       "        <key>ProductBuildVersion</key>\n"
	       "        <string></string>\n"
	       "        <key>ProductName</key>\n"
	       "        <string>%s</string>\n"
	       "        <key>ProductVersion</key>\n"
	       "        <string>%s</string>\n"
	       "</dict>\n"
	       "</plist>\n", product_name, product_version);
      fclose (f);
      free (sv);
      label = grub_util_path_concat (2, core_services, ".disk_label");
      char *label_string = xasprintf ("%s %s", product_name, product_version);
      grub_util_render_label (label_font, label_bgcolor ? : "white",
			      label_color ? : "black", label_string, label);
      free (label);
      label_text = grub_util_path_concat (2, core_services, ".disk_label.contentDetails");
      f = grub_util_fopen (label_text, "wb");
      fprintf (f, "%s\n", label_string);
      fclose (f);
      free (label_string);
      free (label_text);
      if (system_area == SYS_AREA_COMMON)
	{
	  xorriso_push ("-hfsplus");
	  xorriso_push ("-apm-block-size");
	  xorriso_push ("2048");
	  xorriso_push ("-hfsplus-file-creator-type");
	  xorriso_push ("chrp");
	  xorriso_push ("tbxj");
	  xorriso_push ("/System/Library/CoreServices/.disk_label");

	  if (source_dirs[GRUB_INSTALL_PLATFORM_I386_EFI]
	      || source_dirs[GRUB_INSTALL_PLATFORM_X86_64_EFI])
	    {
	      xorriso_push ("-hfs-bless-by");
	      xorriso_push ("i");
	      xorriso_push ("/System/Library/CoreServices/boot.efi");
	    }
	}
    }

  if (source_dirs[GRUB_INSTALL_PLATFORM_I386_EFI]
      || source_dirs[GRUB_INSTALL_PLATFORM_X86_64_EFI]
      || source_dirs[GRUB_INSTALL_PLATFORM_IA64_EFI]
      || source_dirs[GRUB_INSTALL_PLATFORM_ARM_EFI]
      || source_dirs[GRUB_INSTALL_PLATFORM_ARM64_EFI])
    {
      char *efidir = grub_util_make_temporary_dir ();
      char *efidir_efi = grub_util_path_concat (2, efidir, "efi");
      char *efidir_efi_boot = grub_util_path_concat (3, efidir, "efi", "boot");
      char *imgname, *img32, *img64, *img_mac = NULL;
      char *efiimgfat;
      grub_install_mkdir_p (efidir_efi_boot);

      grub_install_push_module ("part_gpt");
      grub_install_push_module ("part_msdos");

      imgname = grub_util_path_concat (2, efidir_efi_boot, "bootia64.efi");
      make_image_fwdisk_abs (GRUB_INSTALL_PLATFORM_IA64_EFI, "ia64-efi", imgname);
      free (imgname);

      grub_install_push_module ("part_apple");
      img64 = grub_util_path_concat (2, efidir_efi_boot, "bootx64.efi");
      make_image_fwdisk_abs (GRUB_INSTALL_PLATFORM_X86_64_EFI, "x86_64-efi", img64);
      grub_install_pop_module ();

      grub_install_push_module ("part_apple");
      img32 = grub_util_path_concat (2, efidir_efi_boot, "bootia32.efi");
      make_image_fwdisk_abs (GRUB_INSTALL_PLATFORM_I386_EFI, "i386-efi", img32);
      grub_install_pop_module ();

      imgname = grub_util_path_concat (2, efidir_efi_boot, "bootarm.efi");
      make_image_fwdisk_abs (GRUB_INSTALL_PLATFORM_ARM_EFI, "arm-efi", imgname);
      free (imgname);

      imgname = grub_util_path_concat (2, efidir_efi_boot, "bootaa64.efi");
      make_image_fwdisk_abs (GRUB_INSTALL_PLATFORM_ARM64_EFI, "arm64-efi",
			     imgname);
      free (imgname);

      if (source_dirs[GRUB_INSTALL_PLATFORM_I386_EFI])
	{
	  imgname = grub_util_path_concat (2, efidir_efi_boot, "boot.efi");
	  /* For old macs. Suggested by Peter Jones.  */
	  grub_install_copy_file (img32, imgname, 1);
	}

      if (source_dirs[GRUB_INSTALL_PLATFORM_I386_EFI]
	  || source_dirs[GRUB_INSTALL_PLATFORM_X86_64_EFI])
	img_mac = grub_util_path_concat (2, core_services, "boot.efi");

      if (source_dirs[GRUB_INSTALL_PLATFORM_I386_EFI]
	  && source_dirs[GRUB_INSTALL_PLATFORM_X86_64_EFI])
	grub_util_glue_efi (img32, img64, img_mac);
      else if (source_dirs[GRUB_INSTALL_PLATFORM_X86_64_EFI])
	grub_install_copy_file (img64, img_mac, 1);
      else if (source_dirs[GRUB_INSTALL_PLATFORM_I386_EFI])
	grub_install_copy_file (img32, img_mac, 1);

      free (img_mac);
      free (img32);
      free (img64);
      free (efidir_efi_boot);

      efiimgfat = grub_util_path_concat (2, iso9660_dir, "efi.img");
      int rv;
      rv = grub_util_exec ((const char * []) { "mformat", "-C", "-f", "2880", "-L", "16", "-i",
	    efiimgfat, "::", NULL });
      if (rv != 0)
	grub_util_error ("`%s` invocation failed\n", "mformat");
      rv = grub_util_exec ((const char * []) { "mcopy", "-s", "-i", efiimgfat, efidir_efi, "::/", NULL });
      if (rv != 0)
	grub_util_error ("`%s` invocation failed\n", "mformat");
      xorriso_push ("--efi-boot");
      xorriso_push ("efi.img");
      xorriso_push ("-efi-boot-part");
      xorriso_push ("--efi-boot-image");

      grub_util_unlink_recursive (efidir);
      free (efiimgfat);
      free (efidir_efi);
      free (efidir);
      grub_install_pop_module ();
      grub_install_pop_module ();
    }

  grub_install_push_module ("part_apple");
  make_image_fwdisk (GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275, "powerpc-ieee1275", "powerpc-ieee1275/core.elf");
  grub_install_pop_module ();

  if (source_dirs[GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275])
    {
      char *grub_chrp = grub_util_path_concat (2, source_dirs[GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275],
					       "grub.chrp");
      char *bisrc = grub_util_path_concat (2, source_dirs[GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275],
					   "bootinfo.txt");
      char *bootx = grub_util_path_concat (2, core_services, "BootX");
      char *ppc_chrp = grub_util_path_concat (3, iso9660_dir, "ppc", "chrp");
      char *bitgt = grub_util_path_concat (3, iso9660_dir, "ppc", "bootinfo.txt");
      grub_install_copy_file (grub_chrp, bootx, 1);
      grub_install_mkdir_p (ppc_chrp);
      grub_install_copy_file (bisrc, bitgt, 1);
      xorriso_link ("/System/Library/CoreServices/grub.elf", "/boot/grub/powerpc-ieee1275/core.elf");
      xorriso_link ("/boot/grub/powerpc.elf", "/boot/grub/powerpc-ieee1275/core.elf");
      /* FIXME: add PreP */
      if (system_area == SYS_AREA_COMMON)
	{
	  xorriso_push ("-hfsplus-file-creator-type");
	  xorriso_push ("chrp");
	  xorriso_push ("tbxi");
	  xorriso_push ("/System/Library/CoreServices/BootX");
	  xorriso_push ("-hfs-bless-by");
	  xorriso_push ("p");
	  xorriso_push ("/System/Library/CoreServices");
	}
      xorriso_push ("-sysid");
      xorriso_push ("PPC");
    }

  make_image_fwdisk (GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275,
		     "sparc64-ieee1275-cdcore", "sparc64-ieee1275/core.img");

  if (source_dirs[GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275]
      && system_area == SYS_AREA_SPARC)
    {
      char *cdboot;
      FILE *in, *out;
      char buf[512];
      sysarea_img = grub_util_make_temporary_file ();
      cdboot = grub_util_path_concat (2, source_dirs[GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275],
				      "cdboot.img");
      in = grub_util_fopen (cdboot, "rb");
      if (!in)
	grub_util_error (_("cannot open `%s': %s"), cdboot,
			 strerror (errno));
      out = grub_util_fopen (sysarea_img, "wb");
      if (!out)
	grub_util_error (_("cannot open `%s': %s"), sysarea_img,
			 strerror (errno));
      memset (buf, 0, 512);
      fwrite (buf, 1, 512, out);
      if (fread (buf, 1, 512, in) != 512)
	grub_util_error (_("cannot read `%s': %s"), cdboot,
			 strerror (errno));
      fwrite (buf, 1, 512, out);
      fclose (in);
      fclose (out);
      xorriso_push ("-G");
      xorriso_push (sysarea_img);
      xorriso_push ("-B");
      xorriso_push (",");
      xorriso_push ("--grub2-sparc-core");
      xorriso_push ("/boot/grub/sparc64-ieee1275/core.img");
    }

  make_image_fwdisk (GRUB_INSTALL_PLATFORM_MIPS_ARC, "mips-arc", "mips-arc/core.img");

  if (source_dirs[GRUB_INSTALL_PLATFORM_MIPS_ARC])
    {
      xorriso_link ("/boot/grub/mips-arc/grub", "/boot/grub/mips-arc/core.img");
      xorriso_link ("/boot/grub/mips-arc/sashARCS", "/boot/grub/mips-arc/core.img");
      xorriso_link ("/boot/grub/mips-arc/sash", "/boot/grub/mips-arc/core.img");
    }
  if (source_dirs[GRUB_INSTALL_PLATFORM_MIPS_ARC] && system_area == SYS_AREA_ARCS)
    {
      xorriso_push ("-mips-boot");
      xorriso_push ("/boot/grub/mips-arc/sashARCS");
      xorriso_push ("-mips-boot");
      xorriso_push ("/boot/grub/mips-arc/sash");
      xorriso_push ("-mips-boot");
      xorriso_push ("/boot/grub/mips-arc/grub");
    }

  make_image_fwdisk (GRUB_INSTALL_PLATFORM_MIPSEL_ARC, "mipsel-arc", "arc.exe");

  grub_install_push_module ("pata");
  make_image (GRUB_INSTALL_PLATFORM_MIPSEL_QEMU_MIPS, "mipsel-qemu_mips-elf", "roms/mipsel-qemu_mips.elf");

  make_image (GRUB_INSTALL_PLATFORM_MIPSEL_LOONGSON, "mipsel-loongson-elf", "loongson.elf");

  make_image (GRUB_INSTALL_PLATFORM_MIPSEL_LOONGSON, "mipsel-yeeloong-flash", "mipsel-yeeloong.bin");
  make_image (GRUB_INSTALL_PLATFORM_MIPSEL_LOONGSON, "mipsel-fuloong2f-flash", "mipsel-fuloong2f.bin");

  make_image (GRUB_INSTALL_PLATFORM_MIPS_QEMU_MIPS, "mips-qemu_mips-elf", "roms/mips-qemu_mips.elf");

  grub_install_push_module ("at_keyboard");

  make_image (GRUB_INSTALL_PLATFORM_I386_QEMU, "i386-qemu", "roms/qemu.img");

  grub_install_push_module ("ahci");

  make_image (GRUB_INSTALL_PLATFORM_I386_COREBOOT, "i386-coreboot", "roms/coreboot.elf");
  grub_install_pop_module ();
  grub_install_pop_module ();
  grub_install_pop_module ();

  if (rom_directory)
    {
      const struct
      {
	enum grub_install_plat plat;
	const char *from, *to;
      } roms[] =
	  {
	    {GRUB_INSTALL_PLATFORM_MIPSEL_QEMU_MIPS, "roms/mipsel-qemu_mips.elf", "mipsel-qemu_mips.elf"},
	    {GRUB_INSTALL_PLATFORM_MIPSEL_LOONGSON, "loongson.elf", "mipsel-loongson.elf"},
	    {GRUB_INSTALL_PLATFORM_MIPSEL_LOONGSON, "roms/mipsel-yeeloong.bin", "mipsel-yeeloong.bin"},
	    {GRUB_INSTALL_PLATFORM_MIPSEL_LOONGSON, "roms/mipsel-fulong.bin", "mipsel-fulong.bin"},
	    {GRUB_INSTALL_PLATFORM_MIPS_QEMU_MIPS, "roms/mips-qemu_mips.elf", "mips-qemu_mips.elf"},
	    {GRUB_INSTALL_PLATFORM_I386_QEMU, "roms/qemu.img", "qemu.img"},
	    {GRUB_INSTALL_PLATFORM_I386_COREBOOT, "roms/coreboot.elf", "coreboot.elf"},
	  };
      grub_size_t i;
      for (i = 0; i < ARRAY_SIZE (roms); i++)
	{
	  char *from = grub_util_path_concat (2, boot_grub, roms[i].from);
	  char *to = grub_util_path_concat (2, rom_directory, roms[i].to);
	  grub_install_copy_file (from, to, 0);
	}
    }

  xorriso_push ("--protective-msdos-label");
  xorriso_push ("-o");
  xorriso_push (output_image);
  xorriso_push ("-r");
  xorriso_push (iso9660_dir);
  xorriso_push ("--sort-weight");
  xorriso_push ("0");
  xorriso_push ("/");
  xorriso_push ("--sort-weight");
  xorriso_push ("1");
  xorriso_push ("/boot");
  int i;
  for (i = 0; i < xorriso_tail_argc; i++)
    xorriso_push (xorriso_tail_argv[i]);

  xorriso_argv[xorriso_argc] = NULL;

  grub_util_exec ((const char *const *)xorriso_argv);

  grub_util_unlink_recursive (iso9660_dir);

  if (sysarea_img)
    grub_util_unlink (sysarea_img);

  free (core_services);
  free (romdir);
  return 0;
}
