5f7b84
commit 711a322a235d4c8177713f11aa59156603b94aeb
5f7b84
Author: Zack Weinberg <zackw@panix.com>
5f7b84
Date:   Mon Mar 11 10:59:27 2019 -0400
5f7b84
5f7b84
    Use a proper C tokenizer to implement the obsolete typedefs test.
5f7b84
    
5f7b84
    The test for obsolete typedefs in installed headers was implemented
5f7b84
    using grep, and could therefore get false positives on e.g. “ulong”
5f7b84
    in a comment.  It was also scanning all of the headers included by
5f7b84
    our headers, and therefore testing headers we don’t control, e.g.
5f7b84
    Linux kernel headers.
5f7b84
    
5f7b84
    This patch splits the obsolete-typedef test from
5f7b84
    scripts/check-installed-headers.sh to a separate program,
5f7b84
    scripts/check-obsolete-constructs.py.  Being implemented in Python,
5f7b84
    it is feasible to make it tokenize C accurately enough to avoid false
5f7b84
    positives on the contents of comments and strings.  It also only
5f7b84
    examines $(headers) in each subdirectory--all the headers we install,
5f7b84
    but not any external dependencies of those headers.  Headers whose
5f7b84
    installed name starts with finclude/ are ignored, on the assumption
5f7b84
    that they contain Fortran.
5f7b84
    
5f7b84
    It is also feasible to make the new test understand the difference
5f7b84
    between _defining_ the obsolete typedefs and _using_ the obsolete
5f7b84
    typedefs, which means posix/{bits,sys}/types.h no longer need to be
5f7b84
    exempted.  This uncovered an actual bug in bits/types.h: __quad_t and
5f7b84
    __u_quad_t were being used to define __S64_TYPE, __U64_TYPE,
5f7b84
    __SQUAD_TYPE and __UQUAD_TYPE.  These are changed to __int64_t and
5f7b84
    __uint64_t respectively.  This is a safe change, despite the comments
5f7b84
    in bits/types.h claiming a difference between __quad_t and __int64_t,
5f7b84
    because those comments are incorrect.  In all current ABIs, both
5f7b84
    __quad_t and __int64_t are ‘long’ when ‘long’ is a 64-bit type, and
5f7b84
    ‘long long’ when ‘long’ is a 32-bit type, and similarly for __u_quad_t
5f7b84
    and __uint64_t.  (Changing the types to be what the comments say they
5f7b84
    are would be an ABI break, as it affects C++ name mangling.)  This
5f7b84
    patch includes a minimal change to make the comments not completely
5f7b84
    wrong.
5f7b84
    
5f7b84
    sys/types.h was defining the legacy BSD u_intN_t typedefs using a
5f7b84
    construct that was not necessarily consistent with how the C99 uintN_t
5f7b84
    typedefs are defined, and is also too complicated for the new script to
5f7b84
    understand (it lexes C relatively accurately, but it does not attempt
5f7b84
    to expand preprocessor macros, nor does it do any actual parsing).
5f7b84
    This patch cuts all of that out and uses bits/types.h's __uintN_t typedefs
5f7b84
    to define u_intN_t instead.  This is verified to not change the ABI on
5f7b84
    any supported architecture, via the c++-types test, which means u_intN_t
5f7b84
    and uintN_t were, in fact, consistent on all supported architectures.
5f7b84
    
5f7b84
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
5f7b84
    
5f7b84
            * scripts/check-obsolete-constructs.py: New test script.
5f7b84
            * scripts/check-installed-headers.sh: Remove tests for
5f7b84
            obsolete typedefs, superseded by check-obsolete-constructs.py.
5f7b84
            * Rules: Run scripts/check-obsolete-constructs.py over $(headers)
5f7b84
            as a special test.  Update commentary.
5f7b84
            * posix/bits/types.h (__SQUAD_TYPE, __S64_TYPE): Define as __int64_t.
5f7b84
            (__UQUAD_TYPE, __U64_TYPE): Define as __uint64_t.
5f7b84
            Update commentary.
5f7b84
            * posix/sys/types.h (__u_intN_t): Remove.
5f7b84
            (u_int8_t): Typedef using __uint8_t.
5f7b84
            (u_int16_t): Typedef using __uint16_t.
5f7b84
            (u_int32_t): Typedef using __uint32_t.
5f7b84
            (u_int64_t): Typedef using __uint64_t.
5f7b84
5f7b84
Conflicts:
5f7b84
	Rules
5f7b84
	  (textual conflicts due to lack of check-wrapper-headers test.)
5f7b84
5f7b84
diff --git a/Rules b/Rules
5f7b84
index 5abb7270aa8e24aa..a07dbb8d978b5769 100644
5f7b84
--- a/Rules
5f7b84
+++ b/Rules
5f7b84
@@ -82,7 +82,8 @@ $(common-objpfx)dummy.c:
5f7b84
 common-generated += dummy.o dummy.c
5f7b84
 
5f7b84
 ifneq "$(headers)" ""
5f7b84
-# Special test of all the installed headers in this directory.
5f7b84
+# Test that all of the headers installed by this directory can be compiled
5f7b84
+# in isolation.
5f7b84
 tests-special += $(objpfx)check-installed-headers-c.out
