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

source code of glibc/stdlib/isomac.c