This commit is contained in:
Neil 2021-03-21 11:11:08 -07:00
commit 45f493ceff
3 changed files with 324 additions and 0 deletions

181
Makefile Normal file
View File

@ -0,0 +1,181 @@
# GNU Make 3.81; MacOSX gcc 4.2.1; MacOSX MinGW 4.3.0
# https://stackoverflow.com/questions/18136918/how-to-get-current-relative-directory-of-your-makefile
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
current_dir := $(notdir $(patsubst %/,%,$(dir $(mkfile_path))))
project := $(current_dir)
# dirs
src := src
test := test
build := build
bin := bin
backup := backup
doc := doc
media := media
#lemon := lemon
PREFIX := /usr/local
# files in $(bin)
install := $(project)-`date +%Y-%m-%d`
# extra stuff we should back up
extra := $(project).xcodeproj
# John Graham-Cumming: rwildcard is a recursive wildcard
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) \
$(filter $(subst *,%,$2),$d))
java_srcs := $(call rwildcard, $(src), *.java)
c_srcs := $(call rwildcard, $(src), *.c)
h_srcs := $(call rwildcard, $(src), *.h)
c_re_srcs := $(call rwildcard, $(src), *.c.re)
c_rec_srcs := $(call rwildcard, $(src), *.c.re_c)
y_srcs := $(call rwildcard, $(src), *.y)
c_tests := $(call rwildcard, $(test), *.c)
h_tests := $(call rwildcard, $(test), *.h)
icons := $(call rwildcard, $(media), *.ico)
# combinations
all_h := $(h_srcs) $(h_tests)
all_srcs := $(java_srcs) $(c_srcs) $(c_re_srcs) $(c_rec_srcs) $(y_srcs)
all_tests := $(c_tests)
all_icons := $(icons)
java_class := $(patsubst $(src)/%.java, $(build)/%.class, $(java_srcs))
c_objs := $(patsubst $(src)/%.c, $(build)/%.o, $(c_srcs))
# must not conflict, eg, foo.c.re and foo.c would go to the same thing
c_re_builds := $(patsubst $(src)/%.c.re, $(build)/%.c, $(c_re_srcs))
c_rec_builds := $(patsubst $(src)/%.c.re_c, $(build)/%.c, $(c_rec_srcs))
c_y_builds := $(patsubst $(src)/%.y, $(build)/%.c, $(y_srcs))
# together .re/.re_c/.y
c_other_objs := $(patsubst $(build)/%.c, $(build)/%.o, $(c_re_builds) \
$(c_rec_builds) $(c_y_builds))
test_c_objs := $(patsubst $(test)/%.c, $(build)/$(test)/%.o, $(c_tests))
html_docs := $(patsubst $(src)/%.c, $(doc)/%.html, $(c_srcs))
cdoc := cdoc
re2c := re2c
mkdir := mkdir -p
cat := cat
zip := zip
bison := bison
#lemon := lemon
CC := clang #gcc
CF := -Wall -Wextra -Wno-format-y2k -Wstrict-prototypes \
-Wmissing-prototypes -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings \
-Wswitch -Wshadow -Wcast-align -Wbad-function-cast -Wchar-subscripts -Winline \
-Wnested-externs -Wredundant-decls -Wfatal-errors -O3 -ffast-math \
-funroll-loops -pedantic -ansi -g # or -std=c99 -mwindows
OF := -O3 # -framework OpenGL -framework GLUT or -lglut -lGLEW
# Jakob Borg and Eldar Abusalimov
# $(ARGS) is all the extra arguments; $(BRGS) is_all_the_extra_arguments
EMPTY :=
SPACE := $(EMPTY) $(EMPTY)
ifeq (backup, $(firstword $(MAKECMDGOALS)))
ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
BRGS := $(subst $(SPACE),_,$(ARGS))
ifneq (,$(BRGS))
BRGS := -$(BRGS)
endif
$(eval $(ARGS):;@:)
endif
######
# compiles the programme by default
default: $(bin)/$(project)
# . . . success; executable is in $(bin)/$(project)
docs: $(html_docs)
# linking
$(bin)/$(project): $(c_objs) $(c_other_objs) $(test_c_objs)
# linking rule
@$(mkdir) $(bin)
$(CC) $(OF) -o $@ $^
# compiling
#$(lemon)/$(bin)/$(lem): $(lemon)/$(src)/lemon.c
# # compiling lemon
# @$(mkdir) $(lemon)/$(bin)
# $(CC) $(CF) -o $@ $<
$(c_objs): $(build)/%.o: $(src)/%.c $(all_h)
# c_objs rule
@$(mkdir) $(build)
$(CC) $(CF) -c -o $@ $<
$(c_other_objs): $(build)/%.o: $(build)/%.c $(all_h)
# c_other_objs rule
$(CC) $(CF) -c -o $@ $<
$(test_c_objs): $(build)/$(test)/%.o: $(test)/%.c $(all_h)
# test_c_objs rule
@$(mkdir) $(build)
@$(mkdir) $(build)/$(test)
$(CC) $(CF) -c -o $@ $<
$(c_re_builds): $(build)/%: $(src)/%.re
# *.re build rule
@$(mkdir) $(build)
$(re2c) -W -T -o $@ $<
$(c_rec_builds): $(build)/%: $(src)/%.re_c
# *.re_c (conditions) build rule
@$(mkdir) $(build)
$(re2c) -W -T -c -o $@ $<
$(c_y_builds): $(build)/%.c: $(src)/%.y # $(lemon)/$(bin)/$(lem)
# .y rule
@$(mkdir) $(build)
$(bison) -o $@ $<
$(html_docs): $(doc)/%.html: $(src)/%.c $(src)/%.h
# docs rule
@$(mkdir) $(doc)
cat $^ | $(cdoc) > $@
######
# phoney targets
.PHONY: setup clean backup icon install uninstall test docs
clean:
-rm -f $(c_objs) $(test_c_objs) $(c_other_objs) $(c_re_builds) \
$(c_rec_builds) $(html_docs)
-rm -rf $(bin)/$(test)
backup:
@$(mkdir) $(backup)
$(zip) $(backup)/$(project)-`date +%Y-%m-%dT%H%M%S`$(BRGS).zip \
readme.txt Makefile $(all_h) $(all_srcs) $(all_tests) $(all_icons)
icon: default
# . . . setting icon on a Mac.
cp $(media)/$(icon) $(bin)/$(icon)
-sips --addIcon $(bin)/$(icon)
-DeRez -only icns $(bin)/$(icon) > $(bin)/$(RSRC)
-Rez -append $(bin)/$(RSRC) -o $(bin)/$(project)
-SetFile -a C $(bin)/$(project)
setup: default icon
@$(mkdir) $(bin)/$(install)
cp $(bin)/$(project) readme.txt $(bin)/$(install)
rm -f $(bin)/$(install)-MacOSX.dmg
# or rm -f $(BDIR)/$(INST)-Win32.zip
hdiutil create $(bin)/$(install)-MacOSX.dmg -volname "$(project)" -srcfolder $(bin)/$(install)
# or zip $(BDIR)/$(INST)-Win32.zip -r $(BDIR)/$(INST)
rm -R $(bin)/$(install)
install: default
@$(mkdir) -p $(DESTDIR)$(PREFIX)/bin
cp $(bin)/$(project) $(DESTDIR)$(PREFIX)/bin/$(project)
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/$(project)
docs: $(html_docs)

