
2017-10-21  Florian Weimer  <fweimer@redhat.com>

	Add array_length and array_end macros.
	* include/array_length.h: New file.
	* stdio-common/bug16.c (do_test): Use array_length.
	* stdio-common/errlist.c (_sys_nerr): Likewise.
	* stdio-common/printf_fp.c (PRINTF_FP_FETCH): Likewise.
	* stdio-common/printf_fphex.c (__printf_fphex): Use array_end.
	* stdio-common/psiginfo.c (psiginfo): Use array_length.
	* stdio-common/test-vfprintf.c (nlocs): Remove definition.
	(do_test): Use array_length.
	* stdio-common/tst-fphex.c (do_test): Use array_end, array_length.
	* stdio-common/tst-long-dbl-fphex.c (do_test): Use array_length.
	* stdio-common/tst-printf-round.c (do_test): Likewise.
	* stdio-common/tst-swprintf.c (nbuf): Remove definition.
	(CHECK): Use array_length.
	* stdio-common/tstdiomisc.c (t3, F): Likewise.
	* stdio-common/tstscanf.c (main): Likewise.
	* stdio-common/vfprintf.c (process_string_arg): Likewise.

diff --git a/include/array_length.h b/include/array_length.h
new file mode 100644
index 0000000000..b1dca0f24b
--- /dev/null
+++ b/include/array_length.h
@@ -0,0 +1,36 @@
+/* The array_length macro.
+   Copyright (C) 1991-2017 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _ARRAY_LENGTH_H
+#define _ARRAY_LENGTH_H
+
+/* array_length (VAR) is the number of elements in the array VAR.  VAR
+   must evaluate to an array, not a pointer.  */
+#define array_length(var)                                               \
+  __extension__ ({                                                      \
+    _Static_assert (!__builtin_types_compatible_p                       \
+                    (__typeof (var), __typeof (&(var)[0])),             \
+                    "argument must be an array");                       \
+    sizeof (var) / sizeof ((var)[0]);                                   \
+  })
+
+/* array_end (VAR) is a pointer one past the end of the array VAR.
+   VAR must evaluate to an array, not a pointer.  */
+#define array_end(var) &(var)[array_length (var)]
+
+#endif /* _ARRAY_LENGTH_H */
diff --git a/stdio-common/bug16.c b/stdio-common/bug16.c
index 1112c39d05..91bc452826 100644
--- a/stdio-common/bug16.c
+++ b/stdio-common/bug16.c
@@ -1,3 +1,4 @@
+#include <array_length.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -19,7 +20,7 @@ do_test (void)
   char buf[100];
   int ret = 0;
 
