Index: gcc/config/darwin-c.c =================================================================== --- gcc/config/darwin-c.c.orig +++ gcc/config/darwin-c.c @@ -565,29 +565,180 @@ find_subframework_header (cpp_reader *pf return 0; } -/* Return the value of darwin_macosx_version_min suitable for the - __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ macro, - so '10.4.2' becomes 1040. The lowest digit is always zero. - Print a warning if the version number can't be understood. */ +/* Given a version string, return the version as a statically-allocated + array of three non-negative integers. If the version string is + invalid, return null. + + Version strings must consist of one, two, or three tokens, each + separated by a single period. Each token must contain only the + characters '0' through '9' and is converted to an equivalent + integer. Omitted tokens are treated as zeros. For example: + + "10" becomes {10,0,0} + "10.10" becomes {10,10,0} + "10.10.1" becomes {10,10,1} + "10.000010.1" becomes {10,10,1} + "10.010.001" becomes {10,10,1} + "000010.10.00001" becomes {10,10,1} */ + +enum version_components { MAJOR, MINOR, TINY }; + +static const unsigned long * +parse_version (const char *version_str) +{ + size_t version_len; + char *end; + static unsigned long version_array[3]; + + if (! version_str) + return NULL; + + version_len = strlen (version_str); + if (version_len < 1) + return NULL; + + /* Version string must consist of digits and periods only. */ + if (strspn (version_str, "0123456789.") != version_len) + return NULL; + + if (! ISDIGIT (version_str[0]) || ! ISDIGIT (version_str[version_len - 1])) + return NULL; + + version_array[MAJOR] = strtoul (version_str, &end, 10); + version_str = end + ((*end == '.') ? 1 : 0); + + /* Version string must not contain adjacent periods. */ + if (*version_str == '.') + return NULL; + + version_array[MINOR] = strtoul (version_str, &end, 10); + version_str = end + ((*end == '.') ? 1 : 0); + + version_array[TINY] = strtoul (version_str, &end, 10); + + /* Version string must contain no more than three tokens. */ + if (*end != '\0') + return NULL; + + return version_array; +} + +/* Given a three-component version represented as an array of + non-negative integers, return a statically-allocated string suitable + for the legacy __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ macro. + If the version is invalid and cannot be coerced into a valid form, + return null. + + The legacy format is a four-character string -- two chars for the + major number and one each for the minor and tiny numbers. Major + numbers are zero-padded if necessary. Minor and tiny numbers from + 10 through 99 are permitted but are clamped to 9 (for example, + {10,9,10} produces "1099"). Versions containing numbers greater + than 99 are rejected. */ + +static const char * +version_as_legacy_macro (const unsigned long *version) +{ + unsigned long major, minor, tiny; + static char result[sizeof "9999"]; + + if (! version) + return NULL; + + major = version[MAJOR]; + minor = version[MINOR]; + tiny = version[TINY]; + + if (major > 99 || minor > 99 || tiny > 99) + return NULL; + + minor = ((minor > 9) ? 9 : minor); + tiny = ((tiny > 9) ? 9 : tiny); + + /* NOTE: Cast result of sizeof so that result of sprintf is not + converted to an unsigned type. */ + if (sprintf (result, "%02lu%lu%lu", major, minor, tiny) + != (int) sizeof "9999" - 1) + return NULL; + + return result; +} + +/* Given a three-component version represented as an array of + non-negative integers, return a statically-allocated string suitable + for the modern __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ macro + or the __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ macro. If the + version is invalid, return null. + + The modern format is a five- or six-character string -- one or two + chars for the major number and two each for the minor and tiny + numbers, which are zero-padded if necessary (for example, {8,1,0} + produces "80100", and {10,10,1} produces "101001"). Versions + containing numbers greater than 99 are rejected. */ + static const char * -version_as_macro (void) +version_as_modern_macro (const unsigned long *version) { - static char result[] = "1000"; + unsigned long major, minor, tiny; + static char result[sizeof "999999"]; + + if (! version) + return NULL; + + major = version[MAJOR]; + minor = version[MINOR]; + tiny = version[TINY]; + + if (major > 99 || minor > 99 || tiny > 99) + return NULL; + + /* NOTE: 'sizeof ((x > y) ? "foo" : "bar")' returns size of char + pointer instead of char array, so use + '(x > y) ? sizeof "foo" : sizeof "bar"' instead. */ + /* NOTE: Cast result of sizeof so that result of sprintf is not + converted to an unsigned type. */ + if (sprintf (result, "%lu%02lu%02lu", major, minor, tiny) + != (int) ((major > 9) ? sizeof "999999" : sizeof "99999") - 1) + return NULL; - if (strncmp (darwin_macosx_version_min, "10.", 3) != 0) + return result; +} + +/* Return the value of darwin_macosx_version_min, suitably formatted + for the __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ macro. Values + representing OS X 10.9 and earlier are encoded using the legacy + four-character format, while 10.10 and later use a modern + six-character format. (For example, "10.9" produces "1090", and + "10.10.1" produces "101001".) If the value is invalid and cannot be + coerced into a valid form, print a warning and return "1000". */ + +static const char * +macosx_version_as_macro (void) +{ + const unsigned long *version_array; + const char *version_macro; + + version_array = parse_version (darwin_macosx_version_min); + if (! version_array) goto fail; - if (! ISDIGIT (darwin_macosx_version_min[3])) + + /* Do not assume that the major number will always be exactly 10. */ + if (version_array[MAJOR] < 10 || version_array[MAJOR] > 10) goto fail; - result[2] = darwin_macosx_version_min[3]; - if (darwin_macosx_version_min[4] != '\0' - && darwin_macosx_version_min[4] != '.') + + if (version_array[MAJOR] == 10 && version_array[MINOR] < 10) + version_macro = version_as_legacy_macro (version_array); + else + version_macro = version_as_modern_macro (version_array); + + if (! version_macro) goto fail; - return result; + return version_macro; fail: - error ("Unknown value %qs of -mmacosx-version-min", - darwin_macosx_version_min); + error ("unknown value %qs of -mmacosx-version-min", + darwin_macosx_version_min); return "1000"; } @@ -606,7 +757,7 @@ darwin_cpp_builtins (cpp_reader *pfile) builtin_define_with_value ("__APPLE_CC__", "1", false); builtin_define_with_value ("__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__", - version_as_macro(), false); + macosx_version_as_macro(), false); } /* Handle C family front-end options. */ Index: gcc/config/darwin-driver.c =================================================================== --- gcc/config/darwin-driver.c.orig +++ gcc/config/darwin-driver.c @@ -120,8 +120,6 @@ darwin_default_min_version (int * argc_p version_p = osversion + 1; if (ISDIGIT (*version_p)) major_vers = major_vers * 10 + (*version_p++ - '0'); - if (major_vers > 4 + 9) - goto parse_failed; if (*version_p++ != '.') goto parse_failed; version_pend = strchr(version_p, '.');