From: Robert Pengelly Date: Mon, 3 Jun 2024 03:37:59 +0000 (+0100) Subject: New server X-Git-Url: https://git.candlhat.org/?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=xmake.git New server --- cc64b0d4ac6a88a04462bf28c1d62a3658de4c84 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +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 diff --git a/Makefile.pdw b/Makefile.pdw new file mode 100644 index 0000000..a6256df --- /dev/null +++ b/Makefile.pdw @@ -0,0 +1,22 @@ +#****************************************************************************** +# @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 diff --git a/Makefile.unix b/Makefile.unix new file mode 100644 index 0000000..988ee8a --- /dev/null +++ b/Makefile.unix @@ -0,0 +1,26 @@ +#****************************************************************************** +# @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 diff --git a/Makefile.w32 b/Makefile.w32 new file mode 100644 index 0000000..7b97bd4 --- /dev/null +++ b/Makefile.w32 @@ -0,0 +1,19 @@ +#****************************************************************************** +# @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 ) diff --git a/README.md b/README.md new file mode 100644 index 0000000..334430a --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +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. diff --git a/cstr.c b/cstr.c new file mode 100644 index 0000000..803ee72 --- /dev/null +++ b/cstr.c @@ -0,0 +1,69 @@ +/****************************************************************************** + * @file cstr.c + *****************************************************************************/ +#include +#include + +#include + +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); + +} diff --git a/hashtab.c b/hashtab.c new file mode 100644 index 0000000..c25447d --- /dev/null +++ b/hashtab.c @@ -0,0 +1,215 @@ +/****************************************************************************** + * @file hashtab.c + *****************************************************************************/ +#include +#include +#include + +#include + +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; + + } + +} diff --git a/include/xmake/command.h b/include/xmake/command.h new file mode 100644 index 0000000..c3ce4e1 --- /dev/null +++ b/include/xmake/command.h @@ -0,0 +1,14 @@ +/****************************************************************************** + * @file command.h + *****************************************************************************/ +#ifndef _COMMAND_H +#define _COMMAND_H + +struct commands { + + char *text; + unsigned long len; + +}; + +#endif /* _COMMAND_H */ diff --git a/include/xmake/cstr.h b/include/xmake/cstr.h new file mode 100644 index 0000000..6202270 --- /dev/null +++ b/include/xmake/cstr.h @@ -0,0 +1,22 @@ +/****************************************************************************** + * @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 */ diff --git a/include/xmake/dep.h b/include/xmake/dep.h new file mode 100644 index 0000000..b683f81 --- /dev/null +++ b/include/xmake/dep.h @@ -0,0 +1,14 @@ +/****************************************************************************** + * @file dep.h + *****************************************************************************/ +#ifndef _DEP_H +#define _DEP_H + +struct dep { + + char *name; + struct dep *next; + +}; + +#endif /* _DEP_H */ diff --git a/include/xmake/hashtab.h b/include/xmake/hashtab.h new file mode 100644 index 0000000..0c40a69 --- /dev/null +++ b/include/xmake/hashtab.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * @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 */ diff --git a/include/xmake/lib.h b/include/xmake/lib.h new file mode 100644 index 0000000..d3eaf80 --- /dev/null +++ b/include/xmake/lib.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * @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 */ diff --git a/include/xmake/read.h b/include/xmake/read.h new file mode 100644 index 0000000..20a1c06 --- /dev/null +++ b/include/xmake/read.h @@ -0,0 +1,19 @@ +/****************************************************************************** + * @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 */ diff --git a/include/xmake/report.h b/include/xmake/report.h new file mode 100644 index 0000000..4e507f4 --- /dev/null +++ b/include/xmake/report.h @@ -0,0 +1,29 @@ +/****************************************************************************** + * @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 */ diff --git a/include/xmake/rule.h b/include/xmake/rule.h new file mode 100644 index 0000000..9cfdcbf --- /dev/null +++ b/include/xmake/rule.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * @file rule.h + *****************************************************************************/ +#ifndef _RULE_H +#define _RULE_H + +#include +#include + +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 */ diff --git a/include/xmake/variable.h b/include/xmake/variable.h new file mode 100644 index 0000000..4c876bc --- /dev/null +++ b/include/xmake/variable.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * @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 */ diff --git a/include/xmake/xmake.h b/include/xmake/xmake.h new file mode 100644 index 0000000..78c67be --- /dev/null +++ b/include/xmake/xmake.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * @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 */ diff --git a/lib.c b/lib.c new file mode 100644 index 0000000..e154e32 --- /dev/null +++ b/lib.c @@ -0,0 +1,541 @@ +/****************************************************************************** + * @file lib.c + *****************************************************************************/ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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 + +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 +# include + +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 diff --git a/read.c b/read.c new file mode 100644 index 0000000..2a72ad1 --- /dev/null +++ b/read.c @@ -0,0 +1,554 @@ +/****************************************************************************** + * @file read.c + *****************************************************************************/ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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); + +} diff --git a/report.c b/report.c new file mode 100644 index 0000000..d36483b --- /dev/null +++ b/report.c @@ -0,0 +1,140 @@ +/****************************************************************************** + * @file report.c + *****************************************************************************/ +#include +#include +#include + +#include + +unsigned long errors = 0; + +#ifndef __PDOS__ +#if defined (_WIN32) +# include +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); + +} diff --git a/rule.c b/rule.c new file mode 100644 index 0000000..3f75db0 --- /dev/null +++ b/rule.c @@ -0,0 +1,67 @@ +/****************************************************************************** + * @file rule.c + *****************************************************************************/ +#include + +#include +#include +#include +#include +#include + +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) {} diff --git a/variable.c b/variable.c new file mode 100644 index 0000000..1bfa197 --- /dev/null +++ b/variable.c @@ -0,0 +1,480 @@ +/****************************************************************************** + * @file variable.c + *****************************************************************************/ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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) {} diff --git a/xmake.c b/xmake.c new file mode 100644 index 0000000..85159c5 --- /dev/null +++ b/xmake.c @@ -0,0 +1,625 @@ +/****************************************************************************** + * @file xmake.c + *****************************************************************************/ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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; + +}