-  for (size_t i = 0; i < sizeof (tests) / sizeof (tests[0]); ++i)
+  for (size_t i = 0; i < array_length (tests); ++i)
     {
       snprintf (buf, sizeof (buf), "%.0LA", tests[i].val);
 
diff --git a/stdio-common/errlist.c b/stdio-common/errlist.c
index 0f1b72b37d..e8d0f62519 100644
--- a/stdio-common/errlist.c
+++ b/stdio-common/errlist.c
@@ -15,9 +15,9 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #include <stddef.h>
 
-
 const char *const _sys_errlist[] =
   {
     "Error 0",			/* 0 */
@@ -33,4 +33,4 @@ const char *const _sys_errlist[] =
     "Too many open files",	/* 10 = EMFILE */
   };
 
-const int _sys_nerr = sizeof (_sys_errlist) / sizeof (_sys_errlist[0]);
+const int _sys_nerr = array_length (_sys_errlist);
diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c
index 994497cd22..52ba46bd08 100644
--- a/stdio-common/printf_fp.c
+++ b/stdio-common/printf_fp.c
@@ -21,6 +21,7 @@
 /* The gmp headers need some configuration frobs.  */
 #define HAVE_ALLOCA 1
 
+#include <array_length.h>
 #include <libioP.h>
 #include <alloca.h>
 #include <ctype.h>
@@ -371,8 +372,7 @@ __printf_fp_l (FILE *fp, locale_t loc,
     else								\
       {									\
 	p.fracsize = __mpn_extract_##SUFFIX				\
-		     (fp_input,						\
-		      (sizeof (fp_input) / sizeof (fp_input[0])),	\
+		     (fp_input, array_length (fp_input),		\
 		      &p.exponent, &is_neg, VAR);			\
 	to_shift = 1 + p.fracsize * BITS_PER_MP_LIMB - MANT_DIG;	\
       }									\
diff --git a/stdio-common/printf_fphex.c b/stdio-common/printf_fphex.c
index 004d2aa716..938b0b32e0 100644
--- a/stdio-common/printf_fphex.c
+++ b/stdio-common/printf_fphex.c
@@ -17,6 +17,7 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #include <ctype.h>
 #include <ieee754.h>
 #include <math.h>
@@ -320,8 +321,8 @@ __printf_fphex (FILE *fp,
   /* Look for trailing zeroes.  */
   if (! zero_mantissa)
     {
-      wnumend = &wnumbuf[sizeof wnumbuf / sizeof wnumbuf[0]];
-      numend = &numbuf[sizeof numbuf / sizeof numbuf[0]];
+      wnumend = array_end (wnumbuf);
+      numend = array_end (numbuf);
       while (wnumend[-1] == L'0')
 	{
 	  --wnumend;
diff --git a/stdio-common/psiginfo.c b/stdio-common/psiginfo.c
index 7bf2e2d13f..b9c455b8f5 100644
--- a/stdio-common/psiginfo.c
+++ b/stdio-common/psiginfo.c
@@ -15,6 +15,7 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #include <errno.h>
 #include <libintl.h>
 #include <signal.h>
@@ -116,7 +117,7 @@ psiginfo (const siginfo_t *pinfo, const char *s)
 	case sig:							      \
 	  base = C(codestrs_, sig).str;					      \
 	  offarr = C (codes_, sig);					      \
-	  offarr_len = sizeof (C (codes_, sig)) / sizeof (C (codes_, sig)[0]);\
+	  offarr_len = array_length (C (codes_, sig));			      \
 	  break
 
 	  H (SIGILL);
diff --git a/stdio-common/test-vfprintf.c b/stdio-common/test-vfprintf.c
index f8bb9cee58..0fcc1adc0e 100644
--- a/stdio-common/test-vfprintf.c
+++ b/stdio-common/test-vfprintf.c
@@ -17,6 +17,7 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #include <locale.h>
 #include <mcheck.h>
 #include <stdint.h>
@@ -32,8 +33,6 @@ const char *locs[] =
 {
   "C", "de_DE.ISO-8859-1", "de_DE.UTF-8", "ja_JP.EUC-JP"
 };
-#define nlocs (sizeof (locs) / sizeof (locs[0]))
-
 
 char large[50000];
 
@@ -56,7 +55,7 @@ do_test (void)
     }
   unlink (buf);
 
-  for (i = 0; i < nlocs; ++i)
+  for (i = 0; i < array_length (locs); ++i)
     {
       FILE *fp;
       struct stat st;
diff --git a/stdio-common/tst-fphex.c b/stdio-common/tst-fphex.c
index c2e8961d62..efba482537 100644
--- a/stdio-common/tst-fphex.c
+++ b/stdio-common/tst-fphex.c
@@ -1,5 +1,6 @@
 /* Test program for %a printf formats.  */
 
+#include <array_length.h>
 #include <stdio.h>
 #include <string.h>
 
@@ -49,12 +50,10 @@ do_test (void)
   const struct testcase *t;
   int result = 0;
 
-  for (t = testcases;
-       t < &testcases[sizeof testcases / sizeof testcases[0]];
-       ++t)
+  for (t = testcases; t < array_end (testcases); ++t)
     {
       CHAR_T buf[1024];
-      int n = SPRINT (buf, sizeof buf / sizeof (buf[0]), t->fmt, t->value);
+      int n = SPRINT (buf, array_length (buf), t->fmt, t->value);
       if (n != STR_LEN (t->expect) || STR_CMP (buf, t->expect) != 0)
 	{
 	  PRINT (L_("" S "\tExpected \"" S "\" (%Zu)\n\tGot      \""
diff --git a/stdio-common/tst-long-dbl-fphex.c b/stdio-common/tst-long-dbl-fphex.c
index 1170c74c4c..6a402a1c89 100644
--- a/stdio-common/tst-long-dbl-fphex.c
+++ b/stdio-common/tst-long-dbl-fphex.c
@@ -16,6 +16,7 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #include <wchar.h>
 
 /* Prototype for our test function.  */
@@ -28,9 +29,9 @@ do_test (void)
   int result = 0;
   const long double x = 24.5;
   wchar_t a[16];
-  swprintf (a, sizeof a / sizeof a[0], L"%La\n", x);
+  swprintf (a, array_length (a), L"%La\n", x);
   wchar_t A[16];
-  swprintf (A, sizeof A / sizeof A[0], L"%LA\n", x);
+  swprintf (A, array_length (a), L"%LA\n", x);
 
   /* Here wprintf can return four valid variants.  We must accept all
      of them.  */
diff --git a/stdio-common/tst-printf-round.c b/stdio-common/tst-printf-round.c
index e0073f2856..a5db3b88c2 100644
--- a/stdio-common/tst-printf-round.c
+++ b/stdio-common/tst-printf-round.c
@@ -16,6 +16,7 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #include <fenv.h>
 #include <stdio.h>
 #include <string.h>
@@ -167,7 +168,7 @@ do_test (void)
   int save_round_mode __attribute__ ((unused)) = fegetround ();
   int result = 0;
 
-  for (size_t i = 0; i < sizeof (dec_tests) / sizeof (dec_tests[0]); i++)
+  for (size_t i = 0; i < array_length (dec_tests); i++)
     {
       result |= test_dec_in_one_mode (dec_tests[i].d, dec_tests[i].fmt,
 				      dec_tests[i].rn, "default rounding mode");
@@ -197,7 +198,7 @@ do_test (void)
 #endif
     }
 
-  for (size_t i = 0; i < sizeof (hex_tests) / sizeof (hex_tests[0]); i++)
+  for (size_t i = 0; i < array_length (hex_tests); i++)
     {
       result |= test_hex_in_one_mode (hex_tests[i].d, hex_tests[i].fmt,
 				      hex_tests[i].rn, "default rounding mode");
diff --git a/stdio-common/tst-sscanf.c b/stdio-common/tst-sscanf.c
index c070e14cd7..9ccd86e7b0 100644
--- a/stdio-common/tst-sscanf.c
+++ b/stdio-common/tst-sscanf.c
@@ -16,6 +16,7 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <locale.h>
@@ -184,7 +185,7 @@ do_test (void)
 	break;
     }
 
-  for (i = 0; i < sizeof (int_tests) / sizeof (int_tests[0]); ++i)
+  for (i = 0; i < array_length (int_tests); ++i)
     {
       long dummy;
       int ret;
@@ -198,7 +199,7 @@ do_test (void)
 	}
     }
 
-  for (i = 0; i < sizeof (double_tests) / sizeof (double_tests[0]); ++i)
+  for (i = 0; i < array_length (double_tests); ++i)
     {
       double dummy;
       int ret;
@@ -212,7 +213,7 @@ do_test (void)
 	}
     }
 
-  for (i = 0; i < sizeof (double_tests2) / sizeof (double_tests2[0]); ++i)
+  for (i = 0; i < array_length (double_tests2); ++i)
     {
       double dummy;
       int ret;
diff --git a/stdio-common/tst-swprintf.c b/stdio-common/tst-swprintf.c
index ce62c6bf68..07194eb67d 100644
--- a/stdio-common/tst-swprintf.c
+++ b/stdio-common/tst-swprintf.c
@@ -1,3 +1,4 @@
+#include <array_length.h>
 #include <locale.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -16,7 +17,6 @@ static int
 do_test (void)
 {
   wchar_t buf[1000];
-#define nbuf (sizeof (buf) / sizeof (buf[0]))
   int result = 0;
   ssize_t n;
 
@@ -27,7 +27,7 @@ do_test (void)
     }
 
 #define CHECK(fmt, nexp, exp) \
-  n = swprintf (buf, nbuf, fmt, input);					      \
+  n = swprintf (buf, array_length (buf), fmt, input);			      \
   if (n != nexp)							      \
     {									      \
       printf ("swprintf (.., .., L\"%ls\", \"%ls\") return %d, not %d\n",     \
diff --git a/stdio-common/tstdiomisc.c b/stdio-common/tstdiomisc.c
index 89009e0549..c2fc73deb1 100644
--- a/stdio-common/tstdiomisc.c
+++ b/stdio-common/tstdiomisc.c
@@ -1,3 +1,4 @@
+#include <array_length.h>
 #include <float.h>
 #include <math.h>
 #include <stdio.h>
@@ -66,8 +67,7 @@ t3 (void)
   retval = sprintf (buf, "%p", (char *) NULL);
   result |= retval != 5 || strcmp (buf, "(nil)") != 0;
 
-  retval = swprintf (wbuf, sizeof (wbuf) / sizeof (wbuf[0]),
-		     L"%p", (char *) NULL);
+  retval = swprintf (wbuf, array_length (wbuf), L"%p", (char *) NULL);
   result |= retval != 5 || wcscmp (wbuf, L"(nil)") != 0;
 
   return result;
@@ -142,38 +142,38 @@ F (void)
   printf ("expected \"-inf -INF -inf -INF -inf -INF -inf -INF\", got \"%s\"\n",
 	  buf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]), L"%a %A %e %E %f %F %g %G",
+  swprintf (wbuf, array_length (wbuf), L"%a %A %e %E %f %F %g %G",
 	    qnanval, qnanval, qnanval, qnanval,
 	    qnanval, qnanval, qnanval, qnanval);
   result |= wcscmp (wbuf, L"nan NAN nan NAN nan NAN nan NAN") != 0;
   printf ("expected L\"nan NAN nan NAN nan NAN nan NAN\", got L\"%S\"\n", wbuf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]), L"%a %A %e %E %f %F %g %G",
+  swprintf (wbuf, array_length (wbuf), L"%a %A %e %E %f %F %g %G",
 	    -qnanval, -qnanval, -qnanval, -qnanval,
 	    -qnanval, -qnanval, -qnanval, -qnanval);
   result |= wcscmp (wbuf, L"-nan -NAN -nan -NAN -nan -NAN -nan -NAN") != 0;
   printf ("expected L\"-nan -NAN -nan -NAN -nan -NAN -nan -NAN\", got L\"%S\"\n",
 	  wbuf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]), L"%a %A %e %E %f %F %g %G",
+  swprintf (wbuf, array_length (wbuf), L"%a %A %e %E %f %F %g %G",
 	    snanval, snanval, snanval, snanval,
 	    snanval, snanval, snanval, snanval);
   result |= wcscmp (wbuf, L"nan NAN nan NAN nan NAN nan NAN") != 0;
   printf ("expected L\"nan NAN nan NAN nan NAN nan NAN\", got L\"%S\"\n", wbuf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]), L"%a %A %e %E %f %F %g %G",
+  swprintf (wbuf, array_length (wbuf), L"%a %A %e %E %f %F %g %G",
 	    msnanval, msnanval, msnanval, msnanval,
 	    msnanval, msnanval, msnanval, msnanval);
   result |= wcscmp (wbuf, L"-nan -NAN -nan -NAN -nan -NAN -nan -NAN") != 0;
   printf ("expected L\"-nan -NAN -nan -NAN -nan -NAN -nan -NAN\", got L\"%S\"\n",
 	  wbuf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]), L"%a %A %e %E %f %F %g %G",
+  swprintf (wbuf, array_length (wbuf), L"%a %A %e %E %f %F %g %G",
 	    infval, infval, infval, infval, infval, infval, infval, infval);
   result |= wcscmp (wbuf, L"inf INF inf INF inf INF inf INF") != 0;
   printf ("expected L\"inf INF inf INF inf INF inf INF\", got L\"%S\"\n", wbuf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]), L"%a %A %e %E %f %F %g %G",
+  swprintf (wbuf, array_length (wbuf), L"%a %A %e %E %f %F %g %G",
 	    -infval, -infval, -infval, -infval,
 	    -infval, -infval, -infval, -infval);
   result |= wcscmp (wbuf, L"-inf -INF -inf -INF -inf -INF -inf -INF") != 0;
@@ -223,14 +223,14 @@ F (void)
   printf ("expected \"-inf -INF -inf -INF -inf -INF -inf -INF\", got \"%s\"\n",
 	  buf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]),
+  swprintf (wbuf, array_length (wbuf),
 	    L"%La %LA %Le %LE %Lf %LF %Lg %LG",
 	    lqnanval, lqnanval, lqnanval, lqnanval,
 	    lqnanval, lqnanval, lqnanval, lqnanval);
   result |= wcscmp (wbuf, L"nan NAN nan NAN nan NAN nan NAN") != 0;
   printf ("expected L\"nan NAN nan NAN nan NAN nan NAN\", got L\"%S\"\n", wbuf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]),
+  swprintf (wbuf, array_length (wbuf),
 	    L"%La %LA %Le %LE %Lf %LF %Lg %LG",
 	    -lqnanval, -lqnanval, -lqnanval, -lqnanval,
 	    -lqnanval, -lqnanval, -lqnanval, -lqnanval);
@@ -238,14 +238,14 @@ F (void)
   printf ("expected L\"-nan -NAN -nan -NAN -nan -NAN -nan -NAN\", got L\"%S\"\n",
 	  wbuf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]),
+  swprintf (wbuf, array_length (wbuf),
 	    L"%La %LA %Le %LE %Lf %LF %Lg %LG",
 	    lsnanval, lsnanval, lsnanval, lsnanval,
 	    lsnanval, lsnanval, lsnanval, lsnanval);
   result |= wcscmp (wbuf, L"nan NAN nan NAN nan NAN nan NAN") != 0;
   printf ("expected L\"nan NAN nan NAN nan NAN nan NAN\", got L\"%S\"\n", wbuf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]),
+  swprintf (wbuf, array_length (wbuf),
 	    L"%La %LA %Le %LE %Lf %LF %Lg %LG",
 	    lmsnanval, lmsnanval, lmsnanval, lmsnanval,
 	    lmsnanval, lmsnanval, lmsnanval, lmsnanval);
@@ -253,14 +253,14 @@ F (void)
   printf ("expected L\"-nan -NAN -nan -NAN -nan -NAN -nan -NAN\", got L\"%S\"\n",
 	  wbuf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]),