5f7b84
 libof-check-installed-headers-c := testsuite
5f7b84
 $(objpfx)check-installed-headers-c.out: \
5f7b84
@@ -93,6 +94,8 @@ $(objpfx)check-installed-headers-c.out: \
5f7b84
 	$(evaluate-test)
5f7b84
 
5f7b84
 ifneq "$(CXX)" ""
5f7b84
+# If a C++ compiler is available, also test that they can be compiled
5f7b84
+# in isolation as C++.
5f7b84
 tests-special += $(objpfx)check-installed-headers-cxx.out
5f7b84
 libof-check-installed-headers-cxx := testsuite
5f7b84
 $(objpfx)check-installed-headers-cxx.out: \
5f7b84
@@ -101,8 +104,19 @@ $(objpfx)check-installed-headers-cxx.out: \
5f7b84
 	  "$(CXX) $(filter-out -std=%,$(CXXFLAGS)) -D_ISOMAC $(+includes)" \
5f7b84
 	  $(headers) > $@; \
5f7b84
 	$(evaluate-test)
5f7b84
-endif
5f7b84
-endif
5f7b84
+endif # $(CXX)
5f7b84
+
5f7b84
+# Test that none of the headers installed by this directory use certain
5f7b84
+# obsolete constructs (e.g. legacy BSD typedefs superseded by stdint.h).
5f7b84
+# This script does not need $(py-env).
5f7b84
+tests-special += $(objpfx)check-obsolete-constructs.out
5f7b84
+libof-check-obsolete-constructs := testsuite
5f7b84
+$(objpfx)check-obsolete-constructs.out: \
5f7b84
+    $(..)scripts/check-obsolete-constructs.py $(headers)
5f7b84
+	$(PYTHON) $^ > $@ 2>&1; \
5f7b84
+	$(evaluate-test)
5f7b84
+
5f7b84
+endif # $(headers)
5f7b84
 
5f7b84
 # This makes all the auxiliary and test programs.
5f7b84
 
5f7b84
diff --git a/posix/bits/types.h b/posix/bits/types.h
5f7b84
index 5e22ce41bf4c29b3..64f344c6e7897491 100644
5f7b84
--- a/posix/bits/types.h
5f7b84
+++ b/posix/bits/types.h
5f7b84
@@ -86,7 +86,7 @@ __extension__ typedef unsigned long long int __uintmax_t;
5f7b84
 	32		-- "natural" 32-bit type (always int)
5f7b84
 	64		-- "natural" 64-bit type (long or long long)
5f7b84
 	LONG32		-- 32-bit type, traditionally long
5f7b84
-	QUAD		-- 64-bit type, always long long
5f7b84
+	QUAD		-- 64-bit type, traditionally long long
5f7b84
 	WORD		-- natural type of __WORDSIZE bits (int or long)
5f7b84
 	LONGWORD	-- type of __WORDSIZE bits, traditionally long
5f7b84
 
5f7b84
@@ -112,14 +112,14 @@ __extension__ typedef unsigned long long int __uintmax_t;
5f7b84
 #define __SLONGWORD_TYPE	long int
5f7b84
 #define __ULONGWORD_TYPE	unsigned long int
5f7b84
 #if __WORDSIZE == 32
5f7b84
-# define __SQUAD_TYPE		__quad_t
5f7b84
-# define __UQUAD_TYPE		__u_quad_t
5f7b84
+# define __SQUAD_TYPE		__int64_t
5f7b84
+# define __UQUAD_TYPE		__uint64_t
5f7b84
 # define __SWORD_TYPE		int
5f7b84
 # define __UWORD_TYPE		unsigned int
5f7b84
 # define __SLONG32_TYPE		long int
5f7b84
 # define __ULONG32_TYPE		unsigned long int
5f7b84
-# define __S64_TYPE		__quad_t
5f7b84
-# define __U64_TYPE		__u_quad_t
5f7b84
+# define __S64_TYPE		__int64_t
5f7b84
+# define __U64_TYPE		__uint64_t
5f7b84
 /* We want __extension__ before typedef's that use nonstandard base types
5f7b84
    such as `long long' in C89 mode.  */
5f7b84
 # define __STD_TYPE		__extension__ typedef
5f7b84
diff --git a/posix/sys/types.h b/posix/sys/types.h
5f7b84
index db524d6cd13f0379..47eff1a7b1a91c81 100644
5f7b84
--- a/posix/sys/types.h
5f7b84
+++ b/posix/sys/types.h
5f7b84
@@ -154,37 +154,20 @@ typedef unsigned int uint;
5f7b84
 
5f7b84
 #include <bits/stdint-intn.h>
5f7b84
 
5f7b84
-#if !__GNUC_PREREQ (2, 7)
5f7b84
-
5f7b84
 /* These were defined by ISO C without the first `_'.  */
