--- /dev/null
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <https://unlicense.org>
--- /dev/null
+#******************************************************************************
+# @file Makefile.pdw
+#******************************************************************************
+AS=aswin
+CC=gccwin
+LD=ldwin
+
+COPTS=-S -O2 -fno-common -ansi -I. -I./include -I../pdos/pdpclib -I../pdos/src -D__WIN32__ -D__NOBIVA__ -D__PDOS__
+COBJ=cstr.o hashtab.o lib.o report.o read.o rule.o variable.o xmake.o
+
+all: clean xmake.exe
+
+xmake.exe: $(COBJ)
+ $(LD) -s -o xmake.exe ../pdos/pdpclib/w32start.o $(COBJ) ../pdos/pdpclib/msvcrt.a ../pdos/src/kernel32.a
+
+.c.o:
+ $(CC) $(COPTS) $<
+ $(AS) -o $@ $*.s
+ rm -f $*.s
+
+clean:
+ rm -f *.o xmake.exe
--- /dev/null
+#******************************************************************************
+# @file Makefile.unix
+#******************************************************************************
+SRCDIR ?= $(CURDIR)
+VPATH := $(SRCDIR)
+
+CC := gcc
+CFLAGS := -D_FILE_OFFSET_BITS=64 -I$(SRCDIR)/include -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90
+
+CSRC := cstr.c hashtab.c lib.c read.c report.c rule.c variable.c xmake.c
+
+ifeq ($(OS), Windows_NT)
+all: xmake.exe
+
+xmake.exe: $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+else
+all: xmake
+
+xmake: $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+endif
+
+clean:
+ if [ -f xmake ]; then rm -rf xmake; fi
+ if [ -f xmake.exe ]; then rm -rf xmake.exe; fi
--- /dev/null
+#******************************************************************************
+# @file Makefile.w32
+#******************************************************************************
+SRCDIR ?= $(CURDIR)
+VPATH := $(SRCDIR)
+
+CC := gcc
+CFLAGS := -D_FILE_OFFSET_BITS=64 -I$(SRCDIR)/include -O2 -Wall -Werror -Wextra -ansi -pedantic -std=c90
+
+CSRC := cstr.c hashtab.c lib.c read.c report.c rule.c variable.c xmake.c
+
+all: xmake.exe
+
+xmake.exe: $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+
+clean:
+ if exist xmake ( del /q xmake )
+ if exist xmake.exe ( del /q xmake.exe )
--- /dev/null
+All source code is Public Domain.
+
+## Obtain the source code
+
+ git clone https://git.candlhat.org/xmake.git
+
+## Building
+
+ BSD:
+
+ Make sure you have gcc and gmake installed then run gmake -f Makefile.unix.
+
+ Linux:
+
+ Make sure you have gcc and make installed then run make -f Makefile.unix.
+
+ macOS:
+
+ Make sure you have xcode command line tools installed then run make -f Makefile.unix.
+
+ Windows:
+
+ Make sure you have mingw installed and the location within your PATH variable then run mingw32-make.exe -f Makefile.w32.
--- /dev/null
+/******************************************************************************
+ * @file cstr.c
+ *****************************************************************************/
+#include <stdlib.h>
+#include <string.h>
+
+#include <xmake/cstr.h>
+
+extern void *xrealloc (void *__ptr, unsigned long __size);
+
+static void cstr_realloc (CString *cstr, long new_size) {
+
+ long size = cstr->size_allocated;
+
+ if (size < 8) {
+ size = 8;
+ }
+
+ while (size < new_size) {
+ size *= 2;
+ }
+
+ cstr->data = xrealloc (cstr->data, size);
+ cstr->size_allocated = size;
+
+}
+
+void cstr_ccat (CString *cstr, int ch) {
+
+ long size = cstr->size + 1;
+
+ if (size > cstr->size_allocated) {
+ cstr_realloc (cstr, size);
+ }
+
+ ((unsigned char *) cstr->data)[size - 1] = ch;
+ cstr->size = size;
+
+}
+
+void cstr_cat (CString *cstr, const char *str, long len) {
+
+ long size;
+
+ if (len <= 0) {
+ len = strlen (str) + 1 + len;
+ }
+
+ size = cstr->size + len;
+
+ if (size > cstr->size_allocated) {
+ cstr_realloc (cstr, size);
+ }
+
+ memmove (((unsigned char *) cstr->data) + cstr->size, str, len);
+ cstr->size = size;
+
+}
+
+void cstr_new (CString *cstr) {
+ memset (cstr, 0, sizeof (CString));
+}
+
+void cstr_free (CString *cstr) {
+
+ free (cstr->data);
+ cstr_new (cstr);
+
+}
--- /dev/null
+/******************************************************************************
+ * @file hashtab.c
+ *****************************************************************************/
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xmake/hashtab.h>
+
+static struct hashtab_entry *find_entry (struct hashtab_entry *entries, unsigned long capacity, struct hashtab_name *key);
+
+static int adjust_capacity (struct hashtab *table, unsigned long new_capacity) {
+
+ struct hashtab_entry *new_entries, *old_entries;
+ unsigned long i, new_count, old_capacity;
+
+ if ((new_entries = malloc (sizeof (*new_entries) * new_capacity)) == NULL) {
+ return -2;
+ }
+
+ for (i = 0; i < new_capacity; i++) {
+
+ struct hashtab_entry *entry = &new_entries[i];
+
+ entry->key = NULL;
+ entry->value = NULL;
+
+ }
+
+ old_entries = table->entries;
+ old_capacity = table->capacity;
+
+ new_count = 0;
+
+ for (i = 0; i < old_capacity; i++) {
+
+ struct hashtab_entry *entry = &old_entries[i], *dest;
+
+ if (entry->key == NULL) {
+ continue;
+ }
+
+ dest = find_entry (new_entries, new_capacity, entry->key);
+
+ dest->key = entry->key;
+ dest->value = entry->value;
+
+ new_count++;
+
+ }
+
+ free (old_entries);
+
+ table->capacity = new_capacity;
+ table->count = new_count;
+ table->entries = new_entries;
+ table->used = new_count;
+
+ return 0;
+
+}
+
+static struct hashtab_entry *find_entry (struct hashtab_entry *entries, unsigned long capacity, struct hashtab_name *key) {
+
+ struct hashtab_entry *tombstone = NULL;
+ unsigned long index;
+
+ for (index = key->hash % capacity; ; index = (index + 1) % capacity) {
+
+ struct hashtab_entry *entry = &entries[index];
+
+ if (entry->key == NULL) {
+
+ if (entry->value == NULL) {
+
+ if (tombstone == NULL) {
+ return entry;
+ }
+
+ return tombstone;
+
+ } else if (tombstone == NULL) {
+ tombstone = entry;
+ }
+
+ } else if (entry->key->bytes == key->bytes) {
+
+ if (memcmp (entry->key->chars, key->chars, key->bytes) == 0 && entry->key->hash == key->hash) {
+ return entry;
+ }
+
+ }
+
+ }
+
+}
+
+static unsigned long hash_string (const void *p, unsigned long length) {
+
+ const unsigned char *str = (const unsigned char *) p;
+ unsigned long i, result = 0;
+
+ for (i = 0; i < length; i++) {
+ result = (str[i] << 24) + (result >> 19) + (result << 16) + (result >> 13) + (str[i] << 8) - result;
+ }
+
+ return result;
+
+}
+
+struct hashtab_name *hashtab_alloc_name (const char *str) {
+
+ struct hashtab_name *name;
+ unsigned long bytes = strlen (str), hash = hash_string (str, bytes);
+
+ if ((name = malloc (sizeof (*name))) == NULL) {
+ return NULL;
+ }
+
+ name->bytes = bytes;
+ name->chars = str;
+ name->hash = hash;
+
+ return name;
+
+}
+
+struct hashtab_name *hashtab_get_key (struct hashtab *table, const char *name) {
+
+ struct hashtab_name *key;
+ struct hashtab_entry *entry;
+
+ if (table == NULL || table->count == 0 || !(key = hashtab_alloc_name (name))) {
+ return 0;
+ }
+
+ entry = find_entry (table->entries, table->capacity, key);
+ free (key);
+
+ return entry->key;
+
+}
+
+void *hashtab_get (struct hashtab *table, struct hashtab_name *key) {
+
+ struct hashtab_entry *entry;
+
+ if (table == NULL || table->count == 0) {
+ return NULL;
+ }
+
+ entry = find_entry (table->entries, table->capacity, key);
+
+ if (entry->key == NULL) {
+ return NULL;
+ }
+
+ return entry->value;
+
+}
+
+int hashtab_put (struct hashtab *table, struct hashtab_name *key, void *value) {
+
+ const long MIN_CAPACITY = 15;
+
+ struct hashtab_entry *entry;
+ int ret = 0;
+
+ if (table->used >= table->capacity / 2) {
+
+ long capacity = table->capacity * 2 - 1;
+
+ if (capacity < MIN_CAPACITY) {
+ capacity = MIN_CAPACITY;
+ }
+
+ if ((ret = adjust_capacity (table, capacity))) {
+ return ret;
+ }
+
+ }
+
+ entry = find_entry (table->entries, table->capacity, key);
+
+ if (entry->key == NULL) {
+
+ table->count++;
+
+ if (entry->value == NULL) {
+ table->used++;
+ }
+
+ }
+
+ entry->key = key;
+ entry->value = value;
+
+ return 0;
+
+}
+
+void hashtab_remove (struct hashtab *table, struct hashtab_name *key) {
+
+ struct hashtab_entry *entry;
+
+ if ((entry = find_entry (table->entries, table->capacity, key)) != NULL) {
+
+ entry->key = NULL;
+ entry->value = NULL;
+
+ --table->count;
+
+ }
+
+}
--- /dev/null
+/******************************************************************************
+ * @file command.h
+ *****************************************************************************/
+#ifndef _COMMAND_H
+#define _COMMAND_H
+
+struct commands {
+
+ char *text;
+ unsigned long len;
+
+};
+
+#endif /* _COMMAND_H */
--- /dev/null
+/******************************************************************************
+ * @file cstr.h
+ *****************************************************************************/
+#ifndef _CSTR_H
+#define _CSTR_H
+
+typedef struct CString {
+
+ long size;
+ void *data;
+
+ long size_allocated;
+
+} CString;
+
+void cstr_ccat (CString *cstr, int ch);
+void cstr_cat (CString *cstr, const char *str, long len);
+
+void cstr_new (CString *cstr);
+void cstr_free (CString *cstr);
+
+#endif /* _CSTR_H */
--- /dev/null
+/******************************************************************************
+ * @file dep.h
+ *****************************************************************************/
+#ifndef _DEP_H
+#define _DEP_H
+
+struct dep {
+
+ char *name;
+ struct dep *next;
+
+};
+
+#endif /* _DEP_H */
--- /dev/null
+/******************************************************************************
+ * @file hashtab.h
+ *****************************************************************************/
+#ifndef _HASHTAB_H
+#define _HASHTAB_H
+
+struct hashtab_name {
+
+ const char *chars;
+ unsigned long bytes, hash;
+
+};
+
+struct hashtab_entry {
+
+ struct hashtab_name *key;
+ void *value;
+
+};
+
+struct hashtab {
+
+ struct hashtab_entry *entries;
+ unsigned long capacity, count, used;
+
+};
+
+struct hashtab_name *hashtab_alloc_name (const char *str);
+struct hashtab_name *hashtab_get_key (struct hashtab *table, const char *name);
+
+int hashtab_put (struct hashtab *table, struct hashtab_name *key, void *value);
+
+void *hashtab_get (struct hashtab *table, struct hashtab_name *key);
+void hashtab_remove (struct hashtab *table, struct hashtab_name *key);
+
+#endif /* _HASHTAB_H */
--- /dev/null
+/******************************************************************************
+ * @file lib.h
+ *****************************************************************************/
+#ifndef _LIB_H
+#define _LIB_H
+
+#if defined (_WIN32)
+# define PATHSEP ";"
+#else
+# define PATHSEP ":"
+#endif
+
+struct option {
+
+ const char *rule;
+ int (*callback) (const char *);
+
+};
+
+char *xstrdup (const char *__s);
+char *xstrndup (const char *__s, unsigned long __len);
+
+int parse_args (char **__args, int __cnt);
+void dynarray_add (void *__ptab, unsigned long *__nb_ptr, void *__data);
+
+void *xmalloc (unsigned long __sz);
+void *xrealloc (void *__ptr, unsigned long __sz);
+
+
+char *get_current_directory (void);
+
+int change_directory (const char *__path);
+int directory_exists (const char *__path);
+
+#endif /* _LIB_H */
--- /dev/null
+/******************************************************************************
+ * @file read.h
+ *****************************************************************************/
+#ifndef _READ_H
+#define _READ_H
+
+int read_makefile (const char *filename);
+
+struct nameseq {
+
+ char *name;
+ struct nameseq *next;
+
+};
+
+void *parse_nameseq (char *line, size_t size);
+void record_files (struct nameseq *filenames, char *cmds, size_t cmds_idx, char *depstr);
+
+#endif /* _READ_H */
--- /dev/null
+/******************************************************************************
+ * @file report.h
+ *****************************************************************************/
+#ifndef _REPORT_H
+#define _REPORT_H
+
+enum report_type {
+
+ REPORT_WARNING,
+ REPORT_ERROR,
+ REPORT_FATAL_ERROR,
+ REPORT_INTERNAL_ERROR
+
+};
+
+#if defined (_WIN32)
+# define COLOR_ERROR 12
+# define COLOR_WARNING 13
+# define COLOR_INTERNAL_ERROR 19
+#else
+# define COLOR_ERROR 91
+# define COLOR_INTERNAL_ERROR 94
+# define COLOR_WARNING 95
+#endif
+
+unsigned long get_error_count (void);
+void report_at (const char *filename, unsigned long lineno, enum report_type type, const char *fmt, ...);
+
+#endif /* _REPORT_H */
--- /dev/null
+/******************************************************************************
+ * @file rule.h
+ *****************************************************************************/
+#ifndef _RULE_H
+#define _RULE_H
+
+#include <xmake/command.h>
+#include <xmake/dep.h>
+
+struct rule {
+
+ char *name;
+
+ struct dep *deps;
+ struct commands *cmds;
+
+};
+
+struct suffix_rule {
+
+ char *first, *second;
+ struct commands *cmds;
+
+ struct suffix_rule *next;
+
+};
+
+extern struct suffix_rule *suffix_rules;
+struct rule *rule_find (const char *name);
+
+void rule_add (char *name, struct dep *deps, struct commands *cmds);
+void rule_add_suffix (char *name, struct commands *cmds);
+void rules_init (void);
+
+#endif /* _RULE_H */
--- /dev/null
+/******************************************************************************
+ * @file variable.h
+ *****************************************************************************/
+#ifndef _VARIABLE_H
+#define _VARIABLE_H
+
+enum variable_flavor {
+
+ VAR_FLAVOR_RECURSIVELY_EXPANDED,
+ VAR_FLAVOR_SIMPLY_EXPANDED,
+ VAR_FLAVOR_IMMEDIATELY_EXPANDED
+
+};
+
+enum variable_origin {
+
+ VAR_ORIGIN_AUTOMATIC,
+ VAR_ORIGIN_COMMAND_LINE,
+ VAR_ORIGIN_FILE
+
+};
+
+struct variable {
+
+ char *name, *value;
+
+ enum variable_flavor flavor;
+ enum variable_origin origin;
+
+};
+
+struct variable *variable_add (char *name, char *value, enum variable_origin origin);
+struct variable *variable_change (char *name, char *value, enum variable_origin origin);
+struct variable *variable_find (char *name);
+
+char *variable_expand_line (char *line);
+
+void parse_var_line (char *line, enum variable_origin origin);
+void variables_init (void);
+
+#endif /* _VARIABLE_H */
--- /dev/null
+/******************************************************************************
+ * @file xmake.h
+ *****************************************************************************/
+#ifndef _XMAKE_H
+#define _XMAKE_H
+
+struct xmake_state {
+
+ char **makefiles;
+ unsigned long nb_makefiles;
+
+ char **goals;
+ unsigned long nb_goals;
+
+ char **include_paths;
+ unsigned long nb_include_paths;
+
+ char **directories;
+ unsigned long nb_directories;
+
+ char *path;
+ int quiet, no_print;
+
+};
+
+extern struct xmake_state *state;
+extern const char *program_name;
+
+int rule_search_and_build (char *name);
+
+#endif /* _XMAKE_H */
--- /dev/null
+/******************************************************************************
+ * @file lib.c
+ *****************************************************************************/
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xmake/cstr.h>
+#include <xmake/lib.h>
+#include <xmake/report.h>
+#include <xmake/variable.h>
+#include <xmake/xmake.h>
+
+size_t len = 0;
+
+static int print_help (const char *arg) {
+
+ (void) arg;
+
+ fprintf (stderr, "Usage: %s [options] [target] ...\n\n", program_name);
+ fprintf (stderr, "Options:\n\n");
+
+ fprintf (stderr, " -B Ignored.\n");
+ fprintf (stderr, "\n");
+
+ fprintf (stderr, " -C DIRECTORY Change to DIRECTORY before doing anything.\n");
+ fprintf (stderr, " -I DIRECTORY Search DIRECTORY for included makefiles.\n");
+ fprintf (stderr, "\n");
+
+ fprintf (stderr, " -f FILE Read FILE as a makefile.\n");
+ fprintf (stderr, " -s Don't echo recipes.\n");
+ fprintf (stderr, "\n");
+
+ fprintf (stderr, " --no-print-directory Don't print the current directory.\n");
+ fprintf (stderr, " --help Print this help information.\n");
+
+ fprintf (stderr, "\n");
+ exit (EXIT_SUCCESS);
+
+}
+
+static int print_version (const char *arg) {
+
+ (void) arg;
+
+ fprintf (stderr, "%s: version 0.0.1\n", program_name);
+ return 1;
+
+}
+
+static int set_quiet (const char *arg) {
+
+ (void) arg;
+
+ state->quiet = 1;
+ return 0;
+
+}
+
+static int set_no_print (const char *arg) {
+
+ (void) arg;
+
+ state->no_print = 1;
+ return 0;
+
+}
+
+static int add_include_path (const char *path) {
+
+ const char *in = path;
+ const char *p;
+
+ do {
+
+ int c;
+
+ CString str;
+ cstr_new (&str);
+
+ for (p = in; c = *p, c != '\0' && c != PATHSEP[0]; p++) {
+
+ if (c == '\\') { c = '/'; }
+ cstr_ccat (&str, c);
+
+ }
+
+ if (str.size) {
+
+ if (((char *) str.data)[str.size - 1] != '/') {
+ cstr_ccat (&str, '/');
+ }
+
+ cstr_ccat (&str, '\0');
+ dynarray_add (&state->include_paths, &state->nb_include_paths, xstrdup (str.data));
+
+ }
+
+ cstr_free (&str);
+ in = (p + 1);
+
+ } while (*p != '\0');
+
+ return 0;
+
+}
+
+static int ignored (const char *arg) {
+
+ (void) arg;
+ return 0;
+
+}
+
+static int add_directory (const char *path) {
+
+ char *arg = xstrdup (path);
+
+ while (arg[strlen (arg) - 1] == '/' || arg[strlen (arg) - 1] == '\\') {
+ arg[strlen (arg) - 1] = '\0';
+ }
+
+ dynarray_add (&state->directories, &state->nb_directories, arg);
+ return 0;
+
+}
+
+static int add_makefile (const char *name) {
+
+ dynarray_add (&state->makefiles, &state->nb_makefiles, xstrdup (name));
+ return 0;
+
+}
+
+static int non_option (const char *arg) {
+
+ if (strchr (arg, '=')) {
+
+ char *temp = xstrdup (arg);
+
+ parse_var_line (temp, VAR_ORIGIN_COMMAND_LINE);
+ free (temp);
+
+ } else {
+ dynarray_add (&state->goals, &state->nb_goals, xstrdup (arg));
+ }
+
+ return 0;
+
+}
+
+static struct option opts[] = {
+
+ { "--help", &print_help },
+ { "-h", &print_help },
+
+ { "--version", &print_version },
+
+ { "--silent", &set_quiet },
+ { "--quiet", &set_quiet },
+ { "-s", &set_quiet },
+
+ { "--no-print-directory", &set_no_print },
+
+ { "--no-builtin-rules", &ignored },
+ { "-r", &ignored },
+
+ { "--include_dir=", &add_include_path },
+ { "-I:", &add_include_path },
+
+ { "--always-make", &ignored },
+ { "-B", &ignored },
+
+ { "--directory=", &add_directory },
+ { "-C:", &add_directory },
+
+ { "--makefile=", &add_makefile },
+ { "--file=", &add_makefile },
+ { "-f:", &add_makefile },
+
+ { 0, &non_option }
+
+};
+
+static int match_rule (const char *rule, const char *arg) {
+
+ const char *ptr;
+
+ assert (rule);
+ assert (arg);
+
+ while (*rule == *arg && *rule && *arg) {
+
+ rule++;
+ arg++;
+
+ }
+
+ switch (*rule) {
+
+ case '\0':
+ return *arg == '\0';
+
+ case '[':
+
+ ptr = ++rule;
+
+ while (*ptr != ']') {
+ ptr++;
+ }
+
+ return !strncmp (rule, arg, ptr - rule) ? match_rule (ptr + 1, arg + (ptr - rule)) : match_rule (ptr + 1, arg);
+
+ case '{':
+
+ do {
+
+ ptr = ++rule;
+
+ while (*ptr != '}' && *ptr != '|') {
+ ptr++;
+ }
+
+ if (strncmp (rule, arg, ptr - rule) == 0) {
+
+ arg = arg + (ptr - rule);
+
+ do {
+
+ rule = ptr;
+ ptr++;
+
+ } while (*rule != '}');
+
+ return match_rule (ptr, arg);
+
+ }
+
+ rule = ptr;
+
+ } while (*rule == '|');
+
+ break;
+
+ }
+
+ return 0;
+
+}
+
+static int match_arg (struct option opt, int argc, char **argv, int *ret) {
+
+ size_t rule_len, arg_len;
+ assert (opt.callback);
+
+ *ret = 0;
+
+ rule_len = strlen (opt.rule);
+ arg_len = strlen (argv[0]);
+
+ switch (opt.rule[rule_len - 1]) {
+
+ case ':':
+
+ rule_len--;
+
+ if (strncmp (opt.rule, argv[0], rule_len) == 0) {
+
+ if (arg_len == rule_len) {
+
+ if (argc < 2) {
+
+ report_at (program_name, 0, REPORT_ERROR, "missing argument to '%s'", opt.rule);
+
+ *ret = 1;
+ return 0;
+
+ }
+
+ *ret = opt.callback (argv[1]);
+ return 2;
+
+ } else {
+
+ assert (arg_len > rule_len);
+
+ *ret = opt.callback (argv[0] + rule_len);
+ return 1;
+
+ }
+
+ }
+
+ break;
+
+ case '<':
+
+ rule_len--;
+ /* fall through */
+
+ case '=':
+
+ if (strncmp (opt.rule, argv[0], rule_len) == 0) {
+
+ if (arg_len == rule_len) {
+
+ report_at (program_name, 0, REPORT_ERROR, "missing argument to '%s'", argv[0]);
+
+ *ret = 1;
+ return 0;
+
+ }
+
+ *ret = opt.callback (argv[0] + rule_len);
+ return 1;
+
+ }
+
+ break;
+
+ default:
+
+ if (match_rule (opt.rule, argv[0])) {
+
+ *ret = opt.callback (argv[0]);
+ return 1;
+
+ }
+
+ }
+
+ return 0;
+
+}
+
+
+char *xstrdup (const char *__s) {
+
+ char *p = xmalloc (strlen (__s) + 1);
+
+ strcpy (p, __s);
+ return p;
+
+}
+
+char *xstrndup (const char *__s, unsigned long __len) {
+
+ char *p = xmalloc (__len + 1);
+
+ memcpy (p, __s, __len);
+ return p;
+
+}
+
+int parse_args (char **__args, int __cnt) {
+
+ struct option *opt, *last;
+ int i, c, ret;
+
+ for (last = opts; last->rule; last++) {
+ ;
+ }
+
+ for (i = 1; i < __cnt; ) {
+
+ if (*(__args[i]) == '-') {
+
+ for (opt = opts, c = 0; opt->rule && !c; opt++) {
+
+ c = match_arg (*opt, __cnt - i, __args + i, &ret);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ }
+
+ if (c) {
+ i += c;
+ } else {
+
+ report_at (program_name, 0, REPORT_ERROR, "unrecognized option '%s'", __args[i]);
+ return 1;
+
+ }
+
+ } else {
+
+ if ((ret = last->callback (__args[i])) != 0) {
+ return ret;
+ }
+
+ i++;
+
+ }
+
+ }
+
+ if (!state->nb_makefiles) {
+ dynarray_add (&state->makefiles, &state->nb_makefiles, xstrdup ("Makefile"));
+ }
+
+ return 0;
+
+}
+
+void dynarray_add (void *__ptab, unsigned long *__nb_ptr, void *__data) {
+
+ long nb, nb_alloc;
+ void **pp;
+
+ nb = *__nb_ptr;
+ pp = *(void ***) __ptab;
+
+ if ((nb & (nb - 1)) == 0) {
+
+ if (!nb) {
+ nb_alloc = 1;
+ } else {
+ nb_alloc = nb * 2;
+ }
+
+ pp = xrealloc (pp, nb_alloc * sizeof (void *));
+ *(void ***) __ptab = pp;
+
+ }
+
+ pp[nb++] = __data;
+ *__nb_ptr = nb;
+
+}
+
+void *xmalloc (unsigned long __sz) {
+
+ void *ptr = malloc (__sz);
+
+ if (!ptr && __sz) {
+
+ report_at (program_name, 0, REPORT_ERROR, "memory full (malloc)");
+ exit (EXIT_FAILURE);
+
+ }
+
+ memset (ptr, 0, __sz);
+ return ptr;
+
+}
+
+void *xrealloc (void *__ptr, unsigned long __sz) {
+
+ void *ptr = realloc (__ptr, __sz);
+
+ if (!ptr && __sz) {
+
+ report_at (program_name, 0, REPORT_ERROR, "memory full (realloc)");
+ exit (EXIT_FAILURE);
+
+ }
+
+ return ptr;
+
+}
+
+
+#if defined(_WIN32)
+# include <windows.h>
+
+char *get_current_directory (void) {
+
+ static TCHAR tszBuffer[4096];
+ size_t i;
+
+ DWORD dwRet;
+
+ if ((dwRet = GetCurrentDirectory (sizeof (tszBuffer), tszBuffer)) == 0) {
+
+ report_at (program_name, 0, REPORT_FATAL_ERROR, "failed to get current directory");
+ exit (EXIT_FAILURE);
+
+ }
+
+ for (i = 0; i < strlen (tszBuffer); i++) {
+
+ if (tszBuffer[i] == '\\') {
+ tszBuffer[i] = '/';
+ }
+
+ }
+
+ return tszBuffer;
+
+}
+
+int change_directory (const char *__path) {
+ return (SetCurrentDirectory (__path) != 0);
+}
+
+int directory_exists (const char *__path) {
+
+ DWORD dwAttrib = GetFileAttributes (__path);
+ return ((dwAttrib != -1LU) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
+
+}
+#else
+# include <dirent.h>
+# include <unistd.h>
+
+char *get_current_directory (void) {
+
+ static char cwd[4096] = { 0 };
+ memset (cwd, 0, sizeof (cwd));
+
+ if (getcwd (cwd, sizeof (cwd))) {
+ return cwd;
+ }
+
+ report_at (program_name, 0, REPORT_FATAL_ERROR, "failed to get current directory");
+ exit (EXIT_FAILURE);
+
+}
+
+int change_directory (const char *__path) {
+ return (chdir (__path) == 0);
+}
+
+int directory_exists (const char *__path) {
+
+ DIR *dir;
+
+ if ((dir = opendir (__path))) {
+
+ closedir (dir);
+ return 1;
+
+ }
+
+ return 0;
+
+}
+#endif
--- /dev/null
+/******************************************************************************
+ * @file read.c
+ *****************************************************************************/
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xmake/command.h>
+#include <xmake/dep.h>
+#include <xmake/lib.h>
+#include <xmake/read.h>
+#include <xmake/rule.h>
+#include <xmake/variable.h>
+#include <xmake/xmake.h>
+
+extern struct variable *default_goal_var;
+
+struct linebuf {
+
+ char *start, *memory;
+ unsigned long size;
+
+ FILE *f;
+
+};
+
+static char *memory_fgets (char *str, int num, char **source_p) {
+
+ char *source = *source_p;
+ char *p = strchr (source, '\n');
+
+ if (source[0] == '\0') {
+ return 0;
+ }
+
+ if (p && p - source < num - 1) {
+
+ memcpy (str, source, p - source + 1);
+ str[p - source + 1] = '\0';
+
+ *source_p += p - source + 1;
+ return str;
+
+ }
+
+ if ((int) strlen (source) > num - 1) {
+
+ memcpy (str, source, num - 1);
+ str[num - 1] = '\0';
+
+ *source_p += num - 1;;
+ return str;
+
+ }
+
+ strcpy (str, source);
+
+ *source_p += strlen (source);
+ return str;
+
+}
+
+static long read_line (struct linebuf *lbuf) {
+
+ long lines_read = 0;
+
+ char *p = lbuf->start;
+ char *end = lbuf->start + lbuf->size;
+
+ while (lbuf->f ? fgets (p, end - p, lbuf->f) : memory_fgets (p, end - p, &(lbuf->memory))) {
+
+ size_t offset;
+
+ p += strlen (p);
+ offset = p - lbuf->start;
+
+ lines_read++;
+
+ if (offset >= 2) {
+
+ if (p[-1] == '\n' && p[-2] != '\\') {
+
+ p[-1] = '\0';
+ break;
+
+ }
+
+ } else if (offset >= 1) {
+
+ if (p[-1] == '\n') {
+
+ p[-1] = '\0';
+ break;
+
+ }
+
+ }
+
+ if (end - p >= 80) {
+ continue;
+ }
+
+ lbuf->size *= 2;
+ lbuf->start = xrealloc (lbuf->start, lbuf->size);
+
+ p = lbuf->start + offset;
+ end = lbuf->start + lbuf->size;
+
+ }
+
+ return lines_read;
+
+}
+
+static void remove_backslash_newlines (char *line) {
+
+ char *in = line, *out = line;
+ char *p;
+
+ if (!(p = strchr (in, '\n'))) {
+ return;
+ }
+
+ do {
+
+ size_t out_line_len = p - in - 1;
+
+ if (out != in) {
+ memmove (out, in, out_line_len);
+ }
+
+ out += out_line_len;
+ in = p + 1;
+
+ while (isspace ((int) *in)) {
+ in++;
+ }
+
+ *(out++) = ' ';
+
+ } while ((p = strchr (in, '\n')));
+
+ memmove (out, in, strlen (in) + 1);
+
+}
+
+static void remove_comment (char *line) {
+
+ char *p;
+
+ if ((p = strchr (line, '#'))) {
+ *p = '\0';
+ }
+
+}
+
+static int include_makefile (const char *filename) {
+
+ char *path, *new_name;
+ unsigned long i;
+
+ if (!read_makefile (filename)) {
+ return 0;
+ }
+
+ for (i = 0; i < state->nb_include_paths; i++) {
+
+ path = state->include_paths[i];
+
+ new_name = xmalloc (strlen (path) + strlen (filename) + 1);
+ sprintf (new_name, "%s%s", path, filename);
+
+ if (!read_makefile (new_name)) {
+
+ free (new_name);
+ return 0;
+
+ }
+
+ free (new_name);
+
+ }
+
+ fprintf (stderr, "%s: *** Failed to include '%s'. Stop.\n", program_name, filename);
+ return 1;
+
+}
+
+static int read_lbuf (struct linebuf *lbuf, int set_default) {
+
+ struct nameseq* filenames = 0;
+ int ret;
+
+ char *cmds, *clean = 0, *depstr = 0, *q;
+ char *colon, *semicolonp, *commentp;
+
+ long lines_read;
+ size_t clean_size = 0, cmds_idx = 0, cmds_sz = 256;
+
+ cmds = xmalloc (cmds_sz);
+
+#define record_waiting_files() \
+ do { \
+ if (filenames) { \
+ record_files (filenames, cmds, cmds_idx, depstr); \
+ filenames = 0; \
+ } \
+ cmds_idx = 0; \
+ } while (0)
+
+ while ((lines_read = read_line (lbuf))) {
+
+ char *line = lbuf->start;
+ char *p, *src, *dst;
+
+ if (line[0] == '\0') {
+ continue;
+ }
+
+ if (line[0] == ' ' || line[0] == '\t') {
+
+ if (filenames) {
+
+ while (line[0] == ' ' || line[0] == '\t') {
+ line++;
+ }
+
+ if (cmds_idx + strlen (line) + 1 > cmds_sz) {
+
+ cmds_sz = (cmds_idx + strlen (line) + 1) * 2;
+ cmds = xrealloc (cmds, cmds_sz);
+
+ }
+
+ src = line;
+ dst = &cmds[cmds_idx];
+
+ for (; *src; src++, dst++) {
+
+ if (src[0] == '\n' && src[-1] == '\\' && src[1] == '\t') {
+
+ *dst = *src;
+
+ src++;
+ continue;
+
+ }
+
+ *dst = *src;
+
+ }
+
+ *dst = '\0';
+
+ cmds_idx += dst - &cmds[cmds_idx] + 1;
+ cmds[cmds_idx - 1] = '\n';
+
+ continue;
+
+ }
+
+ }
+
+ if (strlen (line) + 1 > clean_size) {
+
+ clean_size = strlen (line) + 1;
+
+ free (clean);
+ clean = xmalloc (clean_size);
+
+ }
+
+ strcpy (clean, line);
+
+ remove_backslash_newlines (clean);
+ remove_comment (clean);
+
+ p = clean;
+
+ while (isspace ((int) *p)) {
+ p++;
+ }
+
+ if (*p == '\0') {
+ continue;
+ }
+
+ if (strncmp (p, "include", 7) == 0 && (isspace ((int) p[7]) || p[7] == '\0')) {
+
+ p += 7;
+
+ for (q = p + strlen (p); q > p && isspace ((int) q[-1]); q--) {
+ ;
+ }
+
+ *q = '\0';
+ p = line = variable_expand_line (xstrdup (p));
+
+ while (1) {
+
+ char saved_ch;
+
+ for (; isspace ((int) *p); p++) {
+ ;
+ }
+
+ for (q = p; *q && !isspace ((int) *q); q++) {
+ ;
+ }
+
+ if (q == p) {
+ break;
+ }
+
+ saved_ch = *q;
+ *q = '\0';
+
+ if ((ret = include_makefile (p))) {
+ return ret;
+ }
+
+ *q = saved_ch;
+
+ p = q;
+
+ }
+
+ free (line);
+ continue;
+
+ }
+
+ if (strchr (p, '=')) {
+
+ record_waiting_files ();
+
+ parse_var_line (p, VAR_ORIGIN_FILE);
+ continue;
+
+ }
+
+ record_waiting_files ();
+
+ semicolonp = strchr (line, ';');
+ commentp = strchr (line, '#');
+
+ if (commentp && semicolonp && (commentp < semicolonp)) {
+
+ *commentp = '\0';
+ semicolonp = 0;
+
+ } else if (semicolonp) {
+ *(semicolonp++) = '\0';
+ }
+
+ remove_backslash_newlines (line);
+ line = variable_expand_line (xstrdup (line));
+
+ if (!(colon = strchr (line, ':'))) {
+
+ for (p = line; isspace ((int) *p); p++) {
+ ;
+ }
+
+ if (*p) {
+ fprintf (stderr, "%s: Missing ':' in rule line!\n", program_name);
+ }
+
+ free (line);
+ continue;
+
+ }
+
+ *colon = '\0';
+
+ filenames = parse_nameseq (line, sizeof (*filenames));
+ depstr = xstrdup (colon + 1);
+
+ free (line);
+
+ if (semicolonp) {
+
+ if (cmds_idx + strlen (semicolonp) + 1 > cmds_sz) {
+
+ cmds_sz = (cmds_idx + strlen (semicolonp) + 1) * 2;
+ cmds = xrealloc (cmds, cmds_sz);
+
+ }
+
+ memcpy (&(cmds[cmds_idx]), semicolonp, strlen (semicolonp) + 1);
+ cmds_idx += strlen (semicolonp) + 1;
+
+ }
+
+ if (set_default && default_goal_var->value[0] == '\0') {
+
+ struct nameseq *ns;
+
+ for (ns = filenames; ns; ns = ns->next) {
+
+ if ((ns->name[0] == '.') && (strchr (ns->name, '\\') == 0) && (strchr (ns->name, '/') == 0)) {
+ continue;
+ }
+
+ free (default_goal_var->value);
+
+ default_goal_var->value = xstrdup (ns->name);
+ break;
+
+ }
+
+ }
+
+ }
+
+ record_waiting_files ();
+
+ free (clean);
+ free (cmds);
+
+ return 0;
+
+}
+
+int read_makefile (const char *filename) {
+
+ struct linebuf lbuf;
+ struct variable *makefile_list;
+
+ char *new_value;
+ int ret;
+
+ if (!(lbuf.f = fopen (filename, "r"))) {
+ return 1;
+ }
+
+ lbuf.size = 256;
+ lbuf.start = xmalloc (lbuf.size);
+
+ if ((makefile_list = variable_find ("MAKEFILE_LIST"))) {
+
+ unsigned long old_len = strlen (makefile_list->value);
+
+ new_value = xmalloc (old_len + 1 + strlen (filename) + 1);
+ sprintf (new_value, "%s %s", makefile_list->value, filename);
+
+ variable_change ("MAKEFILE_LIST", new_value, VAR_ORIGIN_FILE);
+
+ } else {
+
+ new_value = xmalloc (strlen (filename) + 1);
+ sprintf (new_value, "%s", filename);
+
+ variable_change ("MAKEFILE_LIST", new_value, VAR_ORIGIN_FILE);
+
+ }
+
+ ret = read_lbuf (&lbuf, 1);
+
+ free (lbuf.start);
+ fclose (lbuf.f);
+
+ return ret;
+
+}
+
+void *parse_nameseq (char *line, size_t size) {
+
+ struct nameseq *start = 0;
+ struct nameseq **pp = &start;
+
+ char *p, *temp;
+ temp = xmalloc (strlen (line) + 1);
+
+#define add_nameseq(_name) \
+ do { \
+ *pp = xmalloc (size); \
+ (*pp)->name = xstrdup (_name); \
+ pp = &((*pp)->next); \
+ } while (0)
+
+ p = line;
+
+ while (1) {
+
+ char *p2;
+
+ while (isspace ((int) *p)) {
+ p++;
+ }
+
+ if (*p == '\0') {
+ break;
+ }
+
+ p2 = p;
+
+ while (*p2 && !isspace ((int) *p2)) {
+ p2++;
+ }
+
+ memcpy (temp, p, p2 - p);
+ temp[p2 - p] = '\0';
+
+ add_nameseq (temp);
+ p = p2;
+
+ }
+
+#undef add_nameseq
+
+ free (temp);
+ return start;
+
+}
+
+void record_files (struct nameseq *filenames, char *cmds, size_t cmds_idx, char *depstr) {
+
+ struct commands *cmds_p;
+
+ struct dep *deps;
+ struct nameseq *ns, *old_ns;
+
+ if (cmds_idx > 0) {
+
+ cmds_p = xmalloc (sizeof (*cmds_p));
+
+ cmds_p->text = xstrndup (cmds, cmds_idx);
+ cmds_p->len = cmds_idx;
+
+ } else {
+ cmds_p = 0;
+ }
+
+ if (depstr) {
+ deps = parse_nameseq (depstr, sizeof (*deps));
+ } else {
+ deps = 0;
+ }
+
+ for (ns = filenames, old_ns = 0; ns; old_ns = ns, ns = ns->next, free (old_ns->name), free (old_ns)) {
+
+ if (ns->name[0] == '.' && !strchr (ns->name, '\\') && !strchr (ns->name, '/')) {
+ rule_add_suffix (ns->name, cmds_p);
+ } else {
+ rule_add (ns->name, deps, cmds_p);
+ }
+
+ }
+
+ free (depstr);
+
+}
--- /dev/null
+/******************************************************************************
+ * @file report.c
+ *****************************************************************************/
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <xmake/report.h>
+
+unsigned long errors = 0;
+
+#ifndef __PDOS__
+#if defined (_WIN32)
+# include <windows.h>
+static int OriginalConsoleColor = -1;
+#endif
+
+static void reset_console_color (void) {
+
+#if defined (_WIN32)
+
+ HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE);
+
+ if (OriginalConsoleColor == -1) { return; }
+
+ SetConsoleTextAttribute (hStdError, OriginalConsoleColor);
+ OriginalConsoleColor = -1;
+
+#else
+
+ fprintf (stderr, "\033[0m");
+
+#endif
+
+}
+
+static void set_console_color (int color) {
+
+#if defined (_WIN32)
+
+ HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE);
+ WORD wColor;
+
+ if (OriginalConsoleColor == -1) {
+
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ if (!GetConsoleScreenBufferInfo (hStdError, &csbi)) {
+ return;
+ }
+
+ OriginalConsoleColor = csbi.wAttributes;
+
+ }
+
+ wColor = (OriginalConsoleColor & 0xF0) + (color & 0xF);
+ SetConsoleTextAttribute (hStdError, wColor);
+
+#else
+
+ fprintf (stderr, "\033[%dm", color);
+
+#endif
+
+}
+#endif
+
+static void output_message (const char *filename, unsigned long lineno, enum report_type type, const char *fmt, va_list ap) {
+
+ if (filename) {
+
+ if (lineno == 0) {
+ fprintf (stderr, "%s: ", filename);
+ } else {
+ fprintf (stderr, "%s:", filename);
+ }
+
+ }
+
+ if (lineno > 0) {
+ fprintf (stderr, "%lu: ", lineno);
+ }
+
+ if (type == REPORT_ERROR || type == REPORT_FATAL_ERROR) {
+
+#ifndef __PDOS__
+ set_console_color (COLOR_ERROR);
+#endif
+
+ if (type == REPORT_ERROR) {
+ fprintf (stderr, "error:");
+ } else {
+ fprintf (stderr, "fatal error:");
+ }
+
+ } else if (type == REPORT_INTERNAL_ERROR) {
+
+#ifndef __PDOS__
+ set_console_color (COLOR_INTERNAL_ERROR);
+#endif
+
+ fprintf (stderr, "internal error:");
+
+ } else if (type == REPORT_WARNING) {
+
+#ifndef __PDOS__
+ set_console_color (COLOR_WARNING);
+#endif
+
+ fprintf (stderr, "warning:");
+
+ }
+
+#ifndef __PDOS__
+ reset_console_color ();
+#endif
+
+ fprintf (stderr, " ");
+ vfprintf (stderr, fmt, ap);
+ fprintf (stderr, "\n");
+
+ if (type != REPORT_WARNING) {
+ ++errors;
+ }
+
+}
+
+unsigned long get_error_count (void) {
+ return errors;
+}
+
+void report_at (const char *filename, unsigned long lineno, enum report_type type, const char *fmt, ...) {
+
+ va_list ap;
+
+ va_start (ap, fmt);
+ output_message (filename, lineno, type, fmt, ap);
+ va_end (ap);
+
+}
--- /dev/null
+/******************************************************************************
+ * @file rule.c
+ *****************************************************************************/
+#include <string.h>
+
+#include <xmake/command.h>
+#include <xmake/dep.h>
+#include <xmake/hashtab.h>
+#include <xmake/lib.h>
+#include <xmake/rule.h>
+
+static struct hashtab hashtab_rules = { 0 };
+struct suffix_rule *suffix_rules = 0;
+
+struct rule *rule_find (const char *name) {
+
+ struct hashtab_name *key;
+
+ if (!(key = hashtab_get_key (&hashtab_rules, name))) {
+ return 0;
+ }
+
+ return hashtab_get (&hashtab_rules, key);
+
+}
+
+void rule_add (char *name, struct dep *deps, struct commands *cmds) {
+
+ struct hashtab_name *key;
+ struct rule *r;
+
+ if (!(key = hashtab_alloc_name (xstrdup (name)))) {
+ return;
+ }
+
+ r = xmalloc (sizeof (*r));
+
+ r->name = xstrdup (name);
+ r->deps = deps;
+ r->cmds = cmds;
+
+ hashtab_put (&hashtab_rules, key, r);
+
+}
+
+void rule_add_suffix (char *name, struct commands *cmds) {
+
+ struct suffix_rule *s = xmalloc (sizeof (*s));
+ char *p;
+
+ if ((p = strchr (name + 1, '.'))) {
+
+ s->second = xstrdup (p);
+ *p = '\0';
+
+ }
+
+ s->first = xstrdup (name);
+
+ s->cmds = cmds;
+ s->next = suffix_rules;
+
+ suffix_rules = s;
+
+}
+
+void rules_init (void) {}
--- /dev/null
+/******************************************************************************
+ * @file variable.c
+ *****************************************************************************/
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xmake/hashtab.h>
+#include <xmake/lib.h>
+#include <xmake/report.h>
+#include <xmake/variable.h>
+#include <xmake/xmake.h>
+
+static struct hashtab hashtab_vars = { 0 };
+
+static char *variable_suffix_replace (char *body, const char *from_s, const char *to_s) {
+
+ char *new_body = xstrdup (body);
+ char *p;
+
+ while ((p = strstr (new_body, from_s))) {
+
+ if (strlen (from_s) == strlen (to_s)) {
+ memcpy (p, to_s, strlen (to_s));
+ } else if (strlen (from_s) > strlen (to_s)) {
+
+ size_t rem = strlen (from_s) - strlen (to_s);
+ memcpy (p, to_s, strlen (to_s));
+
+ while (rem--) {
+ p[strlen (to_s) + rem] = ' ';
+ }
+
+ } else {
+
+ size_t rem = strlen (to_s) - strlen (from_s);
+
+ new_body = xrealloc (new_body, strlen (new_body) + rem);
+ memmove (p + rem, p, strlen (p) + 1);
+ memcpy (p, to_s, strlen (to_s));
+
+ }
+
+ }
+
+ return new_body;
+
+}
+
+struct variable *variable_add (char *name, char *value, enum variable_origin origin) {
+
+ struct hashtab_name *key;
+ struct variable *var;
+
+ if (!(key = hashtab_alloc_name (name))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to allocate memory for name '%s'", name);
+ exit (EXIT_FAILURE);
+
+ }
+
+ var = xmalloc (sizeof (*var));
+ var->flavor = VAR_FLAVOR_RECURSIVELY_EXPANDED;
+ var->name = name;
+ var->origin = origin;
+ var->value = value;
+
+ if (hashtab_put (&hashtab_vars, key, var)) {
+
+ report_at (program_name, 0, REPORT_FATAL_ERROR, "failed to insert variable '%s' into hashtab", var->name);
+ exit (EXIT_FAILURE);
+
+ }
+
+ return var;
+
+}
+
+struct variable *variable_change (char *name, char *value, enum variable_origin origin) {
+
+ struct variable *var;
+
+ if (!(var = variable_find (name))) {
+ return variable_add (xstrdup (name), value, origin);
+ }
+
+ switch (origin) {
+
+ case VAR_ORIGIN_AUTOMATIC:
+ case VAR_ORIGIN_COMMAND_LINE:
+
+ break;
+
+ case VAR_ORIGIN_FILE:
+
+ if (var->origin == VAR_ORIGIN_FILE) {
+ break;
+ }
+
+ free (value);
+ return 0;
+
+ }
+
+ free (var->value);
+
+ var->value = value;
+ var->origin = origin;
+
+ return var;
+
+}
+
+struct variable *variable_find (char *name) {
+
+ struct hashtab_name *key;
+
+ if (!(key = hashtab_get_key (&hashtab_vars, name))) {
+ return 0;
+ }
+
+ return hashtab_get (&hashtab_vars, key);
+
+}
+
+char *variable_expand_line (char *line) {
+
+ size_t pos = 0;
+
+ while (line[pos]) {
+
+ if (line[pos] == '$') {
+
+ char *new, *replacement = "";
+ char *p_after_variable;
+
+ struct variable *var = 0;
+ char *alloc_replacement = 0;
+
+ if (line[pos + 1] == '$') {
+
+ p_after_variable = line + pos + 2;
+ pos += 1;
+
+ } else if (line[pos + 1] == '(' || line[pos + 1] == '{') {
+
+ char *body = line + pos + 2;
+ char *q = body, *content;
+
+ int paren_inbalance = 1;
+ char opening_paren = line[pos + 1];
+
+ while (paren_inbalance) {
+
+ if (*q == opening_paren) {
+ paren_inbalance++;
+ } else if (*q == ')' && opening_paren == '(') {
+ paren_inbalance--;
+ } else if (*q == '}' && opening_paren == '{') {
+ paren_inbalance--;
+ } else if (*q == '\0') {
+
+ fprintf (stderr, "%s: *** unterminated variable reference. Stop.\n", program_name);
+ exit (EXIT_FAILURE);
+
+ }
+
+ q++;
+
+ }
+
+ q--;
+
+ p_after_variable = q + 1;
+ content = variable_expand_line (xstrndup (body, q - body));
+
+ if ((q = strchr (content, '='))) {
+
+ char *colon = strchr (content, ':');
+
+ if (colon && colon != content && colon < q) {
+
+ char *from, *to, *p;
+ *colon = '\0';
+
+ var = variable_find (content);
+
+ for (from = colon + 1; isspace ((int) *from); from++) {
+ ;
+ }
+
+ for (p = q; p != from && isspace ((int) p[-1]); p--) {
+ ;
+ }
+
+ *p = '\0';
+
+ for (to = q + 1; isspace ((int) *to); to++) {
+ ;
+ }
+
+ for (p = to + strlen (to); p != to && isspace ((int) p[-1]); p--) {
+ ;
+ }
+
+ *p = '\0';
+
+ if (*from && var) {
+ alloc_replacement = variable_suffix_replace (var->value, from, to);
+ }
+
+ }
+
+ } else {
+ var = variable_find (content);
+ }
+
+ free (content);
+
+ } else if (line[pos + 1]) {
+
+ char name[2] = { 0, 0 };
+
+ p_after_variable = line + pos + 2;
+ name[0] = line[pos + 1];
+
+ var = variable_find (name);
+
+ } else {
+ p_after_variable = line + pos + 1;
+ }
+
+ if (var) {
+ replacement = var->value;
+ }
+
+ if (alloc_replacement) {
+ replacement = alloc_replacement;
+ }
+
+ new = xmalloc (pos + strlen (replacement) + strlen (p_after_variable) + 1);
+
+ memcpy (new, line, pos);
+ memcpy (new + pos, replacement, strlen (replacement));
+ memcpy (new + pos + strlen (replacement), p_after_variable, strlen (p_after_variable) + 1);
+
+ free (line);
+ line = new;
+
+ if (!alloc_replacement && var && var->flavor == VAR_FLAVOR_SIMPLY_EXPANDED) {
+ pos += strlen (replacement);
+ }
+
+ free (alloc_replacement);
+ continue;
+
+
+ }
+
+ pos++;
+
+ }
+
+ return line;
+
+}
+
+void parse_var_line (char *line, enum variable_origin origin) {
+
+ enum {
+ VAR_ASSIGN,
+ VAR_CONDITIONAL_ASSIGN,
+ VAR_APPEND,
+ VAR_SHELL
+ } opt = VAR_ASSIGN;
+
+ enum variable_flavor flavor = VAR_FLAVOR_RECURSIVELY_EXPANDED;
+ struct variable *var;
+
+ char *var_name, *new_value;
+ char *equals_sign, *p;
+
+ if (!(equals_sign = strchr (line, '='))) {
+
+ fprintf (stderr, "+++ invalid variable definition!\n");
+ return;
+
+ }
+
+ p = equals_sign;
+
+ switch (p - line) {
+
+ default:
+
+ if (p[-1] == ':' && p[-2] == ':' && p[-3] == ':') {
+
+ flavor = VAR_FLAVOR_IMMEDIATELY_EXPANDED;
+
+ p -= 3;
+ break;
+
+ }
+
+ /* fall through */
+
+ case 2:
+
+ if (p[-1] == ':' && p[-2] == ':') {
+
+ flavor = VAR_FLAVOR_SIMPLY_EXPANDED;
+
+ p -= 2;
+ break;
+
+ }
+
+ /* fall through */
+
+ case 1:
+
+ if (p[-1] == ':') {
+
+ flavor = VAR_FLAVOR_SIMPLY_EXPANDED;
+
+ p--;
+ break;
+
+ }
+
+ if (p[-1] == '?') {
+
+ opt = VAR_CONDITIONAL_ASSIGN;
+
+ p--;
+ break;
+
+ }
+
+ if (p[-1] == '+') {
+
+ opt = VAR_APPEND;
+
+ p--;
+ break;
+
+ }
+
+ if (p[-1] == '!') {
+
+ opt = VAR_SHELL;
+
+ p--;
+ break;
+
+ }
+
+ break;
+
+ case 0:
+
+ break;
+
+ }
+
+ for (; p > line && isspace ((int) p[-1]); p--) {
+ ;
+ }
+
+ var_name = variable_expand_line (xstrndup (line, p - line));
+
+ if (*var_name == '\0') {
+
+ fprintf (stderr, "%s: *** empty variable name. Stop.\n", program_name);
+ exit (EXIT_FAILURE);
+
+ }
+
+ var = variable_find (var_name);
+
+ if (opt == VAR_CONDITIONAL_ASSIGN && var) {
+
+ free (var_name);
+ return;
+
+ }
+
+ for (p = equals_sign; isspace ((int) p[1]); p++) {
+ ;
+ }
+
+ new_value = xstrdup (p + 1);
+
+ if (opt == VAR_ASSIGN || opt == VAR_CONDITIONAL_ASSIGN) {
+
+ switch (flavor) {
+
+ case VAR_FLAVOR_RECURSIVELY_EXPANDED:
+
+ break;
+
+ case VAR_FLAVOR_SIMPLY_EXPANDED:
+
+ new_value = variable_expand_line (new_value);
+ break;
+
+ case VAR_FLAVOR_IMMEDIATELY_EXPANDED: {
+
+ size_t dollar_count;
+ char *temp, *p2;
+
+ new_value = variable_expand_line (new_value);
+
+ for (dollar_count = 0, p = new_value; *p; p++) {
+
+ if (*p == '$') {
+ dollar_count++;
+ }
+
+ }
+
+ temp = xmalloc (strlen (new_value) + 1 + dollar_count);
+
+ for (p = new_value, p2 = temp; *p; p++, p2++) {
+
+ *p2 = *p;
+
+ if (*p == '$') {
+
+ p2[1] = '$';
+ p2++;
+
+ }
+
+ *p2 = '\0';
+
+ free (new_value);
+ new_value = temp;
+
+ }
+
+ break;
+
+ }
+
+ }
+
+ } else if (opt == VAR_APPEND) {
+
+ struct variable *var;
+
+ if ((var = variable_find (var_name))) {
+
+ char *temp = xstrdup (new_value);
+ free (new_value);
+
+ new_value = xmalloc (strlen (var->value) + 1 + strlen (temp) + 1);
+ sprintf (new_value, "%s %s", var->value, temp);
+
+ free (temp);
+
+ }
+
+ } else if (opt == VAR_SHELL) {
+
+ fprintf (stderr, "+++internal error: != not supported yet. Stop.\n");
+ exit (EXIT_FAILURE);
+
+ }
+
+ if ((var = variable_change (var_name, new_value, origin))) {
+ var->flavor = flavor;
+ }
+
+ free (var_name);
+
+}
+
+void variables_init (void) {}
--- /dev/null
+/******************************************************************************
+ * @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;
+
+}