From cc64b0d4ac6a88a04462bf28c1d62a3658de4c84 Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Mon, 3 Jun 2024 04:37:59 +0100 Subject: [PATCH 1/1] New server --- LICENSE | 24 ++ Makefile.pdw | 22 ++ Makefile.unix | 26 ++ Makefile.w32 | 19 ++ README.md | 23 ++ cstr.c | 69 +++++ hashtab.c | 215 ++++++++++++++ include/xmake/command.h | 14 + include/xmake/cstr.h | 22 ++ include/xmake/dep.h | 14 + include/xmake/hashtab.h | 36 +++ include/xmake/lib.h | 35 +++ include/xmake/read.h | 19 ++ include/xmake/report.h | 29 ++ include/xmake/rule.h | 35 +++ include/xmake/variable.h | 41 +++ include/xmake/xmake.h | 31 ++ lib.c | 541 +++++++++++++++++++++++++++++++++ read.c | 554 ++++++++++++++++++++++++++++++++++ report.c | 140 +++++++++ rule.c | 67 +++++ variable.c | 480 ++++++++++++++++++++++++++++++ xmake.c | 625 +++++++++++++++++++++++++++++++++++++++ 23 files changed, 3081 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile.pdw create mode 100644 Makefile.unix create mode 100644 Makefile.w32 create mode 100644 README.md create mode 100644 cstr.c create mode 100644 hashtab.c create mode 100644 include/xmake/command.h create mode 100644 include/xmake/cstr.h create mode 100644 include/xmake/dep.h create mode 100644 include/xmake/hashtab.h create mode 100644 include/xmake/lib.h create mode 100644 include/xmake/read.h create mode 100644 include/xmake/report.h create mode 100644 include/xmake/rule.h create mode 100644 include/xmake/variable.h create mode 100644 include/xmake/xmake.h create mode 100644 lib.c create mode 100644 read.c create mode 100644 report.c create mode 100644 rule.c create mode 100644 variable.c create mode 100644 xmake.c 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; + +} -- 2.34.1