5f7b84
-typedef	unsigned char u_int8_t;
5f7b84
-typedef	unsigned short int u_int16_t;
5f7b84
-typedef	unsigned int u_int32_t;
5f7b84
-# if __WORDSIZE == 64
5f7b84
-typedef unsigned long int u_int64_t;
5f7b84
-# else
5f7b84
-__extension__ typedef unsigned long long int u_int64_t;
5f7b84
-# endif
5f7b84
-
5f7b84
-typedef int register_t;
5f7b84
-
5f7b84
-#else
5f7b84
-
5f7b84
-/* For GCC 2.7 and later, we can use specific type-size attributes.  */
5f7b84
-# define __u_intN_t(N, MODE) \
5f7b84
-  typedef unsigned int u_int##N##_t __attribute__ ((__mode__ (MODE)))
5f7b84
-
5f7b84
-__u_intN_t (8, __QI__);
5f7b84
-__u_intN_t (16, __HI__);
5f7b84
-__u_intN_t (32, __SI__);
5f7b84
-__u_intN_t (64, __DI__);
5f7b84
+typedef __uint8_t u_int8_t;
5f7b84
+typedef __uint16_t u_int16_t;
5f7b84
+typedef __uint32_t u_int32_t;
5f7b84
+typedef __uint64_t u_int64_t;
5f7b84
 
5f7b84
+#if __GNUC_PREREQ (2, 7)
5f7b84
 typedef int register_t __attribute__ ((__mode__ (__word__)));
5f7b84
-
5f7b84
+#else
5f7b84
+typedef int register_t;
5f7b84
+#endif
5f7b84
 
5f7b84
 /* Some code from BIND tests this macro to see if the types above are
5f7b84
    defined.  */
5f7b84
-#endif
5f7b84
 #define __BIT_TYPES_DEFINED__	1
5f7b84
 
5f7b84
 
5f7b84
diff --git a/scripts/check-installed-headers.sh b/scripts/check-installed-headers.sh
5f7b84
index 7a1969b43a144ebb..c2aeea5aabcc7ffd 100644
5f7b84
--- a/scripts/check-installed-headers.sh
5f7b84
+++ b/scripts/check-installed-headers.sh
5f7b84
@@ -16,11 +16,9 @@
5f7b84
 # License along with the GNU C Library; if not, see
5f7b84
 # <http://www.gnu.org/licenses/>.
5f7b84
 
5f7b84
-# Check installed headers for cleanliness.  For each header, confirm
5f7b84
-# that it's possible to compile a file that includes that header and
5f7b84
-# does nothing else, in several different compilation modes.  Also,
5f7b84
-# scan the header for a set of obsolete typedefs that should no longer
5f7b84
-# appear.
5f7b84
+# For each installed header, confirm that it's possible to compile a
5f7b84
+# file that includes that header and does nothing else, in several
5f7b84
+# different compilation modes.
5f7b84
 
5f7b84
 # These compilation switches assume GCC or compatible, which is probably
5f7b84
 # fine since we also assume that when _building_ glibc.
5f7b84
@@ -31,13 +29,6 @@ cxx_modes="-std=c++98 -std=gnu++98 -std=c++11 -std=gnu++11"
5f7b84
 # These are probably the most commonly used three.
5f7b84
 lib_modes="-D_DEFAULT_SOURCE=1 -D_GNU_SOURCE=1 -D_XOPEN_SOURCE=700"
5f7b84
 
