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