+/* Given two version numbers, compares the two numbers.
+ A version number must match the regular expression
+ ([1-9][0-9]*|0)(\.([1-9][0-9]*|0))*
+*/
+static int
+compare_version_strings (const char *v1, const char *v2)
+{
+ int rresult;
+ regex_t r;
+
+ if (regcomp (&r, "^([1-9][0-9]*|0)(\\.([1-9][0-9]*|0))*$",
+ REG_EXTENDED | REG_NOSUB) != 0)
+ abort ();
+ rresult = regexec (&r, v1, 0, NULL, 0);
+ if (rresult == REG_NOMATCH)
+ fatal ("invalid version number `%s'", v1);
+ else if (rresult != 0)
+ abort ();
+ rresult = regexec (&r, v2, 0, NULL, 0);
+ if (rresult == REG_NOMATCH)
+ fatal ("invalid version number `%s'", v2);
+ else if (rresult != 0)
+ abort ();
+
+ return strverscmp (v1, v2);
+}
+
+
+/* version_compare built-in spec function.
+
+ This takes an argument of the following form:
+
+ <comparison-op> <arg1> [<arg2>] <switch> <result>
+
+ and produces "result" if the comparison evaluates to true,
+ and nothing if it doesn't.
+
+ The supported <comparison-op> values are:
+
+ >= true if switch is a later (or same) version than arg1
+ !> opposite of >=
+ < true if switch is an earlier version than arg1
+ !< opposite of <
+ >< true if switch is arg1 or later, and earlier than arg2
+ <> true if switch is earlier than arg1 or is arg2 or later
+
+ If the switch is not present, the condition is false unless
+ the first character of the <comparison-op> is '!'.
+
+ For example,
+ %:version-compare(>= 10.3 mmacosx-version-min= -lmx)
+ adds -lmx if -mmacosx-version-min=10.3.9 was passed. */
+
+static const char *
+version_compare_spec_function (int argc, const char **argv)
+{
+ int comp1, comp2;
+ size_t switch_len;
+ const char *switch_value = NULL;
+ int nargs = 1, i;
+ bool result;
+
+ if (argc < 3)
+ fatal ("too few arguments to %%:version-compare");
+ if (argv[0][0] == '\0')
+ abort ();
+ if ((argv[0][1] == '<' || argv[0][1] == '>') && argv[0][0] != '!')
+ nargs = 2;
+ if (argc != nargs + 3)
+ fatal ("too many arguments to %%:version-compare");
+
+ switch_len = strlen (argv[nargs + 1]);
+ for (i = 0; i < n_switches; i++)
+ if (!strncmp (switches[i].part1, argv[nargs + 1], switch_len)
+ && check_live_switch (i, switch_len))
+ switch_value = switches[i].part1 + switch_len;
+
+ if (switch_value == NULL)
+ comp1 = comp2 = -1;
+ else
+ {
+ comp1 = compare_version_strings (switch_value, argv[1]);
+ if (nargs == 2)
+ comp2 = compare_version_strings (switch_value, argv[2]);
+ else
+ comp2 = -1; /* This value unused. */
+ }
+
+ switch (argv[0][0] << 8 | argv[0][1])
+ {
+ case '>' << 8 | '=':
+ result = comp1 >= 0;
+ break;
+ case '!' << 8 | '<':
+ result = comp1 >= 0 || switch_value == NULL;
+ break;
+ case '<' << 8:
+ result = comp1 < 0;
+ break;
+ case '!' << 8 | '>':
+ result = comp1 < 0 || switch_value == NULL;
+ break;
+ case '>' << 8 | '<':
+ result = comp1 >= 0 && comp2 < 0;
+ break;
+ case '<' << 8 | '>':
+ result = comp1 < 0 || comp2 >= 0;
+ break;
+
+ default:
+ fatal ("unknown operator '%s' in %%:version-compare", argv[0]);
+ }
+ if (! result)
+ return NULL;
+
+ return argv[nargs + 2];
+}
+
+/* %:include builtin spec function. This differs from %include in that it
+ can be nested inside a spec, and thus be conditionalized. It takes
+ one argument, the filename, and looks for it in the startfile path.
+ The result is always NULL, i.e. an empty expansion. */
+
+static const char *
+include_spec_function (int argc, const char **argv)
+{
+ char *file;
+
+ if (argc != 1)
+ abort ();
+
+ file = find_a_file (&startfile_prefixes, argv[0], R_OK, 0);
+ read_specs (file ? file : argv[0], FALSE);
+
+ return NULL;
+}
+
+/* %:print-asm-header spec function. Print a banner to say that the
+ following output is from the assembler. */
+
+static const char *
+print_asm_header_spec_function (int arg ATTRIBUTE_UNUSED,
+ const char **argv ATTRIBUTE_UNUSED)
+{
+ printf (_("Assembler options\n=================\n\n"));
+ printf (_("Use \"-Wa,OPTION\" to pass \"OPTION\" to the assembler.\n\n"));
+ fflush (stdout);
+ return NULL;
+}