65
src/main.c Normal file
View File

@ -0,0 +1,65 @@
#include <stdlib.h> /* EXIT_ strtol */
#include <stdio.h> /* FILE fopen fclose fread */
#include <errno.h> /* errno */
#include <ctype.h> /* isdigit */
#include <limits.h> /* LONG_ INT_ _MIN _MAX */
#include <assert.h> /* assert */
#include "min_array.h"
MIN_ARRAY(char, char)
MIN_ARRAY(num, int)
/** Concatenates the contents of `fp` after the file pointer to `string`. The
file pointer is modified. Zeros are preserved, as well as appending a
null-terminator. @return Success, otherwise `string` may have read a partial
read and may not be terminated. @throws[fread, malloc] */
static int cat_fp_to_str(FILE *const fp, struct char_array *const string) {
const size_t granularity = 1024;
size_t nread;
char *cursor;
assert(fp && string);
do {
if(!(cursor = char_array_buffer(string, granularity))) return 0;
char_array_emplace(string, nread = fread(cursor, 1, granularity, fp));
} while(nread == granularity); assert(nread < granularity);
if(ferror(fp) || !(cursor = char_array_new(string))) return 0;
*cursor = '\0';
return 1;
}
int main(void) {
int success = EXIT_FAILURE;
struct char_array str = MIN_ARRAY_IDLE;
struct num_array nums = MIN_ARRAY_IDLE;
int *num;
long big_num;
char *a, *anum = 0;
size_t i;
errno = 0; /* In case we are running it as part of another editor. */
if(!cat_fp_to_str(stdin, &str)) goto catch;
/*printf("<<<%s>>>\n", str.data);*/
/* The first sentinel '\0' it stops, even though it could have more data,
and we could conceivably use the length to continue; it's simple. */
for(a = str.data; a = strpbrk(a, "-0123456789"); ) {
if(*a == '-') { anum = a++; continue; }
if(!(num = num_array_new(&nums))) goto catch;
if(!anum || anum != a - 1) anum = a; /* Wasn't negative. */
big_num = strtol(anum, &a, 0);
if((!big_num || big_num == LONG_MIN || big_num == LONG_MAX) && errno)
goto catch; /* Long conversion failed. */
if(big_num < INT_MIN || big_num > INT_MAX)
{ errno = ERANGE; goto catch; }
*num = (int)big_num; /* Safe now. */
}
for(i = 0; i < nums.size; i++) printf("Extracted %d.\n", nums.data[i]);
success = EXIT_SUCCESS;
goto finally;
catch:
perror("stdin");
finally:
printf("Freeeee!! str %lu, nums %lu\n", str.capacity, nums.capacity);
char_array_(&str);
num_array_(&nums);
printf("yessss.\n");
return success;
}

