On 12.02.2016 20:34, Shea Levy wrote:
> OK. Do you have any thoughts on how best to extract the "load the kernel
> and set the command line to a specific string" functionality out of the
> linux command, then?
> 
At first I wanted to write a short skeleton patch to show what I meant
but once skeleton is done, there is little actual code involved. Please
try attached patch
> On 2016-02-12 14:22, Vladimir 'phcoder' Serbinenko wrote:
>> Separate command is better as it keeps interface tidy and unpoluted,
>> decreasing maintenance cost. Correct me if I'm wrong but it should be
>> clear from context of file is Android image or usual linux one?
>>
>> Le ven. 12 févr. 2016 20:19, Shea Levy <s...@shealevy.com> a écrit :
>>
>>> On 2016-02-12 12:50, Vladimir 'φ-coder/phcoder' Serbinenko wrote:
>>> > On 08.02.2016 21:47, Shea Levy wrote:
>>> >> ---
>>> >>  grub-core/loader/i386/linux.c | 27 +++++++++++++++++++++------
>>> >>  1 file changed, 21 insertions(+), 6 deletions(-)
>>> >>
>>> >> diff --git a/grub-core/loader/i386/linux.c
>>> >> b/grub-core/loader/i386/linux.c
>>> >> index fddcc46..6ab8d3c 100644
>>> >> --- a/grub-core/loader/i386/linux.c
>>> >> +++ b/grub-core/loader/i386/linux.c
>>> >> @@ -35,6 +35,7 @@
>>> >>  #include <grub/i18n.h>
>>> >>  #include <grub/lib/cmdline.h>
>>> >>  #include <grub/linux.h>
>>> >> +#include <grub/android_bootimg.h>
>>> >>
>>> >>  GRUB_MOD_LICENSE ("GPLv3+");
>>> >>
>>> >> @@ -695,7 +696,13 @@ grub_cmd_linux (grub_command_t cmd
>>> >> __attribute__ ((unused)),
>>> >>        goto fail;
>>> >>      }
>>> >>
>>> >> -  file = grub_file_open (argv[0]);
>>> >> +  char android_cmdline[BOOT_ARGS_SIZE];
>>> >> +  android_cmdline[0] = '';
>>> >> +  if (grub_memcmp (argv[0], "android_bootimg:", sizeof
>>> >> "android_bootimg:" - 1) == 0)
>>> >> +    grub_android_bootimg_load_kernel (argv[0] + sizeof
>>> >> "android_bootimg:" - 1,
>>> >> +                                      &file, android_cmdline);
>>> >> +  else
>>> >> +      file = grub_file_open (argv[0]);
>>> > I hoped more for autodetection. This gets a bit hairy and proper
>>> > separation is better. Sorry for confusion. I think it's simpler with
>>> > commands like
>>> > android_bootimg [--no-cmdline] [--no-initrd] IMAGE [EXTRA_ARGUMENTS]
>>> > by default it will load both IMAGE, with cmdline and initrd. With
>>> > --no-initrd you can use initrd for custom initrd.
>>>
>>> Autodetection would be possible actually, I didn't think of that. If
>>> grub_file_open fails, we can try grub_android_bootimg_load_kernel on the
>>> same file. Would that be preferable or do we still want a separate
>>> command?
>>>
>>> >
>>> >>    if (! file)
>>> >>      goto fail;
>>> >>
>>> >> @@ -1008,12 +1015,20 @@ grub_cmd_linux (grub_command_t cmd
>>> >> __attribute__ ((unused)),
>>> >>    linux_cmdline = grub_zalloc (maximal_cmdline_size + 1);
>>> >>    if (!linux_cmdline)
>>> >>      goto fail;
>>> >> -  grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
>>> >> +  grub_size_t cmdline_offset = 0;
>>> >> +  if (android_cmdline[0])
>>> >> +    {
>>> >> +      cmdline_offset = grub_strlen (android_cmdline) + 1;
>>> >> +      grub_memcpy (linux_cmdline, android_cmdline, cmdline_offset -
>>> >> 1);
>>> >> +      linux_cmdline[cmdline_offset - 1] = ' ';
>>> >> +    }
>>> >> +  grub_memcpy (linux_cmdline + cmdline_offset, LINUX_IMAGE, sizeof
>>> >> (LINUX_IMAGE));
>>> >> +  cmdline_offset += sizeof LINUX_IMAGE - 1;
>>> > LINUX_IMAGE must be at the beginning. don't forget brackets around
>>> > sizeof.
>>> >>    grub_create_loader_cmdline (argc, argv,
>>> >> -                          linux_cmdline
>>> >> -                          + sizeof (LINUX_IMAGE) - 1,
>>> >> -                          maximal_cmdline_size
>>> >> -                          - (sizeof (LINUX_IMAGE) - 1));
>>> >> +                              linux_cmdline
>>> >> +                              + cmdline_offset,
>>> >> +                              maximal_cmdline_size
>>> >> +                              - cmdline_offset);
>>> >>
>>> >>    len = prot_file_size;
>>> >>    if (grub_file_read (file, prot_mode_mem, len) != len &&
>>> >> !grub_errno)
>>> >>
>>> >
>>> >
>>> >
>>> > _______________________________________________
>>> > Grub-devel mailing list
>>> > Grub-devel@gnu.org
>>> > https://lists.gnu.org/mailman/listinfo/grub-devel [1]
>>>
>>> _______________________________________________
>>> Grub-devel mailing list
>>> Grub-devel@gnu.org
>>> https://lists.gnu.org/mailman/listinfo/grub-devel [1]
>>
>>
>> Links:
>> ------
>> [1] https://lists.gnu.org/mailman/listinfo/grub-devel
>>
>> _______________________________________________
>> Grub-devel mailing list
>> Grub-devel@gnu.org
>> https://lists.gnu.org/mailman/listinfo/grub-devel
> 
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel

diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
index fddcc46..d3870ae 100644
--- a/grub-core/loader/i386/linux.c
+++ b/grub-core/loader/i386/linux.c
@@ -35,6 +35,7 @@
 #include <grub/i18n.h>
 #include <grub/lib/cmdline.h>
 #include <grub/linux.h>
+#include <grub/android.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -673,11 +674,11 @@ grub_linux_unload (void)
   return GRUB_ERR_NONE;
 }
 
+
 static grub_err_t
-grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
-		int argc, char *argv[])
+linux_load (grub_file_t file, grub_off_t fileoffset, grub_off_t filesize,
+	    const char *extra_cmdline, int argc, char *argv[])
 {
-  grub_file_t file = 0;
   struct linux_kernel_header lh;
   grub_uint8_t setup_sects;
   grub_size_t real_size, prot_size, prot_file_size;
@@ -689,15 +690,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
 
   grub_dl_ref (my_mod);
 
-  if (argc == 0)
-    {
-      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
-      goto fail;
-    }
-
-  file = grub_file_open (argv[0]);
-  if (! file)
-    goto fail;
+  grub_file_seek (file, fileoffset);
 
   if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
     {
@@ -757,7 +750,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
     setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
 
   real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
-  prot_file_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
+  prot_file_size = filesize - real_size - GRUB_DISK_SECTOR_SIZE;
 
   if (grub_le_to_cpu16 (lh.version) >= 0x205
       && lh.kernel_alignment != 0
@@ -871,7 +864,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
 
   /* The other parameters are filled when booting.  */
 
-  grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
+  grub_file_seek (file, fileoffset + real_size + GRUB_DISK_SECTOR_SIZE);
 
   grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n",
 		(unsigned) real_size, (unsigned) prot_size);
@@ -1008,12 +1001,32 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
   linux_cmdline = grub_zalloc (maximal_cmdline_size + 1);
   if (!linux_cmdline)
     goto fail;
-  grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
-  grub_create_loader_cmdline (argc, argv,
-			      linux_cmdline
-			      + sizeof (LINUX_IMAGE) - 1,
-			      maximal_cmdline_size
-			      - (sizeof (LINUX_IMAGE) - 1));
+  char *cmdline_ptr = linux_cmdline;
+  char *cmdline_ptr_end = linux_cmdline + maximal_cmdline_size;
+  /* Construct BOOT_IMAGE=/boot/... */
+  cmdline_ptr = grub_stpcpy (cmdline_ptr, LINUX_IMAGE);
+  grub_create_loader_cmdline (1, argv, cmdline_ptr,
+			      cmdline_ptr_end - cmdline_ptr);
+  cmdline_ptr += grub_strlen (cmdline_ptr);
+  /* Extra */
+  if (cmdline_ptr < cmdline_ptr_end - 1 && *extra_cmdline)
+    {
+      *cmdline_ptr++ = ' ';
+      cmdline_ptr = grub_stpncpy (cmdline_ptr, extra_cmdline,
+				  cmdline_ptr_end - cmdline_ptr);
+    }
+
+  /* Rest of command line.  */
+  if (cmdline_ptr < cmdline_ptr_end - 1 && argc > 1)
+    {
+      *cmdline_ptr++ = ' ';
+      grub_create_loader_cmdline (argc - 1, argv + 1, cmdline_ptr,
+				  cmdline_ptr_end - cmdline_ptr);
+    }
+  else
+    {
+      *cmdline_ptr = '\0';
+    }
 
   len = prot_file_size;
   if (grub_file_read (file, prot_mode_mem, len) != len && !grub_errno)
@@ -1029,9 +1042,6 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
 
  fail:
 
-  if (file)
-    grub_file_close (file);
-
   if (grub_errno != GRUB_ERR_NONE)
     {
       grub_dl_unref (my_mod);
@@ -1042,31 +1052,32 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
 }
 
 static grub_err_t
-grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
-		 int argc, char *argv[])
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+		int argc, char *argv[])
 {
-  grub_size_t size = 0, aligned_size = 0;
-  grub_addr_t addr_min, addr_max;
-  grub_addr_t addr;
+  grub_file_t file = 0;
   grub_err_t err;
-  struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
-
   if (argc == 0)
-    {
-      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
-      goto fail;
-    }
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
 
-  if (! loaded)
-    {
-      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
-      goto fail;
-    }
+  file = grub_file_open (argv[0]);
+  if (! file)
+    return grub_errno;
 
-  if (grub_initrd_init (argc, argv, &initrd_ctx))
-    goto fail;
+  err = linux_load (file, 0, grub_file_size (file), "", argc, argv);
+  grub_file_close (file);
+  return err;
+}
+
+static grub_err_t
+load_initrd (struct grub_linux_initrd_context *initrd_ctx, char *filenames[])
+{
+  grub_size_t size = 0, aligned_size = 0;
+  grub_addr_t addr_min, addr_max;
+  grub_addr_t addr;
+  grub_err_t err;
 
-  size = grub_get_initrd_size (&initrd_ctx);
+  size = grub_get_initrd_size (initrd_ctx);
   aligned_size = ALIGN_UP (size, 4096);
 
   /* Get the highest address available for the initrd.  */
@@ -1098,10 +1109,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
   addr = (addr_max - aligned_size) & ~0xFFF;
 
   if (addr < addr_min)
-    {
-      grub_error (GRUB_ERR_OUT_OF_RANGE, "the initrd is too big");
-      goto fail;
-    }
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, "the initrd is too big");
 
   {
     grub_relocator_chunk_t ch;
@@ -1116,8 +1124,9 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
     initrd_mem_target = get_physical_target_address (ch);
   }
 
