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 and '10.10.0' becomes 101000. The lowest digit is
- always zero, as is the second lowest for '10.10.x' and above.
- Print a warning if the version number can't be understood. */
+/* Given an OS X version VERSION_STR, return it as a statically-allocated array
+ of three integers. If VERSION_STR is invalid, return NULL.
+
+ VERSION_STR 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 non-negative decimal integer. Omitted
+ tokens become 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}
+ ".9.1" is invalid
+ "10..9" is invalid
+ "10.10." is invalid */
+
+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];
+
+ 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 VERSION -- a three-component OS X 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 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. Minor and tiny numbers
+ from 10 through 99 are permitted but are clamped to 9 (for example, {10,9,10}
+ produces "1099"). If VERSION contains numbers greater than 99, it is
+ rejected. */
+
+static const char *
+version_as_legacy_macro (const unsigned long *version)
+{
+ unsigned long major, minor, tiny;
+ static char result[5];
+
+ 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);
+
+ if (sprintf (result, "%lu%lu%lu", major, minor, tiny) != 4)
+ return NULL;
+
+ return result;
+}
+
+/* Given VERSION -- a three-component OS X 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. If VERSION
+ is invalid, return NULL.
+
+ The modern format is a six-character string -- two chars for each component,
+ with zero-padding if necessary (for example, {10,10,1} produces "101001"). If
+ VERSION contains numbers greater than 99, it is rejected. */
+
static const char *
-version_as_macro (void)
+version_as_modern_macro (const unsigned long *version)
{
- static char result[7] = "1000";
- int minorDigitIdx;
+ unsigned long major, minor, tiny;
+ static char result[7];
+
+ major = version[MAJOR];
+ minor = version[MINOR];
+ tiny = version[TINY];
+
+ if (major > 99 || minor > 99 || tiny > 99)
+ return NULL;
+
+ if (sprintf (result, "%02lu%02lu%02lu", major, minor, tiny) != 6)
+ return NULL;
+
+ return result;
+}
- if (strncmp (darwin_macosx_version_min, "10.", 3) != 0)
+/* 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
+ darwin_macosx_version_min 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]))
+
+ if (version_array[MAJOR] != 10)
goto fail;
- minorDigitIdx = 3;
- result[2] = darwin_macosx_version_min[minorDigitIdx++];
- if (ISDIGIT (darwin_macosx_version_min[minorDigitIdx]))
- {
- /* Starting with OS X 10.10, the macro ends '00' rather than '0',
- i.e. 10.10.x becomes 101000 rather than 10100. */
- result[3] = darwin_macosx_version_min[minorDigitIdx++];
- result[4] = '0';
- result[5] = '0';
- result[6] = '\0';
- }
- if (darwin_macosx_version_min[minorDigitIdx] != '\0'
- && darwin_macosx_version_min[minorDigitIdx] != '.')
+ if (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);
+ darwin_macosx_version_min);
return "1000";
}
builtin_define ("__CONSTANT_CFSTRINGS__");
builtin_define_with_value ("__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__",
- version_as_macro(), false);
+ macosx_version_as_macro(), false);
/* Since we do not (at 4.6) support ObjC gc for the NeXT runtime, the
following will cause a syntax error if one tries to compile gc attributed