1 /* Check system header files for ISO 9899:1990 (ISO C) compliance.
2    Copyright (C) 1996-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /* This is a simple minded program that tries to find illegal macro
20    definitions in system header files. Illegal macro definitions are
21    those not from the implementation namespace (i.e. not starting with
22    an underscore) or not matching any identifier mandated by The
23    Standard. Some common macro names are considered okay, e.g. all those
24    beginning with E (which may be defined in <errno.h>) or ending in
25    _MAX. See the arrays prefix[] and suffix[] below for details.
26 
27    In a compliant implementation no other macros can be defined, because
28    you could write strictly conforming programs that may fail to compile
29    due to syntax errors: suppose <stdio.h> defines PIPE_BUF, then the
30    conforming
31 
32    #include <assert.h>
33    #include <stdio.h>      <- or where the bogus macro is defined
34    #include <string.h>
35    #define STR(x) #x
36    #define XSTR(x) STR(x)
37    int main (void)
38    {
39      int PIPE_BUF = 0;
40      assert (strcmp ("PIPE_BUF", XSTR (PIPE_BUF)) == 0);
41      return 0;
42    }
43 
44    is expected to compile and meet the assertion. If it does not, your
45    compiler compiles some other language than Standard C.
46 
47    REQUIREMENTS:
48      This program calls gcc to get the list of defined macros. If you
49      don't have gcc you're probably out of luck unless your compiler or
50      preprocessor has something similar to gcc's -dM option. Tune
51      PRINT_MACROS in this case. This program assumes headers are found
52      under /usr/include and that there is a writable /tmp directory.
53      Tune SYSTEM_INCLUDE if your system differs.
54      #define BROKEN_SYSTEM if system(NULL) bombs -- one more violation
55      of ISO C, by the way.
56 
57    OUTPUT:
58      Each header file name is printed, followed by illegal macro names
59      and their definition. For the above example, you would see
60      ...
61      /usr/include/stdio.h
62      #define PIPE_BUF 5120
63      ...
64      If your implementation does not yet incorporate Amendment 1 you
65      will see messages about iso646.h, wctype.h and wchar.h not being
66      found.  */
67 
68 #ifndef _GNU_SOURCE
69 # define _GNU_SOURCE 1
70 #endif
71 
72 #include <ctype.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <unistd.h>
77 
78 #define HEADER_MAX          256
79 
80 static char macrofile[] = "/tmp/isomac.XXXXXX";
81 
82 /* ISO C header names including Amendment 1 (without ".h" suffix).  */
83 static char *header[] =
84 {
85   "assert", "ctype", "errno", "float", "iso646", "limits", "locale",
86   "math", "setjmp", "signal", "stdarg", "stddef", "stdio", "stdlib",
87   "string", "time", "wchar", "wctype"
88 };
89 
90 /* Macros with these prefixes are considered okay.  */
91 static char *prefix[] =
92 {
93   "_", "E", "is", "str", "mem", "SIG", "FLT_", "DBL_", "LDBL_",
94   "LC_", "wmem", "wcs"
95 };
96 
97 /* Macros with these suffixes are considered okay.  Will not work for
98    parametrized macros with arguments.  */
99 static char *suffix[] =
100 {
101   "_MAX", "_MIN"
102 };
103 
104 /* These macros are considered okay. In fact, these are just more prefixes.  */
105 static char *macros[] =
106 {
107   "BUFSIZ", "CHAR_BIT", "CHAR_MAX", "CHAR_MIN", "CLOCKS_PER_SEC",
108   "DBL_DIG", "DBL_EPSILON", "DBL_MANT_DIG", "DBL_MAX",
109   "DBL_MAX_10_EXP", "DBL_MAX_EXP", "DBL_MIN", "DBL_MIN_10_EXP",
110   "DBL_MIN_EXP", "EDOM", "EILSEQ", "EOF", "ERANGE", "EXIT_FAILURE",
111   "EXIT_SUCCESS", "FILENAME_MAX", "FLT_DIG", "FLT_EPSILON",
112   "FLT_MANT_DIG", "FLT_MAX", "FLT_MAX_10_EXP", "FLT_MAX_EXP",
113   "FLT_MIN", "FLT_MIN_10_EXP", "FLT_MIN_EXP", "FLT_RADIX",
114   "FLT_ROUNDS", "FOPEN_MAX", "HUGE_VAL", "INT_MAX", "INT_MIN",
115   "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MONETARY", "LC_NUMERIC",
116   "LC_TIME", "LDBL_DIG", "LDBL_EPSILON", "LDBL_MANT_DIG", "LDBL_MAX",
117   "LDBL_MAX_10_EXP", "LDBL_MAX_EXP", "LDBL_MIN", "LDBL_MIN_10_EXP",
118   "LDBL_MIN_EXP", "LONG_MAX", "LONG_MIN", "L_tmpnam", "MB_CUR_MAX",
119   "MB_LEN_MAX", "NDEBUG", "NULL", "RAND_MAX", "SCHAR_MAX",
120   "SCHAR_MIN", "SEEK_CUR", "SEEK_END", "SEEK_SET", "SHRT_MAX",
121   "SHRT_MIN", "SIGABRT", "SIGFPE", "SIGILL", "SIGINT", "SIGSEGV",
122   "SIGTERM", "SIG_DFL", "SIG_ERR", "SIG_IGN", "TMP_MAX", "UCHAR_MAX",
123   "UINT_MAX", "ULONG_MAX", "USHRT_MAX", "WCHAR_MAX", "WCHAR_MIN",
124   "WEOF", "_IOFBF", "_IOLBF", "_IONBF", "abort", "abs", "acos",
125   "acosf", "acosl", "and", "and_eq", "asctime", "asin", "asinf",
126   "asinl", "assert", "atan", "atan2", "atan2f", "atan2l", "atanf",
127   "atanl", "atexit", "atof", "atoi", "atol", "bitand", "bitor",
128   "bsearch", "btowc", "calloc", "ceil", "ceilf", "ceill", "clearerr",
129   "clock", "clock_t", "compl", "cos", "cosf", "cosh", "coshf",
130   "coshl", "cosl", "ctime", "difftime", "div", "div_t", "errno",
131   "exit", "exp", "expf", "expl", "fabs", "fabsf", "fabsl", "fclose",
132   "feof", "ferror", "fflush", "fgetc", "fgetpos", "fgets", "fgetwc",
133   "fgetws", "floor", "floorf", "floorl", "fmod", "fmodf", "fmodl",
134   "fopen", "fprintf", "fputc", "fputs", "fputwc", "fputws", "fread",
135   "free", "freopen", "frexp", "frexpf", "frexpl", "fscanf", "fseek",
136   "fsetpos", "ftell", "fwide", "fwprintf", "fwrite", "fwscanf",
137   "getc", "getchar", "getenv", "gets", "getwc", "getwchar", "gmtime",
138   "isalnum", "isalpha", "iscntrl", "isdigit", "isgraph", "islower",
139   "isprint", "ispunct", "isspace", "isupper", "iswalnum", "iswalpha",
140   "iswcntrl", "iswctype", "iswdigit", "iswgraph", "iswlower",
141   "iswprint", "iswpunct", "iswspace", "iswupper", "iswxdigit",
142   "isxdigit", "labs", "ldexp", "ldexpf", "ldexpl", "ldiv", "ldiv_t",
143   "localeconv", "localtime", "log", "log10", "log10f", "log10l",
144   "logf", "logl", "longjmp", "malloc", "mblen", "mbrlen", "mbrtowc",
145   "mbsinit", "mbsrtowcs", "mbstate_t", "mbstowcs", "mbtowc", "memchr",
146   "memcmp", "memcpy", "memmove", "memset", "mktime", "modf", "modff",
147   "modfl", "not", "not_eq", "offsetof", "or", "or_eq", "perror",
148   "pow", "powf", "powl", "printf", "ptrdiff_t", "putc", "putchar",
149   "puts", "putwc", "putwchar", "qsort", "raise", "rand", "realloc",
150   "remove", "rename", "rewind", "scanf", "setbuf", "setjmp",
151   "setlocale", "setvbuf", "sig_atomic_t", "signal", "sin", "sinf",
152   "sinh", "sinhf", "sinhl", "sinl", "size_t", "sprintf", "sqrt",
153   "sqrtf", "sqrtl", "srand", "sscanf", "stderr", "stdin", "stdout",
154   "strcat", "strchr", "strcmp", "strcoll", "strcpy", "strcspn",
155   "strerror", "strftime", "strlen", "strncat", "strncmp", "strncpy",
156   "strpbrk", "strrchr", "strspn", "strstr", "strtod", "strtok",
157   "strtol", "strtoul", "strxfrm", "swprintf", "swscanf", "system",
158   "tan", "tanf", "tanh", "tanhf", "tanhl", "tanl", "time", "time_t",
159   "tmpfile", "tmpnam", "tolower", "toupper", "towctrans", "towlower",
160   "towupper", "ungetc", "ungetwc", "va_arg", "va_copy", "va_end", "va_start",
161   "vfprintf", "vfwprintf", "vprintf", "vsprintf", "vswprintf",
162   "vwprintf", "wchar_t", "wcrtomb", "wcscat", "wcschr", "wcscmp",
163   "wcscoll", "wcscpy", "wcscspn", "wcsftime", "wcslen", "wcsncat",
164   "wcsncmp", "wcsncpy", "wcspbrk", "wcsrchr", "wcsrtombs", "wcsspn",
165   "wcsstr", "wcstod", "wcstok", "wcstol", "wcstombs", "wcstoul",
166   "wcsxfrm", "wctob", "wctomb", "wctrans", "wctrans_t", "wctype",
167   "wctype_t", "wint_t", "wmemchr", "wmemcmp", "wmemcpy", "wmemmove",
168   "wmemset", "wprintf", "wscanf", "xor", "xor_eq"
169 };
170 
171 #define NUMBER_OF_HEADERS              (sizeof header / sizeof *header)
172 #define NUMBER_OF_PREFIXES             (sizeof prefix / sizeof *prefix)
173 #define NUMBER_OF_SUFFIXES             (sizeof suffix / sizeof *suffix)
174 #define NUMBER_OF_MACROS               (sizeof macros / sizeof *macros)
175 
176 
177 /* Format string to build command to invoke compiler.  */
178 static const char fmt[] = "\
179 echo \"#include <%s>\" |\
180 %s -E -dM -ansi -pedantic %s -D_LIBC -D_ISOMAC \
181 -DIN_MODULE=MODULE_extramodules -I. \
182 -isystem `%s --print-prog-name=include` - 2> /dev/null > %s";
183 
184 
185 /* The compiler we use (given on the command line).  */
186 char *CC;
187 /* The -I parameters for CC to find all headers.  */
188 char *INC;
189 
190 static char *xstrndup (const char *, size_t);
191 static const char **get_null_defines (void);
192 static int check_header (const char *, const char **);
193 
194 int
main(int argc,char * argv[])195 main (int argc, char *argv[])
196 {
197   int h;
198   int result = 0;
199   const char **ignore_list;
200 
201   CC = argc > 1 ? argv[1] : "gcc";
202   INC = argc > 2 ? argv[2] : "";
203 
204   if (system (NULL) == 0)
205     {
206       puts ("Sorry, no command processor.");
207       return EXIT_FAILURE;
208     }
209 
210   /* First get list of symbols which are defined by the compiler.  */
211   ignore_list = get_null_defines ();
212 
213   fputs ("Tested files:\n", stdout);
214 
215   for (h = 0; h < NUMBER_OF_HEADERS; ++h)
216     {
217       char file_name[HEADER_MAX];
218       sprintf (file_name, "%s.h", header[h]);
219       result |= check_header (file_name, ignore_list);
220     }
221 
222   remove (macrofile);
223 
224   /* The test suite should return errors but for now this is not
225      practical.  Give a warning and ask the user to correct the bugs.  */
226   return result;
227 }
228 
229 
230 static char *
xstrndup(const char * s,size_t n)231 xstrndup (const char *s, size_t n)
232 {
233   size_t len = n;
234   char *new = malloc (len + 1);
235 
236   if (new == NULL)
237     return NULL;
238 
239   new[len] = '\0';
240   return memcpy (new, s, len);
241 }
242 
243 
244 static const char **
get_null_defines(void)245 get_null_defines (void)
246 {
247   char line[BUFSIZ], *command;
248   char **result = NULL;
249   size_t result_len = 0;
250   size_t result_max = 0;
251   FILE *input;
252   int first = 1;
253 
254   int fd = mkstemp (macrofile);
255   if (fd == -1)
256     {
257       printf ("mkstemp failed: %m\n");
258       exit (1);
259     }
260   close (fd);
261 
262   command = malloc (sizeof fmt + sizeof "/dev/null" + 2 * strlen (CC)
263 		    + strlen (INC) + strlen (macrofile));
264 
265   if (command == NULL)
266     {
267       puts ("No more memory.");
268       exit (1);
269     }
270 
271   sprintf (command, fmt, "/dev/null", CC, INC, CC, macrofile);
272 
273   if (system (command))
274     {
275       puts ("system() returned nonzero");
276       free (command);
277       return NULL;
278     }
279   free (command);
280   input = fopen (macrofile, "r");
281 
282   if (input == NULL)
283     {
284       printf ("Could not read %s: ", macrofile);
285       perror (NULL);
286       return NULL;
287     }
288 
289   while (fgets (line, sizeof line, input) != NULL)
290     {
291       int i, okay = 0;
292       size_t endmac;
293       char *start, *end;
294       if (strlen (line) < 9 || line[7] != ' ')
295 	{ /* "#define A" */
296 	  printf ("Malformed input, expected '#define MACRO'\ngot '%s'\n",
297 		  line);
298 	  continue;
299 	}
300       if (line[8] == '_')
301 	/* It's a safe identifier.  */
302 	continue;
303       if (result_len == result_max)
304 	{
305 	  result_max += 10;
306 	  result = realloc (result, result_max * sizeof (char **));
307 	  if (result == NULL)
308 	    {
309 	      puts ("No more memory.");
310 	      exit (1);
311 	    }
312 	}
313       start = &line[8];
314       for (end = start + 1; !isspace (*end) && *end != '\0'; ++end)
315 	;
316       result[result_len] = xstrndup (start, end - start);
317 
318       if (strcmp (result[result_len], "IN_MODULE") != 0)
319 	{
320 	  if (first)
321 	    {
322 	      fputs ("The following identifiers will be ignored since the compiler defines them\nby default:\n", stdout);
323 	      first = 0;
324 	    }
325 	  puts (result[result_len]);
326 	}
327       ++result_len;
328     }
329   if (result_len == result_max)
330     {
331       result_max += 1;
332       result = realloc (result, result_max * sizeof (char **));
333       if (result == NULL)
334 	{
335 	  puts ("No more memory.");
336 	  exit (1);
337 	}
338     }
339   result[result_len] = NULL;
340   fclose (input);
341 
342   return (const char **) result;
343 }
344 
345 
346 static int
check_header(const char * file_name,const char ** except)347 check_header (const char *file_name, const char **except)
348 {
349   char line[BUFSIZ], *command;
350   FILE *input;
351   int result = 0;
352 
353   command = malloc (sizeof fmt + strlen (file_name) + 2 * strlen (CC)
354 		    + strlen (INC) + strlen (macrofile));
355 
356   if (command == NULL)
357     {
358       puts ("No more memory.");
359       exit (1);
360     }
361 
362   puts (file_name);
363   sprintf (command, fmt, file_name, CC, INC, CC, macrofile);
364 
365   if (system (command))
366     {
367       puts ("system() returned nonzero");
368       result = 1;
369     }
370   free (command);
371   input = fopen (macrofile, "r");
372 
373   if (input == NULL)
374     {
375       printf ("Could not read %s: ", macrofile);
376       perror (NULL);
377       return 1;
378     }
379 
380   while (fgets (line, sizeof line, input) != NULL)
381     {
382       int i, okay = 0;
383       size_t endmac;
384       const char **cpp;
385       if (strlen (line) < 9 || line[7] != ' ')
386 	{ /* "#define A" */
387 	  printf ("Malformed input, expected '#define MACRO'\ngot '%s'\n",
388 		  line);
389 	  result = 1;
390 	  continue;
391 	}
392       for (i = 0; i < NUMBER_OF_PREFIXES; ++i)
393 	{
394 	  if (!strncmp (line+8, prefix[i], strlen (prefix[i]))) {
395 	    ++okay;
396 	    break;
397 	  }
398 	}
399       if (okay)
400 	continue;
401       for (i = 0; i < NUMBER_OF_MACROS; ++i)
402 	{
403 	  if (!strncmp (line + 8, macros[i], strlen (macros[i])))
404 	    {
405 	      ++okay;
406 	      break;
407 	    }
408 	}
409       if (okay)
410 	continue;
411       /* Find next char after the macro identifier; this can be either
412 	 a space or an open parenthesis.  */
413       endmac = strcspn (line + 8, " (");
414       if (line[8+endmac] == '\0')
415 	{
416 	  printf ("malformed input, expected '#define MACRO VALUE'\n"
417 		  "got '%s'\n", line);
418 	  result = 1;
419 	  continue;
420 	}
421       for (i = 0; i < NUMBER_OF_SUFFIXES; ++i)
422 	{
423 	  size_t len = strlen (suffix[i]);
424 	  if (!strncmp (line + 8 + endmac - len, suffix[i], len))
425 	    {
426 	      ++okay;
427 	      break;
428 	    }
429 	}
430       if (okay)
431 	continue;
432       if (except != NULL)
433 	for (cpp = except; *cpp != NULL; ++cpp)
434 	  {
435 	    size_t len = strlen (*cpp);
436 	    if (!strncmp (line + 8, *cpp, len) && isspace (line[8 + len]))
437 	      {
438 		++okay;
439 		break;
440 	      }
441 	  }
442       if (!okay)
443 	{
444 	  fputs (line, stdout);
445 	  result = 2;
446 	}
447     }
448   fclose (input);
449 
450   return result;
451 }
452 
453 /* EOF */
454