+/******************************************************************************
+ * @file xmake.c
+ *****************************************************************************/
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xmake/cstr.h>
+#include <xmake/lib.h>
+#include <xmake/read.h>
+#include <xmake/rule.h>
+#include <xmake/variable.h>
+#include <xmake/xmake.h>
+
+struct variable *default_goal_var = 0;
+
+struct xmake_state *state = 0;
+const char *program_name = 0;
+
+#if defined (_WIN32) || defined (__WIN32__)
+const char *os_name = "Windows_NT";
+#elif defined (__PDOS386__)
+const char *os_name = "PDOS";
+#elif defined (__MSDOS__)
+const char *os_name = "MSDOS";
+#elif defined (__APPLE__)
+const char *os_name = "Apple";
+#elif defined (__linux__)
+const char *os_name = "Linux";
+#else
+const char *os_name = "Unix";
+#endif
+
+static int doing_inference_rule_commands = 0;
+
+static int rule_run_command (const char *name, char *p, char *q) {
+
+ int is_quiet = state->quiet, is_ignoring_errors = 0;
+ char *new_cmds, *s;
+
+ *q = '\0';
+ new_cmds = xstrdup (p);
+
+ *q = '\n';
+ new_cmds = variable_expand_line (new_cmds);
+
+ s = new_cmds;
+
+ while (isspace ((int) *s) || *s == '-' || *s == '@') {
+
+ if (*s == '-') {
+ is_quiet = 1;
+ }
+
+ if (*s == '@') {
+ is_ignoring_errors = 1;
+ }
+
+ s++;
+
+ }
+
+ if (!is_quiet) {
+ printf ("%s\n", new_cmds);
+ }
+
+ /*if (!dry_run) */{
+
+ int error = system (s);
+
+ if (!is_ignoring_errors && error) {
+
+ fprintf (stderr, "[%s] Error %d\n", name, error);
+ return 1;
+
+ }
+
+ }
+
+ free (new_cmds);
+ return 0;
+
+}
+
+static char *parse_command (char *text) {
+
+ char *cwd = get_current_directory ();
+ char *ret, *p;
+
+ if (strncmp (text, "./", 2) && strncmp (text, "../", 3)) {
+ return text;
+ }
+
+ while (1) {
+
+ if (strncmp (text, "./", 2) && strncmp (text, "../", 3)) {
+ break;
+ }
+
+ if (strncmp (text, "./", 2) == 0) {
+ text += 2;
+ } else if (strncmp (text, "../", 3) == 0) {
+
+ text += 3;
+
+ if ((p = strrchr (cwd, '/'))) {
+ *p = '\0';
+ }
+
+ }
+
+ }
+
+ ret = xmalloc (strlen (cwd) + 1 + strlen (text));
+ sprintf (ret, "%s/%s", cwd, text);
+
+ return ret;
+
+}
+
+static int run_commands (struct commands *cmds, char *name) {
+
+ char *p = parse_command (cmds->text), *q;
+ int error;
+
+ while (1) {
+
+ q = strchr (p, '\n');
+
+ if (q != cmds->text) {
+
+ while (q && q[-1] == '\\') {
+ q = strchr (q + 1, '\n');
+ }
+
+ }
+
+ if (!q) { break; };
+
+ if ((error = rule_run_command (name, p, q)) < 0) {
+ return error;
+ }
+
+ p = q + 1;
+
+ }
+
+ return 0;
+
+}
+
+static int rule_use (struct rule *r, char *name) {
+
+ struct dep *dep;
+
+ char *p, *star_name, *lesser_name;
+ int ret;
+
+ CString str;
+ cstr_new (&str);
+
+ for (dep = r->deps; dep; dep = dep->next) {
+
+ cstr_cat (&str, dep->name, strlen (dep->name));
+
+ if ((ret = rule_search_and_build (dep->name))) {
+ return ret;
+ }
+
+ if (dep->next) {
+ cstr_ccat (&str, ' ');
+ }
+
+ }
+
+ cstr_ccat (&str, '\0');
+
+ if (!r->cmds) {
+
+ cstr_free (&str);
+ return 0;
+
+ }
+
+ doing_inference_rule_commands = 0;
+
+ if (r->deps) {
+ lesser_name = xstrdup (r->deps->name);
+ } else {
+ lesser_name = xstrdup ("");
+ }
+
+ star_name = xstrdup (name);
+
+ if ((p = strrchr (star_name, '.'))) {
+ *p = '\0';
+ }
+
+ variable_change ("@", xstrdup (name), VAR_ORIGIN_AUTOMATIC);
+ variable_change ("<", lesser_name, VAR_ORIGIN_AUTOMATIC);
+ variable_change ("*", star_name, VAR_ORIGIN_AUTOMATIC);
+ variable_change ("^", xstrdup (str.data), VAR_ORIGIN_AUTOMATIC);
+
+ return run_commands (r->cmds, name);
+
+}
+
+static int suffix_rule_use (struct suffix_rule *s, char *name) {
+
+ char *lesser_name, *star_name;
+ char *p;
+
+ if (!s->cmds) { return 0; }
+ doing_inference_rule_commands = 1;
+
+ lesser_name = xmalloc (strlen (name) + strlen (s->first) + 1);
+ memcpy (lesser_name, name, strlen (name) + 1);
+
+ if ((p = strrchr (lesser_name, '.'))) {
+ *p = '\0';
+ }
+
+ strcat (lesser_name, s->first);
+ star_name = xstrdup (name);
+
+ if ((p = strrchr (star_name, '.'))) {
+ *p = '\0';
+ }
+
+ variable_change ("<", lesser_name, VAR_ORIGIN_AUTOMATIC);
+ variable_change ("*", star_name, VAR_ORIGIN_AUTOMATIC);
+
+ return run_commands (s->cmds, name);
+
+}
+
+static int single_suffix_rule_use (struct suffix_rule *s, char *name) {
+
+ char *lesser_name, *star_name;
+
+ if (!s->cmds) { return 0; }
+ doing_inference_rule_commands = 1;
+
+ lesser_name = xmalloc (strlen (name) + strlen (s->first) + 1);
+ strcpy (lesser_name, name);
+ strcat (lesser_name, s->first);
+
+ star_name = xstrdup (name);
+
+ variable_change ("<", lesser_name, VAR_ORIGIN_AUTOMATIC);
+ variable_change ("*", star_name, VAR_ORIGIN_AUTOMATIC);
+
+ return run_commands (s->cmds, name);
+
+}
+
+static int file_exists (char *name) {
+
+ FILE *f;
+
+ if (!(f = fopen (name, "r"))) {
+ return 0;
+ }
+
+ fclose (f);
+ return 1;
+
+}
+
+static char *find_target (char *target) {
+
+ struct variable *vpath_var;
+ char *vpath;
+
+ if (file_exists (target)) {
+ return target;
+ }
+
+ if (!(vpath_var = variable_find ("VPATH"))) {
+ return 0;
+ }
+
+ vpath = variable_expand_line (xstrdup (vpath_var->value));
+
+ while (vpath && *vpath) {
+
+ char *vpath_part, *new_target;
+ char saved_ch;
+
+ while (*vpath && (isspace ((int) *vpath) || (*vpath == ';'))) {
+ vpath++;
+ }
+
+ vpath_part = vpath;
+
+ while (*vpath && !isspace ((int) *vpath) && *vpath != ';') {
+ vpath++;
+ }
+
+ saved_ch = *vpath;
+ *vpath = '\0';
+
+ new_target = xmalloc (strlen (vpath_part) + 1 + strlen (target) + 1);
+ strcpy (new_target, vpath_part);
+
+ if (strchr (vpath_part, '\\')) {
+ strcat (new_target, "\\");
+ } else {
+ strcat (new_target, "/");
+ }
+
+ strcat (new_target, target);
+ *vpath = saved_ch;
+
+ if (file_exists (new_target)) {
+ return new_target;
+ }
+
+ free (new_target);
+
+ if (!(vpath = strchr (vpath, ';'))) {
+ break;
+ }
+
+ vpath++;
+
+ }
+
+ return 0;
+
+}
+
+int rule_search_and_build (char *name) {
+
+ struct rule *r;
+ struct suffix_rule *s;
+
+ char *suffix;
+ char *new_name;
+
+ if ((r = rule_find (name))) {
+ return rule_use (r, name);
+ }
+
+ if ((suffix = strrchr (name, '.'))) {
+
+ char *duplicate_name = xstrdup (name);
+ variable_change ("@", xstrdup (duplicate_name), VAR_ORIGIN_AUTOMATIC);
+
+ for (s = suffix_rules; s; s = s->next) {
+
+ if (s->second && strcmp (suffix, s->second) == 0) {
+
+ char *prereq_name, *p;
+ char *new_name;
+
+ prereq_name = xmalloc (strlen (duplicate_name) + strlen (s->first) + 1);
+ sprintf (prereq_name, "%s", duplicate_name);
+
+ if ((p = strrchr (prereq_name, '.'))) {
+ *p = '\0';
+ }
+
+ strcat (prereq_name, s->first);
+
+ if (!(new_name = find_target (prereq_name))) {
+
+ free (prereq_name);
+ continue;
+
+ }
+
+ if (prereq_name == new_name) {
+
+ free (prereq_name);
+ break;
+
+ }
+
+ free (prereq_name);
+
+ if (strlen (s->first) < strlen (s->second)) {
+ new_name = xrealloc (new_name, strlen (new_name) + strlen (s->second) - strlen (s->first) + 1);
+ }
+
+ if ((p = strrchr (new_name, '.'))) {
+ *p = '\0';
+ }
+
+ strcat (new_name, s->second);
+
+ free (duplicate_name);
+ duplicate_name = new_name;
+
+ break;
+
+ }
+
+ }
+
+ if (s) {
+
+ int ret = suffix_rule_use (s, duplicate_name);
+
+ free (duplicate_name);
+ return ret;
+
+ }
+
+ free (duplicate_name);
+
+ } else {
+
+ variable_change ("@", xstrdup (name), VAR_ORIGIN_AUTOMATIC);
+
+ for (s = suffix_rules; s; s = s->next) {
+
+ if (!s->second) {
+
+ char *prereq_name, *p;
+ char *new_name;
+
+ prereq_name = xmalloc (strlen (name) + strlen (s->first) + 1);
+ strcpy (prereq_name, name);
+ strcat (prereq_name, s->first);
+
+ if (!(new_name = find_target (prereq_name))) {
+
+ free (prereq_name);
+ continue;
+
+ }
+
+ if (prereq_name != new_name) {
+ free (prereq_name);
+ }
+
+ p = new_name + strlen (new_name) - strlen (s->first);
+ *p = '\0';
+
+ single_suffix_rule_use (s, new_name);
+ free (new_name);
+
+ return 0;
+
+ }
+
+ }
+
+ }
+
+ new_name = find_target (name);
+
+ if (new_name != name) {
+ free (new_name);
+ }
+
+ if (!new_name) {
+
+ fprintf (stderr, "%s: *** No rule to make target '%s'. Stop.\n", program_name, name);
+ return 1;
+
+ }
+
+ return 0;
+
+}
+
+int main (int argc, char **argv) {
+
+ int ret;
+ unsigned long i;
+
+ if (argc && *argv) {
+
+ char *p;
+ program_name = *argv;
+
+ if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) {
+ program_name = (p + 1);
+ }
+
+ }
+
+ variables_init ();
+ rules_init ();
+
+ default_goal_var = variable_add (xstrdup (".DEFAULT_GOAL"), xstrdup (""), VAR_ORIGIN_FILE);
+
+ variable_add (xstrdup ("OS"), xstrdup (os_name), VAR_ORIGIN_FILE);
+ variable_add (xstrdup ("MAKE"), xstrdup (argv[0]), VAR_ORIGIN_FILE);
+
+ state = xmalloc (sizeof (*state));
+ parse_args (argv, argc);
+
+ if (state->nb_directories > 0) {
+
+ char *arg;
+ size_t len = 0;
+
+ char *cwd = get_current_directory ();
+ unsigned long i;
+
+ state->path = xmalloc (strlen (cwd) + 2);
+ len = sprintf (state->path, "%s/", cwd);
+
+ for (i = 0; i < state->nb_directories; i++) {
+
+ arg = state->directories[i];
+
+#if defined (_WIN32)
+ if (strlen (arg) < 3 || !(isalpha ((int) arg[0]) && arg[1] == ':' && (arg[2] == '/' || arg[2] == '\\'))) {
+#else
+ if (arg[0] != '/') {
+#endif
+
+ state->path = xrealloc (state->path, len + strlen (arg) + 2);
+
+ len += sprintf (state->path + len, "%s/", arg);
+ state->path[len] = '\0';
+
+ } else {
+
+ free (state->path);
+
+ state->path = xmalloc (strlen (arg) + 1);
+ len = sprintf (state->path, "%s/", arg);
+
+ }
+
+ }
+
+ if (!directory_exists (state->path)) {
+
+ fprintf (stderr, "%s: *** '%s': No such directory. Stop.\n", program_name, state->path);
+ return EXIT_FAILURE;
+
+ }
+
+ if (!state->no_print) {
+ fprintf (stderr, "%s: Entering directory '%s'\n", program_name, state->path);
+ }
+
+ if (!change_directory (state->path)) {
+
+ fprintf (stderr, "%s: *** Failed to enter '%s'. Stop.\n", program_name, state->path);
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ {
+
+ char *name = "CURDIR", *cwd, *path;
+ size_t len;
+
+ cwd = get_current_directory ();
+ len = strlen (name) + 4 + strlen (cwd);
+
+ path = xmalloc (len + 1);
+ sprintf (path, "%s ?= %s", name, cwd);
+
+ parse_var_line (path, VAR_ORIGIN_FILE);
+ free (path);
+
+ /*variable_add (xstrdup ("CURDIR"), xstrdup (get_current_directory ()), VAR_ORIGIN_FILE);*/
+
+ }
+
+ if (state->nb_makefiles > 0) {
+
+ char *path;
+ unsigned long i;
+
+ for (i = 0; i < state->nb_makefiles; i++) {
+
+ path = state->makefiles[i];
+
+ if ((ret = read_makefile (path))) {
+
+ fprintf (stderr, "%s: *** Failed to read '%s'. Stop.\n", program_name, path);
+ goto out;
+
+ }
+
+ }
+
+ }
+
+ if (state->nb_goals == 0) {
+
+ if (default_goal_var->value[0] == '\0') {
+
+ fprintf (stderr, "%s: *** No targets. Stop.\n", program_name);
+
+ ret = EXIT_FAILURE;
+ goto out;
+
+ }
+
+ dynarray_add (&state->goals, &state->nb_goals, xstrdup (default_goal_var->value));
+
+ }
+
+ for (i = 0; i < state->nb_goals; i++) {
+
+ if ((ret = rule_search_and_build (state->goals[i]))) {
+ goto out;
+ }
+
+ }
+
+ ret = EXIT_SUCCESS;
+
+out:
+
+ if (state->nb_directories > 0 && !state->no_print) {
+ fprintf (stderr, "%s: Leaving directory '%s'\n", program_name, state->path);
+ }
+
+ return ret;
+
+}