+  swprintf (wbuf, array_length (wbuf),
 	    L"%La %LA %Le %LE %Lf %LF %Lg %LG",
 	    linfval, linfval, linfval, linfval,
 	    linfval, linfval, linfval, linfval);
   result |= wcscmp (wbuf, L"inf INF inf INF inf INF inf INF") != 0;
   printf ("expected L\"inf INF inf INF inf INF inf INF\", got L\"%S\"\n", wbuf);
 
-  swprintf (wbuf, sizeof wbuf / sizeof (wbuf[0]),
+  swprintf (wbuf, array_length (wbuf),
 	    L"%La %LA %Le %LE %Lf %LF %Lg %LG",
 	    -linfval, -linfval, -linfval, -linfval,
 	    -linfval, -linfval, -linfval, -linfval);
diff --git a/stdio-common/tstscanf.c b/stdio-common/tstscanf.c
index 50d0f33acf..d2ef4e8dee 100644
--- a/stdio-common/tstscanf.c
+++ b/stdio-common/tstscanf.c
@@ -15,6 +15,7 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #ifdef	BSD
 #include </usr/include/stdio.h>
 #else
@@ -154,7 +155,7 @@ main (int argc, char **argv)
       {
 	int count;
 
-	if (rounds++ >= sizeof (ok) / sizeof (ok[0]))
+	if (rounds++ >= array_length (ok))
 	  {
 	    fputs ("test failed!\n", stdout);
 	    result = 1;
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 23d2679f39..e272237307 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -15,6 +15,7 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
 #include <ctype.h>
 #include <limits.h>
 #include <printf.h>
@@ -995,11 +996,10 @@ static const uint8_t jump_table[] =
 	if (string == NULL)						      \
 	  {								      \
 	    /* Write "(null)" if there's space.  */			      \
-	    if (prec == -1						      \
-		|| prec >= (int) (sizeof (null) / sizeof (null[0])) - 1)      \
+	    if (prec == -1 || prec >= (int) array_length (null) - 1)          \
 	      {								      \
 		string = (CHAR_T *) null;				      \
-		len = (sizeof (null) / sizeof (null[0])) - 1;		      \
+		len = array_length (null) - 1;				      \
 	      }								      \
 	    else							      \
 	      {								      \
