diff --git a/Makefile.util.def b/Makefile.util.def
index 8742d1583..89e614468 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -660,6 +660,21 @@ program = {
ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
};
+program = {
+ testcase;
+ name = printf_test;
+ common = tests/printf_unit_test.c;
+ common = tests/lib/unit_test.c;
+ common = grub-core/kern/list.c;
+ common = grub-core/kern/misc.c;
+ common = grub-core/tests/lib/test.c;
+ ldadd = libgrubmods.a;
+ ldadd = libgrubgcry.a;
+ ldadd = libgrubkern.a;
+ ldadd = grub-core/gnulib/libgnu.a;
+ ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
+};
+
program = {
name = grub-menulst2cfg;
mansection = 1;
diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
index 8dd7f453f..128139dc8 100644
--- a/grub-core/kern/misc.c
+++ b/grub-core/kern/misc.c
@@ -637,10 +637,13 @@ grub_lltoa (char *str, int c, unsigned long long n)
}
static int
-grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt, va_list args)
+grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, va_list args_in)
{
char c;
+ grub_size_t n = 0;
grub_size_t count = 0;
+ grub_size_t count_args = 0;
+ const char *fmt;
auto void write_char (unsigned char ch);
auto void write_str (const char *s);
auto void write_fill (const char ch, int n);
@@ -659,209 +662,362 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt, va_list ar
write_char (*s++);
}
- void write_fill (const char ch, int n)
+ void write_fill (const char ch, int count_fill)
{
int i;
- for (i = 0; i < n; i++)
+ for (i = 0; i < count_fill; i++)
write_char (ch);
}
+ fmt = fmt0;
while ((c = *fmt++) != 0)
{
if (c != '%')
- write_char (c);
- else
+ continue;
+
+ if (*fmt && *fmt =='-')
+ fmt++;
+
+ while (*fmt && grub_isdigit (*fmt))
+ fmt++;
+
+ if (*fmt && *fmt == '$')
+ fmt++;
+
+ if (*fmt && *fmt =='-')
+ fmt++;
+
+ while (*fmt && grub_isdigit (*fmt))
+ fmt++;
+
+ if (*fmt && *fmt =='.')
+ fmt++;
+
+ while (*fmt && grub_isdigit (*fmt))
+ fmt++;
+
+ c = *fmt++;
+ if (c == 'l')
{
- char tmp[32];
- char *p;
- unsigned int format1 = 0;
- unsigned int format2 = ~ 0U;
- char zerofill = ' ';
- int rightfill = 0;
- int n;
- int longfmt = 0;
- int longlongfmt = 0;
- int unsig = 0;
+ c = *fmt++;
+ if (c == 'l')
+ c = *fmt++;
+ }
+ switch (c)
+ {
+ case 'p':
+ case 'x':
+ case 'u':
+ case 'd':
+ case 'c':
+ case 'C':
+ case 's':
+ count_args++;
+ break;
+ }
+ }
- if (*fmt && *fmt =='-')
+ enum { INT, WCHAR, LONG, LONGLONG, POINTER } types[count_args];
+ union
+ {
+ int i;
+ grub_uint32_t w;
+ long l;
+ long long ll;
+ void *p;
+ } args[count_args];
+
+ grub_memset (types, 0, sizeof (types));
+
+ fmt = fmt0;
+ n = 0;
+ while ((c = *fmt++) != 0)
+ {
+ int longfmt = 0;
+ int longlongfmt = 0;
+ grub_size_t curn;
+ const char *p;
+
+ if (c != '%')
+ continue;
+
+ curn = n++;
+
+ if (*fmt && *fmt =='-')
+ fmt++;
+
+ while (*fmt && grub_isdigit (*fmt))
+ fmt++;
+
+ p = fmt;
+
+ if (*fmt && *fmt == '$')
+ {
+ curn = grub_strtoull (p, 0, 10) - 1;
+ fmt++;
+ }
+
+ while (*fmt && grub_isdigit (*fmt))
+ fmt++;
+
+ c = *fmt++;
+ if (c == 'l')
+ {
+ c = *fmt++;
+ longfmt = 1;
+ if (c == 'l')
{
- rightfill = 1;
- fmt++;
+ c = *fmt++;
+ longlongfmt = 1;
}
+ }
+ if (curn >= count_args)
+ continue;
+ switch (c)
+ {
+ case 'x':
+ case 'u':
+ case 'd':
+ if (longlongfmt)
+ types[curn] = LONGLONG;
+ else if (longfmt)
+ types[curn] = LONG;
+ else
+ types[curn] = INT;
+ break;
+ case 'p':
+ case 's':
+ types[curn] = POINTER;
+ break;
+ case 'c':
+ types[curn] = INT;
+ break;
+ case 'C':
+ types[curn] = WCHAR;
+ break;
+ }
+ }
- p = (char *) fmt;
- /* Read formatting parameters. */
+ for (n = 0; n < count_args; n++)
+ switch (types[n])
+ {
+ case WCHAR:
+ args[n].w = va_arg (args_in, grub_uint32_t);
+ break;
+ case POINTER:
+ args[n].p = va_arg (args_in, void *);
+ break;
+ case INT:
+ args[n].i = va_arg (args_in, int);
+ break;
+ case LONG:
+ args[n].l = va_arg (args_in, long);
+ break;
+ case LONGLONG:
+ args[n].ll = va_arg (args_in, long long);
+ break;
+ }
+
+ fmt = fmt0;
+
+ n = 0;
+ while ((c = *fmt++) != 0)
+ {
+ char tmp[32];
+ char *p;
+ unsigned int format1 = 0;
+ unsigned int format2 = ~ 0U;
+ char zerofill = ' ';
+ int rightfill = 0;
+ int longfmt = 0;
+ int longlongfmt = 0;
+ int unsig = 0;
+ grub_size_t curn;
+
+ if (c != '%')
+ {
+ write_char (c);
+ continue;
+ }
+
+ curn = n++;
+
+ rescan:;
+
+ if (*fmt && *fmt =='-')
+ {
+ rightfill = 1;
+ fmt++;
+ }
+
+ p = (char *) fmt;
+ /* Read formatting parameters. */
+ while (*p && grub_isdigit (*p))
+ p++;
+
+ if (p > fmt)
+ {
+ char s[p - fmt + 1];
+ grub_strncpy (s, fmt, p - fmt);
+ s[p - fmt] = 0;
+ if (s[0] == '0')
+ zerofill = '0';
+ format1 = grub_strtoul (s, 0, 10);
+ fmt = p;
+ }
+
+ if (*p && *p == '.')
+ {
+ p++;
+ fmt++;
while (*p && grub_isdigit (*p))
p++;
if (p > fmt)
{
- char s[p - fmt + 1];
- grub_strncpy (s, fmt, p - fmt);
- s[p - fmt] = 0;
- if (s[0] == '0')
- zerofill = '0';
- format1 = grub_strtoul (s, 0, 10);
+ char fstr[p - fmt + 1];
+ grub_strncpy (fstr, fmt, p - fmt);
+ fstr[p - fmt] = 0;
+ format2 = grub_strtoul (fstr, 0, 10);
fmt = p;
}
+ }
+ if (*fmt == '$')
+ {
+ curn = format1 - 1;
+ fmt++;
+ format1 = 0;
+ format2 = ~ 0U;
+ zerofill = ' ';
+ rightfill = 0;
- if (*p && *p == '.')
- {
- p++;
- fmt++;
- while (*p && grub_isdigit (*p))
- p++;
-
- if (p > fmt)
- {
- char fstr[p - fmt + 1];
- grub_strncpy (fstr, fmt, p - fmt);
- fstr[p - fmt] = 0;
- format2 = grub_strtoul (fstr, 0, 10);
- fmt = p;
- }
- }
+ goto rescan;
+ }
+ c = *fmt++;
+ if (c == 'l')
+ {
+ longfmt = 1;
c = *fmt++;
if (c == 'l')
{
- longfmt = 1;
+ longlongfmt = 1;
c = *fmt++;
- if (c == 'l')
- {
- longlongfmt = 1;
- c = *fmt++;
- }
}
+ }
- switch (c)
- {
- case 'p':
- write_str ("0x");
- c = 'x';
- longlongfmt |= (sizeof (void *) == sizeof (long long));
- /* Fall through. */
- case 'x':
- case 'u':
- unsig = 1;
- /* Fall through. */
- case 'd':
- if (longlongfmt)
- {
- long long ll;
+ if (curn >= count_args)
+ continue;
- ll = va_arg (args, long long);
- grub_lltoa (tmp, c, ll);
- }
- else if (longfmt && unsig)
- {
- unsigned long l = va_arg (args, unsigned long);
- grub_lltoa (tmp, c, l);
- }
- else if (longfmt)
- {
- long l = va_arg (args, long);
- grub_lltoa (tmp, c, l);
- }
- else if (unsig)
- {
- unsigned u = va_arg (args, unsigned);
- grub_lltoa (tmp, c, u);
- }
- else
- {
- n = va_arg (args, int);
- grub_lltoa (tmp, c, n);
- }
- if (! rightfill && grub_strlen (tmp) < format1)
- write_fill (zerofill, format1 - grub_strlen (tmp));
- write_str (tmp);
- if (rightfill && grub_strlen (tmp) < format1)
- write_fill (zerofill, format1 - grub_strlen (tmp));
- break;
+ switch (c)
+ {
+ case 'p':
+ write_str ("0x");
+ c = 'x';
+ longlongfmt |= (sizeof (void *) == sizeof (long long));
+ /* Fall through. */
+ case 'x':
+ case 'u':
+ unsig = 1;
+ /* Fall through. */
+ case 'd':
+ if (longlongfmt)
+ grub_lltoa (tmp, c, args[curn].ll);
+ else if (longfmt && unsig)
+ grub_lltoa (tmp, c, (unsigned long) args[curn].l);
+ else if (longfmt)
+ grub_lltoa (tmp, c, args[curn].l);
+ else if (unsig)
+ grub_lltoa (tmp, c, (unsigned) args[curn].i);
+ else
+ grub_lltoa (tmp, c, args[curn].i);
+ if (! rightfill && grub_strlen (tmp) < format1)
+ write_fill (zerofill, format1 - grub_strlen (tmp));
+ write_str (tmp);
+ if (rightfill && grub_strlen (tmp) < format1)
+ write_fill (zerofill, format1 - grub_strlen (tmp));
+ break;
- case 'c':
- n = va_arg (args, int);
- write_char (n & 0xff);
- break;
+ case 'c':
+ write_char (args[curn].i & 0xff);
+ break;
- case 'C':
+ case 'C':
+ {
+ grub_uint32_t code = args[curn].w;
+ int shift;
+ unsigned mask;
+
+ if (code <= 0x7f)
{
- grub_uint32_t code = va_arg (args, grub_uint32_t);
- int shift;
- unsigned mask;
-
- if (code <= 0x7f)
- {
- shift = 0;
- mask = 0;
- }
- else if (code <= 0x7ff)
- {
- shift = 6;
- mask = 0xc0;
- }
- else if (code <= 0xffff)
- {
- shift = 12;
- mask = 0xe0;
- }
- else if (code <= 0x1fffff)
- {
- shift = 18;
- mask = 0xf0;
- }
- else if (code <= 0x3ffffff)
- {
- shift = 24;
- mask = 0xf8;
- }
- else if (code <= 0x7fffffff)
- {
- shift = 30;
- mask = 0xfc;
- }
- else
- {
- code = '?';
- shift = 0;
- mask = 0;
- }
-
- write_char (mask | (code >> shift));
-
- for (shift -= 6; shift >= 0; shift -= 6)
- write_char (0x80 | (0x3f & (code >> shift)));
+ shift = 0;
+ mask = 0;
+ }
+ else if (code <= 0x7ff)
+ {
+ shift = 6;
+ mask = 0xc0;
+ }
+ else if (code <= 0xffff)
+ {
+ shift = 12;
+ mask = 0xe0;
+ }
+ else if (code <= 0x1fffff)
+ {
+ shift = 18;
+ mask = 0xf0;
+ }
+ else if (code <= 0x3ffffff)
+ {
+ shift = 24;
+ mask = 0xf8;
+ }
+ else if (code <= 0x7fffffff)
+ {
+ shift = 30;
+ mask = 0xfc;
+ }
+ else
+ {
+ code = '?';
+ shift = 0;
+ mask = 0;
}
- break;
- case 's':
- p = va_arg (args, char *);
- if (p)
- {
- grub_size_t len = 0;
- while (len < format2 && p[len])
- len++;
+ write_char (mask | (code >> shift));
- if (!rightfill && len < format1)
- write_fill (zerofill, format1 - len);
+ for (shift -= 6; shift >= 0; shift -= 6)
+ write_char (0x80 | (0x3f & (code >> shift)));
+ }
+ break;
- grub_size_t i;
- for (i = 0; i < len; i++)
- write_char (*p++);
+ case 's':
+ p = args[curn].p;
+ if (p)
+ {
+ grub_size_t len = 0;
+ while (len < format2 && p[len])
+ len++;
- if (rightfill && len < format1)
- write_fill (zerofill, format1 - len);
- }
- else
- write_str ("(null)");
+ if (!rightfill && len < format1)
+ write_fill (zerofill, format1 - len);
- break;
+ grub_size_t i;
+ for (i = 0; i < len; i++)
+ write_char (*p++);
- default:
- write_char (c);
- break;
+ if (rightfill && len < format1)
+ write_fill (zerofill, format1 - len);
}
+ else
+ write_str ("(null)");
+
+ break;
+
+ default:
+ write_char (c);
+ break;
}
}
diff --git a/tests/printf_unit_test.c b/tests/printf_unit_test.c
new file mode 100644
index 000000000..6e601b401
--- /dev/null
+++ b/tests/printf_unit_test.c
@@ -0,0 +1,43 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2011 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 .
+ */
+
+#include
+#include
+#include
+#include
+
+static void
+printf_test (void)
+{
+ char real[512];
+ char expected[512];
+ grub_snprintf (real, sizeof (real), "%d %d %d", 1, 2, 3);
+ snprintf (expected, sizeof (expected), "%d %d %d", 1, 2, 3);
+ grub_test_assert (strcmp (real, expected) == 0);
+ grub_snprintf (real, sizeof (real), "%3$d %2$d %1$d", 1, 2, 3);
+ snprintf (expected, sizeof (expected), "%3$d %2$d %1$d", 1, 2, 3);
+ grub_test_assert (strcmp (real, expected) == 0);
+ grub_snprintf (real, sizeof (real), "%d %lld %d", 1, 2LL, 3);
+ snprintf (expected, sizeof (expected), "%d %lld %d", 1, 2LL, 3);
+ grub_test_assert (strcmp (real, expected) == 0);
+ grub_snprintf (real, sizeof (real), "%3$d %2$lld %1$d", 1, 2LL, 3);
+ snprintf (expected, sizeof (expected), "%3$d %2$lld %1$d", 1, 2LL, 3);
+ grub_test_assert (strcmp (real, expected) == 0);
+}
+
+GRUB_UNIT_TEST ("printf_unit_test", printf_test);