-  if (grub_initrd_load (&initrd_ctx, argv, initrd_mem))
-    goto fail;
+  err = grub_initrd_load (initrd_ctx, filenames, initrd_mem);
+  if (err)
+    return err;
 
   grub_dprintf ("linux", "Initrd, addr=0x%x, size=0x%x\n",
 		(unsigned) addr, (unsigned) size);
@@ -1126,18 +1135,123 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
   linux_params.ramdisk_size = size;
   linux_params.root_dev = 0x0100; /* XXX */
 
- fail:
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+		 int argc, char *argv[])
+{
+  struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
+  grub_err_t err;
+
+  if (argc == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+  if (! loaded)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
+
+  err = grub_initrd_init (argc, argv, &initrd_ctx);
+
+  if (!err)
+    err = load_initrd (&initrd_ctx, argv);
+
   grub_initrd_close (&initrd_ctx);
 
+  return err;
+}
+
+static grub_err_t
+grub_cmd_android_bootimg (grub_command_t cmd __attribute__ ((unused)),
+			  int argc, char *argv[])
+{
+  grub_file_t file = 0;
+  grub_err_t err  = GRUB_ERR_NONE;
+  int do_load_initrd = 1, do_load_cmdline = 1;
+  struct grub_android_boot_img_hdr boot_img_header;
+  char *cmdline = NULL;
+
+  for (; argc > 0; argc--, argv++)
+    {
+      if (grub_strcmp (argv[0], "--no-cmdline") == 0)
+	{
+	  do_load_cmdline = 0;
+	  continue;
+	}
+      if (grub_strcmp (argv[0], "--no-initrd") == 0)
+	{
+	  do_load_initrd = 0;
+	  continue;
+	}
+      break;
+    }
+      
+  if (argc == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+  file = grub_file_open (argv[0]);
+  if (! file)
+    return grub_errno;
+
+  if (grub_file_read (file, &boot_img_header, sizeof (boot_img_header)) != sizeof (boot_img_header))
+    {
+      if (!grub_errno)
+	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+		    argv[0]);
+      err = grub_errno;
+      goto fail;
+    }
+
+  if (grub_memcmp (boot_img_header.magic, GRUB_ANDROID_BOOT_MAGIC, GRUB_ANDROID_BOOT_MAGIC_SIZE) != 0)
+    {
+      err = grub_error (GRUB_ERR_BAD_OS, "invalid android magic");
+      goto fail;
+    }
+
+  if (do_load_cmdline)
+    {
+      cmdline = grub_malloc (GRUB_ANDROID_BOOT_EXTRA_ARGS_SIZE
+			     + GRUB_ANDROID_BOOT_ARGS_SIZE + 1);
+      if (!cmdline)
+	goto fail;
+      grub_memcpy (cmdline, boot_img_header.cmdline, GRUB_ANDROID_BOOT_ARGS_SIZE);
+      grub_memcpy (cmdline + GRUB_ANDROID_BOOT_ARGS_SIZE, boot_img_header.extra_cmdline, GRUB_ANDROID_BOOT_EXTRA_ARGS_SIZE);
+      cmdline[GRUB_ANDROID_BOOT_EXTRA_ARGS_SIZE + GRUB_ANDROID_BOOT_ARGS_SIZE] = '\0';
+    }
+
+  err = linux_load (file, boot_img_header.page_size, boot_img_header.kernel_size, cmdline ? : "", argc, argv);
+  if (err)
+    goto fail;
+
+  if (do_load_initrd && boot_img_header.ramdisk_size)
+    {
+      struct grub_linux_initrd_component component = {
+	.size = boot_img_header.ramdisk_size,
+	.newc_name = 0,
+	.file = file,
+	.fileoffset = ALIGN_UP (boot_img_header.kernel_size, boot_img_header.page_size)
+      };
+      struct grub_linux_initrd_context initrd_ctx = {
+	.nfiles = 1,
+	.size = boot_img_header.ramdisk_size,
+	.components = &component
+      };
+      err = load_initrd (&initrd_ctx, argv);
+    }
+ fail:
+  grub_file_close (file);
   return grub_errno;
 }
 
-static grub_command_t cmd_linux, cmd_initrd;
+
+static grub_command_t cmd_linux, cmd_initrd, cmd_android_bootimg;
 
 GRUB_MOD_INIT(linux)
 {
   cmd_linux = grub_register_command ("linux", grub_cmd_linux,
 				     0, N_("Load Linux."));
+  cmd_android_bootimg = grub_register_command ("android_bootimg", grub_cmd_android_bootimg,
+					       "[--no-initrd] [--no-cmdline] IMAGE [OPTIONS]", N_("Load Android bootimg."));
   cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
 				      0, N_("Load initrd."));
   my_mod = mod;
@@ -1147,4 +1261,5 @@ GRUB_MOD_FINI(linux)
 {
   grub_unregister_command (cmd_linux);
   grub_unregister_command (cmd_initrd);
+  grub_unregister_command (cmd_android_bootimg);
 }
diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c
index be6fa0f..dea0299 100644
--- a/grub-core/loader/linux.c
+++ b/grub-core/loader/linux.c
@@ -23,13 +23,6 @@ struct newc_head
   char check[8];
 } GRUB_PACKED;
 