5f7b84
-# sys/types.h+bits/types.h have to define the obsolete types.
5f7b84
-# rpc(svc)/* have the obsolete types too deeply embedded in their API
5f7b84
-# to remove.
5f7b84
-skip_obsolete_type_check='*/sys/types.h|*/bits/types.h|*/rpc/*|*/rpcsvc/*'
5f7b84
-obsolete_type_re=\
5f7b84
-'\<((__)?(quad_t|u(short|int|long|_(char|short|int([0-9]+_t)?|long|quad_t))))\>'
5f7b84
-
5f7b84
 if [ $# -lt 3 ]; then
5f7b84
     echo "usage: $0 c|c++ \"compile command\" header header header..." >&2
5f7b84
     exit 2
5f7b84
@@ -46,14 +37,10 @@ case "$1" in
5f7b84
     (c)
5f7b84
         lang_modes="$c_modes"
5f7b84
         cih_test_c=$(mktemp ${TMPDIR-/tmp}/cih_test_XXXXXX.c)
5f7b84
-        already="$skip_obsolete_type_check"
5f7b84
     ;;
5f7b84
     (c++)
5f7b84
         lang_modes="$cxx_modes"
5f7b84
         cih_test_c=$(mktemp ${TMPDIR-/tmp}/cih_test_XXXXXX.cc)
5f7b84
-        # The obsolete-type check can be skipped for C++; it is
5f7b84
-        # sufficient to do it for C.
5f7b84
-        already="*"
5f7b84
     ;;
5f7b84
     (*)
5f7b84
         echo "usage: $0 c|c++ \"compile command\" header header header..." >&2
5f7b84
@@ -155,22 +142,8 @@ $expanded_lib_mode
5f7b84
 int avoid_empty_translation_unit;
5f7b84
 EOF
5f7b84
             if $cc_cmd -fsyntax-only $lang_mode "$cih_test_c" 2>&1
5f7b84
-            then
5f7b84
-                includes=$($cc_cmd -fsyntax-only -H $lang_mode \
5f7b84
-                              "$cih_test_c" 2>&1 | sed -ne 's/^[.][.]* //p')
5f7b84
-                for h in $includes; do
5f7b84
-                    # Don't repeat work.
5f7b84
-                    eval 'case "$h" in ('"$already"') continue;; esac'
5f7b84
-
5f7b84
-                    if grep -qE "$obsolete_type_re" "$h"; then
5f7b84
-                        echo "*** Obsolete types detected:"
5f7b84
-                        grep -HE "$obsolete_type_re" "$h"
5f7b84
-                        failed=1
5f7b84
-                    fi
5f7b84
-                    already="$already|$h"
5f7b84
-                done
5f7b84
-            else
5f7b84
-                failed=1
5f7b84
+            then :
5f7b84
+            else failed=1
5f7b84
             fi
5f7b84
         done
5f7b84
     done
5f7b84
diff --git a/scripts/check-obsolete-constructs.py b/scripts/check-obsolete-constructs.py
5f7b84
new file mode 100755
5f7b84
index 0000000000000000..ce5c72251f4d7cc0
5f7b84
--- /dev/null
5f7b84
+++ b/scripts/check-obsolete-constructs.py
5f7b84
@@ -0,0 +1,466 @@
5f7b84
+#! /usr/bin/python3
5f7b84
+# Copyright (C) 2019 Free Software Foundation, Inc.
5f7b84
+# This file is part of the GNU C Library.
5f7b84
+#
5f7b84
+# The GNU C Library is free software; you can redistribute it and/or
5f7b84
+# modify it under the terms of the GNU Lesser General Public
5f7b84
+# License as published by the Free Software Foundation; either
5f7b84
+# version 2.1 of the License, or (at your option) any later version.
5f7b84
+#
5f7b84
+# The GNU C Library is distributed in the hope that it will be useful,
5f7b84
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
5f7b84
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
5f7b84
+# Lesser General Public License for more details.
5f7b84
+#
5f7b84
+# You should have received a copy of the GNU Lesser General Public
5f7b84
+# License along with the GNU C Library; if not, see
5f7b84
+# <http://www.gnu.org/licenses/>.
5f7b84
+
5f7b84
+"""Verifies that installed headers do not use any obsolete constructs:
5f7b84
+ * legacy BSD typedefs superseded by <stdint.h>:
5f7b84
+   ushort uint ulong u_char u_short u_int u_long u_intNN_t quad_t u_quad_t
5f7b84
+   (sys/types.h is allowed to _define_ these types, but not to use them
5f7b84
+    to define anything else).
5f7b84
+"""
5f7b84
+
5f7b84
+import argparse
5f7b84
+import collections
5f7b84
+import re
5f7b84
+import sys
5f7b84
+
5f7b84
+# Simplified lexical analyzer for C preprocessing tokens.
5f7b84
+# Does not implement trigraphs.
5f7b84
+# Does not implement backslash-newline in the middle of any lexical
5f7b84
+#   item other than a string literal.
5f7b84
+# Does not implement universal-character-names in identifiers.
5f7b84
+# Treats prefixed strings (e.g. L"...") as two tokens (L and "...")
5f7b84
+# Accepts non-ASCII characters only within comments and strings.
5f7b84
+
5f7b84
+# Caution: The order of the outermost alternation matters.
5f7b84
+# STRING must be before BAD_STRING, CHARCONST before BAD_CHARCONST,
5f7b84
+# BLOCK_COMMENT before BAD_BLOCK_COM before PUNCTUATOR, and OTHER must
5f7b84
+# be last.
5f7b84
+# Caution: There should be no capturing groups other than the named
5f7b84
+# captures in the outermost alternation.
5f7b84
+
5f7b84
+# For reference, these are all of the C punctuators as of C11:
5f7b84
+#   [ ] ( ) { } , ; ? ~
5f7b84
+#   ! != * *= / /= ^ ^= = ==
5f7b84
+#   # ##
5f7b84
+#   % %= %> %: %:%:
5f7b84
+#   & &= &&
5f7b84
+#   | |= ||
5f7b84
+#   + += ++
5f7b84
+#   - -= -- ->
5f7b84
+#   . ...
5f7b84
+#   : :>
5f7b84
+#   < <% <: << <<= <=
5f7b84
+#   > >= >> >>=
5f7b84
+
5f7b84
+# The BAD_* tokens are not part of the official definition of pp-tokens;
5f7b84
+# they match unclosed strings, character constants, and block comments,
5f7b84
+# so that the regex engine doesn't have to backtrack all the way to the
5f7b84
+# beginning of a broken construct and then emit dozens of junk tokens.
5f7b84
+
5f7b84
+PP_TOKEN_RE_ = re.compile(r"""
5f7b84
+    (?P<STRING>        \"(?:[^\"\\\r\n]|\\(?:[\r\n -~]|\r\n))*\")
5f7b84
+   |(?P<BAD_STRING>    \"(?:[^\"\\\r\n]|\\[ -~])*)
5f7b84
+   |(?P<CHARCONST>     \'(?:[^\'\\\r\n]|\\(?:[\r\n -~]|\r\n))*\')
5f7b84
+   |(?P<BAD_CHARCONST> \'(?:[^\'\\\r\n]|\\[ -~])*)
5f7b84
+   |(?P<BLOCK_COMMENT> /\*(?:\*(?!/)|[^*])*\*/)
5f7b84
+   |(?P<BAD_BLOCK_COM> /\*(?:\*(?!/)|[^*])*\*?)
5f7b84
+   |(?P<LINE_COMMENT>  //[^\r\n]*)
5f7b84
+   |(?P<IDENT>         [_a-zA-Z][_a-zA-Z0-9]*)
5f7b84
+   |(?P<PP_NUMBER>     \.?[0-9](?:[0-9a-df-oq-zA-DF-OQ-Z_.]|[eEpP][+-]?)*)
5f7b84
+   |(?P<PUNCTUATOR>
5f7b84
+       [,;?~(){}\[\]]
5f7b84
+     | [!*/^=]=?
5f7b84
+     | \#\#?
5f7b84
+     | %(?:[=>]|:(?:%:)?)?
5f7b84
+     | &[=&]?
5f7b84
+     |\|[=|]?
5f7b84
+     |\+[=+]?
5f7b84
+     | -[=->]?
5f7b84
+     |\.(?:\.\.)?
5f7b84
+     | :>?
5f7b84
+     | <(?:[%:]|<(?:=|<=?)?)?
5f7b84
+     | >(?:=|>=?)?)
5f7b84
+   |(?P<ESCNL>         \\(?:\r|\n|\r\n))
5f7b84
+   |(?P<WHITESPACE>    [ \t\n\r\v\f]+)
5f7b84
+   |(?P<OTHER>         .)
5f7b84
+""", re.DOTALL | re.VERBOSE)
5f7b84
+
5f7b84
+HEADER_NAME_RE_ = re.compile(r"""
5f7b84
+    < [^>\r\n]+ >
5f7b84
+  | " [^"\r\n]+ "
5f7b84
+""", re.DOTALL | re.VERBOSE)
5f7b84
+
5f7b84
+ENDLINE_RE_ = re.compile(r"""\r|\n|\r\n""")
5f7b84
+
5f7b84
+# based on the sample code in the Python re documentation
5f7b84
+Token_ = collections.namedtuple("Token", (
5f7b84
+    "kind", "text", "line", "column", "context"))
5f7b84
+Token_.__doc__ = """
5f7b84
+   One C preprocessing token, comment, or chunk of whitespace.
5f7b84
+   'kind' identifies the token type, which will be one of:
5f7b84
+       STRING, CHARCONST, BLOCK_COMMENT, LINE_COMMENT, IDENT,
5f7b84
+       PP_NUMBER, PUNCTUATOR, ESCNL, WHITESPACE, HEADER_NAME,
5f7b84
+       or OTHER.  The BAD_* alternatives in PP_TOKEN_RE_ are
5f7b84
+       handled within tokenize_c, below.
5f7b84
+
5f7b84
+   'text' is the sequence of source characters making up the token;
5f7b84
+       no decoding whatsoever is performed.
5f7b84
+
5f7b84
+   'line' and 'column' give the position of the first character of the
5f7b84
+      token within the source file.  They are both 1-based.
5f7b84
+
5f7b84
+   'context' indicates whether or not this token occurred within a
5f7b84
+      preprocessing directive; it will be None for running text,
5f7b84
+      '<null>' for the leading '#' of a directive line (because '#'
5f7b84
+      all by itself on a line is a "null directive"), or the name of
5f7b84
+      the directive for tokens within a directive line, starting with
5f7b84
+      the IDENT for the name itself.
5f7b84
+"""
5f7b84
+
5f7b84
+def tokenize_c(file_contents, reporter):
5f7b84
+    """Yield a series of Token objects, one for each preprocessing
5f7b84
+       token, comment, or chunk of whitespace within FILE_CONTENTS.
5f7b84
+       The REPORTER object is expected to have one method,
5f7b84
+       reporter.error(token, message), which will be called to
5f7b84
+       indicate a lexical error at the position of TOKEN.
5f7b84
+       If MESSAGE contains the four-character sequence '{!r}', that
5f7b84
+       is expected to be replaced by repr(token.text).
5f7b84
+    """
5f7b84
+
5f7b84
+    Token = Token_
5f7b84
+    PP_TOKEN_RE = PP_TOKEN_RE_
5f7b84
+    ENDLINE_RE = ENDLINE_RE_
5f7b84
+    HEADER_NAME_RE = HEADER_NAME_RE_
5f7b84
+
5f7b84
+    line_num = 1
5f7b84
+    line_start = 0
5f7b84
+    pos = 0
5f7b84
+    limit = len(file_contents)
5f7b84
+    directive = None
5f7b84
+    at_bol = True
5f7b84
+    while pos < limit:
5f7b84
+        if directive == "include":
5f7b84
+            mo = HEADER_NAME_RE.match(file_contents, pos)
5f7b84
+            if mo:
5f7b84
+                kind = "HEADER_NAME"
5f7b84
+                directive = "after_include"
5f7b84
+            else:
5f7b84
+                mo = PP_TOKEN_RE.match(file_contents, pos)
5f7b84
+                kind = mo.lastgroup
5f7b84
+                if kind != "WHITESPACE":
5f7b84
+                    directive = "after_include"
5f7b84
+        else:
5f7b84
+            mo = PP_TOKEN_RE.match(file_contents, pos)
5f7b84
+            kind = mo.lastgroup
5f7b84
+
5f7b84
+        text = mo.group()
5f7b84
+        line = line_num
5f7b84
+        column = mo.start() - line_start
5f7b84
+        adj_line_start = 0
5f7b84
+        # only these kinds can contain a newline
5f7b84
+        if kind in ("WHITESPACE", "BLOCK_COMMENT", "LINE_COMMENT",
5f7b84
+                    "STRING", "CHARCONST", "BAD_BLOCK_COM", "ESCNL"):
5f7b84
+            for tmo in ENDLINE_RE.finditer(text):
5f7b84
+                line_num += 1
5f7b84
+                adj_line_start = tmo.end()
5f7b84
+            if adj_line_start:
5f7b84
+                line_start = mo.start() + adj_line_start
5f7b84
+
5f7b84
+        # Track whether or not we are scanning a preprocessing directive.
5f7b84
+        if kind == "LINE_COMMENT" or (kind == "WHITESPACE" and adj_line_start):
5f7b84
+            at_bol = True
5f7b84
+            directive = None
5f7b84
+        else:
5f7b84
+            if kind == "PUNCTUATOR" and text == "#" and at_bol:
5f7b84
+                directive = "<null>"
5f7b84
+            elif kind == "IDENT" and directive == "<null>":
5f7b84
+                directive = text
5f7b84
+            at_bol = False
5f7b84
+
5f7b84
+        # Report ill-formed tokens and rewrite them as their well-formed
5f7b84
+        # equivalents, so downstream processing doesn't have to know about them.
5f7b84
+        # (Rewriting instead of discarding provides better error recovery.)
5f7b84
+        if kind == "BAD_BLOCK_COM":
5f7b84
+            reporter.error(Token("BAD_BLOCK_COM", "", line, column+1, ""),
5f7b84
+                           "unclosed block comment")
5f7b84
+            text += "*/"
5f7b84
+            kind = "BLOCK_COMMENT"
5f7b84
+        elif kind == "BAD_STRING":
5f7b84
+            reporter.error(Token("BAD_STRING", "", line, column+1, ""),
5f7b84
+                           "unclosed string")
5f7b84
+            text += "\""
5f7b84
+            kind = "STRING"
5f7b84
+        elif kind == "BAD_CHARCONST":
5f7b84
+            reporter.error(Token("BAD_CHARCONST", "", line, column+1, ""),
5f7b84
+                           "unclosed char constant")
5f7b84
+            text += "'"
5f7b84
+            kind = "CHARCONST"
5f7b84
+
5f7b84
+        tok = Token(kind, text, line, column+1,
5f7b84
+                    "include" if directive == "after_include" else directive)
5f7b84
+        # Do not complain about OTHER tokens inside macro definitions.
5f7b84
+        # $ and @ appear in macros defined by headers intended to be
5f7b84
+        # included from assembly language, e.g. sysdeps/mips/sys/asm.h.
5f7b84
+        if kind == "OTHER" and directive != "define":
5f7b84
+            self.error(tok, "stray {!r} in program")
5f7b84
+
5f7b84
+        yield tok
5f7b84
+        pos = mo.end()
5f7b84
+
5f7b84
+#
5f7b84
+# Base and generic classes for individual checks.
5f7b84
+#
5f7b84
+
5f7b84
+class ConstructChecker:
5f7b84
+    """Scan a stream of C preprocessing tokens and possibly report
5f7b84
+       problems with them.  The REPORTER object passed to __init__ has
5f7b84
+       one method, reporter.error(token, message), which should be
5f7b84
+       called to indicate a problem detected at the position of TOKEN.
5f7b84
+       If MESSAGE contains the four-character sequence '{!r}' then that
5f7b84
+       will be replaced with a textual representation of TOKEN.
5f7b84
+    """
5f7b84
+    def __init__(self, reporter):
5f7b84
+        self.reporter = reporter
5f7b84
+
5f7b84
+    def examine(self, tok):
5f7b84
+        """Called once for each token in a header file.
5f7b84
+           Call self.reporter.error if a problem is detected.
5f7b84
+        """
5f7b84
+        raise NotImplementedError
5f7b84
+
5f7b84
+    def eof(self):
5f7b84
+        """Called once at the end of the stream.  Subclasses need only
5f7b84
+           override this if it might have something to do."""
5f7b84
+        pass
5f7b84
+
5f7b84
+class NoCheck(ConstructChecker):
5f7b84
+    """Generic checker class which doesn't do anything.  Substitute this
5f7b84
+       class for a real checker when a particular check should be skipped
5f7b84
+       for some file."""
5f7b84
+
5f7b84
+    def examine(self, tok):
5f7b84
+        pass
5f7b84
+
5f7b84
+#
5f7b84
+# Check for obsolete type names.
5f7b84
+#
5f7b84
+
5f7b84
+# The obsolete type names we're looking for:
5f7b84
+OBSOLETE_TYPE_RE_ = re.compile(r"""\A
5f7b84
+  (__)?
5f7b84
+  (   quad_t
5f7b84
+    | u(?: short | int | long
5f7b84
+         | _(?: char | short | int(?:[0-9]+_t)? | long | quad_t )))
5f7b84
+\Z""", re.VERBOSE)
5f7b84
+
5f7b84
+class ObsoleteNotAllowed(ConstructChecker):
5f7b84
+    """Don't allow any use of the obsolete typedefs."""
5f7b84
+    def examine(self, tok):
5f7b84
+        if OBSOLETE_TYPE_RE_.match(tok.text):
5f7b84
+            self.reporter.error(tok, "use of {!r}")
5f7b84
+
5f7b84
+class ObsoletePrivateDefinitionsAllowed(ConstructChecker):
5f7b84
+    """Allow definitions of the private versions of the
5f7b84
+       obsolete typedefs; that is, 'typedef [anything] __obsolete;'
5f7b84
+    """
5f7b84
+    def __init__(self, reporter):
5f7b84
+        super().__init__(reporter)
5f7b84
+        self.in_typedef = False
5f7b84
+        self.prev_token = None
5f7b84
+
5f7b84
+    def examine(self, tok):
5f7b84
+        # bits/types.h hides 'typedef' in a macro sometimes.
5f7b84
+        if (tok.kind == "IDENT"
5f7b84
+            and tok.text in ("typedef", "__STD_TYPE")
5f7b84
+            and tok.context is None):
5f7b84
+            self.in_typedef = True
5f7b84
+        elif tok.kind == "PUNCTUATOR" and tok.text == ";" and self.in_typedef:
5f7b84
+            self.in_typedef = False
5f7b84
+            if self.prev_token.kind == "IDENT":
5f7b84
+                m = OBSOLETE_TYPE_RE_.match(self.prev_token.text)
5f7b84
+                if m and m.group(1) != "__":
5f7b84
+                    self.reporter.error(self.prev_token, "use of {!r}")
5f7b84
+            self.prev_token = None
5f7b84
+        else:
5f7b84
+            self._check_prev()
5f7b84
+
5f7b84
+        self.prev_token = tok
5f7b84
+
5f7b84
+    def eof(self):
5f7b84
+        self._check_prev()
5f7b84
+
5f7b84
+    def _check_prev(self):
5f7b84
+        if (self.prev_token is not None
5f7b84
+            and self.prev_token.kind == "IDENT"
5f7b84
+            and OBSOLETE_TYPE_RE_.match(self.prev_token.text)):
5f7b84
+            self.reporter.error(self.prev_token, "use of {!r}")
5f7b84
+
5f7b84
+class ObsoletePublicDefinitionsAllowed(ConstructChecker):
5f7b84
+    """Allow definitions of the public versions of the obsolete
5f7b84
+       typedefs.  Only specific forms of definition are allowed:
5f7b84
+
5f7b84
+           typedef __obsolete obsolete;  // identifiers must agree
5f7b84
+           typedef __uintN_t u_intN_t;   // N must agree
5f7b84
+           typedef unsigned long int ulong;
5f7b84
+           typedef unsigned short int ushort;
5f7b84
+           typedef unsigned int uint;
5f7b84
+    """
5f7b84
+    def __init__(self, reporter):
5f7b84
+        super().__init__(reporter)
5f7b84
+        self.typedef_tokens = []
5f7b84
+
5f7b84
+    def examine(self, tok):
5f7b84
+        if tok.kind in ("WHITESPACE", "BLOCK_COMMENT",
5f7b84
+                        "LINE_COMMENT", "NL", "ESCNL"):
5f7b84
+            pass
5f7b84
+
5f7b84
+        elif (tok.kind == "IDENT" and tok.text == "typedef"
5f7b84
+              and tok.context is None):
5f7b84
+            if self.typedef_tokens:
5f7b84
+                self.reporter.error(tok, "typedef inside typedef")
5f7b84
+                self._reset()
5f7b84
+            self.typedef_tokens.append(tok)
5f7b84
+
5f7b84
+        elif tok.kind == "PUNCTUATOR" and tok.text == ";":
5f7b84
+            self._finish()
5f7b84
+
5f7b84
+        elif self.typedef_tokens:
5f7b84
+            self.typedef_tokens.append(tok)
5f7b84
+
5f7b84
+    def eof(self):
5f7b84
+        self._reset()
5f7b84
+
5f7b84
+    def _reset(self):
5f7b84
+        while self.typedef_tokens:
5f7b84
+            tok = self.typedef_tokens.pop(0)
5f7b84
+            if tok.kind == "IDENT" and OBSOLETE_TYPE_RE_.match(tok.text):
5f7b84
+                self.reporter.error(tok, "use of {!r}")
5f7b84
+
5f7b84
+    def _finish(self):
5f7b84
+        if not self.typedef_tokens: return
5f7b84
+        if self.typedef_tokens[-1].kind == "IDENT":
5f7b84
+            m = OBSOLETE_TYPE_RE_.match(self.typedef_tokens[-1].text)
5f7b84
+            if m:
5f7b84
+                if self._permissible_public_definition(m):
5f7b84
+                    self.typedef_tokens.clear()
5f7b84
+        self._reset()
5f7b84
+
5f7b84
+    def _permissible_public_definition(self, m):
5f7b84
+        if m.group(1) == "__": return False
5f7b84
+        name = m.group(2)
5f7b84
+        toks = self.typedef_tokens
5f7b84
+        ntok = len(toks)
5f7b84
+        if ntok == 3 and toks[1].kind == "IDENT":
5f7b84
+            defn = toks[1].text
5f7b84
+            n = OBSOLETE_TYPE_RE_.match(defn)
5f7b84
+            if n and n.group(1) == "__" and n.group(2) == name:
5f7b84
+                return True
5f7b84
+
5f7b84
+            if (name[:5] == "u_int" and name[-2:] == "_t"
5f7b84
+                and defn[:6] == "__uint" and defn[-2:] == "_t"
5f7b84
+                and name[5:-2] == defn[6:-2]):
5f7b84
+                return True
5f7b84
+
5f7b84
+            return False
5f7b84
+
5f7b84
+        if (name == "ulong" and ntok == 5
5f7b84
+            and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
5f7b84
+            and toks[2].kind == "IDENT" and toks[2].text == "long"
5f7b84
+            and toks[3].kind == "IDENT" and toks[3].text == "int"):
5f7b84
+            return True
5f7b84
+
5f7b84
+        if (name == "ushort" and ntok == 5
5f7b84
+            and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
5f7b84
+            and toks[2].kind == "IDENT" and toks[2].text == "short"
5f7b84
+            and toks[3].kind == "IDENT" and toks[3].text == "int"):
5f7b84
+            return True
5f7b84
+
5f7b84
+        if (name == "uint" and ntok == 4
5f7b84
+            and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
5f7b84
+            and toks[2].kind == "IDENT" and toks[2].text == "int"):
5f7b84
+            return True
5f7b84
+
5f7b84
+        return False
5f7b84
+
5f7b84
+def ObsoleteTypedefChecker(reporter, fname):
5f7b84
+    """Factory: produce an instance of the appropriate
5f7b84
+       obsolete-typedef checker for FNAME."""
5f7b84
+
5f7b84
+    # The obsolete rpc/ and rpcsvc/ headers are allowed to use the
5f7b84
+    # obsolete types, because it would be more trouble than it's
5f7b84
+    # worth to remove them from headers that we intend to stop
5f7b84
+    # installing eventually anyway.
5f7b84
+    if (fname.startswith("rpc/")
5f7b84
+        or fname.startswith("rpcsvc/")
5f7b84
+        or "/rpc/" in fname
5f7b84
+        or "/rpcsvc/" in fname):
5f7b84
+        return NoCheck(reporter)
5f7b84
+
5f7b84
+    # bits/types.h is allowed to define the __-versions of the
5f7b84
+    # obsolete types.
5f7b84
+    if (fname == "bits/types.h"
5f7b84
+        or fname.endswith("/bits/types.h")):
5f7b84
+        return ObsoletePrivateDefinitionsAllowed(reporter)
5f7b84
+
5f7b84
+    # sys/types.h is allowed to use the __-versions of the
5f7b84
+    # obsolete types, but only to define the unprefixed versions.
5f7b84
+    if (fname == "sys/types.h"
5f7b84
+        or fname.endswith("/sys/types.h")):
5f7b84
+        return ObsoletePublicDefinitionsAllowed(reporter)
5f7b84
+
5f7b84
+    return ObsoleteNotAllowed(reporter)
5f7b84
+
5f7b84
+#
5f7b84
+# Master control
5f7b84
+#
5f7b84
+
5f7b84
+class HeaderChecker:
5f7b84
+    """Perform all of the checks on each header.  This is also the
5f7b84
+       "reporter" object expected by tokenize_c and ConstructChecker.
5f7b84
+    """
5f7b84
+    def __init__(self):
5f7b84
+        self.fname = None
5f7b84
+        self.status = 0
5f7b84
+
5f7b84
+    def error(self, tok, message):
5f7b84
+        self.status = 1
5f7b84
+        if '{!r}' in message:
5f7b84
+            message = message.format(tok.text)
5f7b84
+        sys.stderr.write("{}:{}:{}: error: {}\n".format(
5f7b84
+            self.fname, tok.line, tok.column, message))
5f7b84
+
5f7b84
+    def check(self, fname):
5f7b84
+        self.fname = fname
5f7b84
+        try:
5f7b84
+            with open(fname, "rt") as fp:
5f7b84
+                contents = fp.read()
5f7b84
+        except OSError as e:
5f7b84
+            sys.stderr.write("{}: {}\n".format(fname, e.strerror))
5f7b84
+            self.status = 1
5f7b84
+            return
5f7b84
+
5f7b84
+        typedef_checker = ObsoleteTypedefChecker(self, self.fname)
5f7b84
+
5f7b84
+        for tok in tokenize_c(contents, self):
5f7b84
+            typedef_checker.examine(tok)
5f7b84
+
5f7b84
+def main():
5f7b84
+    ap = argparse.ArgumentParser(description=__doc__)
5f7b84
+    ap.add_argument("headers", metavar="header", nargs="+",
5f7b84
+                    help="one or more headers to scan for obsolete constructs")
5f7b84
+    args = ap.parse_args()
5f7b84
+
5f7b84
+    checker = HeaderChecker()
5f7b84
+    for fname in args.headers:
5f7b84
+        # Headers whose installed name begins with "finclude/" contain
5f7b84
+        # Fortran, not C, and this program should completely ignore them.
5f7b84
+        if not (fname.startswith("finclude/") or "/finclude/" in fname):
5f7b84
+            checker.check(fname)
5f7b84
+    sys.exit(checker.status)
5f7b84
+
5f7b84
+main()