78
src/min_array.h Normal file
View File

@ -0,0 +1,78 @@
/** X-macro for a minimal dynamic array. `MIN_ARRAY(name, type)`, where `name`
is an identifier prefix that satisfies `C` naming conventions when mangled and
`type` is defined tag-type associated therewith. When expanding the array,
resizing may be necessary and incurs amortised cost; any pointers to this
memory may become stale. */
#include <stdlib.h> /* size_t realloc free */
#include <string.h> /* memmove strcmp memcpy */
#include <errno.h> /* errno */
#include <assert.h> /* assert */
#define MIN_ARRAY_IDLE { 0, 0, 0 }
#define MIN_ARRAY(name, type) \
struct name##_array { type *data; size_t size, capacity; }; \
/** Initialises `a` to idle. */ \
static void name##_array(struct name##_array *const a) \
{ assert(a), a->data = 0, a->capacity = a->size = 0; } \
/** Destroys `a` and returns it to idle. */ \
static void name##_array_(struct name##_array *const a) \
{ assert(a), free(a->data), name##_array(a); } \
/** Ensures `min_capacity` of `a`. @param[min_capacity] If zero, does nothing.
@return Success; otherwise, `errno` will be set.
@throws[ERANGE] Tried allocating more then can fit in `size_t` or `realloc`
doesn't follow POSIX. @throws[realloc, ERANGE] */ \
static int name##_array_reserve(struct name##_array *const a, \
const size_t min_capacity) { \
size_t c0; \
type *data; \
const size_t max_size = (size_t)-1 / sizeof *a->data; \
assert(a); \
if(a->data) { \
if(min_capacity <= a->capacity) return 1; \
c0 = a->capacity; \
} else { /* Idle. */ \
if(!min_capacity) return 1; \
c0 = 1; \
} \
if(min_capacity > max_size) return errno = ERANGE, 0; \
/* `c_n = a1.625^n`, approximation golden ratio `\phi ~ 1.618`. */ \
while(c0 < min_capacity) { \
size_t c1 = c0 + (c0 >> 1) + (c0 >> 3) + 1; \
if(c0 > c1) { c0 = max_size; break; } \
c0 = c1; \
} \
if(!(data = realloc(a->data, sizeof *a->data * c0))) \
{ if(!errno) errno = ERANGE; return 0; } \
a->data = data, a->capacity = c0; \
return 1; \
} \
/** Adds at least `buffer` un-initialised and uncounted elements at the back of
`a`. @return A pointer to the start of `buffer` elements, namely
`data + size`. If `a` is idle and `buffer` is zero, a null pointer is
returned, otherwise this indicates an error. @throws[realloc, ERANGE] */ \
static type *name##_array_buffer(struct name##_array *const a, \
const size_t buffer) { \
assert(a); \
if(a->size > (size_t)-1 - buffer) \
{ errno = ERANGE; return 0; } /* Unlikely. */ \
if(!name##_array_reserve(a, a->size + buffer)) return 0; \
return a->data ? a->data + a->size : 0; \
} \
/** Emplaces `n` elements on `a`. `n` must be smaller than or equal to the
highest remaining <fn:<name>_array_buffer>. */ \
static void name##_array_emplace(struct name##_array *const a, \
const size_t n) { \
assert(a && a->capacity >= a->size && n <= a->capacity - a->size); \
a->size += n; \
} \
/** @return Push back a new un-initialized datum of `a`.
@throws[realloc, ERANGE] */ \
static type *name##_array_new(struct name##_array *const a) { \
type *const data = name##_array_buffer(a, 1); \
return data ? name##_array_emplace(a, 1), data : 0; \
} \
/* They don't have to all be used, (destructor is important.) */ \
static void name##_unused_coda(void); static void name##_unused(void) { \
name##_array(0); name##_array_new(0); name##_unused_coda(); } \
static void name##_unused_coda(void) { name##_unused(); }