Back to index

glibc  2.9
isomac.c
Go to the documentation of this file.
00001 /* Check system header files for ISO 9899:1990 (ISO C) compliance.
00002    Copyright (C) 1996,97,98,99,2001,02 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Jens Schweikhardt <schweikh@noc.dfn.de>, 1996.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 /* This is a simple minded program that tries to find illegal macro
00022    definitions in system header files. Illegal macro definitions are
00023    those not from the implementation namespace (i.e. not starting with
00024    an underscore) or not matching any identifier mandated by The
00025    Standard. Some common macro names are considered okay, e.g. all those
00026    beginning with E (which may be defined in <errno.h>) or ending in
00027    _MAX. See the arrays prefix[] and suffix[] below for details.
00028 
00029    In a compliant implementation no other macros can be defined, because
00030    you could write strictly conforming programs that may fail to compile
00031    due to syntax errors: suppose <stdio.h> defines PIPE_BUF, then the
00032    conforming
00033 
00034    #include <assert.h>
00035    #include <stdio.h>      <- or where the bogus macro is defined
00036    #include <string.h>
00037    #define STR(x) #x
00038    #define XSTR(x) STR(x)
00039    int main (void)
00040    {
00041      int PIPE_BUF = 0;
00042      assert (strcmp ("PIPE_BUF", XSTR (PIPE_BUF)) == 0);
00043      return 0;
00044    }
00045 
00046    is expected to compile and meet the assertion. If it does not, your
00047    compiler compiles some other language than Standard C.
00048 
00049    REQUIREMENTS:
00050      This program calls gcc to get the list of defined macros. If you
00051      don't have gcc you're probably out of luck unless your compiler or
00052      preprocessor has something similar to gcc's -dM option. Tune
00053      PRINT_MACROS in this case. This program assumes headers are found
00054      under /usr/include and that there is a writable /tmp directory.
00055      Tune SYSTEM_INCLUDE if your system differs.
00056      #define BROKEN_SYSTEM if system(NULL) bombs -- one more violation
00057      of ISO C, by the way.
00058 
00059    OUTPUT:
00060      Each header file name is printed, followed by illegal macro names
00061      and their definition. For the above example, you would see
00062      ...
00063      /usr/include/stdio.h
00064      #define PIPE_BUF 5120
00065      ...
00066      If your implementation does not yet incorporate Amendment 1 you
00067      will see messages about iso646.h, wctype.h and wchar.h not being
00068      found.  */
00069 
00070 #ifndef _GNU_SOURCE
00071 # define _GNU_SOURCE 1
00072 #endif
00073 
00074 #include <stdio.h>
00075 #include <stdlib.h>
00076 #include <string.h>
00077 
00078 #define HEADER_MAX          256
00079 
00080 static const char *macrofile;
00081 
00082 /* ISO C header names including Amendment 1 (without ".h" suffix).  */
00083 static char *header[] =
00084 {
00085   "assert", "ctype", "errno", "float", "iso646", "limits", "locale",
00086   "math", "setjmp", "signal", "stdarg", "stddef", "stdio", "stdlib",
00087   "string", "time", "wchar", "wctype"
00088 };
00089 
00090 /* Macros with these prefixes are considered okay.  */
00091 static char *prefix[] =
00092 {
00093   "_", "E", "is", "str", "mem", "SIG", "FLT_", "DBL_", "LDBL_",
00094   "LC_", "wmem", "wcs"
00095 };
00096 
00097 /* Macros with these suffixes are considered okay.  Will not work for
00098    parametrized macros with arguments.  */
00099 static char *suffix[] =
00100 {
00101   "_MAX", "_MIN"
00102 };
00103 
00104 /* These macros are considered okay. In fact, these are just more prefixes.  */
00105 static char *macros[] =
00106 {
00107   "BUFSIZ", "CHAR_BIT", "CHAR_MAX", "CHAR_MIN", "CLOCKS_PER_SEC",
00108   "DBL_DIG", "DBL_EPSILON", "DBL_MANT_DIG", "DBL_MAX",
00109   "DBL_MAX_10_EXP", "DBL_MAX_EXP", "DBL_MIN", "DBL_MIN_10_EXP",
00110   "DBL_MIN_EXP", "EDOM", "EILSEQ", "EOF", "ERANGE", "EXIT_FAILURE",
00111   "EXIT_SUCCESS", "FILENAME_MAX", "FLT_DIG", "FLT_EPSILON",
00112   "FLT_MANT_DIG", "FLT_MAX", "FLT_MAX_10_EXP", "FLT_MAX_EXP",
00113   "FLT_MIN", "FLT_MIN_10_EXP", "FLT_MIN_EXP", "FLT_RADIX",
00114   "FLT_ROUNDS", "FOPEN_MAX", "HUGE_VAL", "INT_MAX", "INT_MIN",
00115   "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MONETARY", "LC_NUMERIC",
00116   "LC_TIME", "LDBL_DIG", "LDBL_EPSILON", "LDBL_MANT_DIG", "LDBL_MAX",
00117   "LDBL_MAX_10_EXP", "LDBL_MAX_EXP", "LDBL_MIN", "LDBL_MIN_10_EXP",
00118   "LDBL_MIN_EXP", "LONG_MAX", "LONG_MIN", "L_tmpnam", "MB_CUR_MAX",
00119   "MB_LEN_MAX", "NDEBUG", "NULL", "RAND_MAX", "SCHAR_MAX",
00120   "SCHAR_MIN", "SEEK_CUR", "SEEK_END", "SEEK_SET", "SHRT_MAX",
00121   "SHRT_MIN", "SIGABRT", "SIGFPE", "SIGILL", "SIGINT", "SIGSEGV",
00122   "SIGTERM", "SIG_DFL", "SIG_ERR", "SIG_IGN", "TMP_MAX", "UCHAR_MAX",
00123   "UINT_MAX", "ULONG_MAX", "USHRT_MAX", "WCHAR_MAX", "WCHAR_MIN",
00124   "WEOF", "_IOFBF", "_IOLBF", "_IONBF", "abort", "abs", "acos",
00125   "acosf", "acosl", "and", "and_eq", "asctime", "asin", "asinf",
00126   "asinl", "assert", "atan", "atan2", "atan2f", "atan2l", "atanf",
00127   "atanl", "atexit", "atof", "atoi", "atol", "bitand", "bitor",
00128   "bsearch", "btowc", "calloc", "ceil", "ceilf", "ceill", "clearerr",
00129   "clock", "clock_t", "compl", "cos", "cosf", "cosh", "coshf",
00130   "coshl", "cosl", "ctime", "difftime", "div", "div_t", "errno",
00131   "exit", "exp", "expf", "expl", "fabs", "fabsf", "fabsl", "fclose",
00132   "feof", "ferror", "fflush", "fgetc", "fgetpos", "fgets", "fgetwc",
00133   "fgetws", "floor", "floorf", "floorl", "fmod", "fmodf", "fmodl",
00134   "fopen", "fprintf", "fputc", "fputs", "fputwc", "fputws", "fread",
00135   "free", "freopen", "frexp", "frexpf", "frexpl", "fscanf", "fseek",
00136   "fsetpos", "ftell", "fwide", "fwprintf", "fwrite", "fwscanf",
00137   "getc", "getchar", "getenv", "gets", "getwc", "getwchar", "gmtime",
00138   "isalnum", "isalpha", "iscntrl", "isdigit", "isgraph", "islower",
00139   "isprint", "ispunct", "isspace", "isupper", "iswalnum", "iswalpha",
00140   "iswcntrl", "iswctype", "iswdigit", "iswgraph", "iswlower",
00141   "iswprint", "iswpunct", "iswspace", "iswupper", "iswxdigit",
00142   "isxdigit", "labs", "ldexp", "ldexpf", "ldexpl", "ldiv", "ldiv_t",
00143   "localeconv", "localtime", "log", "log10", "log10f", "log10l",
00144   "logf", "logl", "longjmp", "malloc", "mblen", "mbrlen", "mbrtowc",
00145   "mbsinit", "mbsrtowcs", "mbstate_t", "mbstowcs", "mbtowc", "memchr",
00146   "memcmp", "memcpy", "memmove", "memset", "mktime", "modf", "modff",
00147   "modfl", "not", "not_eq", "offsetof", "or", "or_eq", "perror",
00148   "pow", "powf", "powl", "printf", "ptrdiff_t", "putc", "putchar",
00149   "puts", "putwc", "putwchar", "qsort", "raise", "rand", "realloc",
00150   "remove", "rename", "rewind", "scanf", "setbuf", "setjmp",
00151   "setlocale", "setvbuf", "sig_atomic_t", "signal", "sin", "sinf",
00152   "sinh", "sinhf", "sinhl", "sinl", "size_t", "sprintf", "sqrt",
00153   "sqrtf", "sqrtl", "srand", "sscanf", "stderr", "stdin", "stdout",
00154   "strcat", "strchr", "strcmp", "strcoll", "strcpy", "strcspn",
00155   "strerror", "strftime", "strlen", "strncat", "strncmp", "strncpy",
00156   "strpbrk", "strrchr", "strspn", "strstr", "strtod", "strtok",
00157   "strtol", "strtoul", "strxfrm", "swprintf", "swscanf", "system",
00158   "tan", "tanf", "tanh", "tanhf", "tanhl", "tanl", "time", "time_t",
00159   "tmpfile", "tmpnam", "tolower", "toupper", "towctrans", "towlower",
00160   "towupper", "ungetc", "ungetwc", "va_arg", "va_copy", "va_end", "va_start",
00161   "vfprintf", "vfwprintf", "vprintf", "vsprintf", "vswprintf",
00162   "vwprintf", "wchar_t", "wcrtomb", "wcscat", "wcschr", "wcscmp",
00163   "wcscoll", "wcscpy", "wcscspn", "wcsftime", "wcslen", "wcsncat",
00164   "wcsncmp", "wcsncpy", "wcspbrk", "wcsrchr", "wcsrtombs", "wcsspn",
00165   "wcsstr", "wcstod", "wcstok", "wcstol", "wcstombs", "wcstoul",
00166   "wcsxfrm", "wctob", "wctomb", "wctrans", "wctrans_t", "wctype",
00167   "wctype_t", "wint_t", "wmemchr", "wmemcmp", "wmemcpy", "wmemmove",
00168   "wmemset", "wprintf", "wscanf", "xor", "xor_eq"
00169 };
00170 
00171 #define NUMBER_OF_HEADERS              (sizeof header / sizeof *header)
00172 #define NUMBER_OF_PREFIXES             (sizeof prefix / sizeof *prefix)
00173 #define NUMBER_OF_SUFFIXES             (sizeof suffix / sizeof *suffix)
00174 #define NUMBER_OF_MACROS               (sizeof macros / sizeof *macros)
00175 
00176 
00177 /* Format string to build command to invoke compiler.  */
00178 static const char fmt[] = "\
00179 echo \"#include <%s>\" |\
00180 %s -E -dM -ansi -pedantic %s -D_LIBC -D_ISOMAC -DNOT_IN_libc -I. \
00181 -isystem `%s --print-prog-name=include` - 2> /dev/null > %s";
00182 
00183 
00184 /* The compiler we use (given on the command line).  */
00185 char *CC;
00186 /* The -I parameters for CC to find all headers.  */
00187 char *INC;
00188 
00189 static char *xstrndup (const char *, size_t);
00190 static const char **get_null_defines (void);
00191 static int check_header (const char *, const char **);
00192 
00193 int
00194 main (int argc, char *argv[])
00195 {
00196   int h;
00197   int result = 0;
00198   const char **ignore_list;
00199 
00200   CC = argc > 1 ? argv[1] : "gcc";
00201   INC = argc > 2 ? argv[2] : "";
00202 
00203   if (system (NULL) == 0)
00204     {
00205       puts ("Sorry, no command processor.");
00206       return EXIT_FAILURE;
00207     }
00208 
00209   /* First get list of symbols which are defined by the compiler.  */
00210   ignore_list = get_null_defines ();
00211 
00212   fputs ("Tested files:\n", stdout);
00213 
00214   for (h = 0; h < NUMBER_OF_HEADERS; ++h)
00215     {
00216       char file_name[HEADER_MAX];
00217       sprintf (file_name, "%s.h", header[h]);
00218       result |= check_header (file_name, ignore_list);
00219     }
00220 
00221   /* The test suite should return errors but for now this is not
00222      practical.  Give a warning and ask the user to correct the bugs.  */
00223   return result;
00224 }
00225 
00226 
00227 static char *
00228 xstrndup (const char *s, size_t n)
00229 {
00230   size_t len = n;
00231   char *new = malloc (len + 1);
00232 
00233   if (new == NULL)
00234     return NULL;
00235 
00236   new[len] = '\0';
00237   return memcpy (new, s, len);
00238 }
00239 
00240 
00241 static const char **
00242 get_null_defines (void)
00243 {
00244   char line[BUFSIZ], *command;
00245   char **result = NULL;
00246   size_t result_len = 0;
00247   size_t result_max = 0;
00248   FILE *input;
00249   int first = 1;
00250 
00251   macrofile = tmpnam (NULL);
00252 
00253   command = malloc (sizeof fmt + sizeof "/dev/null" + 2 * strlen (CC)
00254                   + strlen (INC) + strlen (macrofile));
00255 
00256   if (command == NULL)
00257     {
00258       puts ("No more memory.");
00259       exit (1);
00260     }
00261 
00262   sprintf (command, fmt, "/dev/null", CC, INC, CC, macrofile);
00263 
00264   if (system (command))
00265     {
00266       puts ("system() returned nonzero");
00267       return NULL;
00268     }
00269   free (command);
00270   input = fopen (macrofile, "r");
00271 
00272   if (input == NULL)
00273     {
00274       printf ("Could not read %s: ", macrofile);
00275       perror (NULL);
00276       return NULL;
00277     }
00278 
00279   while (fgets (line, sizeof line, input) != NULL)
00280     {
00281       int i, okay = 0;
00282       size_t endmac;
00283       char *start, *end;
00284       if (strlen (line) < 9 || line[7] != ' ')
00285        { /* "#define A" */
00286          printf ("Malformed input, expected '#define MACRO'\ngot '%s'\n",
00287                 line);
00288          continue;
00289        }
00290       if (line[8] == '_')
00291        /* It's a safe identifier.  */
00292        continue;
00293       if (result_len == result_max)
00294        {
00295          result_max += 10;
00296          result = realloc (result, result_max * sizeof (char **));
00297          if (result == NULL)
00298            {
00299              puts ("No more memory.");
00300              exit (1);
00301            }
00302        }
00303       start = &line[8];
00304       for (end = start + 1; !isspace (*end) && *end != '\0'; ++end)
00305        ;
00306       result[result_len] = xstrndup (start, end - start);
00307 
00308       if (strcmp (result[result_len], "NOT_IN_libc") != 0)
00309        {
00310          if (first)
00311            {
00312              fputs ("The following identifiers will be ignored since the compiler defines them\nby default:\n", stdout);
00313              first = 0;
00314            }
00315          puts (result[result_len]);
00316        }
00317       ++result_len;
00318     }
00319   if (result_len == result_max)
00320     {
00321       result_max += 1;
00322       result = realloc (result, result_max * sizeof (char **));
00323       if (result == NULL)
00324        {
00325          puts ("No more memory.");
00326          exit (1);
00327        }
00328     }
00329   result[result_len] = NULL;
00330   fclose (input);
00331   remove (macrofile);
00332 
00333   return (const char **) result;
00334 }
00335 
00336 
00337 static int
00338 check_header (const char *file_name, const char **except)
00339 {
00340   char line[BUFSIZ], *command;
00341   FILE *input;
00342   int result = 0;
00343 
00344   command = malloc (sizeof fmt + strlen (file_name) + 2 * strlen (CC)
00345                   + strlen (INC) + strlen (macrofile));
00346 
00347   if (command == NULL)
00348     {
00349       puts ("No more memory.");
00350       exit (1);
00351     }
00352 
00353   puts (file_name);
00354   sprintf (command, fmt, file_name, CC, INC, CC, macrofile);
00355 
00356   if (system (command))
00357     {
00358       puts ("system() returned nonzero");
00359       result = 1;
00360     }
00361   free (command);
00362   input = fopen (macrofile, "r");
00363 
00364   if (input == NULL)
00365     {
00366       printf ("Could not read %s: ", macrofile);
00367       perror (NULL);
00368       return 1;
00369     }
00370 
00371   while (fgets (line, sizeof line, input) != NULL)
00372     {
00373       int i, okay = 0;
00374       size_t endmac;
00375       const char **cpp;
00376       if (strlen (line) < 9 || line[7] != ' ')
00377        { /* "#define A" */
00378          printf ("Malformed input, expected '#define MACRO'\ngot '%s'\n",
00379                 line);
00380          result = 1;
00381          continue;
00382        }
00383       for (i = 0; i < NUMBER_OF_PREFIXES; ++i)
00384        {
00385          if (!strncmp (line+8, prefix[i], strlen (prefix[i]))) {
00386            ++okay;
00387            break;
00388          }
00389        }
00390       if (okay)
00391        continue;
00392       for (i = 0; i < NUMBER_OF_MACROS; ++i)
00393        {
00394          if (!strncmp (line + 8, macros[i], strlen (macros[i])))
00395            {
00396              ++okay;
00397              break;
00398            }
00399        }
00400       if (okay)
00401        continue;
00402       /* Find next char after the macro identifier; this can be either
00403         a space or an open parenthesis.  */
00404       endmac = strcspn (line + 8, " (");
00405       if (line[8+endmac] == '\0')
00406        {
00407          printf ("malformed input, expected '#define MACRO VALUE'\n"
00408                 "got '%s'\n", line);
00409          result = 1;
00410          continue;
00411        }
00412       for (i = 0; i < NUMBER_OF_SUFFIXES; ++i)
00413        {
00414          size_t len = strlen (suffix[i]);
00415          if (!strncmp (line + 8 + endmac - len, suffix[i], len))
00416            {
00417              ++okay;
00418              break;
00419            }
00420        }
00421       if (okay)
00422        continue;
00423       if (except != NULL)
00424        for (cpp = except; *cpp != NULL; ++cpp)
00425          {
00426            size_t len = strlen (*cpp);
00427            if (!strncmp (line + 8, *cpp, len) && isspace (line[8 + len]))
00428              {
00429               ++okay;
00430               break;
00431              }
00432          }
00433       if (!okay)
00434        {
00435          fputs (line, stdout);
00436          result = 2;
00437        }
00438     }
00439   fclose (input);
00440   remove (macrofile);
00441 
00442   return result;
00443 }
00444 
00445 /* EOF */