-struct grub_linux_initrd_component
-{
-  grub_file_t file;
-  char *newc_name;
-  grub_off_t size;
-};
-
 struct dir
 {
   char *name;
@@ -208,6 +201,7 @@ grub_initrd_init (int argc, char *argv[],
       initrd_ctx->nfiles++;
       initrd_ctx->components[i].size
 	= grub_file_size (initrd_ctx->components[i].file);
+      initrd_ctx->components[i].fileoffset = 0;
       initrd_ctx->size += initrd_ctx->components[i].size;
     }
 
@@ -279,6 +273,8 @@ grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx,
 	}
 
       cursize = initrd_ctx->components[i].size;
+      grub_file_seek (initrd_ctx->components[i].file,
+		      initrd_ctx->components[i].fileoffset);
       if (grub_file_read (initrd_ctx->components[i].file, ptr, cursize)
 	  != cursize)
 	{
diff --git a/include/grub/android.h b/include/grub/android.h
new file mode 100644
index 0000000..7cd25f4
--- /dev/null
+++ b/include/grub/android.h
@@ -0,0 +1,73 @@
+/* tools/mkbootimg/bootimg.h
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+#ifndef _GRUB_ANDROID_BOOT_IMAGE_H_
+#define _GRUB_ANDROID_BOOT_IMAGE_H_
+
+#include <grub/types.h>
+
+#define GRUB_ANDROID_BOOT_MAGIC "ANDROID!"
+#define GRUB_ANDROID_BOOT_MAGIC_SIZE 8
+#define GRUB_ANDROID_BOOT_NAME_SIZE 16
+#define GRUB_ANDROID_BOOT_ARGS_SIZE 512
+#define GRUB_ANDROID_BOOT_EXTRA_ARGS_SIZE 1024
+struct grub_android_boot_img_hdr
+{
+    grub_uint8_t magic[GRUB_ANDROID_BOOT_MAGIC_SIZE];
+    grub_uint32_t kernel_size;  /* size in bytes */
+    grub_uint32_t kernel_addr;  /* physical load addr */
+    grub_uint32_t ramdisk_size; /* size in bytes */
+    grub_uint32_t ramdisk_addr; /* physical load addr */
+    grub_uint32_t second_size;  /* size in bytes */
+    grub_uint32_t second_addr;  /* physical load addr */
+    grub_uint32_t tags_addr;    /* physical addr for kernel tags */
+    grub_uint32_t page_size;    /* flash page size we assume */
+    grub_uint32_t unused[2];    /* future expansion: should be 0 */
+    grub_uint8_t name[GRUB_ANDROID_BOOT_NAME_SIZE]; /* asciiz product name */
+    grub_uint8_t cmdline[GRUB_ANDROID_BOOT_ARGS_SIZE];
+    grub_uint32_t id[8]; /* timestamp / checksum / sha1 / etc */
+    /* Supplemental command line data; kept here to maintain
+     * binary compatibility with older versions of mkbootimg */
+    grub_uint8_t extra_cmdline[GRUB_ANDROID_BOOT_EXTRA_ARGS_SIZE];
+} __attribute__((packed));
+/*
+** +-----------------+ 
+** | boot header     | 1 page
+** +-----------------+
+** | kernel          | n pages  
+** +-----------------+
+** | ramdisk         | m pages  
+** +-----------------+
+** | second stage    | o pages
+** +-----------------+
+**
+** n = (kernel_size + page_size - 1) / page_size
+** m = (ramdisk_size + page_size - 1) / page_size
+** o = (second_size + page_size - 1) / page_size
+**
+** 0. all entities are page_size aligned in flash
+** 1. kernel and ramdisk are required (size != 0)
+** 2. second is optional (second_size == 0 -> no second)
+** 3. load each element (kernel, ramdisk, second) at
+**    the specified physical address (kernel_addr, etc)
+** 4. prepare tags at tag_addr.  kernel_args[] is
+**    appended to the kernel commandline in the tags.
+** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+** 6. if second_size != 0: jump to second_addr
+**    else: jump to kernel_addr
+*/
+
+#endif
diff --git a/include/grub/linux.h b/include/grub/linux.h
index 594a3f3..4a99d78 100644
--- a/include/grub/linux.h
+++ b/include/grub/linux.h
@@ -9,6 +9,14 @@ struct grub_linux_initrd_context
   grub_size_t size;
 };
 
+struct grub_linux_initrd_component
+{
+  grub_file_t file;
+  char *newc_name;
+  grub_off_t fileoffset;
+  grub_off_t size;
+};
+
 grub_err_t
 grub_initrd_init (int argc, char *argv[],
 		  struct grub_linux_initrd_context *ctx);
diff --git a/include/grub/misc.h b/include/grub/misc.h
index 2a9f87c..ef9c9b6 100644
--- a/include/grub/misc.h
+++ b/include/grub/misc.h
@@ -64,6 +64,17 @@ grub_stpcpy (char *dest, const char *src)
   return d - 1;
 }
 
+static inline char *
+grub_stpncpy (char *dest, const char *src, int c)
+{
+  char *p = dest;
+
+  while ((*p++ = *src++) != '\0' && --c)
+    ;
+
+  return p - 1;
+}
+
 /* XXX: If grub_memmove is too slow, we must implement grub_memcpy.  */
 static inline void *
 grub_memcpy (void *dest, const void *src, grub_size_t n)

Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to