From: Thomas Walker Lynch Date: Thu, 28 Mar 2019 01:33:43 +0000 (+0100) Subject: prefer modules to src- dirs X-Git-Url: https://git.reasoningtechnology.com/style/rt_dark_doc.css?a=commitdiff_plain;h=40ee0eedd9d5011de6daecaf4837ecb16dee1078;p=subu prefer modules to src- dirs --- diff --git a/da/makefile b/da/makefile deleted file mode 100644 index 67fbdb9..0000000 --- a/da/makefile +++ /dev/null @@ -1,21 +0,0 @@ -# da/makefile - -SHELL=/bin/bash -MAKE= - --include makefile-flags - -.PHONY: all -all: version dep lib exec - -.PHONY: lib -lib: - $(MAKE) $@ - cp src/da.lib.h include/da.h - -%:: - $(MAKE) $@ - - - - diff --git a/da/makefile-flags b/da/makefile-flags deleted file mode 100644 index d732652..0000000 --- a/da/makefile-flags +++ /dev/null @@ -1,35 +0,0 @@ - -MODULE=da - -# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. -ECHO= echo -#ECHO= echo -e - -# directories used by this makefile, these could all be set to dot for -# the simplest source directory structure - -#LIDBIR, EXECDIR, INCDIR hold the make results that might later be staged -#$(PWD) is the directory that make was called from, this is already build in -#set to dot to use the same directory as the source code -#leave blank to ommit -DEPRDIR=deprecated -DOCDIR=doc -EXECDIR=exec -INCDIR=include -LIBDIR=lib -SRCDIR=src -TESTDIR=test -TMPDIR=tmp -TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) -TRYDIR=try - -# compiler and flags -C=gcc -CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB -#CFLAGS=-std=gnu11 -fPIC -I. -Werror -LINKFLAGS=-Llib -lda - -MAKE=/usr/bin/make --no-print-directory -f $(PROJECT_SUBU)/tools/lib/makefile_cc -#MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_cc - - diff --git a/da/src/da.lib.c b/da/src/da.lib.c deleted file mode 100644 index 0956d7e..0000000 --- a/da/src/da.lib.c +++ /dev/null @@ -1,132 +0,0 @@ -/* -Dynamic Array - -*/ - -#include "da.lib.h" - -#include -#include -#include - -//-------------------------------------------------------------------------------- -// generic -// We manipulate pointers to a smallest addressable unit. The sizeof operator -// returns counts in these addressable units. Sizeof(char) is defined to be 1. - -void da_alloc(Da *dap, size_t element_size){ - dap->element_size = element_size; - dap->size = 4 * element_size; - dap->base = malloc(dap->size); - dap->end = dap->base; -} -void da_free(Da *dap){ - free(dap->base); - dap->size = 0; -} -void da_rewind(Da *dap){ - dap->end = dap->base; -} - -bool da_empty(Da *dap){ - return dap->end == dap->base; -} - -void da_rebase(Da *dap, char *old_base, void *pta){ - char **pt = (char **)pta; - size_t offset = *pt - old_base; - *pt = dap->base + offset; -} - -// Doubles size of of da. Returns old base, so that existing pointers into the -// array can be moved to the new array -char *da_expand(Da *dap){ - char *old_base = dap->base; - size_t end_offset = dap->end - old_base; - size_t new_size = dap->size << 1; - char *new_base = malloc( new_size ); - memcpy( new_base, old_base, end_offset + dap->element_size); - free(old_base); - dap->base = new_base; - dap->end = new_base + end_offset; - dap->size = new_size; - return old_base; -} - -// true when pt has run off the end of the area currently allocated for the array -bool da_endq(Da *dap, void *pt){ - return (char *)pt >= dap->end; -} - -// true when pt has run off the end of the area allocated for the array -bool da_boundq(Da *dap){ - return dap->end >= dap->base + dap->size; -} - -void da_push(Da *dap, void *element){ - if( dap->end >= dap->base + dap->size ) da_expand(dap); - memcpy(dap->end, element, dap->element_size); - dap->end += dap->element_size; -} - -bool da_pop(Da *dap, void *element){ - bool flag = dap->end >= dap->base + dap->element_size; - if( flag ){ - dap->end -= dap->element_size; - if(element) memcpy(element, dap->end, dap->element_size); - } - return flag; -} - -char *da_index(Da *dap, size_t i){ - size_t offset = i * dap->element_size; - char *pt = dap->base + offset; - return pt; -} - -// passed in f(element_pt, arg_pt) -// We have no language support closures, so we pass in an argument for it. -// The closure may be set to NULL if it is not needed. -void da_map(Da *dap, void f(void *, void *), void *closure){ - char *pt = dap->base; - while( pt != dap->end ){ - f(pt, closure); - pt += dap->element_size; - } -} - -// da_lists are sometimes used as resource managers -static void da_free_element(void *pt, void *closure){ - free(*(char **)pt); // free does not care about the pointer type -} - -void da_free_elements(Da *dap){ - da_map(dap, da_free_element, NULL); - da_rewind(dap); -} - -// for the case of an array of strings -void da_strings_puts(Da *dap){ - char *pt = dap->base; - while( pt != dap->end ){ - puts(*(char **)pt); - pt += dap->element_size; - } -} - - -// Puts text from a line into buffer *dap. Does not push EOF or '\n' into the -// buffer. Returns the old_base so that external pointers can be rebased. -// It is possible that the the base hasn't changed. Use feof(FILE *stream) to -// test for EOF; -char *da_fgets(Da *dap, FILE *file){ - char *old_base = dap->base; - int c = fgetc(file); - while( c != EOF && c != '\n' ){ - da_push(dap, &c); - c = fgetc(file); - } - int terminator = 0; - da_push(dap, &terminator); - return old_base; -} diff --git a/da/src/da.lib.h b/da/src/da.lib.h deleted file mode 100644 index 6d3c43b..0000000 --- a/da/src/da.lib.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef DA_LIB_H -#define DA_LIB_H -#include -#include -#include - -typedef struct Da{ - char *base; - char *end; // one byte/one element off the end of the array - size_t size; // size >= (end - base) + 1; - size_t element_size; -} Da; - -#define RETURN(dap, r) \ - { da_map(dap, da_free, NULL); return r; } - -void da_alloc(Da *dap, size_t element_size); -void da_free(Da *dap); -void da_rewind(Da *dap); -char *da_expand(Da *dap); -void da_rebase(Da *dap, char *old_base, void *pta); -bool da_endq(Da *dap, void *pt); -bool da_boundq(Da *dap); -void da_push(Da *dap, void *element); -bool da_pop(Da *dap, void *element); -char *da_index(Da *dap, size_t i); -void da_map(Da *dap, void f(void *, void *), void *closure); -void da_free_elements(Da *dap); -void da_strings_puts(Da *dap); -char *da_fgets(Da *dap, FILE *fd); - -#endif - diff --git a/da/test/makefile b/da/test/makefile deleted file mode 100644 index dbd6be0..0000000 --- a/da/test/makefile +++ /dev/null @@ -1,15 +0,0 @@ -# da/test - -SHELL=/bin/bash - --include makefile-flags - -.PHONY: all -all: version deps lib exec - -%:: - $(MAKE) $@ - - - - diff --git a/da/test/makefile-dep b/da/test/makefile-dep deleted file mode 100644 index cf61fb9..0000000 --- a/da/test/makefile-dep +++ /dev/null @@ -1,5 +0,0 @@ -./test_da.lib.o: test_da.lib.c ../include/da.h test_da.lib.h -./test_da.cli.o: test_da.cli.c test_da.lib.h - -./test_da : ./test_da.cli.o ./libtest.a - gcc -o ./test_da ./test_da.cli.o -L. -L../lib -ltest -lda diff --git a/da/test/makefile-flags b/da/test/makefile-flags deleted file mode 100644 index d916748..0000000 --- a/da/test/makefile-flags +++ /dev/null @@ -1,33 +0,0 @@ - -MODULE=test - -# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. -ECHO= echo -#ECHO= echo -e - -# directories used by this makefile, these could all be set to dot for -# the simplest source directory structure - -#LIDBIR, EXECSDIR, HDIR hold the make results that might later be staged -#$(PWD) is the directory that make was called from, this is already build in -#set to dot to use the same directory as the source code -#leave blank to ommit -DEPRDIR= -DOCDIR= -EXECDIR=. -INCDIR=. -LIBDIR=. -SRCDIR=. -TESTDIR= -TMPDIR=. -TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) -TRYDIR= - -# compiler and flags -C=gcc -CFLAGS=-std=gnu11 -fPIC -I../include -ggdb -Werror -DDEBUG -DDEBUGDB -#CFLAGS=-std=gnu11 -fPIC -I../include -Werror -LINKFLAGS=-L. -L../lib -ltest -lda - -MAKE=/usr/bin/make --no-print-directory -f $(PROJECT_SUBU)/tools/lib/makefile_cc -#MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_cc diff --git a/da/test/results b/da/test/results deleted file mode 100644 index 9e59b33..0000000 --- a/da/test/results +++ /dev/null @@ -1 +0,0 @@ -passed all 4 tests diff --git a/da/test/test.dat b/da/test/test.dat deleted file mode 100644 index 6b4b5bd..0000000 --- a/da/test/test.dat +++ /dev/null @@ -1,3 +0,0 @@ -this is a test -ends without a newline -(setq mode-require-final-newline nil) \ No newline at end of file diff --git a/da/test/test_da.cli.c b/da/test/test_da.cli.c deleted file mode 100644 index bee5a6c..0000000 --- a/da/test/test_da.cli.c +++ /dev/null @@ -1,46 +0,0 @@ - -#include -#include -#include "test_da.lib.h" - -int main(){ - bool da_0_passed = test_da_0(); - - unsigned int passed = 0; - unsigned int failed = 0; - - // enumeration of tests - typedef bool (*test_fun)(); - test_fun tests[] = {test_da_0, test_da_1, test_da_2, test_da_3, NULL}; - char *test_names[] = {"test_da_0", "test_da_1", "test_da_2", "test_da_3", NULL}; - - // call tests - test_fun *tfp = tests; - char **tnp = test_names; - while(*tfp){ - if( !(*tfp)() ){ - failed++; - if(*tnp) - printf("%s failed\n", *tnp); - else - fprintf(stderr, "internal error, no test_names[] entry for test\n"); - }else - passed++; - tfp++; - tnp++; - } - - // summarize results - - if( passed == 0 && failed == 0) - printf("no tests ran\n"); - else if( passed == 0 ) - printf("failed all %u tests\n", failed); - else if( failed == 0 ) - printf("passed all %u tests\n", passed); - else - printf("failed %u of %u tests\n", failed, passed + failed); - - if( passed == 0 || failed != 0 ) return 1; - return 0; -} diff --git a/da/test/test_da.cli.o b/da/test/test_da.cli.o deleted file mode 100644 index d28221c..0000000 Binary files a/da/test/test_da.cli.o and /dev/null differ diff --git a/da/test/test_da.lib.c b/da/test/test_da.lib.c deleted file mode 100644 index 88a94c7..0000000 --- a/da/test/test_da.lib.c +++ /dev/null @@ -1,130 +0,0 @@ -/* -Tests for Da. - -*/ - -#include -#include -#include -#include - -#include "test_da.lib.h" - -// tests push -bool test_da_0(){ - Da da; - da_alloc(&da, sizeof(int)); // leaves room for 4 ints - int i = 0; - int *pt = (int *)da.base; - // will double, 4 -> 8, then double 8 -> 16 - while( i < 10 ){ - da_push(&da, &i); - i++; - pt++; - } - - bool f0 = da.size == sizeof(int) * 16; - bool f1 = 10 == (da.end - da.base) / sizeof(int); - bool f2 = true; - pt = (int *)da.base; - i = 0; - while( i < 10 ){ - f2 = f2 && *pt == i && !da_endq(&da, pt); - i++; - pt++; - } - bool f3 = da_endq(&da, pt); - - return f0 && f1 && f2 && f3; -} - -// tests manual expansion -bool test_da_1(){ - Da da; - da_alloc(&da, sizeof(int)); // leaves room for 4 ints - int i = 0; - int *pt = (int *)da.base; - // will double, 4 -> 8, then double 8 -> 16 - while( i < 10 ){ - da.end += da.element_size; - if( da_boundq(&da) ){ - char *old_base = da_expand(&da); - da_rebase(&da, old_base, &pt); - } - *pt = i; - i++; - pt++; - } - - bool f0 = da.size == sizeof(int) * 16; - bool f1 = 10 == (da.end - da.base) / sizeof(int); - bool f2 = true; - pt = (int *)da.base; - i = 0; - while( i < 10 ){ - f2 = f2 && *pt == i && !da_endq(&da, pt); - i++; - pt++; - } - bool f3 = da_endq(&da, pt); - - return f0 && f1 && f2 && f3; -} - -// da_fgets -bool test_da_2(){ - - FILE *fd = fopen("test.dat","r"); - - Da da; - da_alloc(&da, sizeof(char)); - - da_fgets(&da, fd); - bool f0 = !strcmp(da.base, "this is a test"); - - char *old_base; - da_pop(&da, NULL); // pop the prior null terminator - char *s1 = da.end; - old_base = da_fgets(&da,fd); - da_rebase(&da, old_base, &s1); - bool f1 = !strcmp(s1, "ends without a newline"); - - da_pop(&da, NULL); // pop the prior null terminator - char *s2 = da.end; - old_base = da_fgets(&da,fd); - da_rebase(&da, old_base, &s2); - bool f2 = !strcmp(s2, "(setq mode-require-final-newline nil)"); - - bool f3 = !strcmp(da.base, "this is a testends without a newline(setq mode-require-final-newline nil)"); - - fclose(fd); - return f0 && f1 && f2 && f3; -} - -// da_fgets -bool test_da_3(){ - - FILE *fd = fopen("test.dat","r"); - - Da da; - da_alloc(&da, sizeof(int)); - - int i = 5; - da_push(&da, &i); - i++; - da_push(&da, &i); - i++; - da_push(&da, &i); - - int j; - bool f0 = da_pop(&da, &j) && j == 7; - bool f1 = da_pop(&da, &j) && j == 6; - bool f2 = da_pop(&da, NULL); - bool f3 = !da_pop(&da, &j); - - return f0 && f1 && f2 && f3; -} - - - - diff --git a/da/test/test_da.lib.h b/da/test/test_da.lib.h deleted file mode 100644 index 686b0d0..0000000 --- a/da/test/test_da.lib.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef DA_LIB_H -#define DA_LIB_H - -bool test_da_0(); -bool test_da_1(); -bool test_da_2(); -bool test_da_3(); - -#endif diff --git a/da/test/test_da.lib.o b/da/test/test_da.lib.o deleted file mode 100644 index 2a03683..0000000 Binary files a/da/test/test_da.lib.o and /dev/null differ diff --git a/db/0_makefile b/db/0_makefile deleted file mode 100644 index b8c7b9d..0000000 --- a/db/0_makefile +++ /dev/null @@ -1,40 +0,0 @@ -# src/0_makefile - -SHELL=/bin/bash - --include 0_makefile_flags - -SUID_TOOL=$(TOOLSDIR)/bin/setuid_root.sh -MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_cc - -SOURCES=$(wildcard *.c) -HFILES=$(wildcard *.h) - -all: version deps lib execs - -version: - $(MAKE) $@ - @echo "SUID_TOOL: " $(SUID_TOOL) - -deps: - makeheaders $(SOURCES) $(HFILES) - sed -i '/^ *int *main *(.*)/d' *.h - $(MAKE) $@ - -execs: - $(MAKE) $@ - @echo "-> $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all" - cat $(SUID_TOOL) - @echo -n "Are you sure? [y/N] " && read ans && [ $${ans:-N} == y ] - sudo $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all - -clean: - $(MAKE) $@ - for i in $(HFILES); do rm $$i; done - -%:: - $(MAKE) $@ - - - - diff --git a/db/0_makefile-flags b/db/0_makefile-flags deleted file mode 100644 index 0da8b61..0000000 --- a/db/0_makefile-flags +++ /dev/null @@ -1,31 +0,0 @@ - -# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. -ECHO= echo -#ECHO= echo -e - -# directories used by this makefile, these could all be set to dot for -# the simplest source directory structure - -#LIDBIR, EXECSDIR, HDIR hold the make results that might later be staged -#$(PWD) is the directory that make was called from, this is already build in -#set to dot to use the same directory as the source code -#leave blank to ommit -DEPRDIR=1_deprecated -DOCDIR=1_doc -EXECSDIR=1_execs -HDIR=1_headers -LIBDIR=1_lib -TESTDIR=1_tests -TMPDIR=1_tmp -TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) -TRYDIR=1_try - - -# compiler and flags -CC=gcc -CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB -#CFLAGS=-std=gnu11 -fPIC -I. -Werror -LINKFLAGS=-L1_lib -lsubu -lsqlite3 - -LIBFILE=$(LIBDIR)/libsubu.a - diff --git a/db/1_doc/return-from-transaction.txt b/db/1_doc/return-from-transaction.txt deleted file mode 100644 index b7f8cbb..0000000 --- a/db/1_doc/return-from-transaction.txt +++ /dev/null @@ -1,15 +0,0 @@ - -1. - This sql: - - char *sql = - "BEGIN TRANSACTION;" - "UPDATE Key_Int SET value = value + 1 WHERE key = 'max_subu_number';" - "SELECT value FROM Key_Int WHERE key = 'max_subu_number';" - "COMMIT;" - ; - - with sqlite_exec, the call back is called with the data from the select. - - with sqlite_prepare_v2, sqlite_step just returns SQLITE_DONE, and we never - get to see our data from the select. diff --git a/db/dbprintf.lib.c b/db/dbprintf.lib.c deleted file mode 100644 index d9d236b..0000000 --- a/db/dbprintf.lib.c +++ /dev/null @@ -1,14 +0,0 @@ - -#include "dbprintf.lib.h" - -#include -#include - -int dbprintf(const char *format, ...){ - va_list args; - va_start(args,format); - int ret = vfprintf(stdout, format, args); - fflush(stdout); - va_end(args); - return ret; -} diff --git a/dispatch/makefile b/dispatch/makefile deleted file mode 100644 index 175b683..0000000 --- a/dispatch/makefile +++ /dev/null @@ -1,14 +0,0 @@ -# src-dispatch/makefile - -SHELL=/bin/bash - --include makefile-flags - -all: version dep lib - -%:: - $(MAKE) $@ - - - - diff --git a/dispatch/makefile-flags b/dispatch/makefile-flags deleted file mode 100644 index 48cadc2..0000000 --- a/dispatch/makefile-flags +++ /dev/null @@ -1,20 +0,0 @@ - -# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. -ECHO= echo -#ECHO= echo -e - -# directories used by this makefile, these could all be set to dot for -# the simplest source directory structure - -# compiler and flags -C=gcc -CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB -#CFLAGS=-std=gnu11 -fPIC -I. -Werror - -LINKFLAGS=-L1_lib -lda - -MODULE=dispatch - -MAKE=/usr/bin/make --no-print-directory -f $(PROJECT_SUBU)/tools/lib/makefile_trc -#MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_trc - diff --git a/dispatch/src/dispatch.lib.c b/dispatch/src/dispatch.lib.c deleted file mode 100644 index e34d020..0000000 --- a/dispatch/src/dispatch.lib.c +++ /dev/null @@ -1,166 +0,0 @@ -#tranche tmp/dispatch.trc.c - -#tranche include/dispatch.h -/* - Runs a command or function as its own process. - - The return status integer from command or function must be greater than ERR_DISPATCH. - In the case of dispatch_exec, we only have a promise from the user to not dispatch - non compliant commands. - -*/ -#ifndef DISPATCH_LIB_H -#define DISPATCH_LIB_H -#include -#include - -#define ERR_DISPATCH -1024 -#define ERR_DISPATCH_NEGATIVE_RETURN_STATUS -1024 -#define ERR_DISPATCH_F_FORK -1025 -#define ERR_DISPATCH_F_SETEUID -1026 -#define ERR_DISPATCH_F_SETEGID -1027 -#define ERR_DISPATCH_NULL_EXECUTABLE -1028 -#define ERR_DISPATCH_EXEC -1029 - -// currently both dispatcher and dispatchee strings are statically allocated -struct dispatch_ctx{ - char *dispatcher; // name of the dispatch function ("dispatch_f", "dispatch_f_euid_egid", etc.) - char *dispatchee; // name of the function being dispatched - int err; // error code as listed below, or status returned from dispatchee -}; -#tranche-end - -#include "dispatch.h" - -// we need the declaration for uid_t etc. -// without this #define execvpe is undefined -#define _GNU_SOURCE - -#include -#include -#include -#include - -#tranche include/dispatch.h -void dispatch_f_mess(char *fname, int err, char *dispatchee); -#tranche-end -void dispatch_f_mess(char *fname, int err, char *dispatchee){ - if(err == 0) return; - fprintf(stderr, "%s: ", fname); // if fprintf gets an error, errno will be overwritten - if(err > ERR_DISPATCH){ - fprintf(stderr, "dispatchee \"%s\" returned the error %d\n", dispatchee, err); - return; - } - switch(err){ - case ERR_DISPATCH_NEGATIVE_RETURN_STATUS: - fprintf(stderr, " dispatchee \"%s\" returned a negative status.", dispatchee); - break; - case ERR_DISPATCH_F_FORK: - case ERR_DISPATCH_F_SETEUID: - case ERR_DISPATCH_F_SETEGID: - fputc(' ', stderr); - perror(dispatchee); - break; - case ERR_DISPATCH_NULL_EXECUTABLE: - fprintf(stderr, " executable was not specified"); - break; - case ERR_DISPATCH_EXEC: - // exec is running in another process when it fails, so we can't see the errno value it set - fprintf(stderr, " exec of \"%s\" failed", dispatchee); - break; - default: - fprintf(stderr, " returned undefined status when dispatching \"%s\"", dispatchee); - } - fputc('\n', stderr); -} - -//-------------------------------------------------------------------------------- -// interface call point, dispatch a function -#tranche include/dispatch.h -int dispatch_f(char *fname, int (*f)(void *arg), void *f_arg); -#tranche-end -int dispatch_f(char *fname, int (*f)(void *arg), void *f_arg){ - #ifdef DEBUG - dbprintf("%s %s\n", "dispatch_f", fname); - #endif - pid_t pid = fork(); - if( pid == -1 ) return ERR_DISPATCH_F_FORK; // something went wrong and we are still in the parent process - if( pid == 0 ){ // we are the child - int status = (*f)(f_arg); // we require that f return a zero or positive value - if( status < ERR_DISPATCH ) status = ERR_DISPATCH_NEGATIVE_RETURN_STATUS; - exit(status); - }else{ // we are the parent - int err; - waitpid(pid, &err, 0); - return err; - } -} - -//-------------------------------------------------------------------------------- -// interface call point, dispatch a function with a given euid/egid -// of course this will only work if our euid is root in the first place -#tranche include/dispatch.h -int dispatch_f_euid_egid(char *fname, int (*f)(void *arg), void *f_arg, uid_t euid, gid_t egid); -#tranche-end -int dispatch_f_euid_egid(char *fname, int (*f)(void *arg), void *f_arg, uid_t euid, gid_t egid){ - #ifdef DEBUG - dbprintf("%s %s as euid:%u egid:%u\n", "dispatch_f_euid_egid", fname, euid, egid); - #endif - pid_t pid = fork(); - if( pid == -1 ) return ERR_DISPATCH_F_FORK; - if( pid == 0 ){ // we are the child - int status; - if( seteuid(euid) == -1 ) - status = ERR_DISPATCH_F_SETEUID; - else if( setegid(egid) == -1 ) - status = ERR_DISPATCH_F_SETEGID; - else{ - status = (*f)(f_arg); - if( status <= ERR_DISPATCH ) status = ERR_DISPATCH_NEGATIVE_RETURN_STATUS; - exit(status); - } - }else{ // we are the parent - uint err; - waitpid(pid, &err, 0); - return err; - } -} - -//-------------------------------------------------------------------------------- -// interface call point, dispatch a executable -#tranche include/dispatch.h -int dispatch_exec(char **argv, char **envp); -#tranche-end -int dispatch_exec(char **argv, char **envp){ - char *command; - { - if( !argv || !argv[0] ) return ERR_DISPATCH_NULL_EXECUTABLE; - #ifdef DEBUG - dbprintf("dispatch_exec:"); - char **apt = argv; - while( *apt ){ - dbprintf(" %s",*apt); - apt++; - } - dbprintf("\n"); - #endif - command = argv[0]; - } - pid_t pid = fork(); - if( pid == -1 ) return ERR_DISPATCH_F_FORK; // something went wrong and we are still in the parent process - if( pid == 0 ){ // we are the child - execvpe(command, argv, envp); // exec will only return if it has an error - #ifdef DEBUG - dbprintf("dispatch_exec: exec returned, perror message:"); - perror("dispatch_exec"); // our only chance to print this message, as this is the child process - #endif - fflush(stdout); - exit(ERR_DISPATCH_EXEC); - }else{ // we are the parent - int err; - waitpid(pid, &err, 0); - return err; - } -} - -#tranche-end diff --git a/makefile b/makefile index ddad312..c7a88ed 100755 --- a/makefile +++ b/makefile @@ -1,11 +1,11 @@ #MAKEABLE=$(shell \ -# find .\ +# find src-*\ # \( -name 'makefile' -o -name 'Makefile' \)\ # -printf "%h\n"\ # | grep -v deprecated | grep -v doc | sort -u | sed ':a;N;$!ba;s/\n/ /g' \ #) -MAKEABLE= da da/test tranche +MAKEABLE= module/da module/da/test module/tranche .PHONY: all info clean dist-clean diff --git a/module/da/makefile b/module/da/makefile new file mode 100644 index 0000000..67fbdb9 --- /dev/null +++ b/module/da/makefile @@ -0,0 +1,21 @@ +# da/makefile + +SHELL=/bin/bash +MAKE= + +-include makefile-flags + +.PHONY: all +all: version dep lib exec + +.PHONY: lib +lib: + $(MAKE) $@ + cp src/da.lib.h include/da.h + +%:: + $(MAKE) $@ + + + + diff --git a/module/da/makefile-flags b/module/da/makefile-flags new file mode 100644 index 0000000..8bb0814 --- /dev/null +++ b/module/da/makefile-flags @@ -0,0 +1,35 @@ + +MODULE=da + +# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. +ECHO= echo +#ECHO= echo -e + +# directories used by this makefile, these could all be set to dot for +# the simplest source directory structure + +#LIDBIR, EXECDIR, INCDIR hold the make results that might later be shared +#$(PWD) is the directory that make was called from, this is already build in +#set to dot to use the same directory as the source code +#leave blank to ommit +DEPRDIR=deprecated +DOCDIR=doc +EXECDIR=exec +INCDIR=include +LIBDIR=lib +SRCDIR=src +TESTDIR=test +TMPDIR=tmp +TOOLDIR=$(realpath $(PROJECT_SUBU)/tool) +TRYDIR=try + +# compiler and flags +C=gcc +CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB +#CFLAGS=-std=gnu11 -fPIC -I. -Werror +LINKFLAGS=-Llib -lda + +MAKE=/usr/bin/make --no-print-directory -f $(PROJECT_SUBU)/tool/lib/makefile_cc +#MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tool/lib/makefile_cc + + diff --git a/module/da/src/da.lib.c b/module/da/src/da.lib.c new file mode 100644 index 0000000..0956d7e --- /dev/null +++ b/module/da/src/da.lib.c @@ -0,0 +1,132 @@ +/* +Dynamic Array + +*/ + +#include "da.lib.h" + +#include +#include +#include + +//-------------------------------------------------------------------------------- +// generic +// We manipulate pointers to a smallest addressable unit. The sizeof operator +// returns counts in these addressable units. Sizeof(char) is defined to be 1. + +void da_alloc(Da *dap, size_t element_size){ + dap->element_size = element_size; + dap->size = 4 * element_size; + dap->base = malloc(dap->size); + dap->end = dap->base; +} +void da_free(Da *dap){ + free(dap->base); + dap->size = 0; +} +void da_rewind(Da *dap){ + dap->end = dap->base; +} + +bool da_empty(Da *dap){ + return dap->end == dap->base; +} + +void da_rebase(Da *dap, char *old_base, void *pta){ + char **pt = (char **)pta; + size_t offset = *pt - old_base; + *pt = dap->base + offset; +} + +// Doubles size of of da. Returns old base, so that existing pointers into the +// array can be moved to the new array +char *da_expand(Da *dap){ + char *old_base = dap->base; + size_t end_offset = dap->end - old_base; + size_t new_size = dap->size << 1; + char *new_base = malloc( new_size ); + memcpy( new_base, old_base, end_offset + dap->element_size); + free(old_base); + dap->base = new_base; + dap->end = new_base + end_offset; + dap->size = new_size; + return old_base; +} + +// true when pt has run off the end of the area currently allocated for the array +bool da_endq(Da *dap, void *pt){ + return (char *)pt >= dap->end; +} + +// true when pt has run off the end of the area allocated for the array +bool da_boundq(Da *dap){ + return dap->end >= dap->base + dap->size; +} + +void da_push(Da *dap, void *element){ + if( dap->end >= dap->base + dap->size ) da_expand(dap); + memcpy(dap->end, element, dap->element_size); + dap->end += dap->element_size; +} + +bool da_pop(Da *dap, void *element){ + bool flag = dap->end >= dap->base + dap->element_size; + if( flag ){ + dap->end -= dap->element_size; + if(element) memcpy(element, dap->end, dap->element_size); + } + return flag; +} + +char *da_index(Da *dap, size_t i){ + size_t offset = i * dap->element_size; + char *pt = dap->base + offset; + return pt; +} + +// passed in f(element_pt, arg_pt) +// We have no language support closures, so we pass in an argument for it. +// The closure may be set to NULL if it is not needed. +void da_map(Da *dap, void f(void *, void *), void *closure){ + char *pt = dap->base; + while( pt != dap->end ){ + f(pt, closure); + pt += dap->element_size; + } +} + +// da_lists are sometimes used as resource managers +static void da_free_element(void *pt, void *closure){ + free(*(char **)pt); // free does not care about the pointer type +} + +void da_free_elements(Da *dap){ + da_map(dap, da_free_element, NULL); + da_rewind(dap); +} + +// for the case of an array of strings +void da_strings_puts(Da *dap){ + char *pt = dap->base; + while( pt != dap->end ){ + puts(*(char **)pt); + pt += dap->element_size; + } +} + + +// Puts text from a line into buffer *dap. Does not push EOF or '\n' into the +// buffer. Returns the old_base so that external pointers can be rebased. +// It is possible that the the base hasn't changed. Use feof(FILE *stream) to +// test for EOF; +char *da_fgets(Da *dap, FILE *file){ + char *old_base = dap->base; + int c = fgetc(file); + while( c != EOF && c != '\n' ){ + da_push(dap, &c); + c = fgetc(file); + } + int terminator = 0; + da_push(dap, &terminator); + return old_base; +} diff --git a/module/da/src/da.lib.h b/module/da/src/da.lib.h new file mode 100644 index 0000000..6d3c43b --- /dev/null +++ b/module/da/src/da.lib.h @@ -0,0 +1,33 @@ +#ifndef DA_LIB_H +#define DA_LIB_H +#include +#include +#include + +typedef struct Da{ + char *base; + char *end; // one byte/one element off the end of the array + size_t size; // size >= (end - base) + 1; + size_t element_size; +} Da; + +#define RETURN(dap, r) \ + { da_map(dap, da_free, NULL); return r; } + +void da_alloc(Da *dap, size_t element_size); +void da_free(Da *dap); +void da_rewind(Da *dap); +char *da_expand(Da *dap); +void da_rebase(Da *dap, char *old_base, void *pta); +bool da_endq(Da *dap, void *pt); +bool da_boundq(Da *dap); +void da_push(Da *dap, void *element); +bool da_pop(Da *dap, void *element); +char *da_index(Da *dap, size_t i); +void da_map(Da *dap, void f(void *, void *), void *closure); +void da_free_elements(Da *dap); +void da_strings_puts(Da *dap); +char *da_fgets(Da *dap, FILE *fd); + +#endif + diff --git a/module/da/test/makefile b/module/da/test/makefile new file mode 100644 index 0000000..dbd6be0 --- /dev/null +++ b/module/da/test/makefile @@ -0,0 +1,15 @@ +# da/test + +SHELL=/bin/bash + +-include makefile-flags + +.PHONY: all +all: version deps lib exec + +%:: + $(MAKE) $@ + + + + diff --git a/module/da/test/makefile-dep b/module/da/test/makefile-dep new file mode 100644 index 0000000..cf61fb9 --- /dev/null +++ b/module/da/test/makefile-dep @@ -0,0 +1,5 @@ +./test_da.lib.o: test_da.lib.c ../include/da.h test_da.lib.h +./test_da.cli.o: test_da.cli.c test_da.lib.h + +./test_da : ./test_da.cli.o ./libtest.a + gcc -o ./test_da ./test_da.cli.o -L. -L../lib -ltest -lda diff --git a/module/da/test/makefile-flags b/module/da/test/makefile-flags new file mode 100644 index 0000000..a8adf4d --- /dev/null +++ b/module/da/test/makefile-flags @@ -0,0 +1,33 @@ + +MODULE=test + +# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. +ECHO= echo +#ECHO= echo -e + +# directories used by this makefile, these could all be set to dot for +# the simplest source directory structure + +#LIDBIR, EXECSDIR, HDIR hold the make results that might later be shared +#$(PWD) is the directory that make was called from, this is already build in +#set to dot to use the same directory as the source code +#leave blank to ommit +DEPRDIR= +DOCDIR= +EXECDIR=. +INCDIR=. +LIBDIR=. +SRCDIR=. +TESTDIR= +TMPDIR=. +TOOLDIR=$(realpath $(PROJECT_SUBU)/tool) +TRYDIR= + +# compiler and flags +C=gcc +CFLAGS=-std=gnu11 -fPIC -I../include -ggdb -Werror -DDEBUG -DDEBUGDB +#CFLAGS=-std=gnu11 -fPIC -I../include -Werror +LINKFLAGS=-L. -L../lib -ltest -lda + +MAKE=/usr/bin/make --no-print-directory -f $(PROJECT_SUBU)/tool/lib/makefile_cc +#MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tool/lib/makefile_cc diff --git a/module/da/test/results b/module/da/test/results new file mode 100644 index 0000000..9e59b33 --- /dev/null +++ b/module/da/test/results @@ -0,0 +1 @@ +passed all 4 tests diff --git a/module/da/test/test.dat b/module/da/test/test.dat new file mode 100644 index 0000000..6b4b5bd --- /dev/null +++ b/module/da/test/test.dat @@ -0,0 +1,3 @@ +this is a test +ends without a newline +(setq mode-require-final-newline nil) \ No newline at end of file diff --git a/module/da/test/test_da.cli.c b/module/da/test/test_da.cli.c new file mode 100644 index 0000000..bee5a6c --- /dev/null +++ b/module/da/test/test_da.cli.c @@ -0,0 +1,46 @@ + +#include +#include +#include "test_da.lib.h" + +int main(){ + bool da_0_passed = test_da_0(); + + unsigned int passed = 0; + unsigned int failed = 0; + + // enumeration of tests + typedef bool (*test_fun)(); + test_fun tests[] = {test_da_0, test_da_1, test_da_2, test_da_3, NULL}; + char *test_names[] = {"test_da_0", "test_da_1", "test_da_2", "test_da_3", NULL}; + + // call tests + test_fun *tfp = tests; + char **tnp = test_names; + while(*tfp){ + if( !(*tfp)() ){ + failed++; + if(*tnp) + printf("%s failed\n", *tnp); + else + fprintf(stderr, "internal error, no test_names[] entry for test\n"); + }else + passed++; + tfp++; + tnp++; + } + + // summarize results + + if( passed == 0 && failed == 0) + printf("no tests ran\n"); + else if( passed == 0 ) + printf("failed all %u tests\n", failed); + else if( failed == 0 ) + printf("passed all %u tests\n", passed); + else + printf("failed %u of %u tests\n", failed, passed + failed); + + if( passed == 0 || failed != 0 ) return 1; + return 0; +} diff --git a/module/da/test/test_da.cli.o b/module/da/test/test_da.cli.o new file mode 100644 index 0000000..d28221c Binary files /dev/null and b/module/da/test/test_da.cli.o differ diff --git a/module/da/test/test_da.lib.c b/module/da/test/test_da.lib.c new file mode 100644 index 0000000..88a94c7 --- /dev/null +++ b/module/da/test/test_da.lib.c @@ -0,0 +1,130 @@ +/* +Tests for Da. + +*/ + +#include +#include +#include +#include + +#include "test_da.lib.h" + +// tests push +bool test_da_0(){ + Da da; + da_alloc(&da, sizeof(int)); // leaves room for 4 ints + int i = 0; + int *pt = (int *)da.base; + // will double, 4 -> 8, then double 8 -> 16 + while( i < 10 ){ + da_push(&da, &i); + i++; + pt++; + } + + bool f0 = da.size == sizeof(int) * 16; + bool f1 = 10 == (da.end - da.base) / sizeof(int); + bool f2 = true; + pt = (int *)da.base; + i = 0; + while( i < 10 ){ + f2 = f2 && *pt == i && !da_endq(&da, pt); + i++; + pt++; + } + bool f3 = da_endq(&da, pt); + + return f0 && f1 && f2 && f3; +} + +// tests manual expansion +bool test_da_1(){ + Da da; + da_alloc(&da, sizeof(int)); // leaves room for 4 ints + int i = 0; + int *pt = (int *)da.base; + // will double, 4 -> 8, then double 8 -> 16 + while( i < 10 ){ + da.end += da.element_size; + if( da_boundq(&da) ){ + char *old_base = da_expand(&da); + da_rebase(&da, old_base, &pt); + } + *pt = i; + i++; + pt++; + } + + bool f0 = da.size == sizeof(int) * 16; + bool f1 = 10 == (da.end - da.base) / sizeof(int); + bool f2 = true; + pt = (int *)da.base; + i = 0; + while( i < 10 ){ + f2 = f2 && *pt == i && !da_endq(&da, pt); + i++; + pt++; + } + bool f3 = da_endq(&da, pt); + + return f0 && f1 && f2 && f3; +} + +// da_fgets +bool test_da_2(){ + + FILE *fd = fopen("test.dat","r"); + + Da da; + da_alloc(&da, sizeof(char)); + + da_fgets(&da, fd); + bool f0 = !strcmp(da.base, "this is a test"); + + char *old_base; + da_pop(&da, NULL); // pop the prior null terminator + char *s1 = da.end; + old_base = da_fgets(&da,fd); + da_rebase(&da, old_base, &s1); + bool f1 = !strcmp(s1, "ends without a newline"); + + da_pop(&da, NULL); // pop the prior null terminator + char *s2 = da.end; + old_base = da_fgets(&da,fd); + da_rebase(&da, old_base, &s2); + bool f2 = !strcmp(s2, "(setq mode-require-final-newline nil)"); + + bool f3 = !strcmp(da.base, "this is a testends without a newline(setq mode-require-final-newline nil)"); + + fclose(fd); + return f0 && f1 && f2 && f3; +} + +// da_fgets +bool test_da_3(){ + + FILE *fd = fopen("test.dat","r"); + + Da da; + da_alloc(&da, sizeof(int)); + + int i = 5; + da_push(&da, &i); + i++; + da_push(&da, &i); + i++; + da_push(&da, &i); + + int j; + bool f0 = da_pop(&da, &j) && j == 7; + bool f1 = da_pop(&da, &j) && j == 6; + bool f2 = da_pop(&da, NULL); + bool f3 = !da_pop(&da, &j); + + return f0 && f1 && f2 && f3; +} + + + + diff --git a/module/da/test/test_da.lib.h b/module/da/test/test_da.lib.h new file mode 100644 index 0000000..686b0d0 --- /dev/null +++ b/module/da/test/test_da.lib.h @@ -0,0 +1,9 @@ +#ifndef DA_LIB_H +#define DA_LIB_H + +bool test_da_0(); +bool test_da_1(); +bool test_da_2(); +bool test_da_3(); + +#endif diff --git a/module/da/test/test_da.lib.o b/module/da/test/test_da.lib.o new file mode 100644 index 0000000..2a03683 Binary files /dev/null and b/module/da/test/test_da.lib.o differ diff --git a/module/db/0_makefile b/module/db/0_makefile new file mode 100644 index 0000000..b8c7b9d --- /dev/null +++ b/module/db/0_makefile @@ -0,0 +1,40 @@ +# src/0_makefile + +SHELL=/bin/bash + +-include 0_makefile_flags + +SUID_TOOL=$(TOOLSDIR)/bin/setuid_root.sh +MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_cc + +SOURCES=$(wildcard *.c) +HFILES=$(wildcard *.h) + +all: version deps lib execs + +version: + $(MAKE) $@ + @echo "SUID_TOOL: " $(SUID_TOOL) + +deps: + makeheaders $(SOURCES) $(HFILES) + sed -i '/^ *int *main *(.*)/d' *.h + $(MAKE) $@ + +execs: + $(MAKE) $@ + @echo "-> $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all" + cat $(SUID_TOOL) + @echo -n "Are you sure? [y/N] " && read ans && [ $${ans:-N} == y ] + sudo $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all + +clean: + $(MAKE) $@ + for i in $(HFILES); do rm $$i; done + +%:: + $(MAKE) $@ + + + + diff --git a/module/db/0_makefile-flags b/module/db/0_makefile-flags new file mode 100644 index 0000000..d4df01f --- /dev/null +++ b/module/db/0_makefile-flags @@ -0,0 +1,31 @@ + +# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. +ECHO= echo +#ECHO= echo -e + +# directories used by this makefile, these could all be set to dot for +# the simplest source directory structure + +#LIDBIR, EXECSDIR, HDIR hold the make results that might later be shared +#$(PWD) is the directory that make was called from, this is already build in +#set to dot to use the same directory as the source code +#leave blank to ommit +DEPRDIR=1_deprecated +DOCDIR=1_doc +EXECSDIR=1_execs +HDIR=1_headers +LIBDIR=1_lib +TESTDIR=1_tests +TMPDIR=1_tmp +TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) +TRYDIR=1_try + + +# compiler and flags +CC=gcc +CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB +#CFLAGS=-std=gnu11 -fPIC -I. -Werror +LINKFLAGS=-L1_lib -lsubu -lsqlite3 + +LIBFILE=$(LIBDIR)/libsubu.a + diff --git a/module/db/1_doc/return-from-transaction.txt b/module/db/1_doc/return-from-transaction.txt new file mode 100644 index 0000000..b7f8cbb --- /dev/null +++ b/module/db/1_doc/return-from-transaction.txt @@ -0,0 +1,15 @@ + +1. + This sql: + + char *sql = + "BEGIN TRANSACTION;" + "UPDATE Key_Int SET value = value + 1 WHERE key = 'max_subu_number';" + "SELECT value FROM Key_Int WHERE key = 'max_subu_number';" + "COMMIT;" + ; + + with sqlite_exec, the call back is called with the data from the select. + + with sqlite_prepare_v2, sqlite_step just returns SQLITE_DONE, and we never + get to see our data from the select. diff --git a/module/db/dbprintf.lib.c b/module/db/dbprintf.lib.c new file mode 100644 index 0000000..d9d236b --- /dev/null +++ b/module/db/dbprintf.lib.c @@ -0,0 +1,14 @@ + +#include "dbprintf.lib.h" + +#include +#include + +int dbprintf(const char *format, ...){ + va_list args; + va_start(args,format); + int ret = vfprintf(stdout, format, args); + fflush(stdout); + va_end(args); + return ret; +} diff --git a/module/dispatch/makefile b/module/dispatch/makefile new file mode 100644 index 0000000..175b683 --- /dev/null +++ b/module/dispatch/makefile @@ -0,0 +1,14 @@ +# src-dispatch/makefile + +SHELL=/bin/bash + +-include makefile-flags + +all: version dep lib + +%:: + $(MAKE) $@ + + + + diff --git a/module/dispatch/makefile-flags b/module/dispatch/makefile-flags new file mode 100644 index 0000000..48cadc2 --- /dev/null +++ b/module/dispatch/makefile-flags @@ -0,0 +1,20 @@ + +# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. +ECHO= echo +#ECHO= echo -e + +# directories used by this makefile, these could all be set to dot for +# the simplest source directory structure + +# compiler and flags +C=gcc +CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB +#CFLAGS=-std=gnu11 -fPIC -I. -Werror + +LINKFLAGS=-L1_lib -lda + +MODULE=dispatch + +MAKE=/usr/bin/make --no-print-directory -f $(PROJECT_SUBU)/tools/lib/makefile_trc +#MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_trc + diff --git a/module/dispatch/src/dispatch.lib.c b/module/dispatch/src/dispatch.lib.c new file mode 100644 index 0000000..e34d020 --- /dev/null +++ b/module/dispatch/src/dispatch.lib.c @@ -0,0 +1,166 @@ +#tranche tmp/dispatch.trc.c + +#tranche include/dispatch.h +/* + Runs a command or function as its own process. + + The return status integer from command or function must be greater than ERR_DISPATCH. + In the case of dispatch_exec, we only have a promise from the user to not dispatch + non compliant commands. + +*/ +#ifndef DISPATCH_LIB_H +#define DISPATCH_LIB_H +#include +#include + +#define ERR_DISPATCH -1024 +#define ERR_DISPATCH_NEGATIVE_RETURN_STATUS -1024 +#define ERR_DISPATCH_F_FORK -1025 +#define ERR_DISPATCH_F_SETEUID -1026 +#define ERR_DISPATCH_F_SETEGID -1027 +#define ERR_DISPATCH_NULL_EXECUTABLE -1028 +#define ERR_DISPATCH_EXEC -1029 + +// currently both dispatcher and dispatchee strings are statically allocated +struct dispatch_ctx{ + char *dispatcher; // name of the dispatch function ("dispatch_f", "dispatch_f_euid_egid", etc.) + char *dispatchee; // name of the function being dispatched + int err; // error code as listed below, or status returned from dispatchee +}; +#tranche-end + +#include "dispatch.h" + +// we need the declaration for uid_t etc. +// without this #define execvpe is undefined +#define _GNU_SOURCE + +#include +#include +#include +#include + +#tranche include/dispatch.h +void dispatch_f_mess(char *fname, int err, char *dispatchee); +#tranche-end +void dispatch_f_mess(char *fname, int err, char *dispatchee){ + if(err == 0) return; + fprintf(stderr, "%s: ", fname); // if fprintf gets an error, errno will be overwritten + if(err > ERR_DISPATCH){ + fprintf(stderr, "dispatchee \"%s\" returned the error %d\n", dispatchee, err); + return; + } + switch(err){ + case ERR_DISPATCH_NEGATIVE_RETURN_STATUS: + fprintf(stderr, " dispatchee \"%s\" returned a negative status.", dispatchee); + break; + case ERR_DISPATCH_F_FORK: + case ERR_DISPATCH_F_SETEUID: + case ERR_DISPATCH_F_SETEGID: + fputc(' ', stderr); + perror(dispatchee); + break; + case ERR_DISPATCH_NULL_EXECUTABLE: + fprintf(stderr, " executable was not specified"); + break; + case ERR_DISPATCH_EXEC: + // exec is running in another process when it fails, so we can't see the errno value it set + fprintf(stderr, " exec of \"%s\" failed", dispatchee); + break; + default: + fprintf(stderr, " returned undefined status when dispatching \"%s\"", dispatchee); + } + fputc('\n', stderr); +} + +//-------------------------------------------------------------------------------- +// interface call point, dispatch a function +#tranche include/dispatch.h +int dispatch_f(char *fname, int (*f)(void *arg), void *f_arg); +#tranche-end +int dispatch_f(char *fname, int (*f)(void *arg), void *f_arg){ + #ifdef DEBUG + dbprintf("%s %s\n", "dispatch_f", fname); + #endif + pid_t pid = fork(); + if( pid == -1 ) return ERR_DISPATCH_F_FORK; // something went wrong and we are still in the parent process + if( pid == 0 ){ // we are the child + int status = (*f)(f_arg); // we require that f return a zero or positive value + if( status < ERR_DISPATCH ) status = ERR_DISPATCH_NEGATIVE_RETURN_STATUS; + exit(status); + }else{ // we are the parent + int err; + waitpid(pid, &err, 0); + return err; + } +} + +//-------------------------------------------------------------------------------- +// interface call point, dispatch a function with a given euid/egid +// of course this will only work if our euid is root in the first place +#tranche include/dispatch.h +int dispatch_f_euid_egid(char *fname, int (*f)(void *arg), void *f_arg, uid_t euid, gid_t egid); +#tranche-end +int dispatch_f_euid_egid(char *fname, int (*f)(void *arg), void *f_arg, uid_t euid, gid_t egid){ + #ifdef DEBUG + dbprintf("%s %s as euid:%u egid:%u\n", "dispatch_f_euid_egid", fname, euid, egid); + #endif + pid_t pid = fork(); + if( pid == -1 ) return ERR_DISPATCH_F_FORK; + if( pid == 0 ){ // we are the child + int status; + if( seteuid(euid) == -1 ) + status = ERR_DISPATCH_F_SETEUID; + else if( setegid(egid) == -1 ) + status = ERR_DISPATCH_F_SETEGID; + else{ + status = (*f)(f_arg); + if( status <= ERR_DISPATCH ) status = ERR_DISPATCH_NEGATIVE_RETURN_STATUS; + exit(status); + } + }else{ // we are the parent + uint err; + waitpid(pid, &err, 0); + return err; + } +} + +//-------------------------------------------------------------------------------- +// interface call point, dispatch a executable +#tranche include/dispatch.h +int dispatch_exec(char **argv, char **envp); +#tranche-end +int dispatch_exec(char **argv, char **envp){ + char *command; + { + if( !argv || !argv[0] ) return ERR_DISPATCH_NULL_EXECUTABLE; + #ifdef DEBUG + dbprintf("dispatch_exec:"); + char **apt = argv; + while( *apt ){ + dbprintf(" %s",*apt); + apt++; + } + dbprintf("\n"); + #endif + command = argv[0]; + } + pid_t pid = fork(); + if( pid == -1 ) return ERR_DISPATCH_F_FORK; // something went wrong and we are still in the parent process + if( pid == 0 ){ // we are the child + execvpe(command, argv, envp); // exec will only return if it has an error + #ifdef DEBUG + dbprintf("dispatch_exec: exec returned, perror message:"); + perror("dispatch_exec"); // our only chance to print this message, as this is the child process + #endif + fflush(stdout); + exit(ERR_DISPATCH_EXEC); + }else{ // we are the parent + int err; + waitpid(pid, &err, 0); + return err; + } +} + +#tranche-end diff --git a/module/share/include/da.h b/module/share/include/da.h new file mode 100644 index 0000000..6d3c43b --- /dev/null +++ b/module/share/include/da.h @@ -0,0 +1,33 @@ +#ifndef DA_LIB_H +#define DA_LIB_H +#include +#include +#include + +typedef struct Da{ + char *base; + char *end; // one byte/one element off the end of the array + size_t size; // size >= (end - base) + 1; + size_t element_size; +} Da; + +#define RETURN(dap, r) \ + { da_map(dap, da_free, NULL); return r; } + +void da_alloc(Da *dap, size_t element_size); +void da_free(Da *dap); +void da_rewind(Da *dap); +char *da_expand(Da *dap); +void da_rebase(Da *dap, char *old_base, void *pta); +bool da_endq(Da *dap, void *pt); +bool da_boundq(Da *dap); +void da_push(Da *dap, void *element); +bool da_pop(Da *dap, void *element); +char *da_index(Da *dap, size_t i); +void da_map(Da *dap, void f(void *, void *), void *closure); +void da_free_elements(Da *dap); +void da_strings_puts(Da *dap); +char *da_fgets(Da *dap, FILE *fd); + +#endif + diff --git a/module/share/include/tranche.h b/module/share/include/tranche.h new file mode 100644 index 0000000..27990a6 --- /dev/null +++ b/module/share/include/tranche.h @@ -0,0 +1,8 @@ +#ifndef TRANCHE_LIB_H +#define TRANCHE_LIB_H + +int tranche_send(FILE *src, Da *arg_fds); + + + +#endif diff --git a/module/subu-0/0_makefile b/module/subu-0/0_makefile new file mode 100644 index 0000000..b8c7b9d --- /dev/null +++ b/module/subu-0/0_makefile @@ -0,0 +1,40 @@ +# src/0_makefile + +SHELL=/bin/bash + +-include 0_makefile_flags + +SUID_TOOL=$(TOOLSDIR)/bin/setuid_root.sh +MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_cc + +SOURCES=$(wildcard *.c) +HFILES=$(wildcard *.h) + +all: version deps lib execs + +version: + $(MAKE) $@ + @echo "SUID_TOOL: " $(SUID_TOOL) + +deps: + makeheaders $(SOURCES) $(HFILES) + sed -i '/^ *int *main *(.*)/d' *.h + $(MAKE) $@ + +execs: + $(MAKE) $@ + @echo "-> $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all" + cat $(SUID_TOOL) + @echo -n "Are you sure? [y/N] " && read ans && [ $${ans:-N} == y ] + sudo $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all + +clean: + $(MAKE) $@ + for i in $(HFILES); do rm $$i; done + +%:: + $(MAKE) $@ + + + + diff --git a/module/subu-0/0_makefile-flags b/module/subu-0/0_makefile-flags new file mode 100644 index 0000000..d4df01f --- /dev/null +++ b/module/subu-0/0_makefile-flags @@ -0,0 +1,31 @@ + +# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. +ECHO= echo +#ECHO= echo -e + +# directories used by this makefile, these could all be set to dot for +# the simplest source directory structure + +#LIDBIR, EXECSDIR, HDIR hold the make results that might later be shared +#$(PWD) is the directory that make was called from, this is already build in +#set to dot to use the same directory as the source code +#leave blank to ommit +DEPRDIR=1_deprecated +DOCDIR=1_doc +EXECSDIR=1_execs +HDIR=1_headers +LIBDIR=1_lib +TESTDIR=1_tests +TMPDIR=1_tmp +TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) +TRYDIR=1_try + + +# compiler and flags +CC=gcc +CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB +#CFLAGS=-std=gnu11 -fPIC -I. -Werror +LINKFLAGS=-L1_lib -lsubu -lsqlite3 + +LIBFILE=$(LIBDIR)/libsubu.a + diff --git a/module/subu-0/1_deprecated/dispatch_exec.lib.c b/module/subu-0/1_deprecated/dispatch_exec.lib.c new file mode 100644 index 0000000..024bff8 --- /dev/null +++ b/module/subu-0/1_deprecated/dispatch_exec.lib.c @@ -0,0 +1,60 @@ +/* + fork/execs/wait the command passed in argv[0]; + Returns -1 upon failure. + + The wstatus returned from wait() might be either the error returned by exec + when it failed, or the return value from the command. An arbitary command is + passed in, so we don't know what its return values might be. Consquently, we + have no way of multiplexing a unique exec error code with the command return + value within wstatus. If the prorgrammer knows the return values of the + command passed in, and wants better behavior, he or she can spin a special + version of dispatch for that command. +*/ +#include "dispatch_exec.lib.h" + +// without this #define execvpe is undefined +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + + + +int dispatch_exec(char **argv, char **envp){ + if( !argv || !argv[0] ){ + fprintf(stderr, "argv[0] null. Null command passed into dispatch().\n"); + return -1; + } + #ifdef DEBUG + dbprintf("dispatching exec, args follow:\n"); + char **apt = argv; + while( *apt ){ + dbprintf("\t%s",*apt); + apt++; + } + dbprintf("\n"); + #endif + char *command = argv[0]; + pid_t pid = fork(); + if( pid == -1 ){ + fprintf(stderr, "fork() failed in dispatch().\n"); + return -1; + } + if( pid == 0 ){ // we are the child + execvpe(command, argv, envp); + // exec will only return if it has an error .. + perror(command); + return -1; + }else{ // we are the parent + int wstatus; + waitpid(pid, &wstatus, 0); + if(wstatus) + return -1; + else + return 0; + } +} diff --git a/module/subu-0/1_deprecated/dispatch_f.lib.c b/module/subu-0/1_deprecated/dispatch_f.lib.c new file mode 100644 index 0000000..5c199f5 --- /dev/null +++ b/module/subu-0/1_deprecated/dispatch_f.lib.c @@ -0,0 +1,130 @@ +/* + Forks a new process and runs the a function in that new process. I.e. it 'spawns' a function. + + f must have the specified prototype. I.e. it accepts one void pointer and + returns a positive integer. (Negative integers are used for dispatch error codes.) + + dispatch_f_euid_egid changes to the new euid and egid before running the function. + + If the change to in euid/egid fails, the forked process exits with a negative status. + If the function has an error, it returns a positive status. A status of zero means + that all went well. + + Because f is running in a separate process, the return status is the only means + of communication going back to the calling process. + + +*/ +#define _GNU_SOURCE + +#include "dispatch_f.lib.h" +// we need the declaration for uid_t etc. +// without this #define execvpe is undefined +#define _GNU_SOURCE + +#include +#include +#include +#include + +#if INTERFACE +#include +#include +#endif + +//-------------------------------------------------------------------------------- +// dispatch_f_ctx class +// +#if INTERFACE +#define ERR_NEGATIVE_RETURN_STATUS -1 +#define ERR_DISPATCH_F_FORK -2 +#define ERR_DISPATCH_F_SETEUID -3 +#define ERR_DISPATCH_F_SETEGID -4 + +// both name and fname are static allocations +struct dispatch_f_ctx{ + char *dispatcher; // name of the dispatch function (currently "dispatch_f" or "dispatch_f_euid_egid") + char *dispatchee; // name of the function being dispatched + int err; + int status; // return value from the function +}; +#endif +dispatch_f_ctx *dispatch_f_ctx_mk(char *name, char *fname){ + dispatch_f_ctx *ctxp = malloc(sizeof(dispatch_f_ctx)); + ctxp->dispatcher = name; + ctxp->dispatchee = fname; + ctxp->err = 0; + return ctxp; +} +void dispatch_f_ctx_free(dispatch_f_ctx *ctxp){ + // no dynamic variables to be freed in ctx + free(ctxp); +} +void dispatch_f_mess(struct dispatch_f_ctx *ctxp){ + if(ctxp->err == 0) return; + switch(ctxp->err){ + case ERR_NEGATIVE_RETURN_STATUS: + fprintf(stderr, "%s, function \"%s\" broke contract with a negative return value.", ctxp->dispatcher, ctxp->dispatchee); + break; + case ERR_DISPATCH_F_FORK: + case ERR_DISPATCH_F_SETEUID: + case ERR_DISPATCH_F_SETEGID: + fprintf(stderr, "%s, ", ctxp->dispatcher); + perror(ctxp->dispatcher); + break; + } + fputc('\n', stderr); +} + +//-------------------------------------------------------------------------------- +// interface call point +dispatch_f_ctx *dispatch_f(char *fname, int (*f)(void *arg), void *f_arg){ + dispatch_f_ctx *ctxp = dispatch_f_ctx_mk("dispatch_f", fname); + #ifdef DEBUG + dbprintf("%s %s\n", ctxp->dispatcher, ctxp->dispatchee); + #endif + pid_t pid = fork(); + if( pid == -1 ){ + ctxp->err = ERR_DISPATCH_F_FORK; // we are still in the parent process + return ctxp; + } + if( pid == 0 ){ // we are the child + int status = (*f)(f_arg); // we require that f return a zero or positive value + if( status < 0 ) status = ERR_NEGATIVE_RETURN_STATUS; + exit(status); + }else{ // we are the parent + waitpid(pid, &(ctxp->status), 0); + return ctxp; + } +} + +//-------------------------------------------------------------------------------- +// interface call point +dispatch_f_ctx *dispatch_f_euid_egid(char *fname, int (*f)(void *arg), void *f_arg, uid_t euid, gid_t egid){ + dispatch_f_ctx *ctxp = dispatch_f_ctx_mk("dispatch_f_euid_egid", fname); + #ifdef DEBUG + dbprintf("%s %s as euid:%u egid:%u\n", ctxp->dispatcher, ctxp->dispatchee, euid, egid); + #endif + pid_t pid = fork(); + if( pid == -1 ){ + ctxp->err = ERR_DISPATCH_F_FORK; + return ctxp; + } + if( pid == 0 ){ // we are the child + int status; + if( seteuid(euid) == -1 ) + status = ERR_DISPATCH_F_SETEUID; + else if( setegid(egid) == -1 ) + status = ERR_DISPATCH_F_SETEGID; + else{ + status = (*f)(f_arg); + if( status < 0 ) status = ERR_NEGATIVE_RETURN_STATUS; + exit(status); + } + }else{ // we are the parent + waitpid(pid, &(ctxp->status), 0); + return ctxp; + } +} + + diff --git a/module/subu-0/1_deprecated/dispatch_useradd.lib.c b/module/subu-0/1_deprecated/dispatch_useradd.lib.c new file mode 100644 index 0000000..7b75291 --- /dev/null +++ b/module/subu-0/1_deprecated/dispatch_useradd.lib.c @@ -0,0 +1,68 @@ +/* +There is no C library interface to useradd(8), but if there were, this function +would be found there instead. + +*/ +#include "dispatch_useradd.lib.h" + +#include +#include +#include +#include +#include + +#if INTERFACE +#include +#include +#define ERR_DISPATCH_USERADD_ARGC 1 +#define ERR_DISPATCH_USERADD_DISPATCH 2 +#define ERR_DISPATCH_USERADD_PWREC 3 +struct dispatch_useradd_ret_t{ + uint error; + struct passwd *pw_record; +}; +#endif + + +// we have a contract with the caller that argv[1] is always the subuname +struct dispatch_useradd_ret_t dispatch_useradd(char **argv, char **envp){ + struct dispatch_useradd_ret_t ret; + { + if( !argv || !argv[0] || !argv[1]){ + fprintf(stderr,"useradd() needs a first argument as the name of the user to be made"); + ret.error = ERR_DISPATCH_USERADD_ARGC; + ret.pw_record = NULL; + return ret; + } + + char *subu_name; + { + subu_name = argv[1]; + if( dispatch_exec(argv, envp) == -1 ){ + fprintf(stderr,"%s failed\n", argv[0]); + ret.error = ERR_DISPATCH_USERADD_DISPATCH; + ret.pw_record = NULL; + return ret; + }} + + { + struct passwd *pw_record = getpwnam(subu_name); + uint count = 1; + while( !pw_record && count <= 3 ){ + #ifdef DEBUG + printf("try %u, getpwnam failed, trying again\n", count); + #endif + sleep(1); + pw_record = getpwnam(subu_name); + count++; + } + if( !pw_record ){ + ret.error = ERR_DISPATCH_USERADD_PWREC; + ret.pw_record = NULL; + return ret; + } + ret.error = 0; + ret.pw_record = pw_record; + return ret; + }}} + diff --git a/module/subu-0/1_deprecated/subu-rm-0.lib.c b/module/subu-0/1_deprecated/subu-rm-0.lib.c new file mode 100644 index 0000000..ccad437 --- /dev/null +++ b/module/subu-0/1_deprecated/subu-rm-0.lib.c @@ -0,0 +1,381 @@ +/* + subu-rm-0 subuname + + 1. get our uid and lookup masteru_name in /etc/passwd + 2. lookup masteru_name/subuname in config file, which gives us subu_username + 3. unmount subuland/subuname + 4. userdel subu_username + 5. rmdir subuland/subuname + + Note, as per the man page, we are not allowed to free the memory allocated by getpwid(). + +*/ +#include "subu-mk-0.lib.h" + +// without this #define we get the warning: implicit declaration of function ‘seteuid’/‘setegid’ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#if INTERFACE +#include +#include +#include +#endif + +//-------------------------------------------------------------------------------- +// an instance is subu_rm_0_ctx is returned by subu_rm_0 +// +#if INTERFACE +#define ERR_SUBU_RM_0_MKDIR_SUBUHOME 1 +#define ERR_SUBU_RM_0_RMDIR_SUBUHOME 2 +#define ERR_SUBU_RM_0_SUBUNAME_MALFORMED 3 +#define ERR_SUBU_RM_0_SETUID_ROOT 4 +#define ERR_SUBU_RM_0_MASTERU_HOMELESS 5 +#define ERR_SUBU_RM_0_MALLOC 6 +#define ERR_SUBU_RM_0_CONFIG_FILE 7 +#define ERR_SUBU_RM_0_SUBUHOME_EXISTS 8 +#define ERR_SUBU_RM_0_BUG_SSS 9 +#define ERR_SUBU_RM_0_FAILED_USERADD 10 + +struct subu_rm_0_ctx{ + char *name; + char *subuland; + char *subuhome; + char *subu_username; + bool free_aux; + char *aux; + uint err; +}; +#endif +struct subu_rm_0_ctx *subu_rm_0_ctx_mk(){ + struct subu_rm_0_ctx *ctxp = malloc(sizeof(struct subu_rm_0_ctx)); + ctxp->name = "subu_rm_0"; + ctxp->subuland = 0; + ctxp->subuhome = 0; + ctxp->subu_username = 0; + ctxp->free_aux = false; + ctxp->aux = 0; +} +void subu_rm_0_ctx_free(struct subu_rm_0_ctx *ctxp){ + free(ctxp->subuland); + free(ctxp->subuhome); + free(ctxp->subu_username); + if(ctxp->free_aux) free(ctxp->aux); + free(ctxp); +} +// must be called before any system calls, otherwise perror() will be messed up +void subu_rm_0_mess(struct subu_rm_0_ctx *ctxp){ + switch(ctxp->err){ + case 0: return; + case ERR_SUBU_RM_0_MKDIR_SUBUHOME: + fprintf(stderr, "masteru could not make subuhome, \"%s\"", ctxp->subuhome); + break; + case ERR_SUBU_RM_0_SUBUNAME_MALFORMED: + fprintf(stderr, "subuname, \"%s\" is not in [ _.-a-zA-Z0-9]*", ctxp->aux); + break; + case ERR_SUBU_RM_0_SETUID_ROOT: + fprintf(stderr, "This program must be run setuid root from a user account."); + break; + case ERR_SUBU_RM_0_MASTERU_HOMELESS: + fprintf(stderr,"Masteru, \"%s\", has no home directory", ctxp->aux); + break; + case ERR_SUBU_RM_0_MALLOC: + perror(ctxp->name); + break; + case ERR_SUBU_RM_0_CONFIG_FILE: + fprintf(stderr, "config file error: %s", ctxp->aux); + break; + case ERR_SUBU_RM_0_SUBUHOME_EXISTS: + fprintf(stderr, "a file system object already exists at subuhome, \"%s\"\n", ctxp->subuhome); + break; + case ERR_SUBU_RM_0_BUG_SSS: + perror(ctxp->name); + break; + case ERR_SUBU_RM_0_FAILED_USERADD: + fprintf(stderr, "%u useradd failed\n", ctxp->subu_username); + break; + default: + fprintf(stderr, "unknown error code %d\n", ctxp->err); + } + fputc('\n', stderr); +} + +//-------------------------------------------------------------------------------- +// dispatched functions +// +// the making of subuhome is dispatched to its own process so as to give it its own uid/gid +static int masteru_mkdir_subuhome(void *arg){ + char *subuhome = (char *) arg; + if( mkdir( subuhome, subuhome_perms ) == -1 ){ // find subuhome perms in common + perror("masteru_mkdir_subuhome"); + return ERR_SUBU_RM_0_MKDIR_SUBUHOME; + } + return 0; +} +static int masteru_rmdir_subuhome(void *arg){ + char *subuhome = (char *) arg; + if( rmdir( subuhome ) == -1 ){ // find subuhome perms in common + perror("masteru_rmdir_subuhome"); + return ERR_SUBU_RM_0_RMDIR_SUBUHOME; + } + return 0; +} + +//-------------------------------------------------------------------------------- +// the public call point +struct subu_rm_0_ctx *subu_rm_0(sqlite3 *db, char *subuname){ + + struct subu_rm_0_ctx *ctxp = subu_rm_0_ctx_mk(); + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("Checking that subuname is well formed and finding its length\n"); + #endif + size_t subuname_len; + { + int ret = allowed_subuname(subuname, &subuname_len); + if( ret == -1 ){ + ctxp->err = ERR_SUBU_RM_0_SUBUNAME_MALFORMED; + ctxp->aux = subuname; + return ctxp; + }} + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("Checking that we are running from a user and are setuid root.\n"); + #endif + uid_t masteru_uid; + gid_t masteru_gid; + uid_t set_euid; + gid_t set_egid; + { + masteru_uid = getuid(); + masteru_gid = getgid(); + set_euid = geteuid(); + set_egid = getegid(); + #ifdef DEBUG + dbprintf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid); + #endif + if( masteru_uid == 0 || set_euid != 0 ){ + ctxp->err = ERR_SUBU_RM_0_SETUID_ROOT; + return ctxp; + } + } + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("strings masteru_name and masteru_home\n"); + #endif + + char *masteru_name; + size_t masteru_name_len; + char *masteru_home; + size_t masteru_home_len; + size_t subuland_len; + { + struct passwd *masteru_pw_record_pt = getpwuid(masteru_uid); // reading /etc/passwd + masteru_name = masteru_pw_record_pt->pw_name; + masteru_name_len = strlen(masteru_name); + #ifdef DEBUG + dbprintf("masteru_name \"%s\" %zu\n", masteru_name, masteru_name_len); + #endif + masteru_home = masteru_pw_record_pt->pw_dir; + masteru_home_len = strlen(masteru_home); + #ifdef DEBUG + dbprintf("masteru_home \"%s\" %zu\n", masteru_home, masteru_home_len); + #endif + masteru_home_len = strlen(masteru_home); + if( masteru_home_len == 0 || masteru_home[0] == '(' ){ + ctxp->err = ERR_SUBU_RM_0_MASTERU_HOMELESS; + ctxp->aux = masteru_name; // we can not free a passwd struct, or its fields. I assume then it isn't re-entrant safe. + return ctxp; + } + // char *subuland_extension = "/subuland/"; // moved to common.lib.c + size_t subuland_extension_len = strlen(subuland_extension); + ctxp->subuland = (char *)malloc( masteru_home_len + subuland_extension_len + 1 ); + if(!ctxp->subuland){ + ctxp->err = ERR_SUBU_RM_0_MALLOC; + return ctxp; + } + strcpy(ctxp->subuland, masteru_home); + strcpy(ctxp->subuland + masteru_home_len, subuland_extension); + subuland_len = masteru_home_len + subuland_extension_len; + #ifdef DEBUG + dbprintf("subuland \"%s\" %zu\n", ctxp->subuland, subuland_len); + #endif + } + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("lookup subu_username from masteru_name/subuname in config file\n"); + #endif + char *subu_username; // this is part of ctx and must be freed + { + int ret = subu_get_masteru_subu(db, masteru_name, subuname, &subu_username); + if( ret != SQLITE_DONE ){ + printf("get failed\n"); + return 2; + } + #ifdef DEBUG + printf("subu_username: %s\n", subu_username); + #endif + + } + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("strings subu_username and subuhome\n"); + #endif + size_t subu_username_len; + size_t subuhome_len; + { + char *ns=0; // 'ns' Number as String + char *mess=0; + if( subu_number_get( db, &ns, &mess ) != SQLITE_OK ){ + ctxp->err = ERR_SUBU_RM_0_CONFIG_FILE; + ctxp->aux = mess; + ctxp->free_aux = true; + return ctxp; + } + size_t ns_len = strlen(ns); + ctxp->subu_username = malloc(1 + ns_len + 1); + if( !ctxp->subu_username ){ + ctxp->err = ERR_SUBU_RM_0_MALLOC; + return ctxp; + } + strcpy(ctxp->subu_username, "s"); + strcpy(ctxp->subu_username + 1, ns); + subu_username_len = ns_len + 1; + #ifdef DEBUG + dbprintf("subu_username \"%s\" %zu\n", ctxp->subu_username, subu_username_len); + #endif + + subuhome_len = subuland_len + subuname_len; + ctxp->subuhome = (char *)malloc(subuhome_len + 1); + if( !ctxp->subuhome ){ + ctxp->err = ERR_SUBU_RM_0_MALLOC; + return ctxp; + } + strcpy (ctxp->subuhome, ctxp->subuland); + strcpy (ctxp->subuhome + subuland_len, subuname); + #ifdef DEBUG + dbprintf("subuhome \"%s\" %zu\n", ctxp->subuhome, subuhome_len); + #endif + } + + //-------------------------------------------------------------------------------- + // By having masteru create the subuhome, we know that masteru has rights to + // to access this directory. This will be the mount point for bindfs + { + #ifdef DEBUG + dbprintf("as masteru, making the directory \"%s\"\n", ctxp->subuhome); + #endif + struct stat st; + if( stat(ctxp->subuhome, &st) != -1 ){ + ctxp->err = ERR_SUBU_RM_0_SUBUHOME_EXISTS; + return ctxp; + } + dispatch_ctx *dfr = dispatch_f_euid_egid + ( + "masteru_mkdir_subuhome", + masteru_mkdir_subuhome, + (void *)ctxp->subuhome, + masteru_uid, + masteru_gid + ); + if( dfr->err <= ERR_DISPATCH || dfr->err == ERR_SUBU_RM_0_MKDIR_SUBUHOME ){ + #ifdef DEBUG + if( dfr->err == ERR_SUBU_RM_0_MKDIR_SUBUHOME ) + perror("mkdir"); + else + dispatch_f_mess(dfr); + #endif + ctxp->err = ERR_SUBU_RM_0_MKDIR_SUBUHOME; + return ctxp; + } + } + #ifdef DEBUG + dbprintf("masteru made directory \"%s\"\n", ctxp->subuhome); + #endif + + //-------------------------------------------------------------------------------- + // Make the subservient user account, i.e. the subu + { + #ifdef DEBUG + dbprintf("making subu \"%s\" as user \"%s\"\n", subuname, ctxp->subu_username); + #endif + #if BUG_SSS_CACHE_RUID + #ifdef DEBUG + dbprintf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n"); + #endif + if( setuid(0) == -1 ){ + ctxp->err = ERR_SUBU_RM_0_BUG_SSS; + return ctxp; + } + #endif + char *command = "/usr/sbin/useradd"; + char *argv[3]; + argv[0] = command; + argv[1] = ctxp->subu_username; + argv[2] = (char *) NULL; + char *envp[1]; + envp[0] = (char *) NULL; + dispatch_ctx *dfr = dispatch_exec(argv, envp); + if( dfr->err != 0 ){ + #ifdef DEBUG + if( dfr->err <= ERR_DISPATCH ) + dispatch_f_mess(dfr); + else + perror("useradd"); + #endif + // go back and remove the directory we made in subuland + dispatch_ctx *dfr = dispatch_f_euid_egid + ( + "masteru_rmdir_subuhome", + masteru_rmdir_subuhome, + (void *)ctxp->subuhome, + masteru_uid, + masteru_gid + ); + #ifdef DEBUG + if( dfr->err <= ERR_DISPATCH || dfr->err == ERR_SUBU_RM_0_RMDIR_SUBUHOME ) + if( dfr->err == ERR_SUBU_RM_0_RMDIR_SUBUHOME ) + perror("rmdir"); + else + dispatch_f_mess(dfr); + #endif + ctxp->err = ERR_SUBU_RM_0_FAILED_USERADD; + return ctxp; + } + #ifdef DEBUG + dbprintf("added user \"%s\"\n", ctxp->subu_username); + #endif + } + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("setting the masteru_name, subuname, subu_username relation\n"); + #endif + { + int ret = subu_put_masteru_subu(db, masteru_name, subuname, ctxp->subu_username); + if( ret != SQLITE_DONE ){ + ctxp->err = ERR_SUBU_RM_0_CONFIG_FILE; + ctxp->aux = "insert of masteru subu relation failed"; + return ctxp; + } + } + + #ifdef DEBUG + dbprintf("finished subu-mk-0(%s)\n", subuname); + #endif + ctxp->err = 0; + return ctxp; +} diff --git a/module/subu-0/1_deprecated/subudb-number-next.cli.c b/module/subu-0/1_deprecated/subudb-number-next.cli.c new file mode 100644 index 0000000..3373173 --- /dev/null +++ b/module/subu-0/1_deprecated/subudb-number-next.cli.c @@ -0,0 +1,45 @@ +/* +Set or get a new maximum subu number. Currently doesn't do the setting part. + +*/ +#include "subudb-number-next.cli.h" +#include +#include +#include + +int main(int argc, char **argv){ + + if( argc != 2 ){ + fprintf(stderr, "usage: %s masteru_name \n",argv[0]); + return SUBU_ERR_ARG_CNT; + } + char *masteru_name = argv[1]; + + int rc; + sqlite3 *db; + rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( rc != SQLITE_OK ){ + sqlite3_close(db); + fprintf(stderr, "error exit, could not open db file\n"); + return SUBU_ERR_DB_FILE; + } + + // read and print the current max + char *mess; + int n; + rc = subudb_number_next(db, masteru_name, &n, &mess); + if( rc == SQLITE_DONE ){ + printf("%d\n", n); + }else{ + fprintf(stderr, "subudb_number_next indicates failure by returning %d\n",rc); + fprintf(stderr, "and issues message, %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + return SUBU_ERR_DB_FILE; + } + rc = sqlite3_close(db); + if( rc != SQLITE_OK ){ + fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db)); + return SUBU_ERR_DB_FILE; + } + return 0; +} diff --git a/module/subu-0/1_doc/to_do.txt b/module/subu-0/1_doc/to_do.txt new file mode 100644 index 0000000..0b989cc --- /dev/null +++ b/module/subu-0/1_doc/to_do.txt @@ -0,0 +1,38 @@ +2019-02-05T23:14:40Z + error can cause subu-mk-0 to leave the creating of a subu in an intermediate + state. Rather than bailing on some of the errors we need to clean up instead. + Perhaps the yet to be written subu-rm program will be resilent enough to do + more general cleanup. + +2019-02-23T18:56:31Z + need to modify subu-init to take a configuration file name argument instead of + using a global variabel value. might want to add arguments to other subu + commands also + +2019-03-11T13:48:03Z + in subu.lib.c append cascading rmdir failure mess to useradd failure mess + +2019-03-11T13:48:03Z + want to add subu-type to masteru_subu(), I imagine there will be static, + permanent, and temporary subu types. + +2019-03-12T18:35:06Z + the masteru subu relation should contain the uid of the masteru as + well as the backup type for the subu: git, rdiff, rsync, none. + and the persisitance fo the subu: indefinite, session. + seems that operations need to be logged, in case the db is lost + the transcript can be played back. It should also be possible + to co-opt an existing user as a subu, though, would require + sudo privs. + + need to add messages for subu errors I've added to the end of + the list in subu.lib.c + +2019-03-14T10:43:50Z + + should mod all to he subudb routines to return a message, probably + strdup(sqlite_errmsg(db)), then the callers to these routines can just pass + mess in rather than making up new ones for each situation. The error code + probably already carries the contexts specific message. Or perhaps add + a string cat function for message strings, that would run through a stream + and free the originals. diff --git a/module/subu-0/1_tests/0_makefile b/module/subu-0/1_tests/0_makefile new file mode 100644 index 0000000..67cea25 --- /dev/null +++ b/module/subu-0/1_tests/0_makefile @@ -0,0 +1,32 @@ +# src/1_tests/0_makefile + +SHELL=/bin/bash + +-include 0_makefile_flags + +MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_cc + +SOURCES=$(wildcard *.c) +HFILES=$(wildcard *.h) + +all: version deps lib execs + +deps: + makeheaders $(SOURCES) $(HFILES) + sed -i '/^ *int *main *(.*)/d' *.h + $(MAKE) $@ + +clean: + $(MAKE) $@ + for i in $(HFILES); do rm $$i; done + +dist-clean: + $(MAKE) $@ + if [ -f subudb ]; then rm subudb; fi + +%:: + $(MAKE) $@ + + + + diff --git a/module/subu-0/1_tests/0_makefile_flags b/module/subu-0/1_tests/0_makefile_flags new file mode 100644 index 0000000..bc6bd25 --- /dev/null +++ b/module/subu-0/1_tests/0_makefile_flags @@ -0,0 +1,31 @@ + +# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. +ECHO= echo +#ECHO= echo -e + +# directories used by this makefile, these could all be set to dot for +# the simplest source directory structure + +#LIDBIR, EXECSDIR, HDIR hold the make results that might later be shared +#$(PWD) is the directory that make was called from, this is already build in +#set to dot to use the same directory as the source code +#leave blank to ommit +DEPRDIR= +DOCDIR= +EXECSDIR=. +HDIR=. +LIBDIR=. +TESTDIR=. +TMPDIR=1_tmp +TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) +TRYDIR= + + +# compiler and flags +CC=gcc +CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB +#CFLAGS=-std=gnu11 -fPIC -I. -Werror +LINKFLAGS=-L$(PROJECT_SUBU)/src/$(LIBDIR) -L. -lsubu -lsqlite3 -lsubutests + +LIBFILE=$(LIBDIR)/libtests.a + diff --git a/module/subu-0/1_tests/1_tmp/makefile_deps b/module/subu-0/1_tests/1_tmp/makefile_deps new file mode 100644 index 0000000..9486ae8 --- /dev/null +++ b/module/subu-0/1_tests/1_tmp/makefile_deps @@ -0,0 +1,4 @@ +da.cli.o: da.cli.c da.cli.h + +./da : da.cli.o ./libtests.a + gcc -o ./da da.cli.o -L/home/morpheus/subu_land/subu/src/. -L. -lsubu -lsqlite3 -lsubutests diff --git a/module/subu-0/1_tests/da.cli.c b/module/subu-0/1_tests/da.cli.c new file mode 100644 index 0000000..910a15e --- /dev/null +++ b/module/subu-0/1_tests/da.cli.c @@ -0,0 +1,50 @@ +/* +Tests for da. + +*/ + +#include +#include + +int test_da_0(){ + da da0; + da_alloc(&da0, sizeof(int)); // leaves room for 4 ints + int i = 0; + int *pt = da0->base; + // will double, 4 -> 8, then double 8 -> 16 + while( i < 10 ){ + if(da_boundq(&da0, pt)){ + char *old_base = da_expand(&da); + da_rebase(&da, old_base, pt); + } + *pt = i; + i++; + pt++; + } + + bool f0 = da.size == sizof(int) * 16; + bool f1 = 10 == (da.end - da.base) / sizeof(int); + bool f2 = true; + pt = da0->base; + while( i < 10 ){ + f2 = f2 && *pt == i && !da_endq(&da, pt); + i++; + pt++; + } + bool f3 = da_endq(&da, pt); + + return f0 && f1 && f2 && f3; +} + + +int main(){ + + bool da_0_passed = test_da_0(); + if( da_0_passed ){ + printf("da_0_passed"); + return 0; + } + printf("da_0_failed"); + return 1; + +} diff --git a/module/subu-0/1_tests/libtests.a b/module/subu-0/1_tests/libtests.a new file mode 100644 index 0000000..8b277f0 --- /dev/null +++ b/module/subu-0/1_tests/libtests.a @@ -0,0 +1 @@ +! diff --git a/module/subu-0/1_tmp/da.lib.h b/module/subu-0/1_tmp/da.lib.h new file mode 100644 index 0000000..4702189 --- /dev/null +++ b/module/subu-0/1_tmp/da.lib.h @@ -0,0 +1,23 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +#include +void daps_map(char **base,char **end_pt,void f(void *)); +#define RETURN(rc) \ + { daps_map(mrs, mrs_end, free); return rc; } +void daps_alloc(char ***base,size_t *s); +#define MK_MRS \ + char **mrs; \ + char **mrs_end; \ + size_t mrs_size; \ + daps_alloc(&mrs, &mrs_size);\ + mrs_end = mrs; +void daps_push(char ***base,char ***pt,size_t *s,char *item); +bool daps_bound(char **base,char **pt,size_t s); +void daps_expand(char ***base,char ***pt,size_t *s); +void da_map(void *base,void *end_pt,void f(void *),size_t item_size); +void da_push(void **base,void **pt,size_t *s,void *item,size_t item_size); +bool da_bound(void *base,void *pt,size_t s); +void da_expand(void **base,void **pt,size_t *s); +void da_alloc(void **base,size_t *s,size_t item_size); +#define INTERFACE 0 diff --git a/module/subu-0/1_tmp/dbprintf.lib.h b/module/subu-0/1_tmp/dbprintf.lib.h new file mode 100644 index 0000000..3056cf6 --- /dev/null +++ b/module/subu-0/1_tmp/dbprintf.lib.h @@ -0,0 +1,3 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +int dbprintf(const char *format,...); diff --git a/module/subu-0/1_tmp/dispatch.lib.h b/module/subu-0/1_tmp/dispatch.lib.h new file mode 100644 index 0000000..07e2271 --- /dev/null +++ b/module/subu-0/1_tmp/dispatch.lib.h @@ -0,0 +1,24 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +#include +int dispatch_exec(char **argv,char **envp); +typedef unsigned int uint; +int dispatch_f_euid_egid(char *fname,int(*f)(void *arg),void *f_arg,uid_t euid,gid_t egid); +int dbprintf(const char *format,...); +int dispatch_f(char *fname,int(*f)(void *arg),void *f_arg); +void dispatch_f_mess(char *fname,int err,char *dispatchee); +#define ERR_DISPATCH_EXEC -1029 +#define ERR_DISPATCH_NULL_EXECUTABLE -1028 +#define ERR_DISPATCH_F_SETEGID -1027 +#define ERR_DISPATCH_F_SETEUID -1026 +#define ERR_DISPATCH_F_FORK -1025 +#define ERR_DISPATCH_NEGATIVE_RETURN_STATUS -1024 +#define ERR_DISPATCH -1024 +typedef struct dispatch_ctx dispatch_ctx; +struct dispatch_ctx { + char *dispatcher; // name of the dispatch function ("dispatch_f", "dispatch_f_euid_egid", etc.) + char *dispatchee; // name of the function being dispatched + int err; // error code as listed below, or status returned from dispatchee +}; +#define INTERFACE 0 diff --git a/module/subu-0/1_tmp/subu-bind-all.cli.h b/module/subu-0/1_tmp/subu-bind-all.cli.h new file mode 100644 index 0000000..7e52675 --- /dev/null +++ b/module/subu-0/1_tmp/subu-bind-all.cli.h @@ -0,0 +1,9 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +#include +#include +int subu_bind_all(char **mess,sqlite3 *db); +#define SUBU_ERR_DB_FILE 8 +extern char DB_File[]; +#define SUBU_ERR_ARG_CNT 1 diff --git a/module/subu-0/1_tmp/subu-bind.cli.h b/module/subu-0/1_tmp/subu-bind.cli.h new file mode 100644 index 0000000..af12d61 --- /dev/null +++ b/module/subu-0/1_tmp/subu-bind.cli.h @@ -0,0 +1,7 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +#include +#include +int subu_bind(char **mess,char *masteru_name,char *subu_username,char *subuhome); +#define SUBU_ERR_ARG_CNT 1 diff --git a/module/subu-0/1_tmp/subu-common.lib.h b/module/subu-0/1_tmp/subu-common.lib.h new file mode 100644 index 0000000..cfdc520 --- /dev/null +++ b/module/subu-0/1_tmp/subu-common.lib.h @@ -0,0 +1,9 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +extern char Subuland_Extension[]; +typedef unsigned int uint; +extern uint First_Max_Subunumber; +extern uint Subuhome_Perms; +extern char DB_File[]; +#define BUG_SSS_CACHE_RUID 1 +#define INTERFACE 0 diff --git a/module/subu-0/1_tmp/subu-mk-0.cli.h b/module/subu-0/1_tmp/subu-mk-0.cli.h new file mode 100644 index 0000000..487b509 --- /dev/null +++ b/module/subu-0/1_tmp/subu-mk-0.cli.h @@ -0,0 +1,10 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +#include +#include +void subu_err(char *fname,int err,char *mess); +int subu_mk_0(char **mess,sqlite3 *db,char *subuname); +#define SUBU_ERR_DB_FILE 8 +extern char DB_File[]; +#define SUBU_ERR_ARG_CNT 1 diff --git a/module/subu-0/1_tmp/subu-rm-0.cli.h b/module/subu-0/1_tmp/subu-rm-0.cli.h new file mode 100644 index 0000000..070bfe8 --- /dev/null +++ b/module/subu-0/1_tmp/subu-rm-0.cli.h @@ -0,0 +1,10 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +#include +#include +void subu_err(char *fname,int err,char *mess); +int subu_rm_0(char **mess,sqlite3 *db,char *subuname); +#define SUBU_ERR_DB_FILE 8 +extern char DB_File[]; +#define SUBU_ERR_ARG_CNT 1 diff --git a/module/subu-0/1_tmp/subu.lib.h b/module/subu-0/1_tmp/subu.lib.h new file mode 100644 index 0000000..69c5da7 --- /dev/null +++ b/module/subu-0/1_tmp/subu.lib.h @@ -0,0 +1,66 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +typedef unsigned int uint; +#include +typedef struct subudb_subu_element subudb_subu_element; +int subudb_Masteru_Subu_get_subus(sqlite3 *db,char *masteru_name,subudb_subu_element **sa_pt,subudb_subu_element **sa_end_pt); +struct subudb_subu_element { + char *subuname; + char *subu_username; +}; +#include +#include +int subu_bind_all(char **mess,sqlite3 *db); +int subu_bind(char **mess,char *masteru_name,char *subu_username,char *subuhome); +int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); +int subudb_Masteru_Subu_get_subu_username(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username); +int subu_rm_0(char **mess,sqlite3 *db,char *subuname); +int subudb_Masteru_Subu_put(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); +#include +#include +int dispatch_exec(char **argv,char **envp); +#define BUG_SSS_CACHE_RUID 1 +void dispatch_f_mess(char *fname,int err,char *dispatchee); +#define ERR_DISPATCH -1024 +int dispatch_f_euid_egid(char *fname,int(*f)(void *arg),void *f_arg,uid_t euid,gid_t egid); +#include +void daps_map(char **base,char **end_pt,void f(void *)); +#define RETURN(rc) \ + { daps_map(mrs, mrs_end, free); return rc; } +void daps_push(char ***base,char ***pt,size_t *s,char *item); +int dbprintf(const char *format,...); +void daps_alloc(char ***base,size_t *s); +#define MK_MRS \ + char **mrs; \ + char **mrs_end; \ + size_t mrs_size; \ + daps_alloc(&mrs, &mrs_size);\ + mrs_end = mrs; +int subu_mk_0(char **mess,sqlite3 *db,char *subuname); +extern char Subuland_Extension[]; +int db_commit(sqlite3 *db); +int db_rollback(sqlite3 *db); +int subudb_number_set(sqlite3 *db,int n); +int subudb_number_get(sqlite3 *db,int *n); +int db_begin(sqlite3 *db); +extern uint Subuhome_Perms; +extern char DB_File[]; +void subu_err(char *fname,int err,char *mess); +#define SUBU_ERR_BIND 15 +#define SUBU_ERR_N 14 +#define SUBU_ERR_SUBU_NOT_FOUND 13 +#define SUBU_ERR_FAILED_USERDEL 12 +#define SUBU_ERR_FAILED_USERADD 11 +#define SUBU_ERR_BUG_SSS 10 +#define SUBU_ERR_SUBUHOME_EXISTS 9 +#define SUBU_ERR_DB_FILE 8 +#define SUBU_ERR_HOMELESS 7 +#define SUBU_ERR_SUBUNAME_MALFORMED 6 +#define SUBU_ERR_RMDIR_SUBUHOME 5 +#define SUBU_ERR_MKDIR_SUBUHOME 4 +#define SUBU_ERR_MALLOC 3 +#define SUBU_ERR_SETUID_ROOT 2 +#define SUBU_ERR_ARG_CNT 1 +char *userdel_mess(int err); +char *useradd_mess(int err); +#define INTERFACE 0 diff --git a/module/subu-0/1_tmp/subudb-init.cli.h b/module/subu-0/1_tmp/subudb-init.cli.h new file mode 100644 index 0000000..4435103 --- /dev/null +++ b/module/subu-0/1_tmp/subudb-init.cli.h @@ -0,0 +1,11 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +int db_commit(sqlite3 *db); +int db_rollback(sqlite3 *db); +int subudb_schema(sqlite3 *db); +int db_begin(sqlite3 *db); +#include +#include +#define SUBU_ERR_DB_FILE 8 +extern char DB_File[]; diff --git a/module/subu-0/1_tmp/subudb-number.cli.h b/module/subu-0/1_tmp/subudb-number.cli.h new file mode 100644 index 0000000..c130fbb --- /dev/null +++ b/module/subu-0/1_tmp/subudb-number.cli.h @@ -0,0 +1,11 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +int subudb_number_get(sqlite3 *db,int *n); +int subudb_number_set(sqlite3 *db,int n); +#include +#include +#define SUBU_ERR_N 14 +#define SUBU_ERR_DB_FILE 8 +extern char DB_File[]; +#define SUBU_ERR_ARG_CNT 1 diff --git a/module/subu-0/1_tmp/subudb-rel-get.cli.h b/module/subu-0/1_tmp/subudb-rel-get.cli.h new file mode 100644 index 0000000..4f335be --- /dev/null +++ b/module/subu-0/1_tmp/subudb-rel-get.cli.h @@ -0,0 +1,9 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +int subudb_Masteru_Subu_get_subu_username(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username); +#include +#include +#define SUBU_ERR_DB_FILE 8 +extern char DB_File[]; +#define SUBU_ERR_ARG_CNT 1 diff --git a/module/subu-0/1_tmp/subudb-rel-put.cli.h b/module/subu-0/1_tmp/subudb-rel-put.cli.h new file mode 100644 index 0000000..243b3a9 --- /dev/null +++ b/module/subu-0/1_tmp/subudb-rel-put.cli.h @@ -0,0 +1,8 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +int subudb_Masteru_Subu_put(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); +#include +#include +#define SUBU_ERR_DB_FILE 8 +extern char DB_File[]; diff --git a/module/subu-0/1_tmp/subudb-rel-rm.cli.h b/module/subu-0/1_tmp/subudb-rel-rm.cli.h new file mode 100644 index 0000000..595427f --- /dev/null +++ b/module/subu-0/1_tmp/subudb-rel-rm.cli.h @@ -0,0 +1,8 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); +#include +#include +#define SUBU_ERR_DB_FILE 8 +extern char DB_File[]; diff --git a/module/subu-0/1_tmp/subudb-subus.cli.h b/module/subu-0/1_tmp/subudb-subus.cli.h new file mode 100644 index 0000000..16310b7 --- /dev/null +++ b/module/subu-0/1_tmp/subudb-subus.cli.h @@ -0,0 +1,14 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +typedef struct subudb_subu_element subudb_subu_element; +int subudb_Masteru_Subu_get_subus(sqlite3 *db,char *masteru_name,subudb_subu_element **sa_pt,subudb_subu_element **sa_end_pt); +struct subudb_subu_element { + char *subuname; + char *subu_username; +}; +#include +#include +#define SUBU_ERR_DB_FILE 8 +extern char DB_File[]; +#define SUBU_ERR_ARG_CNT 1 diff --git a/module/subu-0/1_tmp/subudb.lib.h b/module/subu-0/1_tmp/subudb.lib.h new file mode 100644 index 0000000..be73823 --- /dev/null +++ b/module/subu-0/1_tmp/subudb.lib.h @@ -0,0 +1,27 @@ +/* This file was automatically generated. Do not edit! */ +#undef INTERFACE +#include +int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); +#include +#include +void da_expand(void **base,void **pt,size_t *s); +bool da_bound(void *base,void *pt,size_t s); +typedef struct subudb_subu_element subudb_subu_element; +int subudb_Masteru_Subu_get_subus(sqlite3 *db,char *masteru_name,subudb_subu_element **sa_pt,subudb_subu_element **sa_end_pt); +void subu_element_free(subudb_subu_element *base,subudb_subu_element *end_pt); +void da_alloc(void **base,size_t *s,size_t item_size); +struct subudb_subu_element { + char *subuname; + char *subu_username; +}; +int subudb_Masteru_Subu_get_subu_username(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username); +int subudb_Masteru_Subu_put(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); +int subudb_number_set(sqlite3 *db,int n); +int subudb_number_get(sqlite3 *db,int *n); +typedef unsigned int uint; +extern uint First_Max_Subunumber; +int subudb_schema(sqlite3 *db); +int db_rollback(sqlite3 *db); +int db_commit(sqlite3 *db); +int db_begin(sqlite3 *db); +#define INTERFACE 0 diff --git a/module/subu-0/1_try/split.c b/module/subu-0/1_try/split.c new file mode 100644 index 0000000..6d4c6ac --- /dev/null +++ b/module/subu-0/1_try/split.c @@ -0,0 +1,20 @@ +/* +Using preprocessor to make header file? + +gcc -E split.c -DPROTOTYPE + +Outputs source code source comment lines starting with a hash. Resolves all macro commands, +hence the resulting header can not have macro commands. + + */ + +#if PROTOTYPE +##define GREATNESS 21 +int f(int x); +#endif + +#if IMPLEMENTATION +int f(int x){ + return x; +} +#endif diff --git a/module/subu-0/1_try/split_arg.c b/module/subu-0/1_try/split_arg.c new file mode 100644 index 0000000..6d4c6ac --- /dev/null +++ b/module/subu-0/1_try/split_arg.c @@ -0,0 +1,20 @@ +/* +Using preprocessor to make header file? + +gcc -E split.c -DPROTOTYPE + +Outputs source code source comment lines starting with a hash. Resolves all macro commands, +hence the resulting header can not have macro commands. + + */ + +#if PROTOTYPE +##define GREATNESS 21 +int f(int x); +#endif + +#if IMPLEMENTATION +int f(int x){ + return x; +} +#endif diff --git a/module/subu-0/1_try/subudb b/module/subu-0/1_try/subudb new file mode 100644 index 0000000..e69de29 diff --git a/module/subu-0/1_try/voidptr.c b/module/subu-0/1_try/voidptr.c new file mode 100644 index 0000000..bd9c3e5 --- /dev/null +++ b/module/subu-0/1_try/voidptr.c @@ -0,0 +1,48 @@ +/* +They say a cast is not required passing a typed pointer to a void * argument, +but What about void **? + +gcc -std=gnu11 -o voidptr voidptr.c +voidptr.c: In function ‘main’: +voidptr.c:28:5: warning: passing argument 1 of ‘g’ from incompatible pointer type [-Wincompatible-pointer-types] + g(&pt, y); + ^~~ +voidptr.c:13:15: note: expected ‘void **’ but argument is of type ‘int **’ + void g(void **pt0, void *pt1){ + ~~~~~~~^~~ + +*/ +#include + +int f(void *pt){ + return *(int *)pt; +} + +/* fails +void g(void **pt0, int *pt1){ + *pt0 = pt1; +} +*/ + +// passes +void g(void *pt0, int *pt1){ + *(int **)pt0 = pt1; +} + +int main(){ + int x = 5; + int *xp = &x; + printf("%d\n",f(xp)); + + int y[3]; + y[0] = 10; + y[1] = 11; + y[2] = 12; + + int *pt; + g(&pt, y); + printf("%d\n",*pt); + + printf("that's all folks\n"); + return 0; +} diff --git a/module/subu-0/common.lib.c b/module/subu-0/common.lib.c new file mode 100644 index 0000000..9ce5a27 --- /dev/null +++ b/module/subu-0/common.lib.c @@ -0,0 +1,20 @@ + +#include "subu-common.lib.h" + +#if INTERFACE +typedef unsigned int uint; +/* + Fedora 29's sss_cache is checking the inherited uid instead of the effective + uid, so setuid root scripts will fail when calling sss_cache. + + Fedora 29's 'useradd' calls sss_cache, and useradd is called by our setuid root + program subu-mk-0. +*/ +#define BUG_SSS_CACHE_RUID 1 +#endif + +// char *DB_File = "/etc/subudb"; +char DB_File[] = "subudb"; +uint Subuhome_Perms = 0700; +uint First_Max_Subunumber = 114; +char Subuland_Extension[] = "/subuland/"; diff --git a/module/subu-0/db.lib.c b/module/subu-0/db.lib.c new file mode 100644 index 0000000..17a5419 --- /dev/null +++ b/module/subu-0/db.lib.c @@ -0,0 +1,192 @@ +/* +The db file is maintained in SQLite + +Because user names of are of limited length, subu user names are always named _s. +A separate table translates the numbers into the subu names. + +The first argument is the biggest subu number in the system, or one minus an +starting point for subu numbering. + +currently a unit converted to base 10 will always fit in a 21 bit buffer. + +Each of these returns SQLITE_OK upon success +*/ +#include "subudb.lib.h" + +#if INTERFACE +#include +#endif + +#include +#include +#include +#include + +//-------------------------------------------------------------------------------- +// sqlite transactions don't nest. There is a way to use save points, but still +// we can't just nest transactions. Instead use these wrappers around the whole +// of something that needs to be in a transaction. +int db_begin(sqlite3 *db){ + return sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL); +} +int db_commit(sqlite3 *db){ + return sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL); +} +int db_rollback(sqlite3 *db){ + return sqlite3_exec(db, "ROLLBACK;", NULL, NULL, NULL); +} + +//-------------------------------------------------------------------------------- +int subudb_schema(sqlite3 *db){ + int rc; + + { // build tables + char sql[] = + "CREATE TABLE Masteru_Subu(masteru_name TEXT, subuname TEXT, subu_username TEXT);" + "CREATE TABLE Attribute_Int(attribute TEXT, value INT);" + ; + rc = sqlite3_exec(db, sql, NULL, NULL, NULL); + if(rc != SQLITE_OK) return rc; + } + + { // data initialization + char *sql = "INSERT INTO Attribute_Int (attribute, value) VALUES ('Max_Subunumber', ?1);"; + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, First_Max_Subunumber); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + if( rc != SQLITE_DONE ) return rc; + } + + return SQLITE_OK; +} + +//-------------------------------------------------------------------------------- +int subudb_number_get(sqlite3 *db, int *n){ + char *sql = "SELECT value FROM Attribute_Int WHERE attribute = 'Max_Subunumber';"; + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + int rc = sqlite3_step(stmt); + if( rc == SQLITE_ROW ){ + *n = sqlite3_column_int(stmt,0); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + if( rc != SQLITE_DONE ) return rc; + return SQLITE_OK; + } + // should have a message return, suppose + sqlite3_finalize(stmt); + return SQLITE_NOTFOUND; +} + +int subudb_number_set(sqlite3 *db, int n){ + int rc; + char *sql = "UPDATE Attribute_Int SET value = ?1 WHERE attribute = 'Max_Subunumber';"; + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, n); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + if( rc == SQLITE_DONE ) return SQLITE_OK; + return rc; +} + +//-------------------------------------------------------------------------------- +// put relation into Masteru_Subu table +int subudb_Masteru_Subu_put(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username){ + char *sql = "INSERT INTO Masteru_Subu VALUES (?1, ?2, ?3);"; + sqlite3_stmt *stmt; + sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); + sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, subuname, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 3, subu_username, -1, SQLITE_STATIC); + int rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + if( rc == SQLITE_DONE ) return SQLITE_OK; + return rc; +} + +//-------------------------------------------------------------------------------- +int subudb_Masteru_Subu_get_subu_username(sqlite3 *db, char *masteru_name, char *subuname, char **subu_username){ + char *sql = "SELECT subu_username FROM Masteru_Subu WHERE masteru_name = ?1 AND subuname = ?2;"; + size_t sql_len = strlen(sql); + sqlite3_stmt *stmt; + int rc; + rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL); + if( rc != SQLITE_OK ) return rc; + sqlite3_bind_text(stmt, 1, masteru_name, strlen(masteru_name), SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, subuname, strlen(subuname), SQLITE_STATIC); + rc = sqlite3_step(stmt); + if( rc == SQLITE_ROW ){ + const char *username = sqlite3_column_text(stmt, 0); + *subu_username = strdup(username); + }else{ + sqlite3_finalize(stmt); + return rc; // woops this needs to return an error!, be sure it is not SQLITE_DONE + } + rc = sqlite3_step(stmt); + if( rc == SQLITE_DONE ) return SQLITE_OK; + return rc; +} + +//-------------------------------------------------------------------------------- + +// we return and array of subudb_subu_info +#if INTERFACE +struct subudb_subu_element{ + char *subuname; + char *subu_username; +}; +#endif + +int subudb_Masteru_Subu_get_subus +( + sqlite3 *db, + char *masteru_name, + da *subus +){ + char *sql = "SELECT subuname, subu_username" + " FROM Masteru_Subu" + " WHERE masteru_name = ?1;"; + size_t sql_len = strlen(sql); + sqlite3_stmt *stmt; + int rc; + rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL); + if( rc != SQLITE_OK ) return rc; + sqlite3_bind_text(stmt, 1, masteru_name, strlen(masteru_name), SQLITE_STATIC); + + da_alloc(subus, sizeof(subudb_subu_element)); + subudb_subu_element *pt = (subudb_subu_element *)subus->base; + rc = sqlite3_step(stmt); + while( rc == SQLITE_ROW ){ + if( da_boundq(subus, pt) ){ + char *old_base = da_expand(subus); + da_rebase(subus, old_base, pt); + } + pt->subuname = strdup(sqlite3_column_text(stmt, 0)); + pt->subu_username = strdup(sqlite3_column_text(stmt, 1)); + rc = sqlite3_step(stmt); + pt++; + } + sqlite3_finalize(stmt); + if( rc != SQLITE_DONE ) return rc; + return SQLITE_OK; +} + +//-------------------------------------------------------------------------------- +int subudb_Masteru_Subu_rm(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username){ + char *sql = "DELETE FROM Masteru_Subu WHERE masteru_name = ?1 AND subuname = ?2 AND subu_username = ?3;"; + size_t sql_len = strlen(sql); + sqlite3_stmt *stmt; + int rc; + rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL); + if( rc != SQLITE_OK ) return rc; + sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, subuname, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 3, subu_username, -1, SQLITE_STATIC); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + if( rc == SQLITE_DONE ) return SQLITE_OK; + return rc; +} diff --git a/module/subu-0/subu-bind-all.cli.c b/module/subu-0/subu-bind-all.cli.c new file mode 100644 index 0000000..e942909 --- /dev/null +++ b/module/subu-0/subu-bind-all.cli.c @@ -0,0 +1,31 @@ +/* +mount all the subu user directories into master's subuland +uses unmount to undo this + +*/ +#include "subu-bind-all.cli.h" +#include +#include + +int main(int argc, char **argv){ + if( argc != 1){ + fprintf(stderr, "%s does not take arguments\n",argv[0]); + return SUBU_ERR_ARG_CNT; + } + + int rc; + sqlite3 *db; + rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( rc != SQLITE_OK ){ + fprintf(stderr, "could not open db file \"%s\"\n", DB_File); + return SUBU_ERR_DB_FILE; + } + + char *mess; + rc = subu_bind_all(&mess, db); + if(rc != 0){ + fprintf(stderr, "subu-bind: %s\n", mess); + return rc; + } + return 0; +} diff --git a/module/subu-0/subu-bind.cli.c b/module/subu-0/subu-bind.cli.c new file mode 100644 index 0000000..f315823 --- /dev/null +++ b/module/subu-0/subu-bind.cli.c @@ -0,0 +1,25 @@ +/* +mount a subu user directory into master's subuland +uses unmount to undo this + +*/ +#include "subu-bind.cli.h" +#include +#include + +int main(int argc, char **argv){ + + if( argc != 4){ + fprintf(stderr, "usage: %s masteru subu_username subuhome\n",argv[0]); + return SUBU_ERR_ARG_CNT; + } + + int rc; + char *mess; + rc = subu_bind(&mess, argv[1], argv[2], argv[3]); + if(rc != 0){ + fprintf(stderr, "subu-bind: %s\n", mess); + return rc; + } + return 0; +} diff --git a/module/subu-0/subu-mk-0.cli.c b/module/subu-0/subu-mk-0.cli.c new file mode 100644 index 0000000..af0888b --- /dev/null +++ b/module/subu-0/subu-mk-0.cli.c @@ -0,0 +1,43 @@ +/* + subu-mk-0 command + +*/ +#include "subu-mk-0.cli.h" +#include +#include + +int main(int argc, char **argv){ + char *command = argv[0]; + if( argc != 2 ){ + fprintf(stderr, "usage: %s subu", command); + return SUBU_ERR_ARG_CNT; + } + char *subuname = argv[1]; + + int rc; + sqlite3 *db; + rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( rc != SQLITE_OK ){ + fprintf(stderr, "error when opening db, %s\n", DB_File); + fprintf(stderr, "sqlite3 says: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + return SUBU_ERR_DB_FILE; + } + + char *mess; + rc = subu_mk_0(&mess, db, subuname); + if( rc ){ + subu_err("subu_mk_0", rc, mess); + free(mess); + sqlite3_close(db); + return rc; + } + + rc = sqlite3_close(db); + if( rc != SQLITE_OK ){ + fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db)); + return SUBU_ERR_DB_FILE; + } + return 0; + +} diff --git a/module/subu-0/subu-rm-0.cli.c b/module/subu-0/subu-rm-0.cli.c new file mode 100644 index 0000000..a7e5926 --- /dev/null +++ b/module/subu-0/subu-rm-0.cli.c @@ -0,0 +1,32 @@ +/* + subu-mk-0 command + +*/ +#include "subu-rm-0.cli.h" +#include +#include + +int main(int argc, char **argv){ + char *command = argv[0]; + if( argc != 2 ){ + fprintf(stderr, "usage: %s subu", command); + return SUBU_ERR_ARG_CNT; + } + char *subuname = argv[1]; + + sqlite3 *db; + { + int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( ret != SQLITE_OK ){ + fprintf(stderr, "error exit, could not open db file \"%s\"\n", DB_File); + return SUBU_ERR_DB_FILE; + }} + + { + char *mess=0; + int ret = subu_rm_0(&mess, db, subuname); + subu_err("subu_rm_0", ret, mess); + free(mess); + return ret; + } +} diff --git a/module/subu-0/subu.lib.c b/module/subu-0/subu.lib.c new file mode 100644 index 0000000..988bd28 --- /dev/null +++ b/module/subu-0/subu.lib.c @@ -0,0 +1,713 @@ +/* + sqllite3 is used to maintain the db file, which is currently compiled + in as /etc/subu.db, (or just subu.db for testing). + + masteru is the user who ran this script. The masteru name comes from getuid + and /etc/passwd. + + subu is a subservient user. The subuname is passed in as an argument. + + subu-mk-0 synthesizes a new user user name s, calls useradd to creat + the new uswer account, and enters the relationship between masteru, subu, and + s in the db file. It is this relation in the db file that + associates the subuname with the s user. + + subu-rm-0 uses userdel to delete the related s user account. It + then removes the relaton from the db file. + + subu-mk-0 and subu-rm-0 are setuid root scripts. + +*/ +#include "subu.lib.h" + +// without this #define we get the warning: implicit declaration of function ‘seteuid’/‘setegid’ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#if INTERFACE +#include +#include +#include +#endif + +//-------------------------------------------------------------------------------- +// dispatched command errors .. should add mkdir and rmdir ... +// +char *useradd_mess(int err){ + if(err <= 0) return NULL; + char *mess; + switch(err){ + case 1: mess = "can't update password file"; break; + case 2: mess = "invalid command syntax"; break; + case 3: mess = "invalid argument to option"; break; + case 4: mess = "UID already in use (and no -o)"; break; + case 5: mess = "undefined"; break; + case 6: mess = "specified group doesn't exist"; break; + case 7: + case 8: mess = "undefined"; break; + case 9: mess = "username already in use"; break; + case 10: mess = "can't update group file:"; break; + case 11: mess = "undefined"; break; + case 12: mess = "can't create home directory"; break; + case 13: mess = "undefined"; break; + case 14: mess = "can't update SELinux user mapping"; break; + default: mess = "undefined"; break; + } + return strdup(mess); +} +char *userdel_mess(int err){ + if(err <= 0) return NULL; + char *mess; + switch(err){ + case 1: mess = "can't update password file"; break; + case 2: mess = "invalid command syntax"; break; + case 3: + case 4: + case 5: mess = "undefined"; break; + case 6: mess = "specified user doesn't exist"; break; + case 7: + case 8: + case 9: mess = "undefined"; break; + case 10: mess = "can't update group file:"; break; + case 11: mess = "undefined"; break; + case 12: mess = "can't remove home directory"; break; + default: mess = "undefined"; break; + } + return strdup(mess); +} + + +//-------------------------------------------------------------------------------- +// +#if INTERFACE +#define SUBU_ERR_ARG_CNT 1 +#define SUBU_ERR_SETUID_ROOT 2 +#define SUBU_ERR_MALLOC 3 +#define SUBU_ERR_MKDIR_SUBUHOME 4 +#define SUBU_ERR_RMDIR_SUBUHOME 5 +#define SUBU_ERR_SUBUNAME_MALFORMED 6 +#define SUBU_ERR_HOMELESS 7 +#define SUBU_ERR_DB_FILE 8 +#define SUBU_ERR_SUBUHOME_EXISTS 9 +#define SUBU_ERR_BUG_SSS 10 +#define SUBU_ERR_FAILED_USERADD 11 +#define SUBU_ERR_FAILED_USERDEL 12 +#define SUBU_ERR_SUBU_NOT_FOUND 13 +#define SUBU_ERR_N 14 +#define SUBU_ERR_BIND 15 +#endif + +void subu_err(char *fname, int err, char *mess){ + if(!mess) mess = ""; + switch(err){ + case 0: return; + case SUBU_ERR_ARG_CNT: + if(mess[0]) + fprintf(stderr, "Incorrect number of arguments, %s", mess); + else + fprintf(stderr, "Incorrect number of arguments.", mess); + break; + case SUBU_ERR_SETUID_ROOT: + fprintf(stderr, "This program must be run setuid root from a user account."); + break; + case SUBU_ERR_MALLOC: + perror(fname); + break; + case SUBU_ERR_DB_FILE: + fprintf(stderr, "error on %s", DB_File); // DB_File is in common + fprintf(stderr, ": %s", mess); + break; + case SUBU_ERR_HOMELESS: + fprintf(stderr,"Masteru, \"%s\", has no home directory", mess); + break; + case SUBU_ERR_SUBUNAME_MALFORMED: + fprintf(stderr, "subuname, \"%s\" is not in [ _.-a-zA-Z0-9]*", mess); + break; + case SUBU_ERR_SUBUHOME_EXISTS: + fprintf(stderr, "a file system object already exists at subuhome, \"%s\"\n", mess); + break; + case SUBU_ERR_MKDIR_SUBUHOME: + fprintf(stderr, "masteru could not make subuhome, \"%s\"", mess); + break; + case SUBU_ERR_BUG_SSS: + perror(fname); + break; + case SUBU_ERR_FAILED_USERADD: + fprintf(stderr, "%s useradd failed\n", mess); + break; + default: + fprintf(stderr, "unknown error code %d\n", err); + } + fputc('\n', stderr); +} + + +//-------------------------------------------------------------------------------- +// a well formed subuname +// returns the length of the subuname, or -1 +// +static int allowed_subuname(char **mess, char *subuname, size_t *subuname_len){ + char *ch = subuname; + size_t i = 0; + while( + *ch + && + ( *ch >= 'a' && *ch <= 'z' + || + *ch >= 'A' && *ch <= 'Z' + || + *ch >= '0' && *ch <= '9' + || + *ch == '-' + || + *ch == '_' + || + *ch == '.' + || + *ch == ' ' + ) + ){ + ch++; + i++; + } + if( !*ch ){ + *subuname_len = i; + return 0; + }else{ + if(mess) *mess = strdup(subuname); + return SUBU_ERR_SUBUNAME_MALFORMED; + } +} + +//-------------------------------------------------------------------------------- +// dispatched functions +// +static int masteru_mkdir_subuhome(void *arg){ + char *subuhome = (char *) arg; + if( mkdir( subuhome, Subuhome_Perms ) == -1 ){ // find subuhome perms in common + perror("masteru_mkdir_subuhome"); + return SUBU_ERR_MKDIR_SUBUHOME; + } + return 0; +} +static int masteru_rmdir_subuhome(void *arg){ + char *subuhome = (char *) arg; + if( rmdir( subuhome ) == -1 ){ // find subuhome perms in common + perror("masteru_rmdir_subuhome"); + return SUBU_ERR_RMDIR_SUBUHOME; + } + return 0; +} + +//-------------------------------------------------------------------------------- +// build strings +// +static int mk_subu_username(char **mess, sqlite3 *db, char **subu_username){ + int rc,n; + db_begin(db); + if( + (rc = subudb_number_get(db, &n)) + || + (rc = subudb_number_set(db, ++n)) + ){ + db_rollback(db); + return SUBU_ERR_DB_FILE; + } + db_commit(db); + + size_t len = 0; + FILE* name_stream = open_memstream(subu_username, &len); + fprintf(name_stream, "s%d", n); + fclose(name_stream); + return 0; +} + +// man page says that getpwuid strings may not be freed, I don't know how long until they +// are overwritten, so I just make my own copies that can be freed +static int uid_to_name_and_home(uid_t uid, char **name, char **home ){ + struct passwd *pw_record_pt = getpwuid(uid); // reading /etc/passwd + *name = strdup(pw_record_pt->pw_name); + *home = strdup(pw_record_pt->pw_dir); + if( !home || !home[0] || (*home)[0] == '(' ) return SUBU_ERR_HOMELESS; + return 0; +} + +static int username_to_home(char *name, char **home ){ + struct passwd *pw_record_pt = getpwnam(name); // reading /etc/passwd + *home = strdup(pw_record_pt->pw_dir); + if( !home || !home[0] || (*home)[0] == '(' ) return SUBU_ERR_HOMELESS; + return 0; +} + +static int mk_subuland(char *masteru_home, char **subuland){ + size_t masteru_home_len = strlen(masteru_home); + char *Subuland_Extension = "/subuland/"; + size_t Subuland_Extension_len = strlen(Subuland_Extension); + *subuland = (char *)malloc( masteru_home_len + Subuland_Extension_len + 1 ); + if(!*subuland) SUBU_ERR_MALLOC; + strcpy(*subuland, masteru_home); + strcpy(*subuland + masteru_home_len, Subuland_Extension); + return 0; +} + +static int mk_subuhome(char *subuland, char *subuname, char **subuhome){ + size_t subuland_len = strlen(subuland); + size_t subuhome_len = subuland_len + strlen(subuname); // subuland has a trailing '/' + *subuhome = (char *)malloc(subuhome_len + 1); + if(!*subuhome) return SUBU_ERR_MALLOC; + strcpy (*subuhome, subuland); + strcpy (*subuhome + subuland_len, subuname); + return 0; +} + + + +//=============================================================================== +int subu_mk_0(char **mess, sqlite3 *db, char *subuname){ + + int rc; + if(mess)*mess = 0; + da resources; + da_alloc(&resources, sizeof(char *)); + + //-------------------------------------------------------------------------------- + size_t subuname_len; + rc = allowed_subuname(mess, subuname, &subuname_len); + if(rc) return rc; + #ifdef DEBUG + dbprintf("subuname is well formed\n"); + #endif + + //-------------------------------------------------------------------------------- + uid_t masteru_uid; + gid_t masteru_gid; + uid_t set_euid; + gid_t set_egid; + { + masteru_uid = getuid(); + masteru_gid = getgid(); + set_euid = geteuid(); + set_egid = getegid(); + #ifdef DEBUG + dbprintf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid); + #endif + if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT; + } + + //-------------------------------------------------------------------------------- + char *masteru_name = 0; + char *masteru_home = 0; + char *subu_username = 0; + char *subuland = 0; + char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir + da_push(&resources, masteru_name); + da_push(&resources, masteru_home); + da_push(&resources, subu_username); + da_push(&resources, subuland); + da_push(&resources, subuhome); + rc = + uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home) + || + mk_subu_username(mess, db, &subu_username) + || + mk_subuland(masteru_home, &subuland) + || + mk_subuhome(subuland, subuname, &subuhome) + ; + if(rc) RETURN(&resources, rc); + #ifdef DEBUG + dbprintf("subu_username, subuland, subuhome: \"%s\"\"%s\"\"%s\"\n", subu_username, subuland, subuhome); + #endif + + //-------------------------------------------------------------------------------- + // By having masteru create the subuhome, we know that masteru has rights to + // to access this directory. This will be the mount point for bindfs + { + struct stat st; + if( stat(subuhome, &st) != -1 ){ + if(mess)*mess = strdup(subuhome); + RETURN(&resources, SUBU_ERR_SUBUHOME_EXISTS); + } + int dispatch_err = dispatch_f_euid_egid + ( + "masteru_mkdir_subuhome", + masteru_mkdir_subuhome, + (void *)subuhome, + masteru_uid, + masteru_gid + ); + if( dispatch_err <= ERR_DISPATCH || dispatch_err == SUBU_ERR_MKDIR_SUBUHOME ){ + #ifdef DEBUG + dispatch_f_mess("dispatch_f_euid_egid", dispatch_err, "masteru_mkdir_subuhome"); + #endif + if(mess)*mess = strdup(subuhome); + RETURN(&resources, SUBU_ERR_MKDIR_SUBUHOME); + } + } + #ifdef DEBUG + dbprintf("made directory \"%s\"\n", subuhome); + #endif + + //-------------------------------------------------------------------------------- + // Make the subservient user account, i.e. the subu + { + #ifdef DEBUG + dbprintf("making subu \"%s\" as user \"%s\"\n", subuname, subu_username); + #endif + #if BUG_SSS_CACHE_RUID + #ifdef DEBUG + dbprintf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n"); + #endif + if( setuid(0) == -1 ) RETURN(&resources, SUBU_ERR_BUG_SSS); + #endif + char *command = "/usr/sbin/useradd"; + char *argv[3]; + argv[0] = command; + argv[1] = subu_username; + argv[2] = (char *) NULL; + char *envp[1]; + envp[0] = (char *) NULL; + int dispatch_err = dispatch_exec(argv, envp); + if( dispatch_err != 0 ){ + #ifdef DEBUG + dispatch_f_mess("dispatch_exec", dispatch_err, command); + #endif + // go back and remove the directory we made in subuland + int dispatch_err_rmdir = dispatch_f_euid_egid + ( + "masteru_rmdir_subuhome", + masteru_rmdir_subuhome, + (void *)subuhome, + masteru_uid, + masteru_gid + ); + #ifdef DEBUG + dispatch_f_mess("dispatch_f_euid_egid", dispatch_err_rmdir, "masteru_rmdir_subuhome"); + #endif + if(mess)*mess = useradd_mess(dispatch_err); + RETURN(&resources, SUBU_ERR_FAILED_USERADD); + } + #ifdef DEBUG + dbprintf("added user \"%s\"\n", subu_username); + #endif + } + + //-------------------------------------------------------------------------------- + #ifdef DEBUG + dbprintf("setting the masteru_name, subuname, subu_username relation\n"); + #endif + { + int rc = subudb_Masteru_Subu_put(db, masteru_name, subuname, subu_username); + if( rc != SQLITE_OK ){ + if(mess)*mess = strdup("insert of masteru subu relation failed"); + RETURN(&resources, SUBU_ERR_DB_FILE); + } + } + #ifdef DEBUG + dbprintf("finished subu-mk-0(%s)\n", subuname); + #endif + RETURN(&resources, 0); +} + +//================================================================================ +int subu_rm_0(char **mess, sqlite3 *db, char *subuname){ + + int rc; + if(mess)*mess = 0; + da resources; + da_alloc(&resources, sizeof(char *)); + + //-------------------------------------------------------------------------------- + size_t subuname_len; + rc = allowed_subuname(mess, subuname, &subuname_len); + if(rc) return rc; + #ifdef DEBUG + dbprintf("subuname is well formed\n"); + #endif + + //-------------------------------------------------------------------------------- + uid_t masteru_uid; + gid_t masteru_gid; + uid_t set_euid; + gid_t set_egid; + { + masteru_uid = getuid(); + masteru_gid = getgid(); + set_euid = geteuid(); + set_egid = getegid(); + #ifdef DEBUG + dbprintf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid); + #endif + if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT; + } + + //-------------------------------------------------------------------------------- + // various strings that we will need + char *masteru_name = 0; + char *masteru_home = 0; + char *subuland = 0; + char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir + da_push(&resources, masteru_name); + da_push(&resources, masteru_home); + da_push(&resources, subuland); + da_push(&resources, subuhome); + rc = + uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home) + || + mk_subuland(masteru_home, &subuland) + || + mk_subuhome(subuland, subuname, &subuhome) + ; + if(rc) RETURN(&resources, rc); + #ifdef DEBUG + dbprintf("masteru_home, subuhome: \"%s\", \"%s\"\n", masteru_home, subuhome); + #endif + + //-------------------------------------------------------------------------------- + // removal from db + char *subu_username = 0; + da_push(&resources, subu_username); + + db_begin(db); + + rc = subudb_Masteru_Subu_get_subu_username(db, masteru_name, subuname, &subu_username); + if( rc != SQLITE_OK ){ + if(mess) *mess = strdup("subu requested for removal not found under this masteru in db file"); + rc = SUBU_ERR_SUBU_NOT_FOUND; + db_rollback(db); + RETURN(&resources, rc); + } + #ifdef DEBUG + printf("subu_username: \"%s\"\n", subu_username); + #endif + + rc = subudb_Masteru_Subu_rm(db, masteru_name, subuname, subu_username); + if( rc != SQLITE_OK ){ + if(mess)*mess = strdup("removal of masteru subu relation failed"); + db_rollback(db); + RETURN(&resources, SUBU_ERR_DB_FILE); + } + #ifdef DEBUG + dbprintf("removed the masteru_name, subuname, subu_username relation\n"); + #endif + + rc = db_commit(db); + if( rc != SQLITE_OK ){ + if(mess)*mess = strdup("removal of masteru subu relation in unknown state, exiting"); + RETURN(&resources, SUBU_ERR_DB_FILE); + } + + // even after removing the last masteru subu relation, we still do not remove + // the max subu count. Hence, a masteru will keep such for a life time. + + + //-------------------------------------------------------------------------------- + // Only masteru can remove directories from masteru/subuland, so we switch to + // masteru's uid to perform the rmdir. + // + { + #ifdef DEBUG + dbprintf("as masteru, removing the directory \"%s\"\n", subuhome); + #endif + int dispatch_err = dispatch_f_euid_egid + ( + "masteru_rmdir_subuhome", + masteru_rmdir_subuhome, + (void *)subuhome, + masteru_uid, + masteru_gid + ); + if( dispatch_err <= ERR_DISPATCH || dispatch_err == SUBU_ERR_RMDIR_SUBUHOME ){ + #ifdef DEBUG + dispatch_f_mess("dispatch_f_euid_egid", dispatch_err, "masteru_rmdir_subuhome"); + #endif + if(mess)*mess = strdup(subuhome); + RETURN(&resources, SUBU_ERR_RMDIR_SUBUHOME); + } + } + + //-------------------------------------------------------------------------------- + // Delete the subservient user account + { + #ifdef DEBUG + dbprintf("deleting user \"%s\"\n", subu_username); + #endif + #if BUG_SSS_CACHE_RUID + #ifdef DEBUG + dbprintf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n"); + #endif + if( setuid(0) == -1 ){ + RETURN(&resources, SUBU_ERR_BUG_SSS); + } + #endif + char *command = "/usr/sbin/userdel"; + char *argv[4]; + argv[0] = command; + argv[1] = subu_username; + argv[2] = "-r"; + argv[3] = (char *) NULL; + char *envp[1]; + envp[0] = (char *) NULL; + int dispatch_err = dispatch_exec(argv, envp); + if( dispatch_err != 0 ){ + #ifdef DEBUG + dispatch_f_mess("dispatch_exec", dispatch_err, command); + #endif + if(mess)*mess = userdel_mess(dispatch_err); + RETURN(&resources, SUBU_ERR_FAILED_USERDEL); + } + #ifdef DEBUG + dbprintf("deleted user \"%s\"\n", subu_username); + #endif + } + + #ifdef DEBUG + dbprintf("finished subu-rm-0(%s)\n", subuname); + #endif + RETURN(&resources, 0); +} + +//================================================================================ +// identifies masteru, the bindfs maps each subu_user's home to its mount point +// in subuland. +int subu_bind(char **mess, char *masteru_name, char *subu_username, char *subuhome){ + + int rc; + if(mess)*mess = 0; + da resources; + da_alloc(&resources, sizeof(char *)); + + // lookup the subu_user_home + char *subu_user_home = 0; + da_push(&resources, subu_user_home); + + rc = username_to_home(subu_username, &subu_user_home); + if( rc ){ + if(mess) *mess = strdup("in subu_bind, subu user home directory lookup in /etc/passwd failed."); + RETURN(&resources, rc); + } + + size_t len = 0; + char *map = 0; + da_push(&resources, map); + + FILE* map_stream = open_memstream(&map, &len); + fprintf(map_stream, "--map=%s/%s:@%s/@%s", subu_username, masteru_name, subu_username, masteru_name); + fclose(map_stream); + + char *command = "/usr/bin/bindfs"; + char *argv[5]; + argv[0] = command; + argv[1] = map; + argv[2] = subu_user_home; + argv[3] = subuhome; + argv[4] = (char *) NULL; + char *envp[1]; + envp[0] = (char *) NULL; + int dispatch_err = dispatch_exec(argv, envp); + if( dispatch_err != 0 ){ + #ifdef DEBUG + dispatch_f_mess(command, dispatch_err, "dispatch_exec"); + #endif + if(mess)*mess = strdup("bind failed"); + RETURN(&resources, SUBU_ERR_BIND); + } + #ifdef DEBUG + dbprintf("mapped \"%s\" as \"%s\"\n", subu_user_home, subuhome); + #endif + RETURN(&resources, 0); +} + +int subu_bind_all(char **mess, sqlite3 *db){ + + int rc; + if(mess)*mess = 0; + da resources; + da_alloc(&resources, sizeof(char *)); + + //-------------------------------------------------------------------------------- + uid_t masteru_uid; + gid_t masteru_gid; + uid_t set_euid; + gid_t set_egid; + { + masteru_uid = getuid(); + masteru_gid = getgid(); + set_euid = geteuid(); + set_egid = getegid(); + #ifdef DEBUG + dbprintf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid); + #endif + if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT; + } + + //-------------------------------------------------------------------------------- + // various strings that we will need + char *masteru_name = 0; + char *masteru_home = 0; + char *subuland = 0; + da_push(&resources, masteru_name); + da_push(&resources, masteru_home); + da_push(&resources, subuland); + rc = + uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home) + || + mk_subuland(masteru_home, &subuland) + ; + if(rc) RETURN(&resources, rc); + #ifdef DEBUG + if(masteru_name) + dbprintf("masteru_name: \"%s\"", masteru_name); + else + dbprintf("masteru_name unknown"); + if(subuland) + dbprintf("subuland: \"%s\"", subuland); + else + dbprintf("subuland unknown"); + dbprintf("\n"); + #endif + + //-------------------------------------------------------------------------------- + da subus; + rc = subudb_Masteru_Subu_get_subus(db, masteru_name, &subus); + if( rc != SQLITE_OK ){ + if(mess)*mess = strdup("db access failed when fetching a list of subus"); + return rc; + } + // a limitation of our error reporting approach is that we can only + // return one error, but here is a loop that might generate many + rc = 0; + char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir + uint err_cnt = 0; + subudb_subu_element *pt = (subudb_subu_element *)(subus.base); + while( !da_endq(&subus,pt) ){ + rc = mk_subuhome(subuland, pt->subuname, &subuhome); + #ifdef DEBUG + if(subuhome) + dbprintf("subuhome: \"%s\"\n", subuhome); + else + dbprintf("subuhome unknown \n"); + #endif + if(!rc) rc = subu_bind(NULL, masteru_name, pt->subu_username, subuhome); + if(rc) err_cnt++; + free(subuhome); + subuhome=0; + pt++; + } + if(err_cnt==1){ + RETURN(&resources, rc); + } + if(err_cnt > 1){ + *mess = strdup("multiple errors occured while binding subus"); + RETURN(&resources, SUBU_ERR_BIND); + } + RETURN(&resources, 0); +} diff --git a/module/subu-0/subudb-init.cli.c b/module/subu-0/subudb-init.cli.c new file mode 100644 index 0000000..714c7e4 --- /dev/null +++ b/module/subu-0/subudb-init.cli.c @@ -0,0 +1,26 @@ +/* +This command initializes the db file. + +*/ +#include "subudb-init.cli.h" +#include + +int main(){ + sqlite3 *db; + if( sqlite3_open(DB_File, &db) != SQLITE_OK ){ + fprintf(stderr, "error exit, could not open db file \"%s\"\n", DB_File); + return SUBU_ERR_DB_FILE; + } + db_begin(db); + if( subudb_schema(db) != SQLITE_OK ){ + db_rollback(db); + fprintf(stderr, "error exit, opened db file but could not build schema\n"); + return SUBU_ERR_DB_FILE; + } + db_commit(db); + if( sqlite3_close(db) != SQLITE_OK ){ + fprintf(stderr, "error exit, could not close the db\n"); + return SUBU_ERR_DB_FILE; + } + return 0; +} diff --git a/module/subu-0/subudb-number.cli.c b/module/subu-0/subudb-number.cli.c new file mode 100644 index 0000000..60304e3 --- /dev/null +++ b/module/subu-0/subudb-number.cli.c @@ -0,0 +1,63 @@ +/* +Set or get a new maximum subu number. Currently doesn't do the setting part. + +*/ +#include "subudb-number.cli.h" +#include +#include +#include + +int main(int argc, char **argv){ + + if( argc < 1 || argc > 2){ + fprintf(stderr, "usage: %s [n]\n",argv[0]); + return SUBU_ERR_ARG_CNT; + } + + int rc; + sqlite3 *db; + rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( rc != SQLITE_OK ){ + fprintf(stderr, "error exit, could not open db file\n"); + sqlite3_close(db); + return SUBU_ERR_DB_FILE; + } + + // then arg[1] holds a number to set the max to + if(argc == 2){ + long int i = strtol(argv[1], NULL, 10); + if( i < 0 ){ + fprintf(stderr, "n must be positive\n"); + sqlite3_close(db); + return SUBU_ERR_N; + } + if( i > INT_MAX ){ + fprintf(stderr, "n is too big, max supported by this program is %d\n", INT_MAX); + sqlite3_close(db); + return SUBU_ERR_N; + } + rc = subudb_number_set(db, i); + if( rc != SQLITE_OK ){ + fprintf(stderr, "couldn't set Max_Subunumber: %s\n", sqlite3_errmsg(db)); + return SUBU_ERR_N; + } + } + + // read and print the current max + int n; + rc = subudb_number_get(db, &n); + if( rc == SQLITE_OK ){ + printf("%d\n", n); + }else{ + fprintf(stderr, "couldn't get Max_Subunumber: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + return SUBU_ERR_DB_FILE; + } + rc = sqlite3_close(db); + if( rc != SQLITE_OK ){ + fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db)); + return SUBU_ERR_DB_FILE; + } + return 0; + +} diff --git a/module/subu-0/subudb-rel-get.cli.c b/module/subu-0/subudb-rel-get.cli.c new file mode 100644 index 0000000..9828b15 --- /dev/null +++ b/module/subu-0/subudb-rel-get.cli.c @@ -0,0 +1,42 @@ +/* +get the username from the db file +for testing subudb_Masteru_Subu_get_subu_username + +*/ +#include "subudb-rel-get.cli.h" +#include +#include + +int main(int argc, char **argv){ + + if(argc != 3){ + fprintf(stderr, "usage: %s masteru_name subuname\n", argv[0]); + return SUBU_ERR_ARG_CNT; + } + + int rc; + sqlite3 *db; + rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( rc != SQLITE_OK ){ + fprintf(stderr, "could not open db file \"%s\"\n", DB_File); + return SUBU_ERR_DB_FILE; + } + + char *masteru_name = argv[1]; + char *subuname = argv[2]; + char *subu_username; + + int ret = subudb_Masteru_Subu_get_subu_username(db, masteru_name, subuname, &subu_username); + if( ret != SQLITE_DONE ){ + fprintf(stderr, "subudb_Masteru_Subu_get_subu_username indicates failure by returning %d\n",ret); + fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db)); + return SUBU_ERR_DB_FILE; + } + ret = sqlite3_close(db); + if( ret != SQLITE_OK ){ + fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret); + fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db)); + return SUBU_ERR_DB_FILE; + } + return 0; +} diff --git a/module/subu-0/subudb-rel-put.cli.c b/module/subu-0/subudb-rel-put.cli.c new file mode 100644 index 0000000..b19896e --- /dev/null +++ b/module/subu-0/subudb-rel-put.cli.c @@ -0,0 +1,40 @@ +/* +puts a relation in the masteru/subu table + +*/ +#include "subudb-rel-put.cli.h" +#include +#include + +int main(int argc, char **argv){ + + if(argc != 4){ + fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]); + return 1; + } + char *masteru_name = argv[1]; + char *subuname = argv[2]; + char *subu_username = argv[3]; + + sqlite3 *db; + { + int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( ret != SQLITE_OK ){ + fprintf(stderr, "could not open db file \"%s\"\n", DB_File); + return SUBU_ERR_DB_FILE; + }} + + int ret = subudb_Masteru_Subu_put(db, masteru_name, subuname, subu_username); + if( ret != SQLITE_OK ){ + fprintf(stderr, "subudb_Masteru_Subu_put indicates failure by returning %d\n",ret); + fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db)); + return SUBU_ERR_DB_FILE; + } + ret = sqlite3_close(db); + if( ret != SQLITE_OK ){ + fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret); + fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db)); + return SUBU_ERR_DB_FILE; + } + return 0; +} diff --git a/module/subu-0/subudb-rel-rm.cli.c b/module/subu-0/subudb-rel-rm.cli.c new file mode 100644 index 0000000..3d15ca9 --- /dev/null +++ b/module/subu-0/subudb-rel-rm.cli.c @@ -0,0 +1,41 @@ +/* +puts a relation in the masteru/subu table + +*/ +#include "subudb-rel-rm.cli.h" +#include +#include + +int main(int argc, char **argv){ + + if(argc != 4){ + fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]); + return 1; + } + char *masteru_name = argv[1]; + char *subuname = argv[2]; + char *subu_username = argv[3]; + + sqlite3 *db; + { + int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( ret != SQLITE_OK ){ + fprintf(stderr, "could not open db file \"%s\"\n", DB_File); + return SUBU_ERR_DB_FILE; + }} + + int ret = subudb_Masteru_Subu_rm(db, masteru_name, subuname, subu_username); + if( ret != SQLITE_DONE ){ + fprintf(stderr, "subudb_Masteru_Subu_rm indicates failure by returning %d\n",ret); + fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db)); + printf("put failed\n"); + return 2; + } + ret = sqlite3_close(db); + if( ret != SQLITE_OK ){ + fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret); + fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db)); + return SUBU_ERR_DB_FILE; + } + return 0; +} diff --git a/module/subu-0/subudb-subus.cli.c b/module/subu-0/subudb-subus.cli.c new file mode 100644 index 0000000..be3af20 --- /dev/null +++ b/module/subu-0/subudb-subus.cli.c @@ -0,0 +1,47 @@ +/* +Set or get a new maximum subu number. Currently doesn't do the setting part. + +*/ +#include "subudb-subus.cli.h" +#include +#include +#include + +int main(int argc, char **argv){ + + if( argc != 2){ + fprintf(stderr, "usage: %s masteru_name\n",argv[0]); + return SUBU_ERR_ARG_CNT; + } + char *masteru_name = argv[1]; + + int rc; + sqlite3 *db; + rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); + if( rc != SQLITE_OK ){ + fprintf(stderr, "error exit, could not open db file\n"); + sqlite3_close(db); + return SUBU_ERR_DB_FILE; + } + + subudb_subu_element *sa; + subudb_subu_element *sa_end; + rc = subudb_Masteru_Subu_get_subus(db, masteru_name, &sa, &sa_end); + if( rc == SQLITE_OK ){ + subudb_subu_element *pt = sa; + while( pt != sa_end ){ + printf("%s %s\n", pt->subuname, pt->subu_username); + pt++; + } + rc = sqlite3_close(db); + if( rc != SQLITE_OK ){ + fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db)); + return SUBU_ERR_DB_FILE; + } + return 0; + } + fprintf(stderr, "lookup failed %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + return SUBU_ERR_DB_FILE; + +} diff --git a/module/subu-1/subu-mk.py b/module/subu-1/subu-mk.py new file mode 100644 index 0000000..203fab4 --- /dev/null +++ b/module/subu-1/subu-mk.py @@ -0,0 +1,299 @@ +#!/usr/bin/python +# see the help option for syntax +# this script must be run from root or sudo +# +# on Fedora 29 os.getresuid returned all zeros for a script run from sudo. +# Hence, I am using the environment variable SUDO_USER + +import getpass +import os +import sys +import libuser +from __future__ import print_function + +command = os.path.base(argv[0]) + +#-------------------------------------------------------------------------------- +# utilities +# +def prn(str): + print(str,end='') + +#-------------------------------------------------------------------------------- +# help +# +def help(): + print( command + +""" [=help] [=version] [shell=][owner=] [subu=] +Makes a subservient user. +If no arguments are given, or if =help is given, this message is printed. +When this command is invoked through sudo, $SUDO_USER is taken as the owner's username. +Otherwise, when invoked directly from root, the owner= option must be provided. +The subu-username argument is the username for the new subservient user +The the new subu home directory is created in /home/owner/subu/. +Facls are set to give the owner access to the new subu's home directory. +The shell option is not implemented yet. Probably need a number of other options also. +""" + ) + +def version(): + print(" version 0") + +#-------------------------------------------------------------------------------- +# a manager for handling error messages +# +class class_err: +""" +An error record has the form [flag, message, args] + class is fatal, warning, info [currently not implemented] + flag is true if an error has occured [need to change this to a count] + args is an array of strings to be given after the error message is printed. + +The dict holds named error records. + +register() is used to name and place error records in the dict. register() is +typically called multiple times to initialize and error instance. + +tattle() is used by the program at run time in order to signal errors. + +has_error() returns true if tattle was ever called + +report() prints an error report. When errors have occured this + +vector() [unimplemented] returns a bit vector with one bit per fatal error +record, in the order they appear in the dictionary. The bit is set if the error +ever occured. + +We check for as many errors as is convenient to do so rather than stopping on +the first error. +""" + + # field offsets into the error record + flag_dex = 0; + message_dex = 1; + args_dex = 2; + + def __init__(self): + self.total_cnt = 0 + self.dict = {} + + def register(name, message): + self.dict[name] = [False, message, []] + + def tattle(name, *args): + self.total_cnt += 1 + if name in self.dict: + self.dict[name][0] = True + self.dict[name][2].extend(args) + + def report(): + if self.total_cnt: + for k,v in self.dict.items(): + if v[self.flag_dex]: + print(v[self.message_dex],end='') + args = v[self.args_dex] + if length(args) : + print(args[0],end='') + for arg in args[1:]: + print( " " + arg, end='') + print() + +#-------------------------------------------------------------------------------- +# parse the command line +# +err.register( + 'impossible_split', + "It is not possible, yet this token split into other than one or two pieces: " + ) +err.register( + 'lone_delim', + "No spaces should appear around the '=' delimiter." + ) + +args = sys.argv[1:] +if len(args) == 0 : + version() + help() + exit(1) + +#create a dictionary based on the command line arguments +arg_dict = {} +subu_cnt = 0 +delim = '=' +for token in args: + token_pair = split(token, delim); + if len(token_pair) == 1 : #means there was no '=' in the token + arg_dict['subu'] = token_pair + subu_cnt++ + elif len(token_pair) == 2 : + if token_pair[0] == '' and token_pair[1] == '' : + err.tattle('lone_delim') + elif token_pair[1] == '' : # then has trailing delim, will treat the same as a leading delim + arg_dict[token_pair[0]] = None + elif token_pair[0] == '' : # then has leading delim + arg_dict[token_pair[1]] = None + else: + arg_dict[token_pair[0]] = token_pair[1] + else: + err.tattle('impossible_split', token) + +if not arg_dict or arg_dict.get('help'): + help() + err.report() + exit(1) + +if arg_dict.get('version'): + version() + +#-------------------------------------------------------------------------------- +# check that the command line arguments are well formed. +# +err.register( + 'too_many_args', + command + " takes at most one non-option argument, but we counted: " + ) +err.register( + 'no_subu' + command + " missing subservient username." + ) +err.register( + 'bad_username' + "Usernames match ^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$, but found: " + ) +err.register( + 'unknown_option' + command + " doesn't implement option: " + ) + +subu = arg_dict.get('subu') +if subu_cnt > 1: + err.tattle('too_many_args') +elif not subu + err.tattle('no_subu') +elif not re.match("^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$", subu) + err.tattle('bad_username', subu) + +for k in arg_dict: + if k not in ['help', 'version', 'shell', 'owner', 'subu'] : + err.tattle('unkown_option', k) + +if arg_dict.get('shell') : + print "shell option aint implemented yet" + + + +#-------------------------------------------------------------------------------- +# check that we have root privilege +# +err.register( + 'not_root' + command + "requires root privilege" + ) + +uid = os.getuid() +if uid != 0 : + err.tattle('not root') + +username = getpass.getuser() +sudo_caller_username = os.environ.get('SUDO_USER') + +if !sudo_caller_username + if username not == "root": + err.tattle('not_root') + elif: + owner + + + def has_error(*errs): + return self.cnt > 0 + + + +#----- + + + + +#-------------------------------------------------------------------------------- +# pull out the owner_dir and subu_dir +# +admin= libuser.admin() + +err_arg_form = class_err() +err_arg_form.register('too_many', "too many semicolon delineated parts in") + +owner_parts = args[0].split(":") +subu_parts = args[1].split(":") + +owner_user_name = owner_parts[0] +if not owner_user_name: + err_arg_form.tattle('owner_user_name_missing', args[0]) +else: + owner_user = admin.lookupUserByName(owner_user_name) + if owner_user == None: + err_arg_form.tattle('no_such_user_name', owner_user_name) + else: + + +subu_user_name = subu_parts[0] + + +if length(owner_parts) > 2: + err_arg_form.tattle('too_many', args[0]) +elif length(owner_parts) == 2: + owner_dir = owner_parts[1] +else # get the home directory + + + + + +#-------------------------------------------------------------------------------- +# set the home directory +# +if len(args) > args_dir_index: + dir = args[args_dir_index] +else: + dir = os.getcwd() + +home = dir + "/" + name +home_flag = not os.path.exists(home) + +#-------------------------------------------------------------------------------- +# create the user, setfacls +# +err_cnt = 0 +name_available_flag = False + +if name_flag: + admin = libuser.admin() + name_available_flag = name not in admin.enumeratedUsers() + +if owner_flag and name_flag and name_available_flag and home_flag : + user = admin.initUser(name) + user[libuser.HOMEDIRECTORY] = home + if opt_shell : user[libuser.SHELL] = opt_shell + admin.addUser(user) + #setfacl -m d:u:plato:rwx,u:plato:rwx directory + setfacl = "setfacl -m d:u:" + name + ":rwx,u:" + name + ":rwx " + home + exit(0) + +#-------------------------------------------------------------------------------- +# error return +# .. need to grab the return code from setfacl above and delete the user if it fails +# +err_flags = 0 +if not owner_flag : + err_flags |= 2**2 + print "missing owning username argument" +if not name_flag : + err_flags |= 2**3 + print name + "missing subservient username argument" +if not name_available_flag : + err_flags |= 2**4 + print name + "specified subservient username already exists" +if not home_flag : + err_flags |= 2**5 + print "user home directory already exists" + +exit(err_flags) diff --git a/module/tranche/deprecated/0_makefile b/module/tranche/deprecated/0_makefile new file mode 100644 index 0000000..b8c7b9d --- /dev/null +++ b/module/tranche/deprecated/0_makefile @@ -0,0 +1,40 @@ +# src/0_makefile + +SHELL=/bin/bash + +-include 0_makefile_flags + +SUID_TOOL=$(TOOLSDIR)/bin/setuid_root.sh +MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_cc + +SOURCES=$(wildcard *.c) +HFILES=$(wildcard *.h) + +all: version deps lib execs + +version: + $(MAKE) $@ + @echo "SUID_TOOL: " $(SUID_TOOL) + +deps: + makeheaders $(SOURCES) $(HFILES) + sed -i '/^ *int *main *(.*)/d' *.h + $(MAKE) $@ + +execs: + $(MAKE) $@ + @echo "-> $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all" + cat $(SUID_TOOL) + @echo -n "Are you sure? [y/N] " && read ans && [ $${ans:-N} == y ] + sudo $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all + +clean: + $(MAKE) $@ + for i in $(HFILES); do rm $$i; done + +%:: + $(MAKE) $@ + + + + diff --git a/module/tranche/deprecated/0_makefile-flags b/module/tranche/deprecated/0_makefile-flags new file mode 100644 index 0000000..d4df01f --- /dev/null +++ b/module/tranche/deprecated/0_makefile-flags @@ -0,0 +1,31 @@ + +# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. +ECHO= echo +#ECHO= echo -e + +# directories used by this makefile, these could all be set to dot for +# the simplest source directory structure + +#LIDBIR, EXECSDIR, HDIR hold the make results that might later be shared +#$(PWD) is the directory that make was called from, this is already build in +#set to dot to use the same directory as the source code +#leave blank to ommit +DEPRDIR=1_deprecated +DOCDIR=1_doc +EXECSDIR=1_execs +HDIR=1_headers +LIBDIR=1_lib +TESTDIR=1_tests +TMPDIR=1_tmp +TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) +TRYDIR=1_try + + +# compiler and flags +CC=gcc +CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB +#CFLAGS=-std=gnu11 -fPIC -I. -Werror +LINKFLAGS=-L1_lib -lsubu -lsqlite3 + +LIBFILE=$(LIBDIR)/libsubu.a + diff --git a/module/tranche/doc/todo.txt b/module/tranche/doc/todo.txt new file mode 100644 index 0000000..1494e66 --- /dev/null +++ b/module/tranche/doc/todo.txt @@ -0,0 +1,11 @@ + +2019-03-22T13:37:23Z +1. indentation + + the first non blank line after the #tranche keyword sets the indentation + level for the following text. When echoing to the output file + remove that many leading spaces. + + Hmm, or allow options among the tranche parameters, -indent 3 or + -indent ' '. + diff --git a/module/tranche/makefile b/module/tranche/makefile new file mode 100644 index 0000000..4c20d5e --- /dev/null +++ b/module/tranche/makefile @@ -0,0 +1,22 @@ +# tranche/makefile + +SHELL=/bin/bash + +-include makefile-flags + +.PHONY: all version deps lib exec +all: version deps lib exec + +lib: + $(MAKE) $@ + cp src/tranche.lib.h include/tranche.h + +exec: + $(MAKE) $@ + +%:: + $(MAKE) $@ + + + + diff --git a/module/tranche/makefile-flags b/module/tranche/makefile-flags new file mode 100644 index 0000000..1aa3d66 --- /dev/null +++ b/module/tranche/makefile-flags @@ -0,0 +1,31 @@ +MODULE=tranche + +# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. +ECHO= echo +#ECHO= echo -e + +# directories used by this makefile, these could all be set to dot for +# the simplest source directory structure + +#LIDBIR, EXECDIR, INCDIR hold the make results that might later be shared +#$(PWD) is the directory that make was called from, this is already build in +#set to dot to use the same directory as the source code +#leave blank to ommit +DEPRDIR=deprecated +DOCDIR=doc +EXECDIR=exec +INCDIR=include +LIBDIR=lib +SRCDIR=src +TESTDIR=test +TMPDIR=tmp +TOOLDIR=$(realpath $(PROJECT_SUBU)/tool) +TRYDIR=try + +C=gcc +CFLAGS=-std=gnu11 -fPIC -I. -I../share/include -ggdb -Werror -DDEBUG -DDEBUGDB +#CFLAGS=-std=gnu11 -fPIC -I. -Werror +LINKFLAGS=-Llib -L../share/lib/ -lda -ltranche + +MAKE=/usr/bin/make --no-print-directory -f $(PROJECT_SUBU)/tool/lib/makefile_cc +#MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tool/lib/makefile_cc diff --git a/module/tranche/src/tranche.cli.c b/module/tranche/src/tranche.cli.c new file mode 100644 index 0000000..8df4337 --- /dev/null +++ b/module/tranche/src/tranche.cli.c @@ -0,0 +1,25 @@ + +#include +#include +#include +#include "tranche.lib.h" + +int main(int argc, char **argv, char **envp){ + if(argc != 2){ + fprintf(stderr, "usage: %s \n",argv[0]); + return 1; + } + FILE *file = fopen(argv[1], "r"); + if(!file){ + fprintf(stderr,"could not open file %s\n", argv[1]); + return 2; + } + Da targets; + da_alloc(&targets, sizeof(int)); + int fd = STDOUT_FILENO; + da_push(&targets, &fd); + tranche_send(file, &targets); + da_free(&targets); + fclose(file); + return 0; +} diff --git a/module/tranche/src/tranche.lib.c b/module/tranche/src/tranche.lib.c new file mode 100644 index 0000000..9b824d3 --- /dev/null +++ b/module/tranche/src/tranche.lib.c @@ -0,0 +1,146 @@ +/* +The purpose of this tools is to facilitate putting prototypes (declarations) next +to implementations (definitions) in a single source file of a C/C++ programs. + +Splits a single source file into multiple files. Scans through the single +source file looking for lines of the form: + + #tranche-begin filename ... + +With the # as the first non-space character on the line, and only filename +following the tag. Upon finding such a line, copies all following lines into the +listed files, until reaching the end marker: + + #tranche-end + +A next improvement of this file would be to support variables to be passed in +for the file names. As it stands, changing the file name requires editing +the source file. + +Files are opened for create or append, so opening to the same file will append +to the end. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +//-------------------------------------------------------------------------------- +// parsing + +char newline = '\n'; +char terminator = 0; + +char tranche_begin_tag[] = "#tranche"; +size_t tranche_begin_tag_len = 8; + +char tranche_end_tag[] = "#tranche-end"; +size_t tranche_end_tag_len = 12; + +// given a line +// returns beginning of file name list +static char *is_tranche_begin(char *pt){ + while( *pt && isspace(*pt) ) pt++; + if(!*pt) return NULL; + if( strncmp(pt, tranche_begin_tag, tranche_begin_tag_len) ) return NULL; + return pt + tranche_begin_tag_len; +} + +static char *is_tranche_end(char *pt){ + while( *pt && isspace(*pt) ) pt++; + if(!*pt) return NULL; + if( strncmp(pt, tranche_end_tag, tranche_end_tag_len) ) return NULL; + return pt + tranche_end_tag_len; +} + +static bool parse_file_list(Da *file_names, char *pt0){ + char *pt1; + while( *pt0 && isspace(*pt0) ) pt0++; + pt1 = pt0; + while( *pt0 ){ + while( *pt1 && !isspace(*pt1) ) pt1++; + char *file_name = strndup(pt0, pt1 - pt0); + da_push(file_names, &file_name); + while( *pt1 && isspace(*pt1) ) pt1++; + pt0 = pt1; + } +} + + + +//-------------------------------------------------------------------------------- +// da_map calls + +static void tranche_open_fd(void *fnp, void *closure){ + char *file_name = *(char **)fnp; + Da *fdap = (Da *)closure; + int fd = open(file_name, O_WRONLY | O_APPEND | O_CREAT, 0666); + if(fd == -1){ + fprintf(stderr, "Could not open file %s\n", file_name); + return; + } + da_push(fdap, &fd); + return; +} +static void tranche_open_fds(Da *fnap, Da *fdap){ + da_map(fnap, tranche_open_fd, fdap); +} + +static void tranche_close_fd(void *fdp, void *closure){ + close(*(int *)fdp); +} +static void tranche_close_fds(Da *fdap){ + da_map(fdap, tranche_close_fd, NULL); + da_rewind(fdap); +} + +static void tranche_puts(void *fdp, void *string){ + write(*(int *)fdp, string, strlen(string)); +} +static void tranche_puts_all(Da *fdap, char *string){ + da_map(fdap, tranche_puts, string); +} + + + +//-------------------------------------------------------------------------------- +// we have a little problem if the user tries to tranche two things to the same file .. +int tranche_send(FILE *src, Da *arg_fdap){ + char *pt; + Da line; + Da file_name_arr; + Da fda; + da_alloc(&line, sizeof(char)); + da_alloc(&file_name_arr, sizeof(char *)); + da_alloc(&fda, sizeof(int)); + + while( !feof(src) ){ + da_fgets(&line, src); + if( is_tranche_end(line.base) ) break; + pt = is_tranche_begin(line.base); + if(pt){ // then this line is the start of a nested tranche block + parse_file_list(&file_name_arr, pt); + tranche_open_fds(&file_name_arr, &fda); + da_free_elements(&file_name_arr); + tranche_send(src, &fda); + tranche_close_fds(&fda); + }else{ + da_pop(&line, NULL); // pop the terminating zero + da_push(&line, &newline); + da_push(&line, &terminator); + tranche_puts_all(arg_fdap, line.base); + } + da_rewind(&line); + } + + da_free(&line); + da_free(&file_name_arr); + da_free(&fda); + return 0; +} diff --git a/module/tranche/src/tranche.lib.h b/module/tranche/src/tranche.lib.h new file mode 100644 index 0000000..27990a6 --- /dev/null +++ b/module/tranche/src/tranche.lib.h @@ -0,0 +1,8 @@ +#ifndef TRANCHE_LIB_H +#define TRANCHE_LIB_H + +int tranche_send(FILE *src, Da *arg_fds); + + + +#endif diff --git a/module/tranche/test/test1.dat b/module/tranche/test/test1.dat new file mode 100644 index 0000000..b03df3f --- /dev/null +++ b/module/tranche/test/test1.dat @@ -0,0 +1,23 @@ + +#tranche test11.dat test12.dat +The little red hen said to Mick, no thank you not today sir. +And then all the barnes animals shouted out in glee. +No more misery! +#tranche test13.dat +apple banana pear +kiwi +#tranche-end +cows +and cats +#tranche-end + +the between space + +#tranche test14.dat +int float if while +do +function +#tranche-end + +#tranche test15.dat +#tranche-end \ No newline at end of file diff --git a/module/tranche/test/test1.sh b/module/tranche/test/test1.sh new file mode 100644 index 0000000..c1140ca --- /dev/null +++ b/module/tranche/test/test1.sh @@ -0,0 +1,10 @@ +#!/bin/bash -x +./tranche test1.dat >test1stdout.dat +diff test11.dat test11.dat.expected +diff test12.dat test12.dat.expected +diff test13.dat test13.dat.expected +diff test14.dat test14.dat.expected +diff test15.dat test15.dat.expected +diff test1stdout.dat test1stdout.dat.expected +rm test11.dat test12.dat test13.dat test14.dat test15.dat test1stdout.dat + diff --git a/module/tranche/test/test11.dat.expected b/module/tranche/test/test11.dat.expected new file mode 100644 index 0000000..2c2904a --- /dev/null +++ b/module/tranche/test/test11.dat.expected @@ -0,0 +1,5 @@ +The little red hen said to Mick, no thank you not today sir. +And then all the barnes animals shouted out in glee. +No more misery! +cows +and cats diff --git a/module/tranche/test/test12.dat.expected b/module/tranche/test/test12.dat.expected new file mode 100644 index 0000000..2c2904a --- /dev/null +++ b/module/tranche/test/test12.dat.expected @@ -0,0 +1,5 @@ +The little red hen said to Mick, no thank you not today sir. +And then all the barnes animals shouted out in glee. +No more misery! +cows +and cats diff --git a/module/tranche/test/test13.dat.expected b/module/tranche/test/test13.dat.expected new file mode 100644 index 0000000..81fb20c --- /dev/null +++ b/module/tranche/test/test13.dat.expected @@ -0,0 +1,2 @@ +apple banana pear +kiwi diff --git a/module/tranche/test/test14.dat.expected b/module/tranche/test/test14.dat.expected new file mode 100644 index 0000000..0d8b89b --- /dev/null +++ b/module/tranche/test/test14.dat.expected @@ -0,0 +1,3 @@ +int float if while +do +function diff --git a/module/tranche/test/test15.dat.expected b/module/tranche/test/test15.dat.expected new file mode 100644 index 0000000..e69de29 diff --git a/module/tranche/test/test1stdout.dat.expected b/module/tranche/test/test1stdout.dat.expected new file mode 100644 index 0000000..4e519ff --- /dev/null +++ b/module/tranche/test/test1stdout.dat.expected @@ -0,0 +1,5 @@ + + +the between space + + diff --git a/module/tranche/test/test2.c.expected b/module/tranche/test/test2.c.expected new file mode 100644 index 0000000..a4876a1 --- /dev/null +++ b/module/tranche/test/test2.c.expected @@ -0,0 +1,10 @@ + + +#include "test2.h" + + +int f(int x){ + return x; +} + + diff --git a/module/tranche/test/test2.h.expected b/module/tranche/test/test2.h.expected new file mode 100644 index 0000000..fdc4d72 --- /dev/null +++ b/module/tranche/test/test2.h.expected @@ -0,0 +1,4 @@ +#ifndef TEST2_H +#define TEST2_H +int f(int x); +#endif diff --git a/module/tranche/test/test2.trc.c b/module/tranche/test/test2.trc.c new file mode 100644 index 0000000..ff7d696 --- /dev/null +++ b/module/tranche/test/test2.trc.c @@ -0,0 +1,23 @@ + +#tranche test2.c + +#tranche test2.h +#ifndef TEST2_H +#define TEST2_H +#tranche-end + +#include "test2.h" + +#tranche test2.h +int f(int x); +#tranche-end + +int f(int x){ + return x; +} + +#tranche test2.h +#endif +#tranche-end + +#tranche-end diff --git a/module/tranche/test/tranche b/module/tranche/test/tranche new file mode 120000 index 0000000..acf4a6f --- /dev/null +++ b/module/tranche/test/tranche @@ -0,0 +1 @@ +../1_execs/tranche \ No newline at end of file diff --git a/stage/include/da.h b/stage/include/da.h deleted file mode 100644 index 6d3c43b..0000000 --- a/stage/include/da.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef DA_LIB_H -#define DA_LIB_H -#include -#include -#include - -typedef struct Da{ - char *base; - char *end; // one byte/one element off the end of the array - size_t size; // size >= (end - base) + 1; - size_t element_size; -} Da; - -#define RETURN(dap, r) \ - { da_map(dap, da_free, NULL); return r; } - -void da_alloc(Da *dap, size_t element_size); -void da_free(Da *dap); -void da_rewind(Da *dap); -char *da_expand(Da *dap); -void da_rebase(Da *dap, char *old_base, void *pta); -bool da_endq(Da *dap, void *pt); -bool da_boundq(Da *dap); -void da_push(Da *dap, void *element); -bool da_pop(Da *dap, void *element); -char *da_index(Da *dap, size_t i); -void da_map(Da *dap, void f(void *, void *), void *closure); -void da_free_elements(Da *dap); -void da_strings_puts(Da *dap); -char *da_fgets(Da *dap, FILE *fd); - -#endif - diff --git a/stage/include/tranche.h b/stage/include/tranche.h deleted file mode 100644 index 27990a6..0000000 --- a/stage/include/tranche.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef TRANCHE_LIB_H -#define TRANCHE_LIB_H - -int tranche_send(FILE *src, Da *arg_fds); - - - -#endif diff --git a/subu-0/0_makefile b/subu-0/0_makefile deleted file mode 100644 index b8c7b9d..0000000 --- a/subu-0/0_makefile +++ /dev/null @@ -1,40 +0,0 @@ -# src/0_makefile - -SHELL=/bin/bash - --include 0_makefile_flags - -SUID_TOOL=$(TOOLSDIR)/bin/setuid_root.sh -MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_cc - -SOURCES=$(wildcard *.c) -HFILES=$(wildcard *.h) - -all: version deps lib execs - -version: - $(MAKE) $@ - @echo "SUID_TOOL: " $(SUID_TOOL) - -deps: - makeheaders $(SOURCES) $(HFILES) - sed -i '/^ *int *main *(.*)/d' *.h - $(MAKE) $@ - -execs: - $(MAKE) $@ - @echo "-> $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all" - cat $(SUID_TOOL) - @echo -n "Are you sure? [y/N] " && read ans && [ $${ans:-N} == y ] - sudo $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all - -clean: - $(MAKE) $@ - for i in $(HFILES); do rm $$i; done - -%:: - $(MAKE) $@ - - - - diff --git a/subu-0/0_makefile-flags b/subu-0/0_makefile-flags deleted file mode 100644 index 0da8b61..0000000 --- a/subu-0/0_makefile-flags +++ /dev/null @@ -1,31 +0,0 @@ - -# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. -ECHO= echo -#ECHO= echo -e - -# directories used by this makefile, these could all be set to dot for -# the simplest source directory structure - -#LIDBIR, EXECSDIR, HDIR hold the make results that might later be staged -#$(PWD) is the directory that make was called from, this is already build in -#set to dot to use the same directory as the source code -#leave blank to ommit -DEPRDIR=1_deprecated -DOCDIR=1_doc -EXECSDIR=1_execs -HDIR=1_headers -LIBDIR=1_lib -TESTDIR=1_tests -TMPDIR=1_tmp -TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) -TRYDIR=1_try - - -# compiler and flags -CC=gcc -CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB -#CFLAGS=-std=gnu11 -fPIC -I. -Werror -LINKFLAGS=-L1_lib -lsubu -lsqlite3 - -LIBFILE=$(LIBDIR)/libsubu.a - diff --git a/subu-0/1_deprecated/dispatch_exec.lib.c b/subu-0/1_deprecated/dispatch_exec.lib.c deleted file mode 100644 index 024bff8..0000000 --- a/subu-0/1_deprecated/dispatch_exec.lib.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - fork/execs/wait the command passed in argv[0]; - Returns -1 upon failure. - - The wstatus returned from wait() might be either the error returned by exec - when it failed, or the return value from the command. An arbitary command is - passed in, so we don't know what its return values might be. Consquently, we - have no way of multiplexing a unique exec error code with the command return - value within wstatus. If the prorgrammer knows the return values of the - command passed in, and wants better behavior, he or she can spin a special - version of dispatch for that command. -*/ -#include "dispatch_exec.lib.h" - -// without this #define execvpe is undefined -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include - - - -int dispatch_exec(char **argv, char **envp){ - if( !argv || !argv[0] ){ - fprintf(stderr, "argv[0] null. Null command passed into dispatch().\n"); - return -1; - } - #ifdef DEBUG - dbprintf("dispatching exec, args follow:\n"); - char **apt = argv; - while( *apt ){ - dbprintf("\t%s",*apt); - apt++; - } - dbprintf("\n"); - #endif - char *command = argv[0]; - pid_t pid = fork(); - if( pid == -1 ){ - fprintf(stderr, "fork() failed in dispatch().\n"); - return -1; - } - if( pid == 0 ){ // we are the child - execvpe(command, argv, envp); - // exec will only return if it has an error .. - perror(command); - return -1; - }else{ // we are the parent - int wstatus; - waitpid(pid, &wstatus, 0); - if(wstatus) - return -1; - else - return 0; - } -} diff --git a/subu-0/1_deprecated/dispatch_f.lib.c b/subu-0/1_deprecated/dispatch_f.lib.c deleted file mode 100644 index 5c199f5..0000000 --- a/subu-0/1_deprecated/dispatch_f.lib.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - Forks a new process and runs the a function in that new process. I.e. it 'spawns' a function. - - f must have the specified prototype. I.e. it accepts one void pointer and - returns a positive integer. (Negative integers are used for dispatch error codes.) - - dispatch_f_euid_egid changes to the new euid and egid before running the function. - - If the change to in euid/egid fails, the forked process exits with a negative status. - If the function has an error, it returns a positive status. A status of zero means - that all went well. - - Because f is running in a separate process, the return status is the only means - of communication going back to the calling process. - - -*/ -#define _GNU_SOURCE - -#include "dispatch_f.lib.h" -// we need the declaration for uid_t etc. -// without this #define execvpe is undefined -#define _GNU_SOURCE - -#include -#include -#include -#include - -#if INTERFACE -#include -#include -#endif - -//-------------------------------------------------------------------------------- -// dispatch_f_ctx class -// -#if INTERFACE -#define ERR_NEGATIVE_RETURN_STATUS -1 -#define ERR_DISPATCH_F_FORK -2 -#define ERR_DISPATCH_F_SETEUID -3 -#define ERR_DISPATCH_F_SETEGID -4 - -// both name and fname are static allocations -struct dispatch_f_ctx{ - char *dispatcher; // name of the dispatch function (currently "dispatch_f" or "dispatch_f_euid_egid") - char *dispatchee; // name of the function being dispatched - int err; - int status; // return value from the function -}; -#endif -dispatch_f_ctx *dispatch_f_ctx_mk(char *name, char *fname){ - dispatch_f_ctx *ctxp = malloc(sizeof(dispatch_f_ctx)); - ctxp->dispatcher = name; - ctxp->dispatchee = fname; - ctxp->err = 0; - return ctxp; -} -void dispatch_f_ctx_free(dispatch_f_ctx *ctxp){ - // no dynamic variables to be freed in ctx - free(ctxp); -} -void dispatch_f_mess(struct dispatch_f_ctx *ctxp){ - if(ctxp->err == 0) return; - switch(ctxp->err){ - case ERR_NEGATIVE_RETURN_STATUS: - fprintf(stderr, "%s, function \"%s\" broke contract with a negative return value.", ctxp->dispatcher, ctxp->dispatchee); - break; - case ERR_DISPATCH_F_FORK: - case ERR_DISPATCH_F_SETEUID: - case ERR_DISPATCH_F_SETEGID: - fprintf(stderr, "%s, ", ctxp->dispatcher); - perror(ctxp->dispatcher); - break; - } - fputc('\n', stderr); -} - -//-------------------------------------------------------------------------------- -// interface call point -dispatch_f_ctx *dispatch_f(char *fname, int (*f)(void *arg), void *f_arg){ - dispatch_f_ctx *ctxp = dispatch_f_ctx_mk("dispatch_f", fname); - #ifdef DEBUG - dbprintf("%s %s\n", ctxp->dispatcher, ctxp->dispatchee); - #endif - pid_t pid = fork(); - if( pid == -1 ){ - ctxp->err = ERR_DISPATCH_F_FORK; // we are still in the parent process - return ctxp; - } - if( pid == 0 ){ // we are the child - int status = (*f)(f_arg); // we require that f return a zero or positive value - if( status < 0 ) status = ERR_NEGATIVE_RETURN_STATUS; - exit(status); - }else{ // we are the parent - waitpid(pid, &(ctxp->status), 0); - return ctxp; - } -} - -//-------------------------------------------------------------------------------- -// interface call point -dispatch_f_ctx *dispatch_f_euid_egid(char *fname, int (*f)(void *arg), void *f_arg, uid_t euid, gid_t egid){ - dispatch_f_ctx *ctxp = dispatch_f_ctx_mk("dispatch_f_euid_egid", fname); - #ifdef DEBUG - dbprintf("%s %s as euid:%u egid:%u\n", ctxp->dispatcher, ctxp->dispatchee, euid, egid); - #endif - pid_t pid = fork(); - if( pid == -1 ){ - ctxp->err = ERR_DISPATCH_F_FORK; - return ctxp; - } - if( pid == 0 ){ // we are the child - int status; - if( seteuid(euid) == -1 ) - status = ERR_DISPATCH_F_SETEUID; - else if( setegid(egid) == -1 ) - status = ERR_DISPATCH_F_SETEGID; - else{ - status = (*f)(f_arg); - if( status < 0 ) status = ERR_NEGATIVE_RETURN_STATUS; - exit(status); - } - }else{ // we are the parent - waitpid(pid, &(ctxp->status), 0); - return ctxp; - } -} - - diff --git a/subu-0/1_deprecated/dispatch_useradd.lib.c b/subu-0/1_deprecated/dispatch_useradd.lib.c deleted file mode 100644 index 7b75291..0000000 --- a/subu-0/1_deprecated/dispatch_useradd.lib.c +++ /dev/null @@ -1,68 +0,0 @@ -/* -There is no C library interface to useradd(8), but if there were, this function -would be found there instead. - -*/ -#include "dispatch_useradd.lib.h" - -#include -#include -#include -#include -#include - -#if INTERFACE -#include -#include -#define ERR_DISPATCH_USERADD_ARGC 1 -#define ERR_DISPATCH_USERADD_DISPATCH 2 -#define ERR_DISPATCH_USERADD_PWREC 3 -struct dispatch_useradd_ret_t{ - uint error; - struct passwd *pw_record; -}; -#endif - - -// we have a contract with the caller that argv[1] is always the subuname -struct dispatch_useradd_ret_t dispatch_useradd(char **argv, char **envp){ - struct dispatch_useradd_ret_t ret; - { - if( !argv || !argv[0] || !argv[1]){ - fprintf(stderr,"useradd() needs a first argument as the name of the user to be made"); - ret.error = ERR_DISPATCH_USERADD_ARGC; - ret.pw_record = NULL; - return ret; - } - - char *subu_name; - { - subu_name = argv[1]; - if( dispatch_exec(argv, envp) == -1 ){ - fprintf(stderr,"%s failed\n", argv[0]); - ret.error = ERR_DISPATCH_USERADD_DISPATCH; - ret.pw_record = NULL; - return ret; - }} - - { - struct passwd *pw_record = getpwnam(subu_name); - uint count = 1; - while( !pw_record && count <= 3 ){ - #ifdef DEBUG - printf("try %u, getpwnam failed, trying again\n", count); - #endif - sleep(1); - pw_record = getpwnam(subu_name); - count++; - } - if( !pw_record ){ - ret.error = ERR_DISPATCH_USERADD_PWREC; - ret.pw_record = NULL; - return ret; - } - ret.error = 0; - ret.pw_record = pw_record; - return ret; - }}} - diff --git a/subu-0/1_deprecated/subu-rm-0.lib.c b/subu-0/1_deprecated/subu-rm-0.lib.c deleted file mode 100644 index ccad437..0000000 --- a/subu-0/1_deprecated/subu-rm-0.lib.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - subu-rm-0 subuname - - 1. get our uid and lookup masteru_name in /etc/passwd - 2. lookup masteru_name/subuname in config file, which gives us subu_username - 3. unmount subuland/subuname - 4. userdel subu_username - 5. rmdir subuland/subuname - - Note, as per the man page, we are not allowed to free the memory allocated by getpwid(). - -*/ -#include "subu-mk-0.lib.h" - -// without this #define we get the warning: implicit declaration of function ‘seteuid’/‘setegid’ -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include - -#if INTERFACE -#include -#include -#include -#endif - -//-------------------------------------------------------------------------------- -// an instance is subu_rm_0_ctx is returned by subu_rm_0 -// -#if INTERFACE -#define ERR_SUBU_RM_0_MKDIR_SUBUHOME 1 -#define ERR_SUBU_RM_0_RMDIR_SUBUHOME 2 -#define ERR_SUBU_RM_0_SUBUNAME_MALFORMED 3 -#define ERR_SUBU_RM_0_SETUID_ROOT 4 -#define ERR_SUBU_RM_0_MASTERU_HOMELESS 5 -#define ERR_SUBU_RM_0_MALLOC 6 -#define ERR_SUBU_RM_0_CONFIG_FILE 7 -#define ERR_SUBU_RM_0_SUBUHOME_EXISTS 8 -#define ERR_SUBU_RM_0_BUG_SSS 9 -#define ERR_SUBU_RM_0_FAILED_USERADD 10 - -struct subu_rm_0_ctx{ - char *name; - char *subuland; - char *subuhome; - char *subu_username; - bool free_aux; - char *aux; - uint err; -}; -#endif -struct subu_rm_0_ctx *subu_rm_0_ctx_mk(){ - struct subu_rm_0_ctx *ctxp = malloc(sizeof(struct subu_rm_0_ctx)); - ctxp->name = "subu_rm_0"; - ctxp->subuland = 0; - ctxp->subuhome = 0; - ctxp->subu_username = 0; - ctxp->free_aux = false; - ctxp->aux = 0; -} -void subu_rm_0_ctx_free(struct subu_rm_0_ctx *ctxp){ - free(ctxp->subuland); - free(ctxp->subuhome); - free(ctxp->subu_username); - if(ctxp->free_aux) free(ctxp->aux); - free(ctxp); -} -// must be called before any system calls, otherwise perror() will be messed up -void subu_rm_0_mess(struct subu_rm_0_ctx *ctxp){ - switch(ctxp->err){ - case 0: return; - case ERR_SUBU_RM_0_MKDIR_SUBUHOME: - fprintf(stderr, "masteru could not make subuhome, \"%s\"", ctxp->subuhome); - break; - case ERR_SUBU_RM_0_SUBUNAME_MALFORMED: - fprintf(stderr, "subuname, \"%s\" is not in [ _.-a-zA-Z0-9]*", ctxp->aux); - break; - case ERR_SUBU_RM_0_SETUID_ROOT: - fprintf(stderr, "This program must be run setuid root from a user account."); - break; - case ERR_SUBU_RM_0_MASTERU_HOMELESS: - fprintf(stderr,"Masteru, \"%s\", has no home directory", ctxp->aux); - break; - case ERR_SUBU_RM_0_MALLOC: - perror(ctxp->name); - break; - case ERR_SUBU_RM_0_CONFIG_FILE: - fprintf(stderr, "config file error: %s", ctxp->aux); - break; - case ERR_SUBU_RM_0_SUBUHOME_EXISTS: - fprintf(stderr, "a file system object already exists at subuhome, \"%s\"\n", ctxp->subuhome); - break; - case ERR_SUBU_RM_0_BUG_SSS: - perror(ctxp->name); - break; - case ERR_SUBU_RM_0_FAILED_USERADD: - fprintf(stderr, "%u useradd failed\n", ctxp->subu_username); - break; - default: - fprintf(stderr, "unknown error code %d\n", ctxp->err); - } - fputc('\n', stderr); -} - -//-------------------------------------------------------------------------------- -// dispatched functions -// -// the making of subuhome is dispatched to its own process so as to give it its own uid/gid -static int masteru_mkdir_subuhome(void *arg){ - char *subuhome = (char *) arg; - if( mkdir( subuhome, subuhome_perms ) == -1 ){ // find subuhome perms in common - perror("masteru_mkdir_subuhome"); - return ERR_SUBU_RM_0_MKDIR_SUBUHOME; - } - return 0; -} -static int masteru_rmdir_subuhome(void *arg){ - char *subuhome = (char *) arg; - if( rmdir( subuhome ) == -1 ){ // find subuhome perms in common - perror("masteru_rmdir_subuhome"); - return ERR_SUBU_RM_0_RMDIR_SUBUHOME; - } - return 0; -} - -//-------------------------------------------------------------------------------- -// the public call point -struct subu_rm_0_ctx *subu_rm_0(sqlite3 *db, char *subuname){ - - struct subu_rm_0_ctx *ctxp = subu_rm_0_ctx_mk(); - - //-------------------------------------------------------------------------------- - #ifdef DEBUG - dbprintf("Checking that subuname is well formed and finding its length\n"); - #endif - size_t subuname_len; - { - int ret = allowed_subuname(subuname, &subuname_len); - if( ret == -1 ){ - ctxp->err = ERR_SUBU_RM_0_SUBUNAME_MALFORMED; - ctxp->aux = subuname; - return ctxp; - }} - - //-------------------------------------------------------------------------------- - #ifdef DEBUG - dbprintf("Checking that we are running from a user and are setuid root.\n"); - #endif - uid_t masteru_uid; - gid_t masteru_gid; - uid_t set_euid; - gid_t set_egid; - { - masteru_uid = getuid(); - masteru_gid = getgid(); - set_euid = geteuid(); - set_egid = getegid(); - #ifdef DEBUG - dbprintf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid); - #endif - if( masteru_uid == 0 || set_euid != 0 ){ - ctxp->err = ERR_SUBU_RM_0_SETUID_ROOT; - return ctxp; - } - } - - //-------------------------------------------------------------------------------- - #ifdef DEBUG - dbprintf("strings masteru_name and masteru_home\n"); - #endif - - char *masteru_name; - size_t masteru_name_len; - char *masteru_home; - size_t masteru_home_len; - size_t subuland_len; - { - struct passwd *masteru_pw_record_pt = getpwuid(masteru_uid); // reading /etc/passwd - masteru_name = masteru_pw_record_pt->pw_name; - masteru_name_len = strlen(masteru_name); - #ifdef DEBUG - dbprintf("masteru_name \"%s\" %zu\n", masteru_name, masteru_name_len); - #endif - masteru_home = masteru_pw_record_pt->pw_dir; - masteru_home_len = strlen(masteru_home); - #ifdef DEBUG - dbprintf("masteru_home \"%s\" %zu\n", masteru_home, masteru_home_len); - #endif - masteru_home_len = strlen(masteru_home); - if( masteru_home_len == 0 || masteru_home[0] == '(' ){ - ctxp->err = ERR_SUBU_RM_0_MASTERU_HOMELESS; - ctxp->aux = masteru_name; // we can not free a passwd struct, or its fields. I assume then it isn't re-entrant safe. - return ctxp; - } - // char *subuland_extension = "/subuland/"; // moved to common.lib.c - size_t subuland_extension_len = strlen(subuland_extension); - ctxp->subuland = (char *)malloc( masteru_home_len + subuland_extension_len + 1 ); - if(!ctxp->subuland){ - ctxp->err = ERR_SUBU_RM_0_MALLOC; - return ctxp; - } - strcpy(ctxp->subuland, masteru_home); - strcpy(ctxp->subuland + masteru_home_len, subuland_extension); - subuland_len = masteru_home_len + subuland_extension_len; - #ifdef DEBUG - dbprintf("subuland \"%s\" %zu\n", ctxp->subuland, subuland_len); - #endif - } - - //-------------------------------------------------------------------------------- - #ifdef DEBUG - dbprintf("lookup subu_username from masteru_name/subuname in config file\n"); - #endif - char *subu_username; // this is part of ctx and must be freed - { - int ret = subu_get_masteru_subu(db, masteru_name, subuname, &subu_username); - if( ret != SQLITE_DONE ){ - printf("get failed\n"); - return 2; - } - #ifdef DEBUG - printf("subu_username: %s\n", subu_username); - #endif - - } - - //-------------------------------------------------------------------------------- - #ifdef DEBUG - dbprintf("strings subu_username and subuhome\n"); - #endif - size_t subu_username_len; - size_t subuhome_len; - { - char *ns=0; // 'ns' Number as String - char *mess=0; - if( subu_number_get( db, &ns, &mess ) != SQLITE_OK ){ - ctxp->err = ERR_SUBU_RM_0_CONFIG_FILE; - ctxp->aux = mess; - ctxp->free_aux = true; - return ctxp; - } - size_t ns_len = strlen(ns); - ctxp->subu_username = malloc(1 + ns_len + 1); - if( !ctxp->subu_username ){ - ctxp->err = ERR_SUBU_RM_0_MALLOC; - return ctxp; - } - strcpy(ctxp->subu_username, "s"); - strcpy(ctxp->subu_username + 1, ns); - subu_username_len = ns_len + 1; - #ifdef DEBUG - dbprintf("subu_username \"%s\" %zu\n", ctxp->subu_username, subu_username_len); - #endif - - subuhome_len = subuland_len + subuname_len; - ctxp->subuhome = (char *)malloc(subuhome_len + 1); - if( !ctxp->subuhome ){ - ctxp->err = ERR_SUBU_RM_0_MALLOC; - return ctxp; - } - strcpy (ctxp->subuhome, ctxp->subuland); - strcpy (ctxp->subuhome + subuland_len, subuname); - #ifdef DEBUG - dbprintf("subuhome \"%s\" %zu\n", ctxp->subuhome, subuhome_len); - #endif - } - - //-------------------------------------------------------------------------------- - // By having masteru create the subuhome, we know that masteru has rights to - // to access this directory. This will be the mount point for bindfs - { - #ifdef DEBUG - dbprintf("as masteru, making the directory \"%s\"\n", ctxp->subuhome); - #endif - struct stat st; - if( stat(ctxp->subuhome, &st) != -1 ){ - ctxp->err = ERR_SUBU_RM_0_SUBUHOME_EXISTS; - return ctxp; - } - dispatch_ctx *dfr = dispatch_f_euid_egid - ( - "masteru_mkdir_subuhome", - masteru_mkdir_subuhome, - (void *)ctxp->subuhome, - masteru_uid, - masteru_gid - ); - if( dfr->err <= ERR_DISPATCH || dfr->err == ERR_SUBU_RM_0_MKDIR_SUBUHOME ){ - #ifdef DEBUG - if( dfr->err == ERR_SUBU_RM_0_MKDIR_SUBUHOME ) - perror("mkdir"); - else - dispatch_f_mess(dfr); - #endif - ctxp->err = ERR_SUBU_RM_0_MKDIR_SUBUHOME; - return ctxp; - } - } - #ifdef DEBUG - dbprintf("masteru made directory \"%s\"\n", ctxp->subuhome); - #endif - - //-------------------------------------------------------------------------------- - // Make the subservient user account, i.e. the subu - { - #ifdef DEBUG - dbprintf("making subu \"%s\" as user \"%s\"\n", subuname, ctxp->subu_username); - #endif - #if BUG_SSS_CACHE_RUID - #ifdef DEBUG - dbprintf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n"); - #endif - if( setuid(0) == -1 ){ - ctxp->err = ERR_SUBU_RM_0_BUG_SSS; - return ctxp; - } - #endif - char *command = "/usr/sbin/useradd"; - char *argv[3]; - argv[0] = command; - argv[1] = ctxp->subu_username; - argv[2] = (char *) NULL; - char *envp[1]; - envp[0] = (char *) NULL; - dispatch_ctx *dfr = dispatch_exec(argv, envp); - if( dfr->err != 0 ){ - #ifdef DEBUG - if( dfr->err <= ERR_DISPATCH ) - dispatch_f_mess(dfr); - else - perror("useradd"); - #endif - // go back and remove the directory we made in subuland - dispatch_ctx *dfr = dispatch_f_euid_egid - ( - "masteru_rmdir_subuhome", - masteru_rmdir_subuhome, - (void *)ctxp->subuhome, - masteru_uid, - masteru_gid - ); - #ifdef DEBUG - if( dfr->err <= ERR_DISPATCH || dfr->err == ERR_SUBU_RM_0_RMDIR_SUBUHOME ) - if( dfr->err == ERR_SUBU_RM_0_RMDIR_SUBUHOME ) - perror("rmdir"); - else - dispatch_f_mess(dfr); - #endif - ctxp->err = ERR_SUBU_RM_0_FAILED_USERADD; - return ctxp; - } - #ifdef DEBUG - dbprintf("added user \"%s\"\n", ctxp->subu_username); - #endif - } - - //-------------------------------------------------------------------------------- - #ifdef DEBUG - dbprintf("setting the masteru_name, subuname, subu_username relation\n"); - #endif - { - int ret = subu_put_masteru_subu(db, masteru_name, subuname, ctxp->subu_username); - if( ret != SQLITE_DONE ){ - ctxp->err = ERR_SUBU_RM_0_CONFIG_FILE; - ctxp->aux = "insert of masteru subu relation failed"; - return ctxp; - } - } - - #ifdef DEBUG - dbprintf("finished subu-mk-0(%s)\n", subuname); - #endif - ctxp->err = 0; - return ctxp; -} diff --git a/subu-0/1_deprecated/subudb-number-next.cli.c b/subu-0/1_deprecated/subudb-number-next.cli.c deleted file mode 100644 index 3373173..0000000 --- a/subu-0/1_deprecated/subudb-number-next.cli.c +++ /dev/null @@ -1,45 +0,0 @@ -/* -Set or get a new maximum subu number. Currently doesn't do the setting part. - -*/ -#include "subudb-number-next.cli.h" -#include -#include -#include - -int main(int argc, char **argv){ - - if( argc != 2 ){ - fprintf(stderr, "usage: %s masteru_name \n",argv[0]); - return SUBU_ERR_ARG_CNT; - } - char *masteru_name = argv[1]; - - int rc; - sqlite3 *db; - rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); - if( rc != SQLITE_OK ){ - sqlite3_close(db); - fprintf(stderr, "error exit, could not open db file\n"); - return SUBU_ERR_DB_FILE; - } - - // read and print the current max - char *mess; - int n; - rc = subudb_number_next(db, masteru_name, &n, &mess); - if( rc == SQLITE_DONE ){ - printf("%d\n", n); - }else{ - fprintf(stderr, "subudb_number_next indicates failure by returning %d\n",rc); - fprintf(stderr, "and issues message, %s\n", sqlite3_errmsg(db)); - sqlite3_close(db); - return SUBU_ERR_DB_FILE; - } - rc = sqlite3_close(db); - if( rc != SQLITE_OK ){ - fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db)); - return SUBU_ERR_DB_FILE; - } - return 0; -} diff --git a/subu-0/1_doc/to_do.txt b/subu-0/1_doc/to_do.txt deleted file mode 100644 index 0b989cc..0000000 --- a/subu-0/1_doc/to_do.txt +++ /dev/null @@ -1,38 +0,0 @@ -2019-02-05T23:14:40Z - error can cause subu-mk-0 to leave the creating of a subu in an intermediate - state. Rather than bailing on some of the errors we need to clean up instead. - Perhaps the yet to be written subu-rm program will be resilent enough to do - more general cleanup. - -2019-02-23T18:56:31Z - need to modify subu-init to take a configuration file name argument instead of - using a global variabel value. might want to add arguments to other subu - commands also - -2019-03-11T13:48:03Z - in subu.lib.c append cascading rmdir failure mess to useradd failure mess - -2019-03-11T13:48:03Z - want to add subu-type to masteru_subu(), I imagine there will be static, - permanent, and temporary subu types. - -2019-03-12T18:35:06Z - the masteru subu relation should contain the uid of the masteru as - well as the backup type for the subu: git, rdiff, rsync, none. - and the persisitance fo the subu: indefinite, session. - seems that operations need to be logged, in case the db is lost - the transcript can be played back. It should also be possible - to co-opt an existing user as a subu, though, would require - sudo privs. - - need to add messages for subu errors I've added to the end of - the list in subu.lib.c - -2019-03-14T10:43:50Z - - should mod all to he subudb routines to return a message, probably - strdup(sqlite_errmsg(db)), then the callers to these routines can just pass - mess in rather than making up new ones for each situation. The error code - probably already carries the contexts specific message. Or perhaps add - a string cat function for message strings, that would run through a stream - and free the originals. diff --git a/subu-0/1_tests/0_makefile b/subu-0/1_tests/0_makefile deleted file mode 100644 index 67cea25..0000000 --- a/subu-0/1_tests/0_makefile +++ /dev/null @@ -1,32 +0,0 @@ -# src/1_tests/0_makefile - -SHELL=/bin/bash - --include 0_makefile_flags - -MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_cc - -SOURCES=$(wildcard *.c) -HFILES=$(wildcard *.h) - -all: version deps lib execs - -deps: - makeheaders $(SOURCES) $(HFILES) - sed -i '/^ *int *main *(.*)/d' *.h - $(MAKE) $@ - -clean: - $(MAKE) $@ - for i in $(HFILES); do rm $$i; done - -dist-clean: - $(MAKE) $@ - if [ -f subudb ]; then rm subudb; fi - -%:: - $(MAKE) $@ - - - - diff --git a/subu-0/1_tests/0_makefile_flags b/subu-0/1_tests/0_makefile_flags deleted file mode 100644 index 8818691..0000000 --- a/subu-0/1_tests/0_makefile_flags +++ /dev/null @@ -1,31 +0,0 @@ - -# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. -ECHO= echo -#ECHO= echo -e - -# directories used by this makefile, these could all be set to dot for -# the simplest source directory structure - -#LIDBIR, EXECSDIR, HDIR hold the make results that might later be staged -#$(PWD) is the directory that make was called from, this is already build in -#set to dot to use the same directory as the source code -#leave blank to ommit -DEPRDIR= -DOCDIR= -EXECSDIR=. -HDIR=. -LIBDIR=. -TESTDIR=. -TMPDIR=1_tmp -TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) -TRYDIR= - - -# compiler and flags -CC=gcc -CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB -#CFLAGS=-std=gnu11 -fPIC -I. -Werror -LINKFLAGS=-L$(PROJECT_SUBU)/src/$(LIBDIR) -L. -lsubu -lsqlite3 -lsubutests - -LIBFILE=$(LIBDIR)/libtests.a - diff --git a/subu-0/1_tests/1_tmp/makefile_deps b/subu-0/1_tests/1_tmp/makefile_deps deleted file mode 100644 index 9486ae8..0000000 --- a/subu-0/1_tests/1_tmp/makefile_deps +++ /dev/null @@ -1,4 +0,0 @@ -da.cli.o: da.cli.c da.cli.h - -./da : da.cli.o ./libtests.a - gcc -o ./da da.cli.o -L/home/morpheus/subu_land/subu/src/. -L. -lsubu -lsqlite3 -lsubutests diff --git a/subu-0/1_tests/da.cli.c b/subu-0/1_tests/da.cli.c deleted file mode 100644 index 910a15e..0000000 --- a/subu-0/1_tests/da.cli.c +++ /dev/null @@ -1,50 +0,0 @@ -/* -Tests for da. - -*/ - -#include -#include - -int test_da_0(){ - da da0; - da_alloc(&da0, sizeof(int)); // leaves room for 4 ints - int i = 0; - int *pt = da0->base; - // will double, 4 -> 8, then double 8 -> 16 - while( i < 10 ){ - if(da_boundq(&da0, pt)){ - char *old_base = da_expand(&da); - da_rebase(&da, old_base, pt); - } - *pt = i; - i++; - pt++; - } - - bool f0 = da.size == sizof(int) * 16; - bool f1 = 10 == (da.end - da.base) / sizeof(int); - bool f2 = true; - pt = da0->base; - while( i < 10 ){ - f2 = f2 && *pt == i && !da_endq(&da, pt); - i++; - pt++; - } - bool f3 = da_endq(&da, pt); - - return f0 && f1 && f2 && f3; -} - - -int main(){ - - bool da_0_passed = test_da_0(); - if( da_0_passed ){ - printf("da_0_passed"); - return 0; - } - printf("da_0_failed"); - return 1; - -} diff --git a/subu-0/1_tests/libtests.a b/subu-0/1_tests/libtests.a deleted file mode 100644 index 8b277f0..0000000 --- a/subu-0/1_tests/libtests.a +++ /dev/null @@ -1 +0,0 @@ -! diff --git a/subu-0/1_tmp/da.lib.h b/subu-0/1_tmp/da.lib.h deleted file mode 100644 index 4702189..0000000 --- a/subu-0/1_tmp/da.lib.h +++ /dev/null @@ -1,23 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -#include -void daps_map(char **base,char **end_pt,void f(void *)); -#define RETURN(rc) \ - { daps_map(mrs, mrs_end, free); return rc; } -void daps_alloc(char ***base,size_t *s); -#define MK_MRS \ - char **mrs; \ - char **mrs_end; \ - size_t mrs_size; \ - daps_alloc(&mrs, &mrs_size);\ - mrs_end = mrs; -void daps_push(char ***base,char ***pt,size_t *s,char *item); -bool daps_bound(char **base,char **pt,size_t s); -void daps_expand(char ***base,char ***pt,size_t *s); -void da_map(void *base,void *end_pt,void f(void *),size_t item_size); -void da_push(void **base,void **pt,size_t *s,void *item,size_t item_size); -bool da_bound(void *base,void *pt,size_t s); -void da_expand(void **base,void **pt,size_t *s); -void da_alloc(void **base,size_t *s,size_t item_size); -#define INTERFACE 0 diff --git a/subu-0/1_tmp/dbprintf.lib.h b/subu-0/1_tmp/dbprintf.lib.h deleted file mode 100644 index 3056cf6..0000000 --- a/subu-0/1_tmp/dbprintf.lib.h +++ /dev/null @@ -1,3 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -int dbprintf(const char *format,...); diff --git a/subu-0/1_tmp/dispatch.lib.h b/subu-0/1_tmp/dispatch.lib.h deleted file mode 100644 index 07e2271..0000000 --- a/subu-0/1_tmp/dispatch.lib.h +++ /dev/null @@ -1,24 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -#include -int dispatch_exec(char **argv,char **envp); -typedef unsigned int uint; -int dispatch_f_euid_egid(char *fname,int(*f)(void *arg),void *f_arg,uid_t euid,gid_t egid); -int dbprintf(const char *format,...); -int dispatch_f(char *fname,int(*f)(void *arg),void *f_arg); -void dispatch_f_mess(char *fname,int err,char *dispatchee); -#define ERR_DISPATCH_EXEC -1029 -#define ERR_DISPATCH_NULL_EXECUTABLE -1028 -#define ERR_DISPATCH_F_SETEGID -1027 -#define ERR_DISPATCH_F_SETEUID -1026 -#define ERR_DISPATCH_F_FORK -1025 -#define ERR_DISPATCH_NEGATIVE_RETURN_STATUS -1024 -#define ERR_DISPATCH -1024 -typedef struct dispatch_ctx dispatch_ctx; -struct dispatch_ctx { - char *dispatcher; // name of the dispatch function ("dispatch_f", "dispatch_f_euid_egid", etc.) - char *dispatchee; // name of the function being dispatched - int err; // error code as listed below, or status returned from dispatchee -}; -#define INTERFACE 0 diff --git a/subu-0/1_tmp/subu-bind-all.cli.h b/subu-0/1_tmp/subu-bind-all.cli.h deleted file mode 100644 index 7e52675..0000000 --- a/subu-0/1_tmp/subu-bind-all.cli.h +++ /dev/null @@ -1,9 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -#include -#include -int subu_bind_all(char **mess,sqlite3 *db); -#define SUBU_ERR_DB_FILE 8 -extern char DB_File[]; -#define SUBU_ERR_ARG_CNT 1 diff --git a/subu-0/1_tmp/subu-bind.cli.h b/subu-0/1_tmp/subu-bind.cli.h deleted file mode 100644 index af12d61..0000000 --- a/subu-0/1_tmp/subu-bind.cli.h +++ /dev/null @@ -1,7 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -#include -#include -int subu_bind(char **mess,char *masteru_name,char *subu_username,char *subuhome); -#define SUBU_ERR_ARG_CNT 1 diff --git a/subu-0/1_tmp/subu-common.lib.h b/subu-0/1_tmp/subu-common.lib.h deleted file mode 100644 index cfdc520..0000000 --- a/subu-0/1_tmp/subu-common.lib.h +++ /dev/null @@ -1,9 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -extern char Subuland_Extension[]; -typedef unsigned int uint; -extern uint First_Max_Subunumber; -extern uint Subuhome_Perms; -extern char DB_File[]; -#define BUG_SSS_CACHE_RUID 1 -#define INTERFACE 0 diff --git a/subu-0/1_tmp/subu-mk-0.cli.h b/subu-0/1_tmp/subu-mk-0.cli.h deleted file mode 100644 index 487b509..0000000 --- a/subu-0/1_tmp/subu-mk-0.cli.h +++ /dev/null @@ -1,10 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -#include -#include -void subu_err(char *fname,int err,char *mess); -int subu_mk_0(char **mess,sqlite3 *db,char *subuname); -#define SUBU_ERR_DB_FILE 8 -extern char DB_File[]; -#define SUBU_ERR_ARG_CNT 1 diff --git a/subu-0/1_tmp/subu-rm-0.cli.h b/subu-0/1_tmp/subu-rm-0.cli.h deleted file mode 100644 index 070bfe8..0000000 --- a/subu-0/1_tmp/subu-rm-0.cli.h +++ /dev/null @@ -1,10 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -#include -#include -void subu_err(char *fname,int err,char *mess); -int subu_rm_0(char **mess,sqlite3 *db,char *subuname); -#define SUBU_ERR_DB_FILE 8 -extern char DB_File[]; -#define SUBU_ERR_ARG_CNT 1 diff --git a/subu-0/1_tmp/subu.lib.h b/subu-0/1_tmp/subu.lib.h deleted file mode 100644 index 69c5da7..0000000 --- a/subu-0/1_tmp/subu.lib.h +++ /dev/null @@ -1,66 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -typedef unsigned int uint; -#include -typedef struct subudb_subu_element subudb_subu_element; -int subudb_Masteru_Subu_get_subus(sqlite3 *db,char *masteru_name,subudb_subu_element **sa_pt,subudb_subu_element **sa_end_pt); -struct subudb_subu_element { - char *subuname; - char *subu_username; -}; -#include -#include -int subu_bind_all(char **mess,sqlite3 *db); -int subu_bind(char **mess,char *masteru_name,char *subu_username,char *subuhome); -int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); -int subudb_Masteru_Subu_get_subu_username(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username); -int subu_rm_0(char **mess,sqlite3 *db,char *subuname); -int subudb_Masteru_Subu_put(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); -#include -#include -int dispatch_exec(char **argv,char **envp); -#define BUG_SSS_CACHE_RUID 1 -void dispatch_f_mess(char *fname,int err,char *dispatchee); -#define ERR_DISPATCH -1024 -int dispatch_f_euid_egid(char *fname,int(*f)(void *arg),void *f_arg,uid_t euid,gid_t egid); -#include -void daps_map(char **base,char **end_pt,void f(void *)); -#define RETURN(rc) \ - { daps_map(mrs, mrs_end, free); return rc; } -void daps_push(char ***base,char ***pt,size_t *s,char *item); -int dbprintf(const char *format,...); -void daps_alloc(char ***base,size_t *s); -#define MK_MRS \ - char **mrs; \ - char **mrs_end; \ - size_t mrs_size; \ - daps_alloc(&mrs, &mrs_size);\ - mrs_end = mrs; -int subu_mk_0(char **mess,sqlite3 *db,char *subuname); -extern char Subuland_Extension[]; -int db_commit(sqlite3 *db); -int db_rollback(sqlite3 *db); -int subudb_number_set(sqlite3 *db,int n); -int subudb_number_get(sqlite3 *db,int *n); -int db_begin(sqlite3 *db); -extern uint Subuhome_Perms; -extern char DB_File[]; -void subu_err(char *fname,int err,char *mess); -#define SUBU_ERR_BIND 15 -#define SUBU_ERR_N 14 -#define SUBU_ERR_SUBU_NOT_FOUND 13 -#define SUBU_ERR_FAILED_USERDEL 12 -#define SUBU_ERR_FAILED_USERADD 11 -#define SUBU_ERR_BUG_SSS 10 -#define SUBU_ERR_SUBUHOME_EXISTS 9 -#define SUBU_ERR_DB_FILE 8 -#define SUBU_ERR_HOMELESS 7 -#define SUBU_ERR_SUBUNAME_MALFORMED 6 -#define SUBU_ERR_RMDIR_SUBUHOME 5 -#define SUBU_ERR_MKDIR_SUBUHOME 4 -#define SUBU_ERR_MALLOC 3 -#define SUBU_ERR_SETUID_ROOT 2 -#define SUBU_ERR_ARG_CNT 1 -char *userdel_mess(int err); -char *useradd_mess(int err); -#define INTERFACE 0 diff --git a/subu-0/1_tmp/subudb-init.cli.h b/subu-0/1_tmp/subudb-init.cli.h deleted file mode 100644 index 4435103..0000000 --- a/subu-0/1_tmp/subudb-init.cli.h +++ /dev/null @@ -1,11 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -int db_commit(sqlite3 *db); -int db_rollback(sqlite3 *db); -int subudb_schema(sqlite3 *db); -int db_begin(sqlite3 *db); -#include -#include -#define SUBU_ERR_DB_FILE 8 -extern char DB_File[]; diff --git a/subu-0/1_tmp/subudb-number.cli.h b/subu-0/1_tmp/subudb-number.cli.h deleted file mode 100644 index c130fbb..0000000 --- a/subu-0/1_tmp/subudb-number.cli.h +++ /dev/null @@ -1,11 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -int subudb_number_get(sqlite3 *db,int *n); -int subudb_number_set(sqlite3 *db,int n); -#include -#include -#define SUBU_ERR_N 14 -#define SUBU_ERR_DB_FILE 8 -extern char DB_File[]; -#define SUBU_ERR_ARG_CNT 1 diff --git a/subu-0/1_tmp/subudb-rel-get.cli.h b/subu-0/1_tmp/subudb-rel-get.cli.h deleted file mode 100644 index 4f335be..0000000 --- a/subu-0/1_tmp/subudb-rel-get.cli.h +++ /dev/null @@ -1,9 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -int subudb_Masteru_Subu_get_subu_username(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username); -#include -#include -#define SUBU_ERR_DB_FILE 8 -extern char DB_File[]; -#define SUBU_ERR_ARG_CNT 1 diff --git a/subu-0/1_tmp/subudb-rel-put.cli.h b/subu-0/1_tmp/subudb-rel-put.cli.h deleted file mode 100644 index 243b3a9..0000000 --- a/subu-0/1_tmp/subudb-rel-put.cli.h +++ /dev/null @@ -1,8 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -int subudb_Masteru_Subu_put(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); -#include -#include -#define SUBU_ERR_DB_FILE 8 -extern char DB_File[]; diff --git a/subu-0/1_tmp/subudb-rel-rm.cli.h b/subu-0/1_tmp/subudb-rel-rm.cli.h deleted file mode 100644 index 595427f..0000000 --- a/subu-0/1_tmp/subudb-rel-rm.cli.h +++ /dev/null @@ -1,8 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); -#include -#include -#define SUBU_ERR_DB_FILE 8 -extern char DB_File[]; diff --git a/subu-0/1_tmp/subudb-subus.cli.h b/subu-0/1_tmp/subudb-subus.cli.h deleted file mode 100644 index 16310b7..0000000 --- a/subu-0/1_tmp/subudb-subus.cli.h +++ /dev/null @@ -1,14 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -typedef struct subudb_subu_element subudb_subu_element; -int subudb_Masteru_Subu_get_subus(sqlite3 *db,char *masteru_name,subudb_subu_element **sa_pt,subudb_subu_element **sa_end_pt); -struct subudb_subu_element { - char *subuname; - char *subu_username; -}; -#include -#include -#define SUBU_ERR_DB_FILE 8 -extern char DB_File[]; -#define SUBU_ERR_ARG_CNT 1 diff --git a/subu-0/1_tmp/subudb.lib.h b/subu-0/1_tmp/subudb.lib.h deleted file mode 100644 index be73823..0000000 --- a/subu-0/1_tmp/subudb.lib.h +++ /dev/null @@ -1,27 +0,0 @@ -/* This file was automatically generated. Do not edit! */ -#undef INTERFACE -#include -int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); -#include -#include -void da_expand(void **base,void **pt,size_t *s); -bool da_bound(void *base,void *pt,size_t s); -typedef struct subudb_subu_element subudb_subu_element; -int subudb_Masteru_Subu_get_subus(sqlite3 *db,char *masteru_name,subudb_subu_element **sa_pt,subudb_subu_element **sa_end_pt); -void subu_element_free(subudb_subu_element *base,subudb_subu_element *end_pt); -void da_alloc(void **base,size_t *s,size_t item_size); -struct subudb_subu_element { - char *subuname; - char *subu_username; -}; -int subudb_Masteru_Subu_get_subu_username(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username); -int subudb_Masteru_Subu_put(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username); -int subudb_number_set(sqlite3 *db,int n); -int subudb_number_get(sqlite3 *db,int *n); -typedef unsigned int uint; -extern uint First_Max_Subunumber; -int subudb_schema(sqlite3 *db); -int db_rollback(sqlite3 *db); -int db_commit(sqlite3 *db); -int db_begin(sqlite3 *db); -#define INTERFACE 0 diff --git a/subu-0/1_try/split.c b/subu-0/1_try/split.c deleted file mode 100644 index 6d4c6ac..0000000 --- a/subu-0/1_try/split.c +++ /dev/null @@ -1,20 +0,0 @@ -/* -Using preprocessor to make header file? - -gcc -E split.c -DPROTOTYPE - -Outputs source code source comment lines starting with a hash. Resolves all macro commands, -hence the resulting header can not have macro commands. - - */ - -#if PROTOTYPE -##define GREATNESS 21 -int f(int x); -#endif - -#if IMPLEMENTATION -int f(int x){ - return x; -} -#endif diff --git a/subu-0/1_try/split_arg.c b/subu-0/1_try/split_arg.c deleted file mode 100644 index 6d4c6ac..0000000 --- a/subu-0/1_try/split_arg.c +++ /dev/null @@ -1,20 +0,0 @@ -/* -Using preprocessor to make header file? - -gcc -E split.c -DPROTOTYPE - -Outputs source code source comment lines starting with a hash. Resolves all macro commands, -hence the resulting header can not have macro commands. - - */ - -#if PROTOTYPE -##define GREATNESS 21 -int f(int x); -#endif - -#if IMPLEMENTATION -int f(int x){ - return x; -} -#endif diff --git a/subu-0/1_try/subudb b/subu-0/1_try/subudb deleted file mode 100644 index e69de29..0000000 diff --git a/subu-0/1_try/voidptr.c b/subu-0/1_try/voidptr.c deleted file mode 100644 index bd9c3e5..0000000 --- a/subu-0/1_try/voidptr.c +++ /dev/null @@ -1,48 +0,0 @@ -/* -They say a cast is not required passing a typed pointer to a void * argument, -but What about void **? - -gcc -std=gnu11 -o voidptr voidptr.c -voidptr.c: In function ‘main’: -voidptr.c:28:5: warning: passing argument 1 of ‘g’ from incompatible pointer type [-Wincompatible-pointer-types] - g(&pt, y); - ^~~ -voidptr.c:13:15: note: expected ‘void **’ but argument is of type ‘int **’ - void g(void **pt0, void *pt1){ - ~~~~~~~^~~ - -*/ -#include - -int f(void *pt){ - return *(int *)pt; -} - -/* fails -void g(void **pt0, int *pt1){ - *pt0 = pt1; -} -*/ - -// passes -void g(void *pt0, int *pt1){ - *(int **)pt0 = pt1; -} - -int main(){ - int x = 5; - int *xp = &x; - printf("%d\n",f(xp)); - - int y[3]; - y[0] = 10; - y[1] = 11; - y[2] = 12; - - int *pt; - g(&pt, y); - printf("%d\n",*pt); - - printf("that's all folks\n"); - return 0; -} diff --git a/subu-0/common.lib.c b/subu-0/common.lib.c deleted file mode 100644 index 9ce5a27..0000000 --- a/subu-0/common.lib.c +++ /dev/null @@ -1,20 +0,0 @@ - -#include "subu-common.lib.h" - -#if INTERFACE -typedef unsigned int uint; -/* - Fedora 29's sss_cache is checking the inherited uid instead of the effective - uid, so setuid root scripts will fail when calling sss_cache. - - Fedora 29's 'useradd' calls sss_cache, and useradd is called by our setuid root - program subu-mk-0. -*/ -#define BUG_SSS_CACHE_RUID 1 -#endif - -// char *DB_File = "/etc/subudb"; -char DB_File[] = "subudb"; -uint Subuhome_Perms = 0700; -uint First_Max_Subunumber = 114; -char Subuland_Extension[] = "/subuland/"; diff --git a/subu-0/db.lib.c b/subu-0/db.lib.c deleted file mode 100644 index 17a5419..0000000 --- a/subu-0/db.lib.c +++ /dev/null @@ -1,192 +0,0 @@ -/* -The db file is maintained in SQLite - -Because user names of are of limited length, subu user names are always named _s. -A separate table translates the numbers into the subu names. - -The first argument is the biggest subu number in the system, or one minus an -starting point for subu numbering. - -currently a unit converted to base 10 will always fit in a 21 bit buffer. - -Each of these returns SQLITE_OK upon success -*/ -#include "subudb.lib.h" - -#if INTERFACE -#include -#endif - -#include -#include -#include -#include - -//-------------------------------------------------------------------------------- -// sqlite transactions don't nest. There is a way to use save points, but still -// we can't just nest transactions. Instead use these wrappers around the whole -// of something that needs to be in a transaction. -int db_begin(sqlite3 *db){ - return sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL); -} -int db_commit(sqlite3 *db){ - return sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL); -} -int db_rollback(sqlite3 *db){ - return sqlite3_exec(db, "ROLLBACK;", NULL, NULL, NULL); -} - -//-------------------------------------------------------------------------------- -int subudb_schema(sqlite3 *db){ - int rc; - - { // build tables - char sql[] = - "CREATE TABLE Masteru_Subu(masteru_name TEXT, subuname TEXT, subu_username TEXT);" - "CREATE TABLE Attribute_Int(attribute TEXT, value INT);" - ; - rc = sqlite3_exec(db, sql, NULL, NULL, NULL); - if(rc != SQLITE_OK) return rc; - } - - { // data initialization - char *sql = "INSERT INTO Attribute_Int (attribute, value) VALUES ('Max_Subunumber', ?1);"; - sqlite3_stmt *stmt; - sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); - sqlite3_bind_int(stmt, 1, First_Max_Subunumber); - rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - if( rc != SQLITE_DONE ) return rc; - } - - return SQLITE_OK; -} - -//-------------------------------------------------------------------------------- -int subudb_number_get(sqlite3 *db, int *n){ - char *sql = "SELECT value FROM Attribute_Int WHERE attribute = 'Max_Subunumber';"; - sqlite3_stmt *stmt; - sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); - int rc = sqlite3_step(stmt); - if( rc == SQLITE_ROW ){ - *n = sqlite3_column_int(stmt,0); - rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - if( rc != SQLITE_DONE ) return rc; - return SQLITE_OK; - } - // should have a message return, suppose - sqlite3_finalize(stmt); - return SQLITE_NOTFOUND; -} - -int subudb_number_set(sqlite3 *db, int n){ - int rc; - char *sql = "UPDATE Attribute_Int SET value = ?1 WHERE attribute = 'Max_Subunumber';"; - sqlite3_stmt *stmt; - sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); - sqlite3_bind_int(stmt, 1, n); - rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - if( rc == SQLITE_DONE ) return SQLITE_OK; - return rc; -} - -//-------------------------------------------------------------------------------- -// put relation into Masteru_Subu table -int subudb_Masteru_Subu_put(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username){ - char *sql = "INSERT INTO Masteru_Subu VALUES (?1, ?2, ?3);"; - sqlite3_stmt *stmt; - sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); - sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 2, subuname, -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 3, subu_username, -1, SQLITE_STATIC); - int rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - if( rc == SQLITE_DONE ) return SQLITE_OK; - return rc; -} - -//-------------------------------------------------------------------------------- -int subudb_Masteru_Subu_get_subu_username(sqlite3 *db, char *masteru_name, char *subuname, char **subu_username){ - char *sql = "SELECT subu_username FROM Masteru_Subu WHERE masteru_name = ?1 AND subuname = ?2;"; - size_t sql_len = strlen(sql); - sqlite3_stmt *stmt; - int rc; - rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL); - if( rc != SQLITE_OK ) return rc; - sqlite3_bind_text(stmt, 1, masteru_name, strlen(masteru_name), SQLITE_STATIC); - sqlite3_bind_text(stmt, 2, subuname, strlen(subuname), SQLITE_STATIC); - rc = sqlite3_step(stmt); - if( rc == SQLITE_ROW ){ - const char *username = sqlite3_column_text(stmt, 0); - *subu_username = strdup(username); - }else{ - sqlite3_finalize(stmt); - return rc; // woops this needs to return an error!, be sure it is not SQLITE_DONE - } - rc = sqlite3_step(stmt); - if( rc == SQLITE_DONE ) return SQLITE_OK; - return rc; -} - -//-------------------------------------------------------------------------------- - -// we return and array of subudb_subu_info -#if INTERFACE -struct subudb_subu_element{ - char *subuname; - char *subu_username; -}; -#endif - -int subudb_Masteru_Subu_get_subus -( - sqlite3 *db, - char *masteru_name, - da *subus -){ - char *sql = "SELECT subuname, subu_username" - " FROM Masteru_Subu" - " WHERE masteru_name = ?1;"; - size_t sql_len = strlen(sql); - sqlite3_stmt *stmt; - int rc; - rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL); - if( rc != SQLITE_OK ) return rc; - sqlite3_bind_text(stmt, 1, masteru_name, strlen(masteru_name), SQLITE_STATIC); - - da_alloc(subus, sizeof(subudb_subu_element)); - subudb_subu_element *pt = (subudb_subu_element *)subus->base; - rc = sqlite3_step(stmt); - while( rc == SQLITE_ROW ){ - if( da_boundq(subus, pt) ){ - char *old_base = da_expand(subus); - da_rebase(subus, old_base, pt); - } - pt->subuname = strdup(sqlite3_column_text(stmt, 0)); - pt->subu_username = strdup(sqlite3_column_text(stmt, 1)); - rc = sqlite3_step(stmt); - pt++; - } - sqlite3_finalize(stmt); - if( rc != SQLITE_DONE ) return rc; - return SQLITE_OK; -} - -//-------------------------------------------------------------------------------- -int subudb_Masteru_Subu_rm(sqlite3 *db, char *masteru_name, char *subuname, char *subu_username){ - char *sql = "DELETE FROM Masteru_Subu WHERE masteru_name = ?1 AND subuname = ?2 AND subu_username = ?3;"; - size_t sql_len = strlen(sql); - sqlite3_stmt *stmt; - int rc; - rc = sqlite3_prepare_v2(db, sql, sql_len, &stmt, NULL); - if( rc != SQLITE_OK ) return rc; - sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 2, subuname, -1, SQLITE_STATIC); - sqlite3_bind_text(stmt, 3, subu_username, -1, SQLITE_STATIC); - rc = sqlite3_step(stmt); - sqlite3_finalize(stmt); - if( rc == SQLITE_DONE ) return SQLITE_OK; - return rc; -} diff --git a/subu-0/subu-bind-all.cli.c b/subu-0/subu-bind-all.cli.c deleted file mode 100644 index e942909..0000000 --- a/subu-0/subu-bind-all.cli.c +++ /dev/null @@ -1,31 +0,0 @@ -/* -mount all the subu user directories into master's subuland -uses unmount to undo this - -*/ -#include "subu-bind-all.cli.h" -#include -#include - -int main(int argc, char **argv){ - if( argc != 1){ - fprintf(stderr, "%s does not take arguments\n",argv[0]); - return SUBU_ERR_ARG_CNT; - } - - int rc; - sqlite3 *db; - rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); - if( rc != SQLITE_OK ){ - fprintf(stderr, "could not open db file \"%s\"\n", DB_File); - return SUBU_ERR_DB_FILE; - } - - char *mess; - rc = subu_bind_all(&mess, db); - if(rc != 0){ - fprintf(stderr, "subu-bind: %s\n", mess); - return rc; - } - return 0; -} diff --git a/subu-0/subu-bind.cli.c b/subu-0/subu-bind.cli.c deleted file mode 100644 index f315823..0000000 --- a/subu-0/subu-bind.cli.c +++ /dev/null @@ -1,25 +0,0 @@ -/* -mount a subu user directory into master's subuland -uses unmount to undo this - -*/ -#include "subu-bind.cli.h" -#include -#include - -int main(int argc, char **argv){ - - if( argc != 4){ - fprintf(stderr, "usage: %s masteru subu_username subuhome\n",argv[0]); - return SUBU_ERR_ARG_CNT; - } - - int rc; - char *mess; - rc = subu_bind(&mess, argv[1], argv[2], argv[3]); - if(rc != 0){ - fprintf(stderr, "subu-bind: %s\n", mess); - return rc; - } - return 0; -} diff --git a/subu-0/subu-mk-0.cli.c b/subu-0/subu-mk-0.cli.c deleted file mode 100644 index af0888b..0000000 --- a/subu-0/subu-mk-0.cli.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - subu-mk-0 command - -*/ -#include "subu-mk-0.cli.h" -#include -#include - -int main(int argc, char **argv){ - char *command = argv[0]; - if( argc != 2 ){ - fprintf(stderr, "usage: %s subu", command); - return SUBU_ERR_ARG_CNT; - } - char *subuname = argv[1]; - - int rc; - sqlite3 *db; - rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); - if( rc != SQLITE_OK ){ - fprintf(stderr, "error when opening db, %s\n", DB_File); - fprintf(stderr, "sqlite3 says: %s\n", sqlite3_errmsg(db)); - sqlite3_close(db); - return SUBU_ERR_DB_FILE; - } - - char *mess; - rc = subu_mk_0(&mess, db, subuname); - if( rc ){ - subu_err("subu_mk_0", rc, mess); - free(mess); - sqlite3_close(db); - return rc; - } - - rc = sqlite3_close(db); - if( rc != SQLITE_OK ){ - fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db)); - return SUBU_ERR_DB_FILE; - } - return 0; - -} diff --git a/subu-0/subu-rm-0.cli.c b/subu-0/subu-rm-0.cli.c deleted file mode 100644 index a7e5926..0000000 --- a/subu-0/subu-rm-0.cli.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - subu-mk-0 command - -*/ -#include "subu-rm-0.cli.h" -#include -#include - -int main(int argc, char **argv){ - char *command = argv[0]; - if( argc != 2 ){ - fprintf(stderr, "usage: %s subu", command); - return SUBU_ERR_ARG_CNT; - } - char *subuname = argv[1]; - - sqlite3 *db; - { - int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); - if( ret != SQLITE_OK ){ - fprintf(stderr, "error exit, could not open db file \"%s\"\n", DB_File); - return SUBU_ERR_DB_FILE; - }} - - { - char *mess=0; - int ret = subu_rm_0(&mess, db, subuname); - subu_err("subu_rm_0", ret, mess); - free(mess); - return ret; - } -} diff --git a/subu-0/subu.lib.c b/subu-0/subu.lib.c deleted file mode 100644 index 988bd28..0000000 --- a/subu-0/subu.lib.c +++ /dev/null @@ -1,713 +0,0 @@ -/* - sqllite3 is used to maintain the db file, which is currently compiled - in as /etc/subu.db, (or just subu.db for testing). - - masteru is the user who ran this script. The masteru name comes from getuid - and /etc/passwd. - - subu is a subservient user. The subuname is passed in as an argument. - - subu-mk-0 synthesizes a new user user name s, calls useradd to creat - the new uswer account, and enters the relationship between masteru, subu, and - s in the db file. It is this relation in the db file that - associates the subuname with the s user. - - subu-rm-0 uses userdel to delete the related s user account. It - then removes the relaton from the db file. - - subu-mk-0 and subu-rm-0 are setuid root scripts. - -*/ -#include "subu.lib.h" - -// without this #define we get the warning: implicit declaration of function ‘seteuid’/‘setegid’ -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include - -#if INTERFACE -#include -#include -#include -#endif - -//-------------------------------------------------------------------------------- -// dispatched command errors .. should add mkdir and rmdir ... -// -char *useradd_mess(int err){ - if(err <= 0) return NULL; - char *mess; - switch(err){ - case 1: mess = "can't update password file"; break; - case 2: mess = "invalid command syntax"; break; - case 3: mess = "invalid argument to option"; break; - case 4: mess = "UID already in use (and no -o)"; break; - case 5: mess = "undefined"; break; - case 6: mess = "specified group doesn't exist"; break; - case 7: - case 8: mess = "undefined"; break; - case 9: mess = "username already in use"; break; - case 10: mess = "can't update group file:"; break; - case 11: mess = "undefined"; break; - case 12: mess = "can't create home directory"; break; - case 13: mess = "undefined"; break; - case 14: mess = "can't update SELinux user mapping"; break; - default: mess = "undefined"; break; - } - return strdup(mess); -} -char *userdel_mess(int err){ - if(err <= 0) return NULL; - char *mess; - switch(err){ - case 1: mess = "can't update password file"; break; - case 2: mess = "invalid command syntax"; break; - case 3: - case 4: - case 5: mess = "undefined"; break; - case 6: mess = "specified user doesn't exist"; break; - case 7: - case 8: - case 9: mess = "undefined"; break; - case 10: mess = "can't update group file:"; break; - case 11: mess = "undefined"; break; - case 12: mess = "can't remove home directory"; break; - default: mess = "undefined"; break; - } - return strdup(mess); -} - - -//-------------------------------------------------------------------------------- -// -#if INTERFACE -#define SUBU_ERR_ARG_CNT 1 -#define SUBU_ERR_SETUID_ROOT 2 -#define SUBU_ERR_MALLOC 3 -#define SUBU_ERR_MKDIR_SUBUHOME 4 -#define SUBU_ERR_RMDIR_SUBUHOME 5 -#define SUBU_ERR_SUBUNAME_MALFORMED 6 -#define SUBU_ERR_HOMELESS 7 -#define SUBU_ERR_DB_FILE 8 -#define SUBU_ERR_SUBUHOME_EXISTS 9 -#define SUBU_ERR_BUG_SSS 10 -#define SUBU_ERR_FAILED_USERADD 11 -#define SUBU_ERR_FAILED_USERDEL 12 -#define SUBU_ERR_SUBU_NOT_FOUND 13 -#define SUBU_ERR_N 14 -#define SUBU_ERR_BIND 15 -#endif - -void subu_err(char *fname, int err, char *mess){ - if(!mess) mess = ""; - switch(err){ - case 0: return; - case SUBU_ERR_ARG_CNT: - if(mess[0]) - fprintf(stderr, "Incorrect number of arguments, %s", mess); - else - fprintf(stderr, "Incorrect number of arguments.", mess); - break; - case SUBU_ERR_SETUID_ROOT: - fprintf(stderr, "This program must be run setuid root from a user account."); - break; - case SUBU_ERR_MALLOC: - perror(fname); - break; - case SUBU_ERR_DB_FILE: - fprintf(stderr, "error on %s", DB_File); // DB_File is in common - fprintf(stderr, ": %s", mess); - break; - case SUBU_ERR_HOMELESS: - fprintf(stderr,"Masteru, \"%s\", has no home directory", mess); - break; - case SUBU_ERR_SUBUNAME_MALFORMED: - fprintf(stderr, "subuname, \"%s\" is not in [ _.-a-zA-Z0-9]*", mess); - break; - case SUBU_ERR_SUBUHOME_EXISTS: - fprintf(stderr, "a file system object already exists at subuhome, \"%s\"\n", mess); - break; - case SUBU_ERR_MKDIR_SUBUHOME: - fprintf(stderr, "masteru could not make subuhome, \"%s\"", mess); - break; - case SUBU_ERR_BUG_SSS: - perror(fname); - break; - case SUBU_ERR_FAILED_USERADD: - fprintf(stderr, "%s useradd failed\n", mess); - break; - default: - fprintf(stderr, "unknown error code %d\n", err); - } - fputc('\n', stderr); -} - - -//-------------------------------------------------------------------------------- -// a well formed subuname -// returns the length of the subuname, or -1 -// -static int allowed_subuname(char **mess, char *subuname, size_t *subuname_len){ - char *ch = subuname; - size_t i = 0; - while( - *ch - && - ( *ch >= 'a' && *ch <= 'z' - || - *ch >= 'A' && *ch <= 'Z' - || - *ch >= '0' && *ch <= '9' - || - *ch == '-' - || - *ch == '_' - || - *ch == '.' - || - *ch == ' ' - ) - ){ - ch++; - i++; - } - if( !*ch ){ - *subuname_len = i; - return 0; - }else{ - if(mess) *mess = strdup(subuname); - return SUBU_ERR_SUBUNAME_MALFORMED; - } -} - -//-------------------------------------------------------------------------------- -// dispatched functions -// -static int masteru_mkdir_subuhome(void *arg){ - char *subuhome = (char *) arg; - if( mkdir( subuhome, Subuhome_Perms ) == -1 ){ // find subuhome perms in common - perror("masteru_mkdir_subuhome"); - return SUBU_ERR_MKDIR_SUBUHOME; - } - return 0; -} -static int masteru_rmdir_subuhome(void *arg){ - char *subuhome = (char *) arg; - if( rmdir( subuhome ) == -1 ){ // find subuhome perms in common - perror("masteru_rmdir_subuhome"); - return SUBU_ERR_RMDIR_SUBUHOME; - } - return 0; -} - -//-------------------------------------------------------------------------------- -// build strings -// -static int mk_subu_username(char **mess, sqlite3 *db, char **subu_username){ - int rc,n; - db_begin(db); - if( - (rc = subudb_number_get(db, &n)) - || - (rc = subudb_number_set(db, ++n)) - ){ - db_rollback(db); - return SUBU_ERR_DB_FILE; - } - db_commit(db); - - size_t len = 0; - FILE* name_stream = open_memstream(subu_username, &len); - fprintf(name_stream, "s%d", n); - fclose(name_stream); - return 0; -} - -// man page says that getpwuid strings may not be freed, I don't know how long until they -// are overwritten, so I just make my own copies that can be freed -static int uid_to_name_and_home(uid_t uid, char **name, char **home ){ - struct passwd *pw_record_pt = getpwuid(uid); // reading /etc/passwd - *name = strdup(pw_record_pt->pw_name); - *home = strdup(pw_record_pt->pw_dir); - if( !home || !home[0] || (*home)[0] == '(' ) return SUBU_ERR_HOMELESS; - return 0; -} - -static int username_to_home(char *name, char **home ){ - struct passwd *pw_record_pt = getpwnam(name); // reading /etc/passwd - *home = strdup(pw_record_pt->pw_dir); - if( !home || !home[0] || (*home)[0] == '(' ) return SUBU_ERR_HOMELESS; - return 0; -} - -static int mk_subuland(char *masteru_home, char **subuland){ - size_t masteru_home_len = strlen(masteru_home); - char *Subuland_Extension = "/subuland/"; - size_t Subuland_Extension_len = strlen(Subuland_Extension); - *subuland = (char *)malloc( masteru_home_len + Subuland_Extension_len + 1 ); - if(!*subuland) SUBU_ERR_MALLOC; - strcpy(*subuland, masteru_home); - strcpy(*subuland + masteru_home_len, Subuland_Extension); - return 0; -} - -static int mk_subuhome(char *subuland, char *subuname, char **subuhome){ - size_t subuland_len = strlen(subuland); - size_t subuhome_len = subuland_len + strlen(subuname); // subuland has a trailing '/' - *subuhome = (char *)malloc(subuhome_len + 1); - if(!*subuhome) return SUBU_ERR_MALLOC; - strcpy (*subuhome, subuland); - strcpy (*subuhome + subuland_len, subuname); - return 0; -} - - - -//=============================================================================== -int subu_mk_0(char **mess, sqlite3 *db, char *subuname){ - - int rc; - if(mess)*mess = 0; - da resources; - da_alloc(&resources, sizeof(char *)); - - //-------------------------------------------------------------------------------- - size_t subuname_len; - rc = allowed_subuname(mess, subuname, &subuname_len); - if(rc) return rc; - #ifdef DEBUG - dbprintf("subuname is well formed\n"); - #endif - - //-------------------------------------------------------------------------------- - uid_t masteru_uid; - gid_t masteru_gid; - uid_t set_euid; - gid_t set_egid; - { - masteru_uid = getuid(); - masteru_gid = getgid(); - set_euid = geteuid(); - set_egid = getegid(); - #ifdef DEBUG - dbprintf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid); - #endif - if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT; - } - - //-------------------------------------------------------------------------------- - char *masteru_name = 0; - char *masteru_home = 0; - char *subu_username = 0; - char *subuland = 0; - char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir - da_push(&resources, masteru_name); - da_push(&resources, masteru_home); - da_push(&resources, subu_username); - da_push(&resources, subuland); - da_push(&resources, subuhome); - rc = - uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home) - || - mk_subu_username(mess, db, &subu_username) - || - mk_subuland(masteru_home, &subuland) - || - mk_subuhome(subuland, subuname, &subuhome) - ; - if(rc) RETURN(&resources, rc); - #ifdef DEBUG - dbprintf("subu_username, subuland, subuhome: \"%s\"\"%s\"\"%s\"\n", subu_username, subuland, subuhome); - #endif - - //-------------------------------------------------------------------------------- - // By having masteru create the subuhome, we know that masteru has rights to - // to access this directory. This will be the mount point for bindfs - { - struct stat st; - if( stat(subuhome, &st) != -1 ){ - if(mess)*mess = strdup(subuhome); - RETURN(&resources, SUBU_ERR_SUBUHOME_EXISTS); - } - int dispatch_err = dispatch_f_euid_egid - ( - "masteru_mkdir_subuhome", - masteru_mkdir_subuhome, - (void *)subuhome, - masteru_uid, - masteru_gid - ); - if( dispatch_err <= ERR_DISPATCH || dispatch_err == SUBU_ERR_MKDIR_SUBUHOME ){ - #ifdef DEBUG - dispatch_f_mess("dispatch_f_euid_egid", dispatch_err, "masteru_mkdir_subuhome"); - #endif - if(mess)*mess = strdup(subuhome); - RETURN(&resources, SUBU_ERR_MKDIR_SUBUHOME); - } - } - #ifdef DEBUG - dbprintf("made directory \"%s\"\n", subuhome); - #endif - - //-------------------------------------------------------------------------------- - // Make the subservient user account, i.e. the subu - { - #ifdef DEBUG - dbprintf("making subu \"%s\" as user \"%s\"\n", subuname, subu_username); - #endif - #if BUG_SSS_CACHE_RUID - #ifdef DEBUG - dbprintf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n"); - #endif - if( setuid(0) == -1 ) RETURN(&resources, SUBU_ERR_BUG_SSS); - #endif - char *command = "/usr/sbin/useradd"; - char *argv[3]; - argv[0] = command; - argv[1] = subu_username; - argv[2] = (char *) NULL; - char *envp[1]; - envp[0] = (char *) NULL; - int dispatch_err = dispatch_exec(argv, envp); - if( dispatch_err != 0 ){ - #ifdef DEBUG - dispatch_f_mess("dispatch_exec", dispatch_err, command); - #endif - // go back and remove the directory we made in subuland - int dispatch_err_rmdir = dispatch_f_euid_egid - ( - "masteru_rmdir_subuhome", - masteru_rmdir_subuhome, - (void *)subuhome, - masteru_uid, - masteru_gid - ); - #ifdef DEBUG - dispatch_f_mess("dispatch_f_euid_egid", dispatch_err_rmdir, "masteru_rmdir_subuhome"); - #endif - if(mess)*mess = useradd_mess(dispatch_err); - RETURN(&resources, SUBU_ERR_FAILED_USERADD); - } - #ifdef DEBUG - dbprintf("added user \"%s\"\n", subu_username); - #endif - } - - //-------------------------------------------------------------------------------- - #ifdef DEBUG - dbprintf("setting the masteru_name, subuname, subu_username relation\n"); - #endif - { - int rc = subudb_Masteru_Subu_put(db, masteru_name, subuname, subu_username); - if( rc != SQLITE_OK ){ - if(mess)*mess = strdup("insert of masteru subu relation failed"); - RETURN(&resources, SUBU_ERR_DB_FILE); - } - } - #ifdef DEBUG - dbprintf("finished subu-mk-0(%s)\n", subuname); - #endif - RETURN(&resources, 0); -} - -//================================================================================ -int subu_rm_0(char **mess, sqlite3 *db, char *subuname){ - - int rc; - if(mess)*mess = 0; - da resources; - da_alloc(&resources, sizeof(char *)); - - //-------------------------------------------------------------------------------- - size_t subuname_len; - rc = allowed_subuname(mess, subuname, &subuname_len); - if(rc) return rc; - #ifdef DEBUG - dbprintf("subuname is well formed\n"); - #endif - - //-------------------------------------------------------------------------------- - uid_t masteru_uid; - gid_t masteru_gid; - uid_t set_euid; - gid_t set_egid; - { - masteru_uid = getuid(); - masteru_gid = getgid(); - set_euid = geteuid(); - set_egid = getegid(); - #ifdef DEBUG - dbprintf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid); - #endif - if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT; - } - - //-------------------------------------------------------------------------------- - // various strings that we will need - char *masteru_name = 0; - char *masteru_home = 0; - char *subuland = 0; - char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir - da_push(&resources, masteru_name); - da_push(&resources, masteru_home); - da_push(&resources, subuland); - da_push(&resources, subuhome); - rc = - uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home) - || - mk_subuland(masteru_home, &subuland) - || - mk_subuhome(subuland, subuname, &subuhome) - ; - if(rc) RETURN(&resources, rc); - #ifdef DEBUG - dbprintf("masteru_home, subuhome: \"%s\", \"%s\"\n", masteru_home, subuhome); - #endif - - //-------------------------------------------------------------------------------- - // removal from db - char *subu_username = 0; - da_push(&resources, subu_username); - - db_begin(db); - - rc = subudb_Masteru_Subu_get_subu_username(db, masteru_name, subuname, &subu_username); - if( rc != SQLITE_OK ){ - if(mess) *mess = strdup("subu requested for removal not found under this masteru in db file"); - rc = SUBU_ERR_SUBU_NOT_FOUND; - db_rollback(db); - RETURN(&resources, rc); - } - #ifdef DEBUG - printf("subu_username: \"%s\"\n", subu_username); - #endif - - rc = subudb_Masteru_Subu_rm(db, masteru_name, subuname, subu_username); - if( rc != SQLITE_OK ){ - if(mess)*mess = strdup("removal of masteru subu relation failed"); - db_rollback(db); - RETURN(&resources, SUBU_ERR_DB_FILE); - } - #ifdef DEBUG - dbprintf("removed the masteru_name, subuname, subu_username relation\n"); - #endif - - rc = db_commit(db); - if( rc != SQLITE_OK ){ - if(mess)*mess = strdup("removal of masteru subu relation in unknown state, exiting"); - RETURN(&resources, SUBU_ERR_DB_FILE); - } - - // even after removing the last masteru subu relation, we still do not remove - // the max subu count. Hence, a masteru will keep such for a life time. - - - //-------------------------------------------------------------------------------- - // Only masteru can remove directories from masteru/subuland, so we switch to - // masteru's uid to perform the rmdir. - // - { - #ifdef DEBUG - dbprintf("as masteru, removing the directory \"%s\"\n", subuhome); - #endif - int dispatch_err = dispatch_f_euid_egid - ( - "masteru_rmdir_subuhome", - masteru_rmdir_subuhome, - (void *)subuhome, - masteru_uid, - masteru_gid - ); - if( dispatch_err <= ERR_DISPATCH || dispatch_err == SUBU_ERR_RMDIR_SUBUHOME ){ - #ifdef DEBUG - dispatch_f_mess("dispatch_f_euid_egid", dispatch_err, "masteru_rmdir_subuhome"); - #endif - if(mess)*mess = strdup(subuhome); - RETURN(&resources, SUBU_ERR_RMDIR_SUBUHOME); - } - } - - //-------------------------------------------------------------------------------- - // Delete the subservient user account - { - #ifdef DEBUG - dbprintf("deleting user \"%s\"\n", subu_username); - #endif - #if BUG_SSS_CACHE_RUID - #ifdef DEBUG - dbprintf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n"); - #endif - if( setuid(0) == -1 ){ - RETURN(&resources, SUBU_ERR_BUG_SSS); - } - #endif - char *command = "/usr/sbin/userdel"; - char *argv[4]; - argv[0] = command; - argv[1] = subu_username; - argv[2] = "-r"; - argv[3] = (char *) NULL; - char *envp[1]; - envp[0] = (char *) NULL; - int dispatch_err = dispatch_exec(argv, envp); - if( dispatch_err != 0 ){ - #ifdef DEBUG - dispatch_f_mess("dispatch_exec", dispatch_err, command); - #endif - if(mess)*mess = userdel_mess(dispatch_err); - RETURN(&resources, SUBU_ERR_FAILED_USERDEL); - } - #ifdef DEBUG - dbprintf("deleted user \"%s\"\n", subu_username); - #endif - } - - #ifdef DEBUG - dbprintf("finished subu-rm-0(%s)\n", subuname); - #endif - RETURN(&resources, 0); -} - -//================================================================================ -// identifies masteru, the bindfs maps each subu_user's home to its mount point -// in subuland. -int subu_bind(char **mess, char *masteru_name, char *subu_username, char *subuhome){ - - int rc; - if(mess)*mess = 0; - da resources; - da_alloc(&resources, sizeof(char *)); - - // lookup the subu_user_home - char *subu_user_home = 0; - da_push(&resources, subu_user_home); - - rc = username_to_home(subu_username, &subu_user_home); - if( rc ){ - if(mess) *mess = strdup("in subu_bind, subu user home directory lookup in /etc/passwd failed."); - RETURN(&resources, rc); - } - - size_t len = 0; - char *map = 0; - da_push(&resources, map); - - FILE* map_stream = open_memstream(&map, &len); - fprintf(map_stream, "--map=%s/%s:@%s/@%s", subu_username, masteru_name, subu_username, masteru_name); - fclose(map_stream); - - char *command = "/usr/bin/bindfs"; - char *argv[5]; - argv[0] = command; - argv[1] = map; - argv[2] = subu_user_home; - argv[3] = subuhome; - argv[4] = (char *) NULL; - char *envp[1]; - envp[0] = (char *) NULL; - int dispatch_err = dispatch_exec(argv, envp); - if( dispatch_err != 0 ){ - #ifdef DEBUG - dispatch_f_mess(command, dispatch_err, "dispatch_exec"); - #endif - if(mess)*mess = strdup("bind failed"); - RETURN(&resources, SUBU_ERR_BIND); - } - #ifdef DEBUG - dbprintf("mapped \"%s\" as \"%s\"\n", subu_user_home, subuhome); - #endif - RETURN(&resources, 0); -} - -int subu_bind_all(char **mess, sqlite3 *db){ - - int rc; - if(mess)*mess = 0; - da resources; - da_alloc(&resources, sizeof(char *)); - - //-------------------------------------------------------------------------------- - uid_t masteru_uid; - gid_t masteru_gid; - uid_t set_euid; - gid_t set_egid; - { - masteru_uid = getuid(); - masteru_gid = getgid(); - set_euid = geteuid(); - set_egid = getegid(); - #ifdef DEBUG - dbprintf("masteru_uid %u, masteru_gid %u, set_euid %u set_egid %u\n", masteru_uid, masteru_gid, set_euid, set_egid); - #endif - if( masteru_uid == 0 || set_euid != 0 ) return SUBU_ERR_SETUID_ROOT; - } - - //-------------------------------------------------------------------------------- - // various strings that we will need - char *masteru_name = 0; - char *masteru_home = 0; - char *subuland = 0; - da_push(&resources, masteru_name); - da_push(&resources, masteru_home); - da_push(&resources, subuland); - rc = - uid_to_name_and_home(masteru_uid, &masteru_name, &masteru_home) - || - mk_subuland(masteru_home, &subuland) - ; - if(rc) RETURN(&resources, rc); - #ifdef DEBUG - if(masteru_name) - dbprintf("masteru_name: \"%s\"", masteru_name); - else - dbprintf("masteru_name unknown"); - if(subuland) - dbprintf("subuland: \"%s\"", subuland); - else - dbprintf("subuland unknown"); - dbprintf("\n"); - #endif - - //-------------------------------------------------------------------------------- - da subus; - rc = subudb_Masteru_Subu_get_subus(db, masteru_name, &subus); - if( rc != SQLITE_OK ){ - if(mess)*mess = strdup("db access failed when fetching a list of subus"); - return rc; - } - // a limitation of our error reporting approach is that we can only - // return one error, but here is a loop that might generate many - rc = 0; - char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir - uint err_cnt = 0; - subudb_subu_element *pt = (subudb_subu_element *)(subus.base); - while( !da_endq(&subus,pt) ){ - rc = mk_subuhome(subuland, pt->subuname, &subuhome); - #ifdef DEBUG - if(subuhome) - dbprintf("subuhome: \"%s\"\n", subuhome); - else - dbprintf("subuhome unknown \n"); - #endif - if(!rc) rc = subu_bind(NULL, masteru_name, pt->subu_username, subuhome); - if(rc) err_cnt++; - free(subuhome); - subuhome=0; - pt++; - } - if(err_cnt==1){ - RETURN(&resources, rc); - } - if(err_cnt > 1){ - *mess = strdup("multiple errors occured while binding subus"); - RETURN(&resources, SUBU_ERR_BIND); - } - RETURN(&resources, 0); -} diff --git a/subu-0/subudb-init.cli.c b/subu-0/subudb-init.cli.c deleted file mode 100644 index 714c7e4..0000000 --- a/subu-0/subudb-init.cli.c +++ /dev/null @@ -1,26 +0,0 @@ -/* -This command initializes the db file. - -*/ -#include "subudb-init.cli.h" -#include - -int main(){ - sqlite3 *db; - if( sqlite3_open(DB_File, &db) != SQLITE_OK ){ - fprintf(stderr, "error exit, could not open db file \"%s\"\n", DB_File); - return SUBU_ERR_DB_FILE; - } - db_begin(db); - if( subudb_schema(db) != SQLITE_OK ){ - db_rollback(db); - fprintf(stderr, "error exit, opened db file but could not build schema\n"); - return SUBU_ERR_DB_FILE; - } - db_commit(db); - if( sqlite3_close(db) != SQLITE_OK ){ - fprintf(stderr, "error exit, could not close the db\n"); - return SUBU_ERR_DB_FILE; - } - return 0; -} diff --git a/subu-0/subudb-number.cli.c b/subu-0/subudb-number.cli.c deleted file mode 100644 index 60304e3..0000000 --- a/subu-0/subudb-number.cli.c +++ /dev/null @@ -1,63 +0,0 @@ -/* -Set or get a new maximum subu number. Currently doesn't do the setting part. - -*/ -#include "subudb-number.cli.h" -#include -#include -#include - -int main(int argc, char **argv){ - - if( argc < 1 || argc > 2){ - fprintf(stderr, "usage: %s [n]\n",argv[0]); - return SUBU_ERR_ARG_CNT; - } - - int rc; - sqlite3 *db; - rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); - if( rc != SQLITE_OK ){ - fprintf(stderr, "error exit, could not open db file\n"); - sqlite3_close(db); - return SUBU_ERR_DB_FILE; - } - - // then arg[1] holds a number to set the max to - if(argc == 2){ - long int i = strtol(argv[1], NULL, 10); - if( i < 0 ){ - fprintf(stderr, "n must be positive\n"); - sqlite3_close(db); - return SUBU_ERR_N; - } - if( i > INT_MAX ){ - fprintf(stderr, "n is too big, max supported by this program is %d\n", INT_MAX); - sqlite3_close(db); - return SUBU_ERR_N; - } - rc = subudb_number_set(db, i); - if( rc != SQLITE_OK ){ - fprintf(stderr, "couldn't set Max_Subunumber: %s\n", sqlite3_errmsg(db)); - return SUBU_ERR_N; - } - } - - // read and print the current max - int n; - rc = subudb_number_get(db, &n); - if( rc == SQLITE_OK ){ - printf("%d\n", n); - }else{ - fprintf(stderr, "couldn't get Max_Subunumber: %s\n", sqlite3_errmsg(db)); - sqlite3_close(db); - return SUBU_ERR_DB_FILE; - } - rc = sqlite3_close(db); - if( rc != SQLITE_OK ){ - fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db)); - return SUBU_ERR_DB_FILE; - } - return 0; - -} diff --git a/subu-0/subudb-rel-get.cli.c b/subu-0/subudb-rel-get.cli.c deleted file mode 100644 index 9828b15..0000000 --- a/subu-0/subudb-rel-get.cli.c +++ /dev/null @@ -1,42 +0,0 @@ -/* -get the username from the db file -for testing subudb_Masteru_Subu_get_subu_username - -*/ -#include "subudb-rel-get.cli.h" -#include -#include - -int main(int argc, char **argv){ - - if(argc != 3){ - fprintf(stderr, "usage: %s masteru_name subuname\n", argv[0]); - return SUBU_ERR_ARG_CNT; - } - - int rc; - sqlite3 *db; - rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); - if( rc != SQLITE_OK ){ - fprintf(stderr, "could not open db file \"%s\"\n", DB_File); - return SUBU_ERR_DB_FILE; - } - - char *masteru_name = argv[1]; - char *subuname = argv[2]; - char *subu_username; - - int ret = subudb_Masteru_Subu_get_subu_username(db, masteru_name, subuname, &subu_username); - if( ret != SQLITE_DONE ){ - fprintf(stderr, "subudb_Masteru_Subu_get_subu_username indicates failure by returning %d\n",ret); - fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db)); - return SUBU_ERR_DB_FILE; - } - ret = sqlite3_close(db); - if( ret != SQLITE_OK ){ - fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret); - fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db)); - return SUBU_ERR_DB_FILE; - } - return 0; -} diff --git a/subu-0/subudb-rel-put.cli.c b/subu-0/subudb-rel-put.cli.c deleted file mode 100644 index b19896e..0000000 --- a/subu-0/subudb-rel-put.cli.c +++ /dev/null @@ -1,40 +0,0 @@ -/* -puts a relation in the masteru/subu table - -*/ -#include "subudb-rel-put.cli.h" -#include -#include - -int main(int argc, char **argv){ - - if(argc != 4){ - fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]); - return 1; - } - char *masteru_name = argv[1]; - char *subuname = argv[2]; - char *subu_username = argv[3]; - - sqlite3 *db; - { - int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); - if( ret != SQLITE_OK ){ - fprintf(stderr, "could not open db file \"%s\"\n", DB_File); - return SUBU_ERR_DB_FILE; - }} - - int ret = subudb_Masteru_Subu_put(db, masteru_name, subuname, subu_username); - if( ret != SQLITE_OK ){ - fprintf(stderr, "subudb_Masteru_Subu_put indicates failure by returning %d\n",ret); - fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db)); - return SUBU_ERR_DB_FILE; - } - ret = sqlite3_close(db); - if( ret != SQLITE_OK ){ - fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret); - fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db)); - return SUBU_ERR_DB_FILE; - } - return 0; -} diff --git a/subu-0/subudb-rel-rm.cli.c b/subu-0/subudb-rel-rm.cli.c deleted file mode 100644 index 3d15ca9..0000000 --- a/subu-0/subudb-rel-rm.cli.c +++ /dev/null @@ -1,41 +0,0 @@ -/* -puts a relation in the masteru/subu table - -*/ -#include "subudb-rel-rm.cli.h" -#include -#include - -int main(int argc, char **argv){ - - if(argc != 4){ - fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]); - return 1; - } - char *masteru_name = argv[1]; - char *subuname = argv[2]; - char *subu_username = argv[3]; - - sqlite3 *db; - { - int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); - if( ret != SQLITE_OK ){ - fprintf(stderr, "could not open db file \"%s\"\n", DB_File); - return SUBU_ERR_DB_FILE; - }} - - int ret = subudb_Masteru_Subu_rm(db, masteru_name, subuname, subu_username); - if( ret != SQLITE_DONE ){ - fprintf(stderr, "subudb_Masteru_Subu_rm indicates failure by returning %d\n",ret); - fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db)); - printf("put failed\n"); - return 2; - } - ret = sqlite3_close(db); - if( ret != SQLITE_OK ){ - fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret); - fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db)); - return SUBU_ERR_DB_FILE; - } - return 0; -} diff --git a/subu-0/subudb-subus.cli.c b/subu-0/subudb-subus.cli.c deleted file mode 100644 index be3af20..0000000 --- a/subu-0/subudb-subus.cli.c +++ /dev/null @@ -1,47 +0,0 @@ -/* -Set or get a new maximum subu number. Currently doesn't do the setting part. - -*/ -#include "subudb-subus.cli.h" -#include -#include -#include - -int main(int argc, char **argv){ - - if( argc != 2){ - fprintf(stderr, "usage: %s masteru_name\n",argv[0]); - return SUBU_ERR_ARG_CNT; - } - char *masteru_name = argv[1]; - - int rc; - sqlite3 *db; - rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL); - if( rc != SQLITE_OK ){ - fprintf(stderr, "error exit, could not open db file\n"); - sqlite3_close(db); - return SUBU_ERR_DB_FILE; - } - - subudb_subu_element *sa; - subudb_subu_element *sa_end; - rc = subudb_Masteru_Subu_get_subus(db, masteru_name, &sa, &sa_end); - if( rc == SQLITE_OK ){ - subudb_subu_element *pt = sa; - while( pt != sa_end ){ - printf("%s %s\n", pt->subuname, pt->subu_username); - pt++; - } - rc = sqlite3_close(db); - if( rc != SQLITE_OK ){ - fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db)); - return SUBU_ERR_DB_FILE; - } - return 0; - } - fprintf(stderr, "lookup failed %s\n", sqlite3_errmsg(db)); - sqlite3_close(db); - return SUBU_ERR_DB_FILE; - -} diff --git a/subu-1/subu-mk.py b/subu-1/subu-mk.py deleted file mode 100644 index 203fab4..0000000 --- a/subu-1/subu-mk.py +++ /dev/null @@ -1,299 +0,0 @@ -#!/usr/bin/python -# see the help option for syntax -# this script must be run from root or sudo -# -# on Fedora 29 os.getresuid returned all zeros for a script run from sudo. -# Hence, I am using the environment variable SUDO_USER - -import getpass -import os -import sys -import libuser -from __future__ import print_function - -command = os.path.base(argv[0]) - -#-------------------------------------------------------------------------------- -# utilities -# -def prn(str): - print(str,end='') - -#-------------------------------------------------------------------------------- -# help -# -def help(): - print( command + -""" [=help] [=version] [shell=][owner=] [subu=] -Makes a subservient user. -If no arguments are given, or if =help is given, this message is printed. -When this command is invoked through sudo, $SUDO_USER is taken as the owner's username. -Otherwise, when invoked directly from root, the owner= option must be provided. -The subu-username argument is the username for the new subservient user -The the new subu home directory is created in /home/owner/subu/. -Facls are set to give the owner access to the new subu's home directory. -The shell option is not implemented yet. Probably need a number of other options also. -""" - ) - -def version(): - print(" version 0") - -#-------------------------------------------------------------------------------- -# a manager for handling error messages -# -class class_err: -""" -An error record has the form [flag, message, args] - class is fatal, warning, info [currently not implemented] - flag is true if an error has occured [need to change this to a count] - args is an array of strings to be given after the error message is printed. - -The dict holds named error records. - -register() is used to name and place error records in the dict. register() is -typically called multiple times to initialize and error instance. - -tattle() is used by the program at run time in order to signal errors. - -has_error() returns true if tattle was ever called - -report() prints an error report. When errors have occured this - -vector() [unimplemented] returns a bit vector with one bit per fatal error -record, in the order they appear in the dictionary. The bit is set if the error -ever occured. - -We check for as many errors as is convenient to do so rather than stopping on -the first error. -""" - - # field offsets into the error record - flag_dex = 0; - message_dex = 1; - args_dex = 2; - - def __init__(self): - self.total_cnt = 0 - self.dict = {} - - def register(name, message): - self.dict[name] = [False, message, []] - - def tattle(name, *args): - self.total_cnt += 1 - if name in self.dict: - self.dict[name][0] = True - self.dict[name][2].extend(args) - - def report(): - if self.total_cnt: - for k,v in self.dict.items(): - if v[self.flag_dex]: - print(v[self.message_dex],end='') - args = v[self.args_dex] - if length(args) : - print(args[0],end='') - for arg in args[1:]: - print( " " + arg, end='') - print() - -#-------------------------------------------------------------------------------- -# parse the command line -# -err.register( - 'impossible_split', - "It is not possible, yet this token split into other than one or two pieces: " - ) -err.register( - 'lone_delim', - "No spaces should appear around the '=' delimiter." - ) - -args = sys.argv[1:] -if len(args) == 0 : - version() - help() - exit(1) - -#create a dictionary based on the command line arguments -arg_dict = {} -subu_cnt = 0 -delim = '=' -for token in args: - token_pair = split(token, delim); - if len(token_pair) == 1 : #means there was no '=' in the token - arg_dict['subu'] = token_pair - subu_cnt++ - elif len(token_pair) == 2 : - if token_pair[0] == '' and token_pair[1] == '' : - err.tattle('lone_delim') - elif token_pair[1] == '' : # then has trailing delim, will treat the same as a leading delim - arg_dict[token_pair[0]] = None - elif token_pair[0] == '' : # then has leading delim - arg_dict[token_pair[1]] = None - else: - arg_dict[token_pair[0]] = token_pair[1] - else: - err.tattle('impossible_split', token) - -if not arg_dict or arg_dict.get('help'): - help() - err.report() - exit(1) - -if arg_dict.get('version'): - version() - -#-------------------------------------------------------------------------------- -# check that the command line arguments are well formed. -# -err.register( - 'too_many_args', - command + " takes at most one non-option argument, but we counted: " - ) -err.register( - 'no_subu' - command + " missing subservient username." - ) -err.register( - 'bad_username' - "Usernames match ^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$, but found: " - ) -err.register( - 'unknown_option' - command + " doesn't implement option: " - ) - -subu = arg_dict.get('subu') -if subu_cnt > 1: - err.tattle('too_many_args') -elif not subu - err.tattle('no_subu') -elif not re.match("^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$", subu) - err.tattle('bad_username', subu) - -for k in arg_dict: - if k not in ['help', 'version', 'shell', 'owner', 'subu'] : - err.tattle('unkown_option', k) - -if arg_dict.get('shell') : - print "shell option aint implemented yet" - - - -#-------------------------------------------------------------------------------- -# check that we have root privilege -# -err.register( - 'not_root' - command + "requires root privilege" - ) - -uid = os.getuid() -if uid != 0 : - err.tattle('not root') - -username = getpass.getuser() -sudo_caller_username = os.environ.get('SUDO_USER') - -if !sudo_caller_username - if username not == "root": - err.tattle('not_root') - elif: - owner - - - def has_error(*errs): - return self.cnt > 0 - - - -#----- - - - - -#-------------------------------------------------------------------------------- -# pull out the owner_dir and subu_dir -# -admin= libuser.admin() - -err_arg_form = class_err() -err_arg_form.register('too_many', "too many semicolon delineated parts in") - -owner_parts = args[0].split(":") -subu_parts = args[1].split(":") - -owner_user_name = owner_parts[0] -if not owner_user_name: - err_arg_form.tattle('owner_user_name_missing', args[0]) -else: - owner_user = admin.lookupUserByName(owner_user_name) - if owner_user == None: - err_arg_form.tattle('no_such_user_name', owner_user_name) - else: - - -subu_user_name = subu_parts[0] - - -if length(owner_parts) > 2: - err_arg_form.tattle('too_many', args[0]) -elif length(owner_parts) == 2: - owner_dir = owner_parts[1] -else # get the home directory - - - - - -#-------------------------------------------------------------------------------- -# set the home directory -# -if len(args) > args_dir_index: - dir = args[args_dir_index] -else: - dir = os.getcwd() - -home = dir + "/" + name -home_flag = not os.path.exists(home) - -#-------------------------------------------------------------------------------- -# create the user, setfacls -# -err_cnt = 0 -name_available_flag = False - -if name_flag: - admin = libuser.admin() - name_available_flag = name not in admin.enumeratedUsers() - -if owner_flag and name_flag and name_available_flag and home_flag : - user = admin.initUser(name) - user[libuser.HOMEDIRECTORY] = home - if opt_shell : user[libuser.SHELL] = opt_shell - admin.addUser(user) - #setfacl -m d:u:plato:rwx,u:plato:rwx directory - setfacl = "setfacl -m d:u:" + name + ":rwx,u:" + name + ":rwx " + home - exit(0) - -#-------------------------------------------------------------------------------- -# error return -# .. need to grab the return code from setfacl above and delete the user if it fails -# -err_flags = 0 -if not owner_flag : - err_flags |= 2**2 - print "missing owning username argument" -if not name_flag : - err_flags |= 2**3 - print name + "missing subservient username argument" -if not name_available_flag : - err_flags |= 2**4 - print name + "specified subservient username already exists" -if not home_flag : - err_flags |= 2**5 - print "user home directory already exists" - -exit(err_flags) diff --git a/temp b/temp new file mode 100644 index 0000000..10ac0e2 --- /dev/null +++ b/temp @@ -0,0 +1,23 @@ +./tool/lib/makefile_cc: @echo "TOOLSDIR: " $(TOOLSDIR) +./module/db/0_makefile:SUID_TOOL=$(TOOLSDIR)/bin/setuid_root.sh +./module/db/0_makefile: @echo "SUID_TOOL: " $(SUID_TOOL) +./module/db/0_makefile: @echo "-> $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all" +./module/db/0_makefile: cat $(SUID_TOOL) +./module/db/0_makefile: sudo $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all +./module/db/0_makefile-flags:TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) +./module/subu-0/1_tests/0_makefile_flags:TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) +./module/subu-0/0_makefile:SUID_TOOL=$(TOOLSDIR)/bin/setuid_root.sh +./module/subu-0/0_makefile: @echo "SUID_TOOL: " $(SUID_TOOL) +./module/subu-0/0_makefile: @echo "-> $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all" +./module/subu-0/0_makefile: cat $(SUID_TOOL) +./module/subu-0/0_makefile: sudo $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all +./module/subu-0/0_makefile-flags:TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) +./module/tranche/deprecated/0_makefile:SUID_TOOL=$(TOOLSDIR)/bin/setuid_root.sh +./module/tranche/deprecated/0_makefile: @echo "SUID_TOOL: " $(SUID_TOOL) +./module/tranche/deprecated/0_makefile: @echo "-> $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all" +./module/tranche/deprecated/0_makefile: cat $(SUID_TOOL) +./module/tranche/deprecated/0_makefile: sudo $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all +./module/tranche/deprecated/0_makefile-flags:TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) +./module/tranche/makefile-flags:TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) +./module/da/test/makefile-flags:TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) +./module/da/makefile-flags:TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) diff --git a/tool/bin/gitadd b/tool/bin/gitadd new file mode 100755 index 0000000..9355724 --- /dev/null +++ b/tool/bin/gitadd @@ -0,0 +1,5 @@ +#!/bin/bash +set -x +make dist-clean +git add "$@" + diff --git a/tool/bin/makeheaders b/tool/bin/makeheaders new file mode 100755 index 0000000..a50d9a0 Binary files /dev/null and b/tool/bin/makeheaders differ diff --git a/tool/bin/setuid_root.sh b/tool/bin/setuid_root.sh new file mode 100755 index 0000000..aedd564 --- /dev/null +++ b/tool/bin/setuid_root.sh @@ -0,0 +1,4 @@ +#!/bin/bash +chown root "$@" && \ +chmod u+rsx,u-w,go+rx-s-w "$@" + diff --git a/tool/bin/tranche b/tool/bin/tranche new file mode 100755 index 0000000..eb63c28 Binary files /dev/null and b/tool/bin/tranche differ diff --git a/tool/doc/makefile.txt b/tool/doc/makefile.txt new file mode 100644 index 0000000..722de84 --- /dev/null +++ b/tool/doc/makefile.txt @@ -0,0 +1,91 @@ +these are the comments from my RT makefile: + +# +# 2010 11 20 TWL Created +# 2011 05 26 TWL Modified to generalize +# 2012 02 23 NLS Add ECHO variable to use on different environnement +# corrected setup macro --> add include directory in the path to copy +# corrected install macro --> change the name of installed library : lib$(LIB)$(LIBSUFFIX) +# changed DOC_DIR directory name to 5_documents +# 2012 02 23 TWL removed LIB variable which is now set from the command line so +# so that all source dirs can use the same makefile +# 2012 02 23 TWL added target make dist_clean which also deletes the 2_makefile_deps file +# 2012 04 11 AWW added creation of temporary disk before each test is run +# 2012 06 05 TWL moved tests and try .cc files to directories. caused rtmake tests to +# dist_clean and make deps +# +# +#---------------------------------------------------------------------------- +# use this makefile to compile and test the code: +# +# for a first time run, or for regression use the following: +# +# $ make setup # makes the directories, though should already exist +# $ make regress +# +# the usual development workflow makes use of these: +# +# $ make deps # only when needed, for example if headers includes change or new files introduced +# $ cd tests; make deps # only when needed +# $ make lib # this makes the local library +# $ make tests # this updates tests and compiles +# $ make clean # deletes the .o files and library to force a recompile +# $ cd 1_tests; make clean +# +# for a release of a component +# +# $ make regress +# $ make install # this will only work if all the tests in 1_tests are passing +# +# before a checkin +# +# $ make dist_clean # will also clean the tests and try directories +# +# .lib.cc c++ files taken as source of object files for local build library +# .exl.cc c++ files taken to have main calls and are linked against local build libary +# .ex.cc c++ files taken to have main calls and are not linked against the local build library +# there are no rules for other files in this makefile +# +# about dependencies +# The makefile has no way of knowing if an edit changed the dependencies. Often they do not +# and it would be unwieldy to make the deps every time. Hence *the programmer* must delete +# the deps file if he has made any changes that change the dependencies. +# +# The makefile will make the 2_makefile_deps if the file is missing. +# +# +# about testing +# +# the name of the directory you run make in is taken to also be: the name of the library, +# the name of the main include file (with a .h added), and the name of the include directory +# where the individual headers are found. It is called LIB +# +# test programs are kept in a subdirectory called 1_tests, and are either .exl.cc, ex.cc, +# .sh files. When 'make tests' target is invoked they are all run. Test executables return 0 +# if the test fails, non-zero otherwise. +# +# to remove a test from the pool move it into the subdirectory in 1_tests, 9_broken, +# 5_more_tests of 5_scratch. broken tests are things that are known but must be fixed +# before a release. 5_more_tests are tests being worked on. 5_scratch is stuff that is +# probably going to be deleted. if there is a 5_deprecated, that is for good stuff but it +# is no longer used for some reason or other. +# +# There is a standard source code template and a +# messaging convention. Also, the names, by convention,are test_xxxx_ where xxx is a +# hexadecimal series nummber. If all the test executables pass the file 1_TESTS_PASSED is +# left in the directory. Otherwise the file 1_TESTS_FAILED is left in the directory. +# +# about release directory +# +# this is set in the ApplicationBase variable by rt_init +# +# after the tests pass stuff might be copied to the release directory using +# +# make install +# +# the release directory must have these subdirectories: +# +# bin documents include src +# +# +# diff --git a/tool/doc/makeheaders-notes.txt b/tool/doc/makeheaders-notes.txt new file mode 100644 index 0000000..5aebda9 --- /dev/null +++ b/tool/doc/makeheaders-notes.txt @@ -0,0 +1,16 @@ + +This worked to force the include to be part of the interface: + +#if INTERFACE +#include +#endif + +But this did not: + +#if INTERFACE + #include +#endif + +makeheaders looks to be sensitive to indentation + + diff --git a/tool/doc/makeheaders.html b/tool/doc/makeheaders.html new file mode 100644 index 0000000..289da58 --- /dev/null +++ b/tool/doc/makeheaders.html @@ -0,0 +1,1150 @@ + +The Makeheaders Program + +

The Makeheaders Program

+ + +

+This document describes makeheaders, +a tool that automatically generates “.h” +files for a C or C++ programming project. +

+ + +

Table Of Contents

+ + +

1.0 Background

+ +

+A piece of C source code can be one of two things: +a declaration or a definition. +A declaration is source text that gives information to the +compiler but doesn't directly result in any code being generated. +A definition is source text that results in executable machine +instructions or initialization data. +(These two terms are sometimes used inconsistently by other authors. +In particular, many people reverse the meanings of these words when +discussing Pascal or Ada code. +The meanings described here are the same as used in the ANSI-C +standards document.) +

+ +

+Declarations in C include things such as the following: +

    +
  • Typedefs. +
  • Structure, union and enumeration declarations. +
  • Function and procedure prototypes. +
  • Preprocessor macros and #defines. +
  • extern” variable declarations. +
+

+ +

+Definitions in C, on the other hand, include these kinds of things: +

    +
  • Variable definitions. +
  • The bodies of functions and procedures. +
  • Initialization data. +
+

+ +

+The distinction between a declaration and a definition is common in +modern software engineering. +Another way of looking at the difference is that the declaration +is the interface and the definition is the implementation. +

+ +

+In C programs, it has always been the tradition that declarations are +put in files with the “.h” suffix and definitions are +placed in “.c” files. +The .c files contain “#include” preprocessor statements +that cause the contents of .h files to be included as part of the +source code when the .c file is compiled. +In this way, the .h files define the interface to a subsystem and +the .c files define how the subsystem is implemented. +

+ + +

1.1 Problems With The Traditional Approach

+ +

+As the art of computer programming continues to advance, and the size +and complexity of programs continues to swell, the traditional C +approach of placing declarations and definitions in separate files begins +to present the programmer with logistics and +maintenance problems. +To wit: +

+ +

+

    +

  1. +In large codes with many source files, it becomes difficult to determine +which .h files should be included in which .c files. +

  2. +It is typically the case that a .h file will be forced to include +another .h files, which in turn might include other .h files, +and so forth. +The .c file must be recompiled when any of the .h files in this chain +are altered, but it can be difficult to determine what .h files are found +in the include chain. +A frequent Makefile error is to omit some .h files from a dependency +list even though those files are on the include file chain. +

  3. +Some information is common to both the declaration and the definition of +an object in C, and so must be repeated in both the .h and the .c files +for that object. +In a large project, it can become increasingly difficult to keep the two +files in sync. +

  4. +When a .c file includes a .h file and the .h files changes, the .c file +must be recompiled, even if the part of the .h file that changed is not +actually used by the .c file. +In a large program, it is generally the case that almost every .c file ends up +depending on one or two of the more important .h files, and so when those .h +files change, the entire program must be recompiled. +It also happens that those important .h files tend to be the ones that +change most frequently. +This means that the entire program must be recompiled frequently, +leading to a lengthy modify-compile-test cycle and a corresponding +decrease in programmer productivity. +

  5. +The C programming language requires that declarations depending upon +each other must occur in a particular order. +In a program with complex, interwoven data structures, the correct +declaration order can become very difficult to determine manually, +especially when the declarations involved are spread out over several +files. +
+

+ + +

1.2 The Makeheaders Solution

+ +

+The makeheaders program is designed to ameliorate the problems associated +with the traditional C programming model by automatically generating +the interface information in the .h files from +interface information contained in other .h files and +from implementation information in the .c files. +When the makeheaders program is run, it scans the source +files for a project, +then generates a series of new .h files, one for each .c file. +The generated .h files contain exactly those declarations required by the +corresponding .c files, no more and no less. +

+ +

+The makeheaders programming model overcomes all of the objections to the +traditional C programming model. +

    +

  1. +Because all declarations needed by a .c file are contained in a +single .h file, there is never any question about what .h files +a .c will need to include. If the .c file is named +alpha.c then it must include only the single .h file +named alpha.h. +(The .c file might also use some include files from the standard +library, such as <stdio.h>, but that is another matter.) +

  2. +The generated .h files do not include other .h files, and so there +are no include chains to worry about. +The file alpha.c depends on alpha.h and +nothing more. +

  3. +There is still duplication in the .h and the .c file, but because +the duplicate information is automatically generated, it is no longer +a problem. +Simply rerun makeheaders to resynchronize everything. +

  4. +The generated .h file contains the minimal set of declarations needed +by the .c file. +This means that when something changes, a minimal amount of recompilation +is required to produce an updated executable. +Experience has shown that this gives a dramatic improvement +in programmer productivity by facilitating a rapid modify-compile-test +cycle during development. +

  5. +The makeheaders program automatically sorts declarations into the +correct order, completely eliminating the wearisome and error-prone +task of sorting declarations by hand. +
+

+ +

+In addition, the makeheaders program is fast and unintrusive. +It is a simple matter to incorporate makeheaders into a Makefile +so that makeheaders will be run automatically whenever the project +is rebuilt. +And the burden of running makeheaders is light. +It will easily process tens of thousands of lines of source +code per second. +

+ + +

2.0 Running The Makeheaders Program

+ +

+The makeheaders program is very easy to run. +If you have a collection of C source code and include files in the working +directory, then you can run makeheaders to generate appropriate .h +files using the following command: +

+   makeheaders *.[ch]
+
+That's really all there is to it! +This command will generate one .h file for every .c file. +Any .h files that were generated by a prior run of makeheaders +are ignored, +but manually entered .h files +that contain structure declarations and so forth will be scanned and +the declarations will be copied into the generated .h files as +appropriate. +But if makeheaders sees that the .h file that it has generated is no +different from the .h file it generated last time, it doesn't update +the file. +This prevents the corresponding .c files from having to +be needlessly recompiled. +

+ +

+There are several options to the makeheaders program that can +be used to alter its behavior. +The default behavior is to write a single .h file for each .c file and +to give the .h file the same base name as the .c file. +Instead of generating a whole mess of .h files, you can, if you choose, +generate a single big .h file that contains all declarations needed +by all the .c files. Do this using the -h option to makeheaders. +As follows: +

+   makeheaders -h *.[ch] >common.h
+
+With the -h option, the .h file is not actually written to a disk file but +instead appears on standard output, where you are free to redirect it +into the file of your choice. +

+ +

+A similar option is -H. Like the lower-case -h option, big -H +generates a single include file on standard output. But unlike +small -h, the big -H only emits prototypes and declarations that +have been designated as “exportable”. +The idea is that -H will generate an include file that defines +the interface to a library. +More will be said about this in section 3.4. +

+ +

+Sometimes you want the base name of the .c file and the .h file to +be different. +For example, suppose you want the include file for alpha.c +to be called beta.h. +In this case, you would invoke makeheaders as follows: +

+   makeheaders alpha.c:beta.h
+
+Any time a filename argument contains a colon, the name before the +colon is taken to be the name of the .c file and the name after the +colon is taken to be the name of the .h file. +You can't use the shell's wildcard mechanism with this approach, but that +normally isn't a problem in Makefiles, which is where this stuff +comes in handy. +

+ +

+If you want a particular file to be scanned by makeheaders but you +don't want makeheaders to generate a header file for that file, +then you can supply an empty header filename, like this: +

+   makeheaders alpha.c beta.c gamma.c:
+
+In this example, makeheaders will scan the three files named +“alpha.c”, +“beta.c” and +“gamma.c” +but because of the colon on the end of third filename +it will only generate headers for the first two files. +Unfortunately, +it is not possible to get makeheaders to process any file whose +name contains a colon. +

+ +

+In a large project, the length of the command line for makeheaders +can become very long. +If the operating system doesn't support long command lines +(example: DOS and Win32) you may not be able to list all of the +input files in the space available. +In that case, you can use the “-f” option followed +by the name of a file to cause makeheaders to read command line +options and filename from the file instead of from the command line. +For example, you might prepare a file named “mkhdr.dat” +that contains text like this: +

+  src/alpha.c:hdr/alpha.h
+  src/beta.c:hdr/beta.h
+  src/gamma.c:hdr/gamma.h
+  ...
+
+Then invoke makeheaders as follows: +
+  makeheaders -f mkhdr.dat
+
+

+ +

+The “-local” option causes makeheaders to +generate of prototypes for “static” functions and +procedures. +Such prototypes are normally omitted. +

+ +

+Finally, makeheaders also includes a “-doc” option. +This command line option prevents makeheaders from generating any +headers at all. +Instead, makeheaders will write to standard output +information about every definition and declaration that it encounters +in its scan of source files. +The information output includes the type of the definition or +declaration and any comment that preceeds the definition or +declaration. +The output is in a format that can be easily parsed, and is +intended to be read by another program that will generate +documentation about the program. +We'll talk more about this feature later. +

+ +

+If you forget what command line options are available, or forget +their exact name, you can invoke makeheaders using an unknown +command line option (like “--help” or +“-?”) +and it will print a summary of the available options on standard +error. +If you need to process a file whose name begins with +“-”, +you can prepend a “./” to its name in order to get it +accepted by the command line parser. +Or, you can insert the special option “--” on the +command line to cause all subsequent command line arguments to be treated as +filenames even if their names begin with “-”. +

+ + +

3.0 Preparing Source Files For Use With Makeheaders

+ +

+Very little has to be done to prepare source files for use with +makeheaders since makeheaders will read and understand ordinary +C code. +But it is important that you structure your files in a way that +makes sense in the makeheaders context. +This section will describe several typical uses of makeheaders. +

+ + +

3.1 The Basic Setup

+ +

+The simplest way to use makeheaders is to put all definitions in +one or more .c files and all structure and type declarations in +separate .h files. +The only restriction is that you should take care to chose basenames +for your .h files that are different from the basenames for your +.c files. +Recall that if your .c file is named (for example) +“alpha.c” +makeheaders will attempt to generate a corresponding header file +named “alpha.h”. +For that reason, you don't want to use that name for +any of the .h files you write since that will prevent makeheaders +from generating the .h file automatically. +

+ +

+The structure of a .c file intented for use with makeheaders is very +simple. +All you have to do is add a single “#include” to the +top of the file that sources the header file that makeheaders will generate. +Hence, the beginning of a source file named “alpha.c” +might look something like this: +

+ +
+   /*
+    * Introductory comment...
+    */
+   #include "alpha.h"
+
+   /* The rest of your code... */
+
+ +

+Your manually generated header files require no special attention at all. +Code them as you normally would. +However, makeheaders will work better if you omit the +“#if” statements people often put around the outside of +header files that prevent the files from being included more than once. +For example, to create a header file named “beta.h”, +many people will habitually write the following: + +

+   #ifndef BETA_H
+   #define BETA_H
+
+   /* declarations for beta.h go here */
+
+   #endif
+
+ +You can forego this cleverness with makeheaders. +Remember that the header files you write will never really be +included by any C code. +Instead, makeheaders will scan your header files to extract only +those declarations that are needed by individual .c files and then +copy those declarations to the .h files corresponding to the .c files. +Hence, the “#if” wrapper serves no useful purpose. +But it does make makeheaders work harder, forcing it to put +the statements + +
+   #if !defined(BETA_H)
+   #endif
+
+ +around every declaration that it copies out of your header file. +No ill effect should come of this, but neither is there any benefit. +

+ +

+Having prepared your .c and .h files as described above, you can +cause makeheaders to generate its .h files using the following simple +command: + +

+   makeheaders *.[ch]
+
+ +The makeheaders program will scan all of the .c files and all of the +manually written .h files and then automatically generate .h files +corresponding to all .c files. +

+ +

+Note that +the wildcard expression used in the above example, +“*.[ch]”, +will expand to include all .h files in the current directory, both +those entered manually be the programmer and others generated automatically +by a prior run of makeheaders. +But that is not a problem. +The makeheaders program will recognize and ignore any files it +has previously generated that show up on its input list. +

+ + +

3.2 What Declarations Get Copied

+ +

+The following list details all of the code constructs that makeheaders +will extract and place in +the automatically generated .h files: +

+ +
    +

  • +When a function is defined in any .c file, a prototype of that function +is placed in the generated .h file of every .c file that +calls the function.

    + +

    If the “static” keyword of C appears at the +beginning of the function definition, the prototype is suppressed. +If you use the “LOCAL” keyword where you would normally +say “static”, then a prototype is generated, but it +will only appear in the single header file that corresponds to the +source file containing the function. For example, if the file +alpha.c contains the following: +

    +  LOCAL int testFunc(void){
    +    return 0;
    +  }
    +
    +Then the header file alpha.h will contain +
    +  #define LOCAL static
    +  LOCAL int testFunc(void);
    +
    +However, no other generated header files will contain a prototype for +testFunc() since the function has only file scope.

    + +

    When the “LOCAL” keyword is used, makeheaders will +also generate a #define for LOCAL, like this: +

    +   #define LOCAL static
    +
    +so that the C compiler will know what it means.

    + +

    If you invoke makeheaders with a “-local” +command-line option, then it treats the “static” +keyword like “LOCAL” and generates prototypes in the +header file that corresponds to the source file containing the function +definition.

    + +

  • +When a global variable is defined in a .c file, an +“extern” +declaration of that variable is placed in the header of every +.c file that uses the variable. +

    + +

  • +When a structure, union or enumeration declaration or a +function prototype or a C++ class declaration appears in a +manually produced .h file, that declaration is copied into the +automatically generated +.h files of all .c files that use the structure, union, enumeration, +function or class. +But declarations that appear in a +.c file are considered private to that .c file and are not copied into +any automatically generated files. +

    + +

  • +All #defines and typedefs that appear in manually produced .h files +are copied into automatically generated .h files as needed. +Similar constructs that appear in .c files are considered private to +those files and are not copied. +

    + +

  • +When a structure, union or enumeration declaration appears in a .h +file, makeheaders will automatically +generate a typedef that allows the declaration to be referenced without +the “struct”, “union” or +“enum” qualifier. +In other words, if makeheaders sees the code: +
    +  struct Examp { /* ... */ };
    +
    +it will automatically generate a corresponding typedef like this: +
    +  typedef struct Examp Examp;
    +
    +

    + +

  • +Makeheaders generates an error message if it encounters a function or +variable definition within a .h file. +The .h files are suppose to contain only interface, not implementation. +C compilers will not enforce this convention, but makeheaders does. +
+ +

+As a final note, we observe that automatically generated declarations +are ordered as required by the ANSI-C programming language. +If the declaration of some structure “X” requires a +prior declaration of another structure “Y”, then Y will +appear first in the generated headers. +

+ + +

3.3 How To Avoid Having To Write Any Header Files

+ +

+In my experience, large projects work better if all of the manually +written code is placed in .c files and all .h files are generated +automatically. +This is slightly different for the traditional C method of placing +the interface in .h files and the implementation in .c files, but +it is a refreshing change that brings a noticable improvement to the +coding experience. +Others, I believe, share this view since I've +noticed recent languages (ex: java, tcl, perl, awk) tend to +support the one-file approach to coding as the only option. +

+ +

+The makeheaders program supports putting both +interface and implementation into the same source file. +But you do have to tell makeheaders which part of the source file is the +interface and which part is the implementation. +Makeheaders has to know this in order to be able to figure out whether or +not structures declarations, typedefs, #defines and so forth should +be copied into the generated headers of other source files. +

+ +

+You can instruct makeheaders to treat any part of a .c file as if +it were a .h file by enclosing that part of the .c file within: +

+   #if INTERFACE
+   #endif
+
+Thus any structure definitions that appear after the +“#if INTERFACE” but before the corresponding +“#endif” are eligable to be copied into the +automatically generated +.h files of other .c files. +

+ +

+If you use the “#if INTERFACE” mechanism in a .c file, +then the generated header for that .c file will contain a line +like this: +

+   #define INTERFACE 0
+
+In other words, the C compiler will never see any of the text that +defines the interface. +But makeheaders will copy all necessary definitions and declarations +into the .h file it generates, so .c files will compile as if the +declarations were really there. +This approach has the advantage that you don't have to worry with +putting the declarations in the correct ANSI-C order -- makeheaders +will do that for you automatically. +

+ +

+Note that you don't have to use this approach exclusively. +You can put some declarations in .h files and others within the +“#if INTERFACE” regions of .c files. +Makeheaders treats all declarations alike, no matter where they +come from. +You should also note that a single .c file can contain as many +“#if INTERFACE” regions as desired. +

+ + +

3.4 Designating Declarations For Export

+ +

+In a large project, one will often construct a hierarchy of +interfaces. +For example, you may have a group of 20 or so files that form +a library used in several other parts of the system. +Each file in this library will present two interfaces. +One interface will be the routines and data structures it is +willing to share with other files in the same library, and the +second interface is those routines and data structures it wishes +to make available to other subsystems. +(The second interface is normally a subset of the first.) +Ordinary C does not provide support for a tiered interface +like this, but makeheaders does. +

+ +

+Using makeheaders, it is possible to designate routines and data +structures as being for “export”. +Exported objects are visible not only to other files within the +same library or subassembly but also to other +libraries and subassemblies in the larger program. +By default, makeheaders only makes objects visible to other members +of the same library. +

+ +

+That isn't the complete truth, actually. +The semantics of C are such that once an object becomes visible +outside of a single source file, it is also visible to any user +of the library that is made from the source file. +Makeheaders can not prevent outsiders for using non-exported resources, +but it can discourage the practice by refusing to provide prototypes +and declarations for the services it does not want to export. +Thus the only real effect of the making an object exportable is +to include it in the output makeheaders generates when it is run +using the -H command line option. +This is not a perfect solution, but it works well in practice. +

+ +

+But trouble quickly arises when we attempt to devise a mechanism for +telling makeheaders which prototypes it should export and which it should +keep local. +The built-in “static” keyword of C works well for +prohibiting prototypes from leaving a single source file, but because C doesn't +support a linkage hierarchy, there is nothing in the C language to help us. +We'll have to invite our own keyword: “EXPORT” +

+ +

+Makeheaders allows the EXPORT keyword to precede any function or +procedure definition. +The routine following the EXPORT keyword is then eligable to appear +in the header file generated using the -H command line option. +Note that if a .c file contains the EXPORT keyword, makeheaders will +put the macro +

+   #define EXPORT
+
+in the header file it generates for the .c file so that the EXPORT keyword +will never be seen by the C compiler. +

+ +

+But the EXPORT keyword only works for function and procedure definitions. +For structure, union and enum definitions, typedefs, #defines and +class declarations, a second mechanism is used. +Just as any declarations or definition contained within +

+   #if INTERFACE
+   #endif
+
+are visible to all files within the library, any declarations +or definitions within +
+   #if EXPORT_INTERFACE
+   #endif
+
+will become part of the exported interface. +The “#if EXPORT_INTERFACE” mechanism can be used in +either .c or .h files. +(The “#if INTERFACE” can also be used in both .h and +.c files, but since it's use in a .h file would be redundant, we haven't +mentioned it before.) +

+ + +

3.5 Local declarations processed by makeheaders

+ +

+Structure declarations and typedefs that appear in .c files are normally +ignored by makeheaders. +Such declarations are only intended for use by the source file in which +they appear and so makeheaders doesn't need to copy them into any +generated header files. +We call such declarations “private”. +

+ +

+Sometimes it is convenient to have makeheaders sort a sequence +of private declarations into the correct order for us automatically. +Or, we could have static functions and procedures for which we would like +makeheaders to generate prototypes, but the arguments to these +functions and procedures uses private declarations. +In both of these cases, we want makeheaders to be aware of the +private declarations and copy them into the local header file, +but we don't want makeheaders to propagate the +declarations outside of the file in which they are declared. +

+ +

+When this situation arises, enclose the private declarations +within +

+  #if LOCAL_INTERFACE
+  #endif
+
+A “LOCAL_INTERFACE” block works very much like the +“INTERFACE” and +“EXPORT_INTERFACE” +blocks described above, except that makeheaders insures that the +objects declared in a LOCAL_INTERFACE are only visible to the +file containing the LOCAL_INTERFACE. +

+ + +

3.6 Using Makeheaders With C++ Code

+ +

+You can use makeheaders to generate header files for C++ code, in +addition to C. +Makeheaders will recognize and copy both “class” +declarations +and inline function definitions, and it knows not to try to generate +prototypes for methods. +

+ +

+In fact, makeheaders is smart enough to be used in projects that employ +a mixture of C and C++. +For example, if a C function is called from within a C++ code module, +makeheaders will know to prepend the text +

+   extern "C"
+
+to the prototype for that function in the C++ header file. +Going the other way, +if you try to call a C++ function from within C, an +appropriate error message is issued, since C++ routines can not +normally be called by C code (due to fact that most C++ compilers +use name mangling to facilitate type-safe linkage.) +

+ +

+No special command-line options are required to use makeheaders with +C++ input. Makeheaders will recognize that its source code is C++ +by the suffix on the source code filename. Simple ".c" or ".h" suffixes +are assumed to be ANSI-C. Anything else, including ".cc", ".C" and +".cpp" is assumed to be C++. +The name of the header file generated by makeheaders is derived from +the name of the source file by converting every "c" to "h" and +every "C" to "H" in the suffix of the filename. +Thus the C++ source +file “alpha.cpp” will induce makeheaders to +generate a header file named “alpha.hpp”. +

+ +

+Makeheaders augments class definitions by inserting prototypes to +methods where appropriate. If a method definition begins with one +of the special keywords PUBLIC, PROTECTED, or +PRIVATE (in upper-case to distinguish them from the regular +C++ keywords with the same meaning) then a prototype for that +method will be inserted into the class definition. If none of +these keywords appear, then the prototype is not inserted. For +example, in the following code, the constructor is not explicitly +declared in the class definition but makeheaders will add it there +because of the PUBLIC keyword that appears before the constructor +definition. +

+ +
+#if INTERFACE
+class Example1 {
+private:
+  int v1;
+};
+#endif
+PUBLIC Example1::Example1(){
+  v1 = 0;
+}
+
+ +

+The code above is equivalent to the following: +

+ +
+#if INTERFACE
+class Example1 {
+private:
+  int v1;
+public:
+  Example1();
+};
+#endif
+Example1::Example1(){
+  v1 = 0;
+}
+
+ +

+The first form is preferred because only a single declaration of +the constructor is required. The second form requires two declarations, +one in the class definition and one on the defintion of the constructor. +

+ +

3.6.1 C++ Limitations

+ +

+Makeheaders does not understand more recent +C++ syntax such as templates and namespaces. +Perhaps these issues will be addressed in future revisions. +

+ + +

3.7 Conditional Compilation

+ +

+The makeheaders program understands and tracks the conditional +compilation constructs in the source code files it scans. +Hence, if the following code appears in a source file +

+  #ifdef UNIX
+  #  define WORKS_WELL 1
+  #else
+  #  define WORKS_WELL 0
+  #endif
+
+then the next patch of code will appear in the generated header for +every .c file that uses the WORKS_WELL constant: +
+  #if defined(UNIX)
+  #  define WORKS_WELL 1
+  #endif
+  #if !defined(UNIX)
+  #  define WORKS_WELL 0
+  #endif
+
+The conditional compilation constructs can be nested to any depth. +Makeheaders also recognizes the special case of +
+  #if 0
+  #endif
+
+and treats the enclosed text as a comment. +

+ + +

3.8 Caveats

+ +

+The makeheaders system is designed to be robust +but it is possible for a devious programmer to fool the system, +usually with unhelpful consequences. +This subsection is a guide to helping you avoid trouble. +

+ +

+Makeheaders does not understand the old K&R style of function +and procedure definitions. +It only understands the modern ANSI-C style, and will probably +become very confused if it encounters an old K&R function. +Therefore you should take care to avoid putting K&R function definitions +in your code. +

+ +

+Makeheaders does not understand when you define more than one +global variable with the same type separated by a comma. +In other words, makeheaders does not understand this: +

+   int a = 4, b = 5;
+
+The makeheaders program wants every variable to have its own +definition. Like this: +
+   int a = 4;
+   int b = 5;
+
+Notice that this applies to global variables only, not to variables +you declare inside your functions. +Since global variables ought to be exceedingly rare, and since it is +good style to declare them separately anyhow, this restriction is +not seen as a terrible hardship. +

+ +

+Makeheaders does not support defining an enumerated or aggregate type in +the same statement as a variable declaration. None of the following +statements work completely: +

+struct {int field;} a;
+struct Tag {int field;} b;
+struct Tag c;
+
+Instead, define types separately from variables: +
+#if INTERFACE
+struct Tag {int field;};
+#endif
+Tag a;
+Tag b; /* No more than one variable per declaration. */
+Tag c; /* So must put each on its own line. */
+
+See 3.2 What Declarations Get Copied for details, +including on the automatic typedef. +

+ +

+The makeheaders program processes its source file prior to sending +those files through the C preprocessor. +Hence, if you hide important structure information in preprocessor defines, +makeheaders might not be able to successfully extract the information +it needs from variables, functions and procedure definitions. +For example, if you write this: +

+  #define BEGIN {
+  #define END }
+
+at the beginning of your source file, and then try to create a function +definition like this: +
+  char *StrDup(const char *zSrc)
+    BEGIN
+      /* Code here */
+    END
+
+then makeheaders won't be able to find the end of the function definition +and bad things are likely to happen. +

+ +

+For most projects the code constructs that makeheaders cannot +handle are very rare. +As long as you avoid excessive cleverness, makeheaders will +probably be able to figure out what you want and will do the right +thing. +

+ +

+Makeheaders has limited understanding of enums. In particular, it does +not realize the significance of enumerated values, so the enum is not +emitted in the header files when its enumerated values are used unless +the name associated with the enum is also used. Moreover, enums can be +completely anonymous, e.g. “enum {X, Y, Z};”. +Makeheaders ignores such enums so they can at least be used within a +single source file. Makeheaders expects you to use #define constants +instead. If you want enum features that #define lacks, and you need the +enum in the interface, bypass makeheaders and write a header file by +hand, or teach makeheaders to emit the enum definition when any of the +enumerated values are used, rather than only when the top-level name (if +any) is used. +

+ + +

4.0 Using Makeheaders To Generate Documentation

+ +

+Many people have observed the advantages of generating program +documentation directly from the source code: +

    +
  • Less effort is involved. It is easier to write a program than + it is to write a program and a document. +
  • The documentation is more likely to agree with the code. + When documentation is derived directly from the code, or is + contained in comments immediately adjacent to the code, it is much + more likely to be correct than if it is contained in a separate + unrelated file in a different part of the source tree. +
  • Information is kept in only one place. When a change occurs + in the code, it is not necessary to make a corresponding change + in a separate document. Just rerun the documentation generator. +
+The makeheaders program does not generate program documentation itself. +But you can use makeheaders to parse the program source code, extract +the information that is relevant to the documentation and to pass this +information to another tool to do the actual documentation preparation. +

+ +

+When makeheaders is run with the “-doc” option, it +emits no header files at all. +Instead, it does a complete dump of its internal tables to standard +output in a form that is easily parsed. +This output can then be used by another program (the implementation +of which is left as an exercise to the reader) that will use the +information to prepare suitable documentation. +

+ +

+The “-doc” option causes makeheaders to print +information to standard output about all of the following objects: +

    +
  • C++ class declarations +
  • Structure and union declarations +
  • Enumerations +
  • Typedefs +
  • Procedure and function definitions +
  • Global variables +
  • Preprocessor macros (ex: “#define”) +
+For each of these objects, the following information is output: +
    +
  • The name of the object. +
  • The type of the object. (Structure, typedef, macro, etc.) +
  • Flags to indicate if the declaration is exported (contained within + an EXPORT_INTERFACE block) or local (contained with LOCAL_INTERFACE). +
  • A flag to indicate if the object is declared in a C++ file. +
  • The name of the file in which the object was declared. +
  • The complete text of any block comment that preceeds the declarations. +
  • If the declaration occurred inside a preprocessor conditional + (“#if”) then the text of that conditional is + provided. +
  • The complete text of a declaration for the object. +
+The exact output format will not be described here. +It is simple to understand and parse and should be obvious to +anyone who inspects some sample output. +

+ + +

5.0 Compiling The Makeheaders Program

+ +

+The source code for makeheaders is a single file of ANSI-C code, +approximately 3000 lines in length. +The program makes only modest demands of the system and C library +and should compile without alteration on most ANSI C compilers +and on most operating systems. +It is known to compile using several variations of GCC for Unix +as well as Cygwin32 and MSVC 5.0 for Win32. +

+ + +

6.0 History

+ +

+The makeheaders program was first written by D. Richard Hipp +(also the original author of +SQLite and +Fossil) in 1993. +Hipp open-sourced the project immediately, but it never caught +on with any other developers and it continued to be used mostly +by Hipp himself for over a decade. When Hipp was first writing +the Fossil version control system in 2006 and 2007, he used +makeheaders on that project to help simplify the source code. +As the popularity of Fossil increased, the makeheaders +that was incorporated into the Fossil source tree became the +"official" makeheaders implementation. +

+ +

+As this paragraph is being composed (2016-11-05), Fossil is the +only project known to Hipp that is still using makeheaders. On +the other hand, makeheaders has served the Fossil project well and +there are no plans remove it. +

+ + +

7.0 Summary And Conclusion

+ +

+The makeheaders program will automatically generate a minimal header file +for each of a set of C source and header files, and will +generate a composite header file for the entire source file suite, +for either internal or external use. +It can also be used as the parser in an automated program +documentation system. +

+ +

+The makeheaders program has been in use since 1994, +in a wide variety of projects under both UNIX and Win32. +In every project where it has been used, makeheaders has proven +to be a very helpful aid +in the construction and maintenance of large C codes. +In at least two cases, makeheaders has facilitated development +of programs that would have otherwise been all but impossible +due to their size and complexity. +

+ + diff --git a/tool/lib/bashrc b/tool/lib/bashrc new file mode 100755 index 0000000..b2f3ded --- /dev/null +++ b/tool/lib/bashrc @@ -0,0 +1,20 @@ +# + +umask 0077 + +if [ $INSIDE_EMACS ]; then + echo Hello Emacs +fi +export PS1='\n$(/usr/local/bin/Z)\n\u@\h§\w§\n> ' +export PS2='>>' +PATH=~/bin:$PATH + +#temporarily while active in subu code development +export PROJECT_SUBU=~/subu +PATH="$PROJECT_SUBU"/tool/bin:$PATH + +export PS_FORMAT=user:15,pid,%cpu,%mem,vsz,rss,tty,stat,start,time,command + +export EDITOR=emacs + + diff --git a/tool/lib/dot_emacs b/tool/lib/dot_emacs new file mode 100755 index 0000000..27132b6 --- /dev/null +++ b/tool/lib/dot_emacs @@ -0,0 +1,202 @@ + +(defun undedicate-window (&optional window) + (interactive) + (set-window-dedicated-p (or window (get-buffer-window)) nil)) + +;; Removing annoying dedicated buffer nonsense +(defun switch-to-buffer! (buffer-or-name &optional norecord force-same-window) + "Like switch-to-buffer but works for dedicated buffers \(though +it will ask first)." + (interactive + (list (read-buffer-to-switch "Switch to buffer: ") nil 'force-same-window)) + (when (and (window-dedicated-p (get-buffer-window)) + (yes-or-no-p "This window is dedicated, undedicate it? ")) + (undedicate-window)) + (switch-to-buffer buffer-or-name norecord force-same-window)) + +(defun toggle-window-dedication (&optional window) + (interactive) + (let ((window (or window (get-buffer-window)))) + (set-window-dedicated-p window (not (window-dedicated-p window))))) + +(global-set-key (kbd "C-x d") 'toggle-window-dedication) + +;; fix 'feature' of broken gdb where it takes control of an +;; emacs window, and locks the user out from switching from it +;; +;; yes and this fix breaks file name completion... +;; +;; (defun set-window-undedicated-p (window flag) +;; "Never set window dedicated." +;; flag) +;; (advice-add 'set-window-dedicated-p :override #'set-window-undedicated-p) +;; +;; Toggle window dedication + + (defun toggle-window-dedicated () + "Toggle whether the current active window is dedicated or not" + (interactive) + (message + (if (let (window (get-buffer-window (current-buffer))) + (set-window-dedicated-p window + (not (window-dedicated-p window)))) + "Window '%s' is dedicated" + "Window '%s' is normal") + (current-buffer))) + + (global-set-key "\C-q" 'toggle-window-dedicated) + +;; (setq ring-bell-function (lambda () +;; (call-process-shell-command +;; "xset led 3; xset -led 3" nil 0 nil))) +;; +;; (setq ring-bell-function nil) + + (setq ring-bell-function + (lambda () + (call-process-shell-command "xset led named 'Scroll Lock'") + (call-process-shell-command "xset -led named 'Scroll Lock'"))) + + +;; preferable to have keys for the characters, but the keyboard is already overloaded .. +;; (define-key key-translation-map (kbd " p") (kbd "¬")) +;; (set-input-method “latin-9-prefix) + + (global-set-key [f1] 'help-command) + (global-set-key "\C-h" 'nil) + (define-key key-translation-map (kbd "M-S") (kbd "§")) + + (global-set-key (kbd "C-x g phi SPC") [?φ]) ; phi for phase + (global-set-key (kbd "C-x g Phi SPC") [?Φ]) + + (global-set-key (kbd "C-x g d SPC") [?δ]) + (global-set-key (kbd "C-x g D SPC") [?Δ]) ; this is 'delta' is not 'increment'! + (global-set-key (kbd "C-x g delta SPC") [?δ]) + (global-set-key (kbd "C-x g Delta SPC") [?Δ]) ; this is 'delta' is not 'increment'! + + + (global-set-key (kbd "C-x g g SPC") [?γ]) + (global-set-key (kbd "C-x g G SPC") [?Γ]) + (global-set-key (kbd "C-x g gamma SPC") [?γ]) + (global-set-key (kbd "C-x g Gamma SPC") [?Γ]) + + (global-set-key (kbd "C-x g l SPC") [?λ]) + (global-set-key (kbd "C-x g L SPC") [?Λ]) + (global-set-key (kbd "C-x g lambda SPC") [?λ]) + (global-set-key (kbd "C-x g Lambda SPC") [?Λ]) + + (global-set-key (kbd "C-x g p SPC") [?π]) + (global-set-key (kbd "C-x g P SPC") [?Π]) + (global-set-key (kbd "C-x g pi SPC") [?π]) + (global-set-key (kbd "C-x g Pi SPC") [?Π]) + + (global-set-key (kbd "C-x g > = SPC") [?≥]) + (global-set-key (kbd "C-x g < = SPC") [?≤]) + (global-set-key (kbd "C-x g ! = SPC") [?≠]) + (global-set-key (kbd "C-x g neq SPC") [?≠]) + + (global-set-key (kbd "C-x g nil SPC") [?∅]) + + (global-set-key (kbd "C-x g not SPC") [?¬]) + + (global-set-key (kbd "C-x g and SPC") [?∧]) + (global-set-key (kbd "C-x g or SPC") [?∨]) + + (global-set-key (kbd "C-x g exists SPC") [?∃]) + (global-set-key (kbd "C-x g all SPC") [?∀]) + + (global-set-key (kbd "C-x g do SPC") [?⟳]) ; do + (global-set-key (kbd "C-x g rb SPC") [?◨]) + (global-set-key (kbd "C-x g lb SPC") [?◧]) + + (global-set-key (kbd "C-x g cont SPC") [?➜]) ; continue + (global-set-key (kbd "C-x g thread SPC") [?☥]) ; thread + + (global-set-key (kbd "C-x g in SPC") [?∈]) ; set membership + + + +;; lisp +;; + (setq lisp-indent-offset 2) + (setq inferior-lisp-program "sbcl") + + (modify-syntax-entry ?[ "(]" lisp-mode-syntax-table) + (modify-syntax-entry ?] ")[" lisp-mode-syntax-table) + (modify-syntax-entry ?{ "(}" lisp-mode-syntax-table) + (modify-syntax-entry ?} "){" lisp-mode-syntax-table) + +;; get the pwd in shell mode from the prompt rather than guessing by +;; watching the commands typed .. yes! now shell variables and source +;; scripts will work +;; in bashrc: export PS1='\n$(/usr/local/bin/Z)\u@\h§\w§\n> ' +;; + (add-hook 'shell-mode-hook + (lambda () + (shell-dirtrack-mode -1) + (dirtrack-mode 1))) + + (add-hook 'dirtrack-directory-change-hook + (lambda () + (message default-directory))) + + (setq dirtrack-list '("§\\(.*\\)§\n> " 1)) + +;; use a backrevs dir rather than leaving ~file droppings everywhere +;; + (setq backup-directory-alist `(("." . "~/emacs_backrevs"))) + (setq backup-by-copying t) + +;; stop the 'tab' character polution +;; + (setq-default indent-tabs-mode nil) + +;; turn off the poison C-z key. Use C-x C-z or the command suspend-emacs +;; + (global-set-key (kbd "C-z") nil) + +;; truncate rather than wrapping lines (use horizontal scroll to see to the right) +;; + (set-default 'truncate-lines t) + (setq truncate-partial-width-windows nil) + (setq-default fill-column 80) + +;; recover some window real estate +;; c-x mode-line to toggle the mode-line on and off +;; + (defun mode-line () "toggles the modeline on and off" + (interactive) + (setq mode-line-format + (if (equal mode-line-format nil) + (default-value 'mode-line-format)) ) + (redraw-display)) + + (tool-bar-mode -1) + (menu-bar-mode -1) + + +(put 'upcase-region 'disabled nil) +(put 'narrow-to-region 'disabled nil) +(put 'downcase-region 'disabled nil) +(put 'set-goal-column 'disabled nil) + + +(custom-set-variables + ;; custom-set-variables was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(ansi-color-names-vector + ["#212526" "#ff4b4b" "#b4fa70" "#fce94f" "#729fcf" "#e090d7" "#8cc4ff" "#eeeeec"]) + '(custom-enabled-themes (quote (wheatgrass))) + '(geiser-racket-binary "racket") + '(send-mail-function (quote smtpmail-send-it)) + '(tool-bar-mode nil)) +(custom-set-faces + ;; custom-set-faces was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(default ((t (:family "DejaVu Sans Mono" :foundry "PfEd" :slant normal :weight bold :height 98 :width normal))))) + +(put 'erase-buffer 'disabled nil) diff --git a/tool/lib/makefile_cc b/tool/lib/makefile_cc new file mode 100755 index 0000000..3cbb5fe --- /dev/null +++ b/tool/lib/makefile_cc @@ -0,0 +1,168 @@ + +#should add test that incdir is not pwd before deleting the include file in +#dist-clean currently make is looking for module.h while the include file is +#always module.lib.h so the lib include is not getting clobbered + +SHELL=/bin/bash + +# these are the sources edited by the programmer +C_SOURCE_LIB= $(wildcard $(SRCDIR)/*.lib.c) +C_SOURCE_CLI= $(wildcard $(SRCDIR)/*.cli.c) +CC_SOURCE_LIB= $(wildcard $(SRCDIR)/*.lib.cc) +CC_SOURCE_CLI= $(wildcard $(SRCDIR)/*.cli.cc) + +#remove the suffix to get base name +C_BASE_LIB= $(sort $(patsubst %.lib.c, %, $(notdir $(C_SOURCE_LIB)))) +C_BASE_CLI= $(sort $(patsubst %.cli.c, %, $(notdir $(C_SOURCE_CLI)))) +CC_BASE_LIB= $(sort $(patsubst %.lib.cc, %, $(notdir $(CC_SOURCE_LIB)))) +CC_BASE_CLI= $(sort $(patsubst %.cli.cc, %, $(notdir $(CC_SOURCE_CLI)))) + +# two sets of object files, one for the lib, and one for the command line interface progs +OBJECT_LIB= $(patsubst %, $(TMPDIR)/%.lib.o, $(C_BASE_LIB) $(CC_BASE_LIB)) +OBJECT_CLI= $(patsubst %, $(TMPDIR)/%.cli.o, $(C_BASE_CLI) $(CC_BASE_CLI)) + +# executables are made from CLI sources +EXEC= $(patsubst %, $(EXECDIR)/%, $(C_BASE_CLI) $(CC_BASE_CLI)) + +#otherwise make provides default values for these +C= +CC= + +DEPFILE=$(TMPDIR)/makefile-dep +LIBFILE=$(LIBDIR)/lib$(MODULE).a +INCFILE=$(INCDIR)/$(MODULE).h + +-include makefile-flags + +# a single space literal, for example if you wanted to subsitute commas to +# spaces: $(subst $(space),;,$(string)) we ran into this out of a need to send +# multiple separate command arguments to a shell script from one variable value +blank := +space :=$(blank) $(blank) + +.PHONY: all +all: version dep lib exec + +.PHONY: version +version: + @echo makefile version 4.0 + @echo "PWD: " $(PWD) + @echo "MAKEFILE_LIST: " $(MAKEFILE_LIST) + @echo "C: " $(C) + @echo "CFLAGS: " $(CFLAGS) + @echo "CC: " $(CC) + @echo "CCFLAGS: " $(CCFLAGS) + @echo "LINKFLAGS: " $(LINKFLAGS) + +.PHONY: info +info: + @echo "DEPRDIR: " $(DEPRDIR) + @echo "DOCDIR: " $(DOCDIR) + @echo "EXECDIR: " $(EXECDIR) + @echo "INCDIR: " $(INCDIR) + @echo "LIBDIR: " $(LIBDIR) + @echo "TESTDIR: " $(TESTDIR) + @echo "TMPDIR: " $(TMPDIR) + @echo "TOOLDIR: " $(TOOLDIR) + @echo "TRYDIR: " $(TRYDIR) + @echo "DEPFILE: " $(DEPFILE) + @echo "LIBFILE: " $(LIBFILE) + @echo "INCFILE: " $(INCFILE) + @echo "C_SOURCE_LIB: " $(C_SOURCE_LIB) + @echo "C_SOURCE_CLI: " $(C_SOURCE_CLI) + @echo "CC_SOURCE_LIB: " $(CC_SOURCE_LIB) + @echo "CC_SOURCE_CLI: " $(CC_SOURCE_CLI) + @echo "C_BASE_LIB: " $(C_BASE_LIB) + @echo "C_BASE_CLI: " $(C_BASE_CLI) + @echo "CC_BASE_LIB: " $(CC_BASE_LIB) + @echo "CC_BASE_CLI: " $(CC_BASE_CLI) + @echo "OBJECT_LIB: " $(OBJECT_LIB) + @echo "OBJECT_CLI: " $(OBJECT_CLI) + @echo "EXEC: " $(EXEC) + +# should be safe to run this in an already setup or partially setup directory +.PHONY: setup +setup: + [ ! -e $(DEPRDIR) ] && mkdir $(DEPRDIR) || true + [ ! -e $(DOCDIR) ] && mkdir $(DOCDIR) || true + [ ! -e $(EXECDIR) ] && mkdir $(EXECDIR) || true + [ ! -e $(INCDIR) ] && mkdir $(INCDIR) || true + [ ! -e $(LIBDIR) ] && mkdir $(LIBDIR) || true + [ ! -e $(SRCDIR) ] && mkdir $(SRCDIR) || true + [ ! -e $(TESTDIR) ] && mkdir $(TESTDIR) || true + [ ! -e $(TMPDIR) ] && mkdir $(TMPDIR) || true + [ ! -e $(TRYDIR) ] && mkdir $(TRYDIR) || true + +#better to put the dependency generation into a loop on the source file and specify -MT for the target +#also should be redirecting only stdout so stderr messages will be displayed >>1 depfile +.PHONY: dep +dep: + @if [ -z "$(CC)" ]; then\ + if [ -z "$C()" ]; then\ + echo "No compiler specified";\ + exit 1;\ + else\ + echo "C compiler only deps" ;\ + $(C) $(CFLAGS) -MM $(C_SOURCE_LIB) $(C_SOURCE_CLI) | sed 's|^.*\.o|$(TMPDIR)/&|' > $(DEPFILE);\ + echo "deps for C linking";\ + for i in $(C_BASE_CLI) ; do\ + $(ECHO) >> $(DEPFILE);\ + $(ECHO) "$(EXECDIR)/$$i : $(TMPDIR)/$$i.cli.o $(LIBFILE)" >> $(DEPFILE);\ + $(ECHO) " $(C) -o $(EXECDIR)/$$i $(TMPDIR)/$$i.cli.o $(LINKFLAGS)" >> $(DEPFILE);\ + done;\ + fi;\ + else\ + $(CC) $(CCFLAGS) -MM $(CC_SOURCE_LIB) $(CC_SOURCE_CLI) | sed 's|^.*\.o|$(TMPDIR)/&|' > $(DEPFILE);\ + if [ -z "$C()" ]; then\ + echo "CC compiler only deps" ;\ + else\ + echo "CC and C mixed compile deps" ;\ + $(C) $(CFLAGS) -MM $(C_SOURCE_LIB) $(C_SOURCE_CLI) | sed 's|^.*\.o|$(TMPDIR)/&|' > $(DEPFILE);\ + fi;\ + echo "deps for CC linking";\ + for i in $(CC_BASE_CLI) $(C_BASE_CLI) ; do\ + $(ECHO) >> $(DEPFILE);\ + $(ECHO) "$(EXECDIR)/$$i : $(TMPDIR)/$$i.cli.o $(LIBFILE)" >> $(DEPFILE);\ + $(ECHO) " $(CC) -o $(EXECDIR)/$$i $(TMPDIR)/$$i.cli.o $(LINKFLAGS)" >> $(DEPFILE);\ + done;\ + fi + +.PHONY: lib +lib: $(LIBFILE) + +$(LIBFILE): $(OBJECT_LIB) + ar rcs $(LIBFILE) $(OBJECT_LIB) + +.PHONY: exec +exec: $(LIBFILE) + make sub_exec + +.PHONY: sub_exec +sub_exec: $(EXEC) + +.PHONY: stage +stage: + [ -f $(LIBFILE) ] && cp $(LIBFILE) $(PROJECT_SUBU)/stage/lib || true + [ -f $(INCFILE) ] && cp $(INCFILE) $(PROJECT_SUBU)/stage/include || true + [ $(shell ls -A $(EXECDIR)) ] && cp $(EXECDIR)/* $(PROJECT_SUBU)/stage/bin || true + +.PHONY: clean +clean: + for i in $(wildcard tmp/*); do rm $$i || true; done + +.PHONY: dist-clean +dist-clean: clean + for i in $(EXEC); do [ -e $$i ] && rm $$i || true; done + rm $(INCFILE) || true + rm $(LIBFILE) || true + +-include $(DEPFILE) + +# recipes + +$(TMPDIR)/%.o : $(SRCDIR)/%.c + $(C) $(CFLAGS) -o $@ -c $< + +$(TMPDIR)/%.o : $(SRCDIR)/%.cc + $(CC) $(CCFLAGS) -o $@ -c $< + diff --git a/tool/lib/makefile_trc b/tool/lib/makefile_trc new file mode 100644 index 0000000..1edc8b3 --- /dev/null +++ b/tool/lib/makefile_trc @@ -0,0 +1,188 @@ + +# We should read the tranche sources and see what files they output, and then +# build the deps accordingly, but this makefile is more primitive approach. +# Here the programmer must have one output .c file or .cc file per input tranche +# file and it must be name .{lib,cli}.{c,cc} and the {.c,.cc} output +# tranche must be named .trc.{c,cc} + +# will re-orgnaize the directory structure .. will name the src-da .. etc. to the +# name of the module, then have a source dir inside that has only the stuff +# programmer's edit, then get rid of the numbers + +SHELL=/bin/bash + +#LIDBIR, EXECDIR, HDIR hold the make results that might later be staged +#$(PWD) is the directory that make was called from, this is already build in +#set to dot to use the same directory as the source code +#leave blank to ommit +DEPRDIR=deprecated +DOCDIR=doc +EXECDIR=exec +INCDIR=include +LIBDIR=lib +SRCDIR=src +TESTDIR=test +TMPDIR=tmp +TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) +TRYDIR=try + +LIBFILE=$(LIBDIR)/lib$(MODULE).a +INCFILE=$(MODULE).h + +# these are the sources edited by the programmer +C_SOURCE_LIB= $(wildcard $(SRCDIR)/*.lib.c) +C_SOURCE_CLI= $(wildcard $(SRCDIR)/*.cli.c) +CC_SOURCE_LIB= $(wildcard $(SRCDIR)/*.lib.cc) +CC_SOURCE_CLI= $(wildcard $(SRCDIR)/*.cli.cc) + +#remove the suffix to get base name +C_BASE_LIB= $(sort $(patsubst %.lib.c, %, $(notdir $(C_SOURCE_LIB)))) +C_BASE_CLI= $(sort $(patsubst %.cli.c, %, $(notdir $(C_SOURCE_CLI)))) +CC_BASE_LIB= $(sort $(patsubst %.lib.cc, %, $(notdir $(CC_SOURCE_LIB)))) +CC_BASE_CLI= $(sort $(patsubst %.cli.cc, %, $(notdir $(CC_SOURCE_CLI)))) + +# the progreammer must name the c and cc tranches accordingly: +C_TRC_LIB= $(patasubst %, $(TMPDIR)/%.trc.c, C_BASE_LIB) +C_TRC_CLI= $(patasubst %, $(TMPDIR)/%.trc.c, C_BASE_CLI) +CC_TRC_LIB= $(patasubst %, $(TMPDIR)/%.trc.cc, CC_BASE_LIB) +CC_TRC_CLI= $(patasubst %, $(TMPDIR)/%.trc.cc, CC_BASE_CLI) + +# two sets of object files, one for the lib, and one for the command line interface progs +OBJECT_LIB= $(patsubst %, $(TMDIR)/%.o, $(C_BASE_LIB) $(CC_BASE_LIB)) +OBJECT_CLI= $(patsubst %, $(TMDIR)/%.o, $(C_BASE_CLI) $(CC_BASE_CLI)) + +# executables are made from CLI sources +EXEC= (patsubst %, $(EXECDIR)/%, $(C_BASE_CLI) $(CC_BASE_CLI)) + +#otherwise make provides default values for these +C= +CC= + +-include makefile-flags + +DEPFILE=$(TMPDIR)/makefile_dep + +# a single space literal, for example if you wanted to subsitute commas to +# spaces: $(subst $(space),;,$(string)) we ran into this out of a need to send +# multiple separate command arguments to a shell script from one variable value +blank := +space :=$(blank) $(blank) + +all: version deps lib execs + +version: + @echo makefile version 3.0 + @echo "PWD: " $(PWD) + @echo "MAKEFILE_LIST: " $(MAKEFILE_LIST) + @echo "C: " $(C) + @echo "CFLAGS: " $(CFLAGS) + @echo "CC: " $(CC) + @echo "CCFLAGS: " $(CCFLAGS) + @echo "LINKFLAGS: " $(LINKFLAGS) + +info: + @echo "DEPDIR: " $(DEPDIR) + @echo "DOCDIR: " $(DOCDIR) + @echo "EXECSDIR: " $(EXECSDIR) + @echo "INCDIR: " $(INCDIR) + @echo "LIBDIR: " $(LIBDIR) + @echo "TESTDIR: " $(TESTDIR) + @echo "TMPDIR: " $(TMPDIR) + @echo "TOOLSDIR: " $(TOOLSDIR) + @echo "TRYDIR: " $(TRYDIR) + @echo "DEPSFILE: " $(DEPSFILE) + @echo "LIBFILE: " $(LIBFILE) + @echo "TRC_TRC: " $(TRC_TRC) + @echo "C_BASE_LIB: " $(C_BASE_LIB) + @echo "C_BASE_CLI: " $(C_BASE_CLI) + @echo "CC_BASE_LIB: " $(CC_BASE_LIB) + @echo "CC_BASE_CLI: " $(CC_BASE_CLI) + +# should be safe to run this in an already setup or partially setup directory +setup: + if [ ! -e $(DEPRDIR) ]; then mkdir $(DEPRDIR); fi + if [ ! -e $(DOCDIR) ]; then mkdir $(DOCDIR); fi + if [ ! -e $(EXECSDIR) ]; then mkdir $(EXECSDIR); fi + if [ ! -e $(INCDIR) ]; then mkdir $(INCDIR); fi + if [ ! -e $(LIBDIR) ]; then mkdir $(LIBDIR); fi + if [ ! -e $(SRCDIR) ]; then mkdir $(SRCDIR); fi + if [ ! -e $(TESTDIR) ]; then mkdir $(TESTDIR); fi + if [ ! -e $(TMPDIR) ]; then mkdir $(TMPDIR); fi + if [ ! -e $(TRYDIR) ]; then mkdir $(TRYDIR); fi + +dep: $(C_TRC_LIB) $(C_TRC_CLI) $(CC_TRC_LIB) $(CC_TRC_CLI) + if [ -z "$(CC)" ]; then\ + if [ -z "$C()" ]; then\ + echo "No compiler specified";\ + exit 1;\ + else\ + echo "C compiler only deps" ;\ + $(C) $(CFLAGS) -MM $(C_TRC_LIB) $(C_TRC_CLI) 1> $(DEPSFILE);\ + echo "deps for C linking";\ + for i in $(EXECS) ; do\ + $(ECHO) >> $(DEPSFILE);\ + $(ECHO) "$(EXECSDIR)/$$i : $$i.cli.o $(LIBDIR)/$(LIBFILE)" >> $(DEPSFILE);\ + $(ECHO) " $(C) -o $(EXECSDIR)/$$i $$i.cli.o $(LINKFLAGS)" >> $(DEPSFILE);\ + done;\ + fi;\ + else\ + $(CC) $(CCFLAGS) -MM $(CC_TRC_LIB) $(CC_TRC_CLI) 1> $(DEPSFILE);\ + if [ -z "$C()" ]; then\ + echo "CC compiler only deps" ;\ + else\ + echo "CC and C mixed compile deps" ;\ + $(C) $(CFLAGS) -MM $(C_TRC_LIB) $(C_TRC_CLI) 1>> $(DEPSFILE);\ + fi;\ + echo "deps for CC linking";\ + for i in $(EXECS) ; do\ + $(ECHO) >> $(DEPSFILE);\ + $(ECHO) "$(EXECSDIR)/$$i : $$i.cli.o $(LIBDIR)/$(LIBFILE)" >> $(DEPSFILE);\ + $(ECHO) " $(CC) -o $(EXECSDIR)/$$i $$i.cli.o $(LINKFLAGS)" >> $(DEPSFILE);\ + done;\ + fi + +lib: + make $(LIBDIR)/$(LIBFILE) + +$(LIBDIR)/$(LIBFILE) : $(OBJECTS_LIB) + ar rcs $(LIBDIR)/$(LIBFILE) $(OBJECTS_LIB) + +exec: $(LIBDIR)/$(LIBFILE) + make sub_exec + +sub_exec: $(patsubst %, $(EXECSDIR)/%, $(EXECS)) + +stage: + if [ -f $(LIBDIR)/$(LIBFILE) ]; then cp $(LIBDIR)/$(LIBFILE) $(PROJECT_SUBU)/stage/lib; fi + if [ -f $(INCDIR)/$(INCFILE) ]; then cp $(INCDIR)/$(INCFILE) $(PROJECT_SUBU)/stage/include; fi + -cp $(EXECSDIR)/* $(PROJECT_SUBU)/stage/bin + +clean: + for i in $(wildcard *~); do mv $$i $(TMPDIR); done + for i in $(wildcard *.lib.o) $(wildcard *.cli.o); do rm $$i; done + for i in $(EXECS); do if [ -e $(EXECSDIR)/$$i ]; then rm $(EXECSDIR)/$$i; fi; done + if [ -f $(LIBDIR)/$(LIBFILE) ]; then rm $(LIBDIR)/$(LIBFILE); fi + if [ -f $(DEPSFILE) ]; then rm $(DEPSFILE); fi + +-include $(DEPSFILE) + +# recipes +# +%.trc.c : %.lib.c + $(TRANCHE) $< + +%.trc.c : %.cli.c + $(TRANCHE) $< + +%.trc.cc : %.lib.cc + $(TRANCHE) $< + +%.trc.cc : %.cli.cc + $(TRANCHE) $< + +%.o : %.c + $(C) $(CFLAGS) -c $< + +%.o : %.cc + $(CC) $(CCFLAGS) -c $< + diff --git a/tool/src/makeheaders.c b/tool/src/makeheaders.c new file mode 100644 index 0000000..b58e787 --- /dev/null +++ b/tool/src/makeheaders.c @@ -0,0 +1,3739 @@ + + + + + + +Fossil: File Content + + + + +
+

Fossil

File Content
+ +
+ +
+
+ + + +
+ +
+

Latest version of file 'src/makeheaders.c':

+
    +
  • File +src/makeheaders.c +— part of check-in +[8cecc544] +at +2018-11-02 15:21:54 +on branch trunk +— Enhance makeheaders so that it is able to deal with static_assert() statements. +(These do not come up in Fossil itself. This check-in is in response to use +of Makeheaders by external projects.) + (user: +drh +size: 100011) +[more...] +
+
+
+
+/*
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** Copyright 1993 D. Richard Hipp. All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or
+** without modification, are permitted provided that the following
+** conditions are met:
+**
+**   1. Redistributions of source code must retain the above copyright
+**      notice, this list of conditions and the following disclaimer.
+**
+**   2. Redistributions in binary form must reproduce the above copyright
+**      notice, this list of conditions and the following disclaimer in
+**      the documentation and/or other materials provided with the
+**      distribution.
+**
+** This software is provided "as is" and any express or implied warranties,
+** including, but not limited to, the implied warranties of merchantability
+** and fitness for a particular purpose are disclaimed.  In no event shall
+** the author or contributors be liable for any direct, indirect, incidental,
+** special, exemplary, or consequential damages (including, but not limited
+** to, procurement of substitute goods or services; loss of use, data or
+** profits; or business interruption) however caused and on any theory of
+** liability, whether in contract, strict liability, or tort (including
+** negligence or otherwise) arising in any way out of the use of this
+** software, even if advised of the possibility of such damage.
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+** appropriate header files.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <string.h>
+
+#if defined( __MINGW32__) ||  defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__)
+#  ifndef WIN32
+#    define WIN32
+#  endif
+#else
+# include <unistd.h>
+#endif
+
+/*
+** Macros for debugging.
+*/
+#ifdef DEBUG
+static int debugMask = 0;
+# define debug0(F,M)       if( (F)&debugMask ){ fprintf(stderr,M); }
+# define debug1(F,M,A)     if( (F)&debugMask ){ fprintf(stderr,M,A); }
+# define debug2(F,M,A,B)   if( (F)&debugMask ){ fprintf(stderr,M,A,B); }
+# define debug3(F,M,A,B,C) if( (F)&debugMask ){ fprintf(stderr,M,A,B,C); }
+# define PARSER      0x00000001
+# define DECL_DUMP   0x00000002
+# define TOKENIZER   0x00000004
+#else
+# define debug0(Flags, Format)
+# define debug1(Flags, Format, A)
+# define debug2(Flags, Format, A, B)
+# define debug3(Flags, Format, A, B, C)
+#endif
+
+/*
+** The following macros are purely for the purpose of testing this
+** program on itself.  They don't really contribute to the code.
+*/
+#define INTERFACE 1
+#define EXPORT_INTERFACE 1
+#define EXPORT
+
+/*
+** Each token in a source file is represented by an instance of
+** the following structure.  Tokens are collected onto a list.
+*/
+typedef struct Token Token;
+struct Token {
+  const char *zText;      /* The text of the token */
+  int nText;              /* Number of characters in the token's text */
+  int eType;              /* The type of this token */
+  int nLine;              /* The line number on which the token starts */
+  Token *pComment;        /* Most recent block comment before this token */
+  Token *pNext;           /* Next token on the list */
+  Token *pPrev;           /* Previous token on the list */
+};
+
+/*
+** During tokenization, information about the state of the input
+** stream is held in an instance of the following structure
+*/
+typedef struct InStream InStream;
+struct InStream {
+  const char *z;          /* Complete text of the input */
+  int i;                  /* Next character to read from the input */
+  int nLine;              /* The line number for character z[i] */
+};
+
+/*
+** Each declaration in the C or C++ source files is parsed out and stored as
+** an instance of the following structure.
+**
+** A "forward declaration" is a declaration that an object exists that
+** doesn't tell about the objects structure.  A typical forward declaration
+** is:
+**
+**          struct Xyzzy;
+**
+** Not every object has a forward declaration.  If it does, thought, the
+** forward declaration will be contained in the zFwd field for C and
+** the zFwdCpp for C++.  The zDecl field contains the complete
+** declaration text.
+*/
+typedef struct Decl Decl;
+struct Decl {
+  char *zName;       /* Name of the object being declared.  The appearance
+                     ** of this name is a source file triggers the declaration
+                     ** to be added to the header for that file. */
+  const char *zFile; /* File from which extracted.  */
+  char *zIf;         /* Surround the declaration with this #if */
+  char *zFwd;        /* A forward declaration.  NULL if there is none. */
+  char *zFwdCpp;     /* Use this forward declaration for C++. */
+  char *zDecl;       /* A full declaration of this object */
+  char *zExtra;      /* Extra declaration text inserted into class objects */
+  int extraType;     /* Last public:, protected: or private: in zExtraDecl */
+  struct Include *pInclude;   /* #includes that come before this declaration */
+  int flags;         /* See the "Properties" below */
+  Token *pComment;   /* A block comment associated with this declaration */
+  Token tokenCode;   /* Implementation of functions and procedures */
+  Decl *pSameName;   /* Next declaration with the same "zName" */
+  Decl *pSameHash;   /* Next declaration with same hash but different zName */
+  Decl *pNext;       /* Next declaration with a different name */
+};
+
+/*
+** Properties associated with declarations.
+**
+** DP_Forward and DP_Declared are used during the generation of a single
+** header file in order to prevent duplicate declarations and definitions.
+** DP_Forward is set after the object has been given a forward declaration
+** and DP_Declared is set after the object gets a full declarations.
+** (Example:  A forward declaration is "typedef struct Abc Abc;" and the
+** full declaration is "struct Abc { int a; float b; };".)
+**
+** The DP_Export and DP_Local flags are more permanent.  They mark objects
+** that have EXPORT scope and LOCAL scope respectively.  If both of these
+** marks are missing, then the object has library scope.  The meanings of
+** the scopes are as follows:
+**
+**    LOCAL scope         The object is only usable within the file in
+**                        which it is declared.
+**
+**    library scope       The object is visible and usable within other
+**                        files in the same project.  By if the project is
+**                        a library, then the object is not visible to users
+**                        of the library.  (i.e. the object does not appear
+**                        in the output when using the -H option.)
+**
+**    EXPORT scope        The object is visible and usable everywhere.
+**
+** The DP_Flag is a temporary use flag that is used during processing to
+** prevent an infinite loop.  It's use is localized.
+**
+** The DP_Cplusplus, DP_ExternCReqd and DP_ExternReqd flags are permanent
+** and are used to specify what type of declaration the object requires.
+*/
+#define DP_Forward      0x001   /* Has a forward declaration in this file */
+#define DP_Declared     0x002   /* Has a full declaration in this file */
+#define DP_Export       0x004   /* Export this declaration */
+#define DP_Local        0x008   /* Declare in its home file only */
+#define DP_Flag         0x010   /* Use to mark a subset of a Decl list
+                                ** for special processing */
+#define DP_Cplusplus    0x020   /* Has C++ linkage and cannot appear in a
+                                ** C header file */
+#define DP_ExternCReqd  0x040   /* Prepend 'extern "C"' in a C++ header.
+                                ** Prepend nothing in a C header */
+#define DP_ExternReqd   0x080   /* Prepend 'extern "C"' in a C++ header if
+                                ** DP_Cplusplus is not also set. If DP_Cplusplus
+                                ** is set or this is a C header then
+                                ** prepend 'extern' */
+
+/*
+** Convenience macros for dealing with declaration properties
+*/
+#define DeclHasProperty(D,P)    (((D)->flags&(P))==(P))
+#define DeclHasAnyProperty(D,P) (((D)->flags&(P))!=0)
+#define DeclSetProperty(D,P)    (D)->flags |= (P)
+#define DeclClearProperty(D,P)  (D)->flags &= ~(P)
+
+/*
+** These are state properties of the parser.  Each of the values is
+** distinct from the DP_ values above so that both can be used in
+** the same "flags" field.
+**
+** Be careful not to confuse PS_Export with DP_Export or
+** PS_Local with DP_Local.  Their names are similar, but the meanings
+** of these flags are very different.
+*/
+#define PS_Extern        0x000800    /* "extern" has been seen */
+#define PS_Export        0x001000    /* If between "#if EXPORT_INTERFACE"
+                                     ** and "#endif" */
+#define PS_Export2       0x002000    /* If "EXPORT" seen */
+#define PS_Typedef       0x004000    /* If "typedef" has been seen */
+#define PS_Static        0x008000    /* If "static" has been seen */
+#define PS_Interface     0x010000    /* If within #if INTERFACE..#endif */
+#define PS_Method        0x020000    /* If "::" token has been seen */
+#define PS_Local         0x040000    /* If within #if LOCAL_INTERFACE..#endif */
+#define PS_Local2        0x080000    /* If "LOCAL" seen. */
+#define PS_Public        0x100000    /* If "PUBLIC" seen. */
+#define PS_Protected     0x200000    /* If "PROTECTED" seen. */
+#define PS_Private       0x400000    /* If "PRIVATE" seen. */
+#define PS_PPP           0x700000    /* If any of PUBLIC, PRIVATE, PROTECTED */
+
+/*
+** The following set of flags are ORed into the "flags" field of
+** a Decl in order to identify what type of object is being
+** declared.
+*/
+#define TY_Class         0x00100000
+#define TY_Subroutine    0x00200000
+#define TY_Macro         0x00400000
+#define TY_Typedef       0x00800000
+#define TY_Variable      0x01000000
+#define TY_Structure     0x02000000
+#define TY_Union         0x04000000
+#define TY_Enumeration   0x08000000
+#define TY_Defunct       0x10000000  /* Used to erase a declaration */
+
+/*
+** Each nested #if (or #ifdef or #ifndef) is stored in a stack of
+** instances of the following structure.
+*/
+typedef struct Ifmacro Ifmacro;
+struct Ifmacro {
+  int nLine;         /* Line number where this macro occurs */
+  char *zCondition;  /* Text of the condition for this macro */
+  Ifmacro *pNext;    /* Next down in the stack */
+  int flags;         /* Can hold PS_Export, PS_Interface or PS_Local flags */
+};
+
+/*
+** When parsing a file, we need to keep track of what other files have
+** be #include-ed.  For each #include found, we create an instance of
+** the following structure.
+*/
+typedef struct Include Include;
+struct Include {
+  char *zFile;       /* The name of file include.  Includes "" or <> */
+  char *zIf;         /* If not NULL, #include should be enclosed in #if */
+  char *zLabel;      /* A unique label used to test if this #include has
+                      * appeared already in a file or not */
+  Include *pNext;    /* Previous include file, or NULL if this is the first */
+};
+
+/*
+** Identifiers found in a source file that might be used later to provoke
+** the copying of a declaration into the corresponding header file are
+** stored in a hash table as instances of the following structure.
+*/
+typedef struct Ident Ident;
+struct Ident {
+  char *zName;        /* The text of this identifier */
+  Ident *pCollide;    /* Next identifier with the same hash */
+  Ident *pNext;       /* Next identifier in a list of them all */
+};
+
+/*
+** A complete table of identifiers is stored in an instance of
+** the next structure.
+*/
+#define IDENT_HASH_SIZE 2237
+typedef struct IdentTable IdentTable;
+struct IdentTable {
+  Ident *pList;                     /* List of all identifiers in this table */
+  Ident *apTable[IDENT_HASH_SIZE];  /* The hash table */
+};
+
+/*
+** The following structure holds all information for a single
+** source file named on the command line of this program.
+*/
+typedef struct InFile InFile;
+struct InFile {
+  char *zSrc;              /* Name of input file */
+  char *zHdr;              /* Name of the generated .h file for this input.
+                           ** Will be NULL if input is to be scanned only */
+  int flags;               /* One or more DP_, PS_ and/or TY_ flags */
+  InFile *pNext;           /* Next input file in the list of them all */
+  IdentTable idTable;      /* All identifiers in this input file */
+};
+
+/*
+** An unbounded string is able to grow without limit.  We use these
+** to construct large in-memory strings from lots of smaller components.
+*/
+typedef struct String String;
+struct String {
+  int nAlloc;      /* Number of bytes allocated */
+  int nUsed;       /* Number of bytes used (not counting null terminator) */
+  char *zText;     /* Text of the string */
+};
+
+/*
+** The following structure contains a lot of state information used
+** while generating a .h file.  We put the information in this structure
+** and pass around a pointer to this structure, rather than pass around
+** all of the information separately.  This helps reduce the number of
+** arguments to generator functions.
+*/
+typedef struct GenState GenState;
+struct GenState {
+  String *pStr;          /* Write output to this string */
+  IdentTable *pTable;    /* A table holding the zLabel of every #include that
+                          * has already been generated.  Used to avoid
+                          * generating duplicate #includes. */
+  const char *zIf;       /* If not NULL, then we are within a #if with
+                          * this argument. */
+  int nErr;              /* Number of errors */
+  const char *zFilename; /* Name of the source file being scanned */
+  int flags;             /* Various flags (DP_ and PS_ flags above) */
+};
+
+/*
+** The following text line appears at the top of every file generated
+** by this program.  By recognizing this line, the program can be sure
+** never to read a file that it generated itself.
+**
+** The "#undef INTERFACE" part is a hack to work around a name collision
+** in MSVC 2008.
+*/
+const char zTopLine[] =
+  "/* \aThis file was automatically generated.  Do not edit! */\n"
+  "#undef INTERFACE\n";
+#define nTopLine (sizeof(zTopLine)-1)
+
+/*
+** The name of the file currently being parsed.
+*/
+static const char *zFilename;
+
+/*
+** The stack of #if macros for the file currently being parsed.
+*/
+static Ifmacro *ifStack = 0;
+
+/*
+** A list of all files that have been #included so far in a file being
+** parsed.
+*/
+static Include *includeList = 0;
+
+/*
+** The last block comment seen.
+*/
+static Token *blockComment = 0;
+
+/*
+** The following flag is set if the -doc flag appears on the
+** command line.
+*/
+static int doc_flag = 0;
+
+/*
+** If the following flag is set, then makeheaders will attempt to
+** generate prototypes for static functions and procedures.
+*/
+static int proto_static = 0;
+
+/*
+** A list of all declarations.  The list is held together using the
+** pNext field of the Decl structure.
+*/
+static Decl *pDeclFirst;    /* First on the list */
+static Decl *pDeclLast;     /* Last on the list */
+
+/*
+** A hash table of all declarations
+*/
+#define DECL_HASH_SIZE 3371
+static Decl *apTable[DECL_HASH_SIZE];
+
+/*
+** The TEST macro must be defined to something.  Make sure this is the
+** case.
+*/
+#ifndef TEST
+# define TEST 0
+#endif
+
+#ifdef NOT_USED
+/*
+** We do our own assertion macro so that we can have more control
+** over debugging.
+*/
+#define Assert(X)    if(!(X)){ CantHappen(__LINE__); }
+#define CANT_HAPPEN  CantHappen(__LINE__)
+static void CantHappen(int iLine){
+  fprintf(stderr,"Assertion failed on line %d\n",iLine);
+  *(char*)1 = 0;  /* Force a core-dump */
+}
+#endif
+
+/*
+** Memory allocation functions that are guaranteed never to return NULL.
+*/
+static void *SafeMalloc(int nByte){
+  void *p = malloc( nByte );
+  if( p==0 ){
+    fprintf(stderr,"Out of memory.  Can't allocate %d bytes.\n",nByte);
+    exit(1);
+  }
+  return p;
+}
+static void SafeFree(void *pOld){
+  if( pOld ){
+    free(pOld);
+  }
+}
+static void *SafeRealloc(void *pOld, int nByte){
+  void *p;
+  if( pOld==0 ){
+    p = SafeMalloc(nByte);
+  }else{
+    p = realloc(pOld, nByte);
+    if( p==0 ){
+      fprintf(stderr,
+        "Out of memory.  Can't enlarge an allocation to %d bytes\n",nByte);
+      exit(1);
+    }
+  }
+  return p;
+}
+static char *StrDup(const char *zSrc, int nByte){
+  char *zDest;
+  if( nByte<=0 ){
+    nByte = strlen(zSrc);
+  }
+  zDest = SafeMalloc( nByte + 1 );
+  strncpy(zDest,zSrc,nByte);
+  zDest[nByte] = 0;
+  return zDest;
+}
+
+/*
+** Return TRUE if the character X can be part of an identifier
+*/
+#define ISALNUM(X)  ((X)=='_' || isalnum(X))
+
+/*
+** Routines for dealing with unbounded strings.
+*/
+static void StringInit(String *pStr){
+  pStr->nAlloc = 0;
+  pStr->nUsed = 0;
+  pStr->zText = 0;
+}
+static void StringReset(String *pStr){
+  SafeFree(pStr->zText);
+  StringInit(pStr);
+}
+static void StringAppend(String *pStr, const char *zText, int nByte){
+  if( nByte<=0 ){
+    nByte = strlen(zText);
+  }
+  if( pStr->nUsed + nByte >= pStr->nAlloc ){
+    if( pStr->nAlloc==0 ){
+      pStr->nAlloc = nByte + 100;
+      pStr->zText = SafeMalloc( pStr->nAlloc );
+    }else{
+      pStr->nAlloc = pStr->nAlloc*2 + nByte;
+      pStr->zText = SafeRealloc(pStr->zText, pStr->nAlloc);
+    }
+  }
+  strncpy(&pStr->zText[pStr->nUsed],zText,nByte);
+  pStr->nUsed += nByte;
+  pStr->zText[pStr->nUsed] = 0;
+}
+#define StringGet(S) ((S)->zText?(S)->zText:"")
+
+/*
+** Compute a hash on a string.  The number returned is a non-negative
+** value between 0 and 2**31 - 1
+*/
+static int Hash(const char *z, int n){
+  int h = 0;
+  if( n<=0 ){
+    n = strlen(z);
+  }
+  while( n-- ){
+    h = h ^ (h<<5) ^ *z++;
+  }
+  return h & 0x7fffffff;
+}
+
+/*
+** Given an identifier name, try to find a declaration for that
+** identifier in the hash table.  If found, return a pointer to
+** the Decl structure.  If not found, return 0.
+*/
+static Decl *FindDecl(const char *zName, int len){
+  int h;
+  Decl *p;
+
+  if( len<=0 ){
+    len = strlen(zName);
+  }
+  h = Hash(zName,len) % DECL_HASH_SIZE;
+  p = apTable[h];
+  while( p && (strncmp(p->zName,zName,len)!=0 || p->zName[len]!=0) ){
+    p = p->pSameHash;
+  }
+  return p;
+}
+
+/*
+** Install the given declaration both in the hash table and on
+** the list of all declarations.
+*/
+static void InstallDecl(Decl *pDecl){
+  int h;
+  Decl *pOther;
+
+  h = Hash(pDecl->zName,0) % DECL_HASH_SIZE;
+  pOther = apTable[h];
+  while( pOther && strcmp(pDecl->zName,pOther->zName)!=0 ){
+    pOther = pOther->pSameHash;
+  }
+  if( pOther ){
+    pDecl->pSameName = pOther->pSameName;
+    pOther->pSameName = pDecl;
+  }else{
+    pDecl->pSameName = 0;
+    pDecl->pSameHash = apTable[h];
+    apTable[h] = pDecl;
+  }
+  pDecl->pNext = 0;
+  if( pDeclFirst==0 ){
+    pDeclFirst = pDeclLast = pDecl;
+  }else{
+    pDeclLast->pNext = pDecl;
+    pDeclLast = pDecl;
+  }
+}
+
+/*
+** Look at the current ifStack.  If anything declared at the current
+** position must be surrounded with
+**
+**      #if   STUFF
+**      #endif
+**
+** Then this routine computes STUFF and returns a pointer to it.  Memory
+** to hold the value returned is obtained from malloc().
+*/
+static char *GetIfString(void){
+  Ifmacro *pIf;
+  char *zResult = 0;
+  int hasIf = 0;
+  String str;
+
+  for(pIf = ifStack; pIf; pIf=pIf->pNext){
+    if( pIf->zCondition==0 || *pIf->zCondition==0 ) continue;
+    if( !hasIf ){
+      hasIf = 1;
+      StringInit(&str);
+    }else{
+      StringAppend(&str," && ",4);
+    }
+    StringAppend(&str,pIf->zCondition,0);
+  }
+  if( hasIf ){
+    zResult = StrDup(StringGet(&str),0);
+    StringReset(&str);
+  }else{
+    zResult = 0;
+  }
+  return zResult;
+}
+
+/*
+** Create a new declaration and put it in the hash table.  Also
+** return a pointer to it so that we can fill in the zFwd and zDecl
+** fields, and so forth.
+*/
+static Decl *CreateDecl(
+  const char *zName,       /* Name of the object being declared. */
+  int nName                /* Length of the name */
+){
+  Decl *pDecl;
+
+  pDecl = SafeMalloc( sizeof(Decl) + nName + 1);
+  memset(pDecl,0,sizeof(Decl));
+  pDecl->zName = (char*)&pDecl[1];
+  sprintf(pDecl->zName,"%.*s",nName,zName);
+  pDecl->zFile = zFilename;
+  pDecl->pInclude = includeList;
+  pDecl->zIf = GetIfString();
+  InstallDecl(pDecl);
+  return pDecl;
+}
+
+/*
+** Insert a new identifier into an table of identifiers.  Return TRUE if
+** a new identifier was inserted and return FALSE if the identifier was
+** already in the table.
+*/
+static int IdentTableInsert(
+  IdentTable *pTable,       /* The table into which we will insert */
+  const char *zId,          /* Name of the identifiers */
+  int nId                   /* Length of the identifier name */
+){
+  int h;
+  Ident *pId;
+
+  if( nId<=0 ){
+    nId = strlen(zId);
+  }
+  h = Hash(zId,nId) % IDENT_HASH_SIZE;
+  for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){
+    if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){
+      /* printf("Already in table: %.*s\n",nId,zId); */
+      return 0;
+    }
+  }
+  pId = SafeMalloc( sizeof(Ident) + nId + 1 );
+  pId->zName = (char*)&pId[1];
+  sprintf(pId->zName,"%.*s",nId,zId);
+  pId->pNext = pTable->pList;
+  pTable->pList = pId;
+  pId->pCollide = pTable->apTable[h];
+  pTable->apTable[h] = pId;
+  /* printf("Add to table: %.*s\n",nId,zId); */
+  return 1;
+}
+
+/*
+** Check to see if the given value is in the given IdentTable.  Return
+** true if it is and false if it is not.
+*/
+static int IdentTableTest(
+  IdentTable *pTable,       /* The table in which to search */
+  const char *zId,          /* Name of the identifiers */
+  int nId                   /* Length of the identifier name */
+){
+  int h;
+  Ident *pId;
+
+  if( nId<=0 ){
+    nId = strlen(zId);
+  }
+  h = Hash(zId,nId) % IDENT_HASH_SIZE;
+  for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){
+    if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/*
+** Remove every identifier from the given table.   Reset the table to
+** its initial state.
+*/
+static void IdentTableReset(IdentTable *pTable){
+  Ident *pId, *pNext;
+
+  for(pId = pTable->pList; pId; pId = pNext){
+    pNext = pId->pNext;
+    SafeFree(pId);
+  }
+  memset(pTable,0,sizeof(IdentTable));
+}
+
+#ifdef DEBUG
+/*
+** Print the name of every identifier in the given table, one per line
+*/
+static void IdentTablePrint(IdentTable *pTable, FILE *pOut){
+  Ident *pId;
+
+  for(pId = pTable->pList; pId; pId = pId->pNext){
+    fprintf(pOut,"%s\n",pId->zName);
+  }
+}
+#endif
+
+/*
+** Read an entire file into memory.  Return a pointer to the memory.
+**
+** The memory is obtained from SafeMalloc and must be freed by the
+** calling function.
+**
+** If the read fails for any reason, 0 is returned.
+*/
+static char *ReadFile(const char *zFilename){
+  struct stat sStat;
+  FILE *pIn;
+  char *zBuf;
+  int n;
+
+  if( stat(zFilename,&sStat)!=0
+#ifndef WIN32
+    || !S_ISREG(sStat.st_mode)
+#endif
+  ){
+    return 0;
+  }
+  pIn = fopen(zFilename,"r");
+  if( pIn==0 ){
+    return 0;
+  }
+  zBuf = SafeMalloc( sStat.st_size + 1 );
+  n = fread(zBuf,1,sStat.st_size,pIn);
+  zBuf[n] = 0;
+  fclose(pIn);
+  return zBuf;
+}
+
+/*
+** Write the contents of a string into a file.  Return the number of
+** errors
+*/
+static int WriteFile(const char *zFilename, const char *zOutput){
+  FILE *pOut;
+  pOut = fopen(zFilename,"w");
+  if( pOut==0 ){
+    return 1;
+  }
+  fwrite(zOutput,1,strlen(zOutput),pOut);
+  fclose(pOut);
+  return 0;
+}
+
+/*
+** Major token types
+*/
+#define TT_Space           1   /* Contiguous white space */
+#define TT_Id              2   /* An identifier */
+#define TT_Preprocessor    3   /* Any C preprocessor directive */
+#define TT_Comment         4   /* Either C or C++ style comment */
+#define TT_Number          5   /* Any numeric constant */
+#define TT_String          6   /* String or character constants. ".." or '.' */
+#define TT_Braces          7   /* All text between { and a matching } */
+#define TT_EOF             8   /* End of file */
+#define TT_Error           9   /* An error condition */
+#define TT_BlockComment    10  /* A C-Style comment at the left margin that
+                                * spans multiple lines */
+#define TT_Other           0   /* None of the above */
+
+/*
+** Get a single low-level token from the input file.  Update the
+** file pointer so that it points to the first character beyond the
+** token.
+**
+** A "low-level token" is any token except TT_Braces.  A TT_Braces token
+** consists of many smaller tokens and is assembled by a routine that
+** calls this one.
+**
+** The function returns the number of errors.  An error is an
+** unterminated string or character literal or an unterminated
+** comment.
+**
+** Profiling shows that this routine consumes about half the
+** CPU time on a typical run of makeheaders.
+*/
+static int GetToken(InStream *pIn, Token *pToken){
+  int i;
+  const char *z;
+  int cStart;
+  int c;
+  int startLine;   /* Line on which a structure begins */
+  int nlisc = 0;   /* True if there is a new-line in a ".." or '..' */
+  int nErr = 0;    /* Number of errors seen */
+
+  z = pIn->z;
+  i = pIn->i;
+  pToken->nLine = pIn->nLine;
+  pToken->zText = &z[i];
+  switch( z[i] ){
+    case 0:
+      pToken->eType = TT_EOF;
+      pToken->nText = 0;
+      break;
+
+    case '#':
+      if( i==0 || z[i-1]=='\n' || (i>1 && z[i-1]=='\r' && z[i-2]=='\n')){
+        /* We found a preprocessor statement */
+        pToken->eType = TT_Preprocessor;
+        i++;
+        while( z[i]!=0 && z[i]!='\n' ){
+          if( z[i]=='\\' ){
+            i++;
+            if( z[i]=='\n' ) pIn->nLine++;
+          }
+          i++;
+        }
+        pToken->nText = i - pIn->i;
+      }else{
+        /* Just an operator */
+        pToken->eType = TT_Other;
+        pToken->nText = 1;
+      }
+      break;
+
+    case ' ':
+    case '\t':
+    case '\r':
+    case '\f':
+    case '\n':
+      while( isspace(z[i]) ){
+        if( z[i]=='\n' ) pIn->nLine++;
+        i++;
+      }
+      pToken->eType = TT_Space;
+      pToken->nText = i - pIn->i;
+      break;
+
+    case '\\':
+      pToken->nText = 2;
+      pToken->eType = TT_Other;
+      if( z[i+1]=='\n' ){
+        pIn->nLine++;
+        pToken->eType = TT_Space;
+      }else if( z[i+1]==0 ){
+        pToken->nText = 1;
+      }
+      break;
+
+    case '\'':
+    case '\"':
+      cStart = z[i];
+      startLine = pIn->nLine;
+      do{
+        i++;
+        c = z[i];
+        if( c=='\n' ){
+          if( !nlisc ){
+            fprintf(stderr,
+              "%s:%d: (warning) Newline in string or character literal.\n",
+              zFilename, pIn->nLine);
+            nlisc = 1;
+          }
+          pIn->nLine++;
+        }
+        if( c=='\\' ){
+          i++;
+          c = z[i];
+          if( c=='\n' ){
+            pIn->nLine++;
+          }
+        }else if( c==cStart ){
+          i++;
+          c = 0;
+        }else if( c==0 ){
+          fprintf(stderr, "%s:%d: Unterminated string or character literal.\n",
+             zFilename, startLine);
+          nErr++;
+        }
+      }while( c );
+      pToken->eType = TT_String;
+      pToken->nText = i - pIn->i;
+      break;
+
+    case '/':
+      if( z[i+1]=='/' ){
+        /* C++ style comment */
+        while( z[i] && z[i]!='\n' ){ i++; }
+        pToken->eType = TT_Comment;
+        pToken->nText = i - pIn->i;
+      }else if( z[i+1]=='*' ){
+        /* C style comment */
+        int isBlockComment = i==0 || z[i-1]=='\n';
+        i += 2;
+        startLine = pIn->nLine;
+        while( z[i] && (z[i]!='*' || z[i+1]!='/') ){
+          if( z[i]=='\n' ){
+            pIn->nLine++;
+            if( isBlockComment ){
+              if( z[i+1]=='*' || z[i+2]=='*' ){
+                 isBlockComment = 2;
+              }else{
+                 isBlockComment = 0;
+              }
+            }
+          }
+          i++;
+        }
+        if( z[i] ){
+          i += 2;
+        }else{
+          isBlockComment = 0;
+          fprintf(stderr,"%s:%d: Unterminated comment\n",
+            zFilename, startLine);
+          nErr++;
+        }
+        pToken->eType = isBlockComment==2 ? TT_BlockComment : TT_Comment;
+        pToken->nText = i - pIn->i;
+      }else{
+        /* A divide operator */
+        pToken->eType = TT_Other;
+        pToken->nText = 1 + (z[i+1]=='+');
+      }
+      break;
+
+    case '0':
+      if( z[i+1]=='x' || z[i+1]=='X' ){
+        /* A hex constant */
+        i += 2;
+        while( isxdigit(z[i]) ){ i++; }
+      }else{
+        /* An octal constant */
+        while( isdigit(z[i]) ){ i++; }
+      }
+      pToken->eType = TT_Number;
+      pToken->nText = i - pIn->i;
+      break;
+
+    case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+      while( isdigit(z[i]) ){ i++; }
+      if( (c=z[i])=='.' ){
+         i++;
+         while( isdigit(z[i]) ){ i++; }
+         c = z[i];
+         if( c=='e' || c=='E' ){
+           i++;
+           if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; }
+           while( isdigit(z[i]) ){ i++; }
+           c = z[i];
+         }
+         if( c=='f' || c=='F' || c=='l' || c=='L' ){ i++; }
+      }else if( c=='e' || c=='E' ){
+         i++;
+         if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; }
+         while( isdigit(z[i]) ){ i++; }
+      }else if( c=='L' || c=='l' ){
+         i++;
+         c = z[i];
+         if( c=='u' || c=='U' ){ i++; }
+      }else if( c=='u' || c=='U' ){
+         i++;
+         c = z[i];
+         if( c=='l' || c=='L' ){ i++; }
+      }
+      pToken->eType = TT_Number;
+      pToken->nText = i - pIn->i;
+      break;
+
+    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+    case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+    case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+    case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B':
+    case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I':
+    case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P':
+    case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W':
+    case 'X': case 'Y': case 'Z': case '_':
+      while( isalnum(z[i]) || z[i]=='_' ){ i++; };
+      pToken->eType = TT_Id;
+      pToken->nText = i - pIn->i;
+      break;
+
+    case ':':
+      pToken->eType = TT_Other;
+      pToken->nText = 1 + (z[i+1]==':');
+      break;
+
+    case '=':
+    case '<':
+    case '>':
+    case '+':
+    case '-':
+    case '*':
+    case '%':
+    case '^':
+    case '&':
+    case '|':
+      pToken->eType = TT_Other;
+      pToken->nText = 1 + (z[i+1]=='=');
+      break;
+
+    default:
+      pToken->eType = TT_Other;
+      pToken->nText = 1;
+      break;
+  }
+  pIn->i += pToken->nText;
+  return nErr;
+}
+
+/*
+** This routine recovers the next token from the input file which is
+** not a space or a comment or any text between an "#if 0" and "#endif".
+**
+** This routine returns the number of errors encountered.  An error
+** is an unterminated token or unmatched "#if 0".
+**
+** Profiling shows that this routine uses about a quarter of the
+** CPU time in a typical run.
+*/
+static int GetNonspaceToken(InStream *pIn, Token *pToken){
+  int nIf = 0;
+  int inZero = 0;
+  const char *z;
+  int value;
+  int startLine;
+  int nErr = 0;
+
+  startLine = pIn->nLine;
+  while( 1 ){
+    nErr += GetToken(pIn,pToken);
+    /* printf("%04d: Type=%d nIf=%d [%.*s]\n",
+       pToken->nLine,pToken->eType,nIf,pToken->nText,
+       pToken->eType!=TT_Space ? pToken->zText : "<space>"); */
+    pToken->pComment = blockComment;
+    switch( pToken->eType ){
+      case TT_Comment:          /*0123456789 12345678 */
+       if( strncmp(pToken->zText, "/*MAKEHEADERS-STOP", 18)==0 ) return nErr;
+       break;
+
+      case TT_Space:
+        break;
+
+      case TT_BlockComment:
+        if( doc_flag ){
+          blockComment = SafeMalloc( sizeof(Token) );
+          *blockComment = *pToken;
+        }
+        break;
+
+      case TT_EOF:
+        if( nIf ){
+          fprintf(stderr,"%s:%d: Unterminated \"#if\"\n",
+             zFilename, startLine);
+          nErr++;
+        }
+        return nErr;
+
+      case TT_Preprocessor:
+        z = &pToken->zText[1];
+        while( *z==' ' || *z=='\t' ) z++;
+        if( sscanf(z,"if %d",&value)==1 && value==0 ){
+          nIf++;
+          inZero = 1;
+        }else if( inZero ){
+          if( strncmp(z,"if",2)==0 ){
+            nIf++;
+          }else if( strncmp(z,"endif",5)==0 ){
+            nIf--;
+            if( nIf==0 ) inZero = 0;
+          }
+        }else{
+          return nErr;
+        }
+        break;
+
+      default:
+        if( !inZero ){
+          return nErr;
+        }
+        break;
+    }
+  }
+  /* NOT REACHED */
+}
+
+/*
+** This routine looks for identifiers (strings of contiguous alphanumeric
+** characters) within a preprocessor directive and adds every such string
+** found to the given identifier table
+*/
+static void FindIdentifiersInMacro(Token *pToken, IdentTable *pTable){
+  Token sToken;
+  InStream sIn;
+  int go = 1;
+
+  sIn.z = pToken->zText;
+  sIn.i = 1;
+  sIn.nLine = 1;
+  while( go && sIn.i < pToken->nText ){
+    GetToken(&sIn,&sToken);
+    switch( sToken.eType ){
+      case TT_Id:
+        IdentTableInsert(pTable,sToken.zText,sToken.nText);
+        break;
+
+      case TT_EOF:
+        go = 0;
+        break;
+
+      default:
+        break;
+    }
+  }
+}
+
+/*
+** This routine gets the next token.  Everything contained within
+** {...} is collapsed into a single TT_Braces token.  Whitespace is
+** omitted.
+**
+** If pTable is not NULL, then insert every identifier seen into the
+** IdentTable.  This includes any identifiers seen inside of {...}.
+**
+** The number of errors encountered is returned.  An error is an
+** unterminated token.
+*/
+static int GetBigToken(InStream *pIn, Token *pToken, IdentTable *pTable){
+  const char *zStart;
+  int iStart;
+  int nBrace;
+  int c;
+  int nLine;
+  int nErr;
+
+  nErr = GetNonspaceToken(pIn,pToken);
+  switch( pToken->eType ){
+    case TT_Id:
+      if( pTable!=0 ){
+        IdentTableInsert(pTable,pToken->zText,pToken->nText);
+      }
+      return nErr;
+
+    case TT_Preprocessor:
+      if( pTable!=0 ){
+        FindIdentifiersInMacro(pToken,pTable);
+      }
+      return nErr;
+
+    case TT_Other:
+      if( pToken->zText[0]=='{' ) break;
+      return nErr;
+
+    default:
+      return nErr;
+  }
+
+  iStart = pIn->i;
+  zStart = pToken->zText;
+  nLine = pToken->nLine;
+  nBrace = 1;
+  while( nBrace ){
+    nErr += GetNonspaceToken(pIn,pToken);
+    /* printf("%04d: nBrace=%d [%.*s]\n",pToken->nLine,nBrace,
+       pToken->nText,pToken->zText); */
+    switch( pToken->eType ){
+      case TT_EOF:
+        fprintf(stderr,"%s:%d: Unterminated \"{\"\n",
+           zFilename, nLine);
+        nErr++;
+        pToken->eType = TT_Error;
+        return nErr;
+
+      case TT_Id:
+        if( pTable ){
+          IdentTableInsert(pTable,pToken->zText,pToken->nText);
+        }
+        break;
+
+      case TT_Preprocessor:
+        if( pTable!=0 ){
+          FindIdentifiersInMacro(pToken,pTable);
+        }
+        break;
+
+      case TT_Other:
+        if( (c = pToken->zText[0])=='{' ){
+          nBrace++;
+        }else if( c=='}' ){
+          nBrace--;
+        }
+        break;
+
+      default:
+        break;
+    }
+  }
+  pToken->eType = TT_Braces;
+  pToken->nText = 1 + pIn->i - iStart;
+  pToken->zText = zStart;
+  pToken->nLine = nLine;
+  return nErr;
+}
+
+/*
+** This routine frees up a list of Tokens.  The pComment tokens are
+** not cleared by this.  So we leak a little memory when using the -doc
+** option.  So what.
+*/
+static void FreeTokenList(Token *pList){
+  Token *pNext;
+  while( pList ){
+    pNext = pList->pNext;
+    SafeFree(pList);
+    pList = pNext;
+  }
+}
+
+/*
+** Tokenize an entire file.  Return a pointer to the list of tokens.
+**
+** Space for each token is obtained from a separate malloc() call.  The
+** calling function is responsible for freeing this space.
+**
+** If pTable is not NULL, then fill the table with all identifiers seen in
+** the input file.
+*/
+static Token *TokenizeFile(const char *zFile, IdentTable *pTable){
+  InStream sIn;
+  Token *pFirst = 0, *pLast = 0, *pNew;
+  int nErr = 0;
+
+  sIn.z = zFile;
+  sIn.i = 0;
+  sIn.nLine = 1;
+  blockComment = 0;
+
+  while( sIn.z[sIn.i]!=0 ){
+    pNew = SafeMalloc( sizeof(Token) );
+    nErr += GetBigToken(&sIn,pNew,pTable);
+    debug3(TOKENIZER, "Token on line %d: [%.*s]\n",
+       pNew->nLine, pNew->nText<50 ? pNew->nText : 50, pNew->zText);
+    if( pFirst==0 ){
+      pFirst = pLast = pNew;
+      pNew->pPrev = 0;
+    }else{
+      pLast->pNext = pNew;
+      pNew->pPrev = pLast;
+      pLast = pNew;
+    }
+    if( pNew->eType==TT_EOF ) break;
+  }
+  if( pLast ) pLast->pNext = 0;
+  blockComment = 0;
+  if( nErr ){
+    FreeTokenList(pFirst);
+    pFirst = 0;
+  }
+
+  return pFirst;
+}
+
+#if TEST==1
+/*
+** Use the following routine to test or debug the tokenizer.
+*/
+void main(int argc, char **argv){
+  char *zFile;
+  Token *pList, *p;
+  IdentTable sTable;
+
+  if( argc!=2 ){
+    fprintf(stderr,"Usage: %s filename\n",*argv);
+    exit(1);
+  }
+  memset(&sTable,0,sizeof(sTable));
+  zFile = ReadFile(argv[1]);
+  if( zFile==0 ){
+    fprintf(stderr,"Can't read file \"%s\"\n",argv[1]);
+    exit(1);
+  }
+  pList = TokenizeFile(zFile,&sTable);
+  for(p=pList; p; p=p->pNext){
+    int j;
+    switch( p->eType ){
+      case TT_Space:
+        printf("%4d: Space\n",p->nLine);
+        break;
+      case TT_Id:
+        printf("%4d: Id           %.*s\n",p->nLine,p->nText,p->zText);
+        break;
+      case TT_Preprocessor:
+        printf("%4d: Preprocessor %.*s\n",p->nLine,p->nText,p->zText);
+        break;
+      case TT_Comment:
+        printf("%4d: Comment\n",p->nLine);
+        break;
+      case TT_BlockComment:
+        printf("%4d: Block Comment\n",p->nLine);
+        break;
+      case TT_Number:
+        printf("%4d: Number       %.*s\n",p->nLine,p->nText,p->zText);
+        break;
+      case TT_String:
+        printf("%4d: String       %.*s\n",p->nLine,p->nText,p->zText);
+        break;
+      case TT_Other:
+        printf("%4d: Other        %.*s\n",p->nLine,p->nText,p->zText);
+        break;
+      case TT_Braces:
+        for(j=0; j<p->nText && j<30 && p->zText[j]!='\n'; j++){}
+        printf("%4d: Braces       %.*s...}\n",p->nLine,j,p->zText);
+        break;
+      case TT_EOF:
+        printf("%4d: End of file\n",p->nLine);
+        break;
+      default:
+        printf("%4d: type %d\n",p->nLine,p->eType);
+        break;
+    }
+  }
+  FreeTokenList(pList);
+  SafeFree(zFile);
+  IdentTablePrint(&sTable,stdout);
+}
+#endif
+
+#ifdef DEBUG
+/*
+** For debugging purposes, write out a list of tokens.
+*/
+static void PrintTokens(Token *pFirst, Token *pLast){
+  int needSpace = 0;
+  int c;
+
+  pLast = pLast->pNext;
+  while( pFirst!=pLast ){
+    switch( pFirst->eType ){
+      case TT_Preprocessor:
+        printf("\n%.*s\n",pFirst->nText,pFirst->zText);
+        needSpace = 0;
+        break;
+
+      case TT_Id:
+      case TT_Number:
+        printf("%s%.*s", needSpace ? " " : "", pFirst->nText, pFirst->zText);
+        needSpace = 1;
+        break;
+
+      default:
+        c = pFirst->zText[0];
+        printf("%s%.*s",
+          (needSpace && (c=='*' || c=='{')) ? " " : "",
+          pFirst->nText, pFirst->zText);
+        needSpace = pFirst->zText[0]==',';
+        break;
+    }
+    pFirst = pFirst->pNext;
+  }
+}
+#endif
+
+/*
+** Convert a sequence of tokens into a string and return a pointer
+** to that string.  Space to hold the string is obtained from malloc()
+** and must be freed by the calling function.
+**
+** Certain keywords (EXPORT, PRIVATE, PUBLIC, PROTECTED) are always
+** skipped.
+**
+** If pSkip!=0 then skip over nSkip tokens beginning with pSkip.
+**
+** If zTerm!=0 then append the text to the end.
+*/
+static char *TokensToString(
+  Token *pFirst,    /* First token in the string */
+  Token *pLast,     /* Last token in the string */
+  char *zTerm,      /* Terminate the string with this text if not NULL */
+  Token *pSkip,     /* Skip this token if not NULL */
+  int nSkip         /* Skip a total of this many tokens */
+){
+  char *zReturn;
+  String str;
+  int needSpace = 0;
+  int c;
+  int iSkip = 0;
+  int skipOne = 0;
+
+  StringInit(&str);
+  pLast = pLast->pNext;
+  while( pFirst!=pLast ){
+    if( pFirst==pSkip ){ iSkip = nSkip; }
+    if( iSkip>0 ){
+      iSkip--;
+      pFirst=pFirst->pNext;
+      continue;
+    }
+    switch( pFirst->eType ){
+      case TT_Preprocessor:
+        StringAppend(&str,"\n",1);
+        StringAppend(&str,pFirst->zText,pFirst->nText);
+        StringAppend(&str,"\n",1);
+        needSpace = 0;
+        break;
+
+      case TT_Id:
+        switch( pFirst->zText[0] ){
+          case 'E':
+            if( pFirst->nText==6 && strncmp(pFirst->zText,"EXPORT",6)==0 ){
+              skipOne = 1;
+            }
+            break;
+          case 'P':
+            switch( pFirst->nText ){
+              case 6:  skipOne = !strncmp(pFirst->zText,"PUBLIC", 6);    break;
+              case 7:  skipOne = !strncmp(pFirst->zText,"PRIVATE",7);    break;
+              case 9:  skipOne = !strncmp(pFirst->zText,"PROTECTED",9);  break;
+              default: break;
+            }
+            break;
+          default:
+            break;
+        }
+        if( skipOne ){
+          pFirst = pFirst->pNext;
+          continue;
+        }
+        /* Fall thru to the next case */
+      case TT_Number:
+        if( needSpace ){
+          StringAppend(&str," ",1);
+        }
+        StringAppend(&str,pFirst->zText,pFirst->nText);
+        needSpace = 1;
+        break;
+
+      default:
+        c = pFirst->zText[0];
+        if( needSpace && (c=='*' || c=='{') ){
+          StringAppend(&str," ",1);
+        }
+        StringAppend(&str,pFirst->zText,pFirst->nText);
+        /* needSpace = pFirst->zText[0]==','; */
+        needSpace = 0;
+        break;
+    }
+    pFirst = pFirst->pNext;
+  }
+  if( zTerm && *zTerm ){
+    StringAppend(&str,zTerm,strlen(zTerm));
+  }
+  zReturn = StrDup(StringGet(&str),0);
+  StringReset(&str);
+  return zReturn;
+}
+
+/*
+** This routine is called when we see one of the keywords "struct",
+** "enum", "union" or "class".  This might be the beginning of a
+** type declaration.  This routine will process the declaration and
+** remove the declaration tokens from the input stream.
+**
+** If this is a type declaration that is immediately followed by a
+** semicolon (in other words it isn't also a variable definition)
+** then set *pReset to ';'.  Otherwise leave *pReset at 0.  The
+** *pReset flag causes the parser to skip ahead to the next token
+** that begins with the value placed in the *pReset flag, if that
+** value is different from 0.
+*/
+static int ProcessTypeDecl(Token *pList, int flags, int *pReset){
+  Token *pName, *pEnd;
+  Decl *pDecl;
+  String str;
+  int need_to_collapse = 1;
+  int type = 0;
+
+  *pReset = 0;
+  if( pList==0 || pList->pNext==0 || pList->pNext->eType!=TT_Id ){
+    return 0;
+  }
+  pName = pList->pNext;
+
+  /* Catch the case of "struct Foo;" and skip it. */
+  if( pName->pNext && pName->pNext->zText[0]==';' ){
+    *pReset = ';';
+    return 0;
+  }
+
+  for(pEnd=pName->pNext; pEnd && pEnd->eType!=TT_Braces; pEnd=pEnd->pNext){
+    switch( pEnd->zText[0] ){
+      case '(':
+      case ')':
+      case '*':
+      case '[':
+      case '=':
+      case ';':
+        return 0;
+    }
+  }
+  if( pEnd==0 ){
+    return 0;
+  }
+
+  /*
+  ** At this point, we know we have a type declaration that is bounded
+  ** by pList and pEnd and has the name pName.
+  */
+
+  /*
+  ** If the braces are followed immediately by a semicolon, then we are
+  ** dealing a type declaration only.  There is not variable definition
+  ** following the type declaration.  So reset...
+  */
+  if( pEnd->pNext==0 || pEnd->pNext->zText[0]==';' ){
+    *pReset = ';';
+    need_to_collapse = 0;
+  }else{
+    need_to_collapse = 1;
+  }
+
+  if( proto_static==0 && (flags & (PS_Local|PS_Export|PS_Interface))==0 ){
+    /* Ignore these objects unless they are explicitly declared as interface,
+    ** or unless the "-local" command line option was specified. */
+    *pReset = ';';
+    return 0;
+  }
+
+#ifdef DEBUG
+  if( debugMask & PARSER ){
+    printf("**** Found type: %.*s %.*s...\n",
+      pList->nText, pList->zText, pName->nText, pName->zText);
+    PrintTokens(pList,pEnd);
+    printf(";\n");
+  }
+#endif
+
+  /*
+  ** Create a new Decl object for this definition.  Actually, if this
+  ** is a C++ class definition, then the Decl object might already exist,
+  ** so check first for that case before creating a new one.
+  */
+  switch( *pList->zText ){
+    case 'c':  type = TY_Class;        break;
+    case 's':  type = TY_Structure;    break;
+    case 'e':  type = TY_Enumeration;  break;
+    case 'u':  type = TY_Union;        break;
+    default:   /* Can't Happen */      break;
+  }
+  if( type!=TY_Class ){
+    pDecl = 0;
+  }else{
+    pDecl = FindDecl(pName->zText, pName->nText);
+    if( pDecl && (pDecl->flags & type)!=type ) pDecl = 0;
+  }
+  if( pDecl==0 ){
+    pDecl = CreateDecl(pName->zText,pName->nText);
+  }
+  if( (flags & PS_Static) || !(flags & (PS_Interface|PS_Export)) ){
+    DeclSetProperty(pDecl,DP_Local);
+  }
+  DeclSetProperty(pDecl,type);
+
+  /* The object has a full declaration only if it is contained within
+  ** "#if INTERFACE...#endif" or "#if EXPORT_INTERFACE...#endif" or
+  ** "#if LOCAL_INTERFACE...#endif".  Otherwise, we only give it a
+  ** forward declaration.
+  */
+  if( flags & (PS_Local | PS_Export | PS_Interface)  ){
+    pDecl->zDecl = TokensToString(pList,pEnd,";\n",0,0);
+  }else{
+    pDecl->zDecl = 0;
+  }
+  pDecl->pComment = pList->pComment;
+  StringInit(&str);
+  StringAppend(&str,"typedef ",0);
+  StringAppend(&str,pList->zText,pList->nText);
+  StringAppend(&str," ",0);
+  StringAppend(&str,pName->zText,pName->nText);
+  StringAppend(&str," ",0);
+  StringAppend(&str,pName->zText,pName->nText);
+  StringAppend(&str,";\n",2);
+  pDecl->zFwd = StrDup(StringGet(&str),0);
+  StringReset(&str);
+  StringInit(&str);
+  StringAppend(&str,pList->zText,pList->nText);
+  StringAppend(&str," ",0);
+  StringAppend(&str,pName->zText,pName->nText);
+  StringAppend(&str,";\n",2);
+  pDecl->zFwdCpp = StrDup(StringGet(&str),0);
+  StringReset(&str);
+  if( flags & PS_Export ){
+    DeclSetProperty(pDecl,DP_Export);
+  }else if( flags & PS_Local ){
+    DeclSetProperty(pDecl,DP_Local);
+  }
+
+  /* Here's something weird.  ANSI-C doesn't allow a forward declaration
+  ** of an enumeration.  So we have to build the typedef into the
+  ** definition.
+  */
+  if( pDecl->zDecl && DeclHasProperty(pDecl, TY_Enumeration) ){
+    StringInit(&str);
+    StringAppend(&str,pDecl->zDecl,0);
+    StringAppend(&str,pDecl->zFwd,0);
+    SafeFree(pDecl->zDecl);
+    SafeFree(pDecl->zFwd);
+    pDecl->zFwd = 0;
+    pDecl->zDecl = StrDup(StringGet(&str),0);
+    StringReset(&str);
+  }
+
+  if( pName->pNext->zText[0]==':' ){
+    DeclSetProperty(pDecl,DP_Cplusplus);
+  }
+  if( pName->nText==5 && strncmp(pName->zText,"class",5)==0 ){
+    DeclSetProperty(pDecl,DP_Cplusplus);
+  }
+
+  /*
+  ** Remove all but pList and pName from the input stream.
+  */
+  if( need_to_collapse ){
+    while( pEnd!=pName ){
+      Token *pPrev = pEnd->pPrev;
+      pPrev->pNext = pEnd->pNext;
+      pEnd->pNext->pPrev = pPrev;
+      SafeFree(pEnd);
+      pEnd = pPrev;
+    }
+  }
+  return 0;
+}
+
+/*
+** Given a list of tokens that declare something (a function, procedure,
+** variable or typedef) find the token which contains the name of the
+** thing being declared.
+**
+** Algorithm:
+**
+**   The name is:
+**
+**     1.  The first identifier that is followed by a "[", or
+**
+**     2.  The first identifier that is followed by a "(" where the
+**         "(" is followed by another identifier, or
+**
+**     3.  The first identifier followed by "::", or
+**
+**     4.  If none of the above, then the last identifier.
+**
+**   In all of the above, certain reserved words (like "char") are
+**   not considered identifiers.
+*/
+static Token *FindDeclName(Token *pFirst, Token *pLast){
+  Token *pName = 0;
+  Token *p;
+  int c;
+
+  if( pFirst==0 || pLast==0 ){
+    return 0;
+  }
+  pLast = pLast->pNext;
+  for(p=pFirst; p && p!=pLast; p=p->pNext){
+    if( p->eType==TT_Id ){
+      static IdentTable sReserved;
+      static int isInit = 0;
+      static const char *aWords[] = { "char", "class",
+       "const", "double", "enum", "extern", "EXPORT", "ET_PROC",
+       "float", "int", "long",
+       "PRIVATE", "PROTECTED", "PUBLIC",
+       "register", "static", "struct", "sizeof", "signed", "typedef",
+       "union", "volatile", "virtual", "void", };
+
+      if( !isInit ){
+        int i;
+        for(i=0; i<sizeof(aWords)/sizeof(aWords[0]); i++){
+          IdentTableInsert(&sReserved,aWords[i],0);
+        }
+        isInit = 1;
+      }
+      if( !IdentTableTest(&sReserved,p->zText,p->nText) ){
+        pName = p;
+      }
+    }else if( p==pFirst ){
+      continue;
+    }else if( (c=p->zText[0])=='[' && pName ){
+      break;
+    }else if( c=='(' && p->pNext && p->pNext->eType==TT_Id && pName ){
+      break;
+    }else if( c==':' && p->zText[1]==':' && pName ){
+      break;
+    }
+  }
+  return pName;
+}
+
+/*
+** This routine is called when we see a method for a class that begins
+** with the PUBLIC, PRIVATE, or PROTECTED keywords.  Such methods are
+** added to their class definitions.
+*/
+static int ProcessMethodDef(Token *pFirst, Token *pLast, int flags){
+  Token *pClass;
+  char *zDecl;
+  Decl *pDecl;
+  String str;
+  int type;
+
+  pLast = pLast->pPrev;
+  while( pFirst->zText[0]=='P' ){
+    int rc = 1;
+    switch( pFirst->nText ){
+      case 6:  rc = strncmp(pFirst->zText,"PUBLIC",6); break;
+      case 7:  rc = strncmp(pFirst->zText,"PRIVATE",7); break;
+      case 9:  rc = strncmp(pFirst->zText,"PROTECTED",9); break;
+      default:  break;
+    }
+    if( rc ) break;
+    pFirst = pFirst->pNext;
+  }
+  pClass = FindDeclName(pFirst,pLast);
+  if( pClass==0 ){
+    fprintf(stderr,"%s:%d: Unable to find the class name for this method\n",
+       zFilename, pFirst->nLine);
+    return 1;
+  }
+  pDecl = FindDecl(pClass->zText, pClass->nText);
+  if( pDecl==0 || (pDecl->flags & TY_Class)!=TY_Class ){
+    pDecl = CreateDecl(pClass->zText, pClass->nText);
+    DeclSetProperty(pDecl, TY_Class);
+  }
+  StringInit(&str);
+  if( pDecl->zExtra ){
+    StringAppend(&str, pDecl->zExtra, 0);
+    SafeFree(pDecl->zExtra);
+    pDecl->zExtra = 0;
+  }
+  type = flags & PS_PPP;
+  if( pDecl->extraType!=type ){
+    if( type & PS_Public ){
+      StringAppend(&str, "public:\n", 0);
+      pDecl->extraType = PS_Public;
+    }else if( type & PS_Protected ){
+      StringAppend(&str, "protected:\n", 0);
+      pDecl->extraType = PS_Protected;
+    }else if( type & PS_Private ){
+      StringAppend(&str, "private:\n", 0);
+      pDecl->extraType = PS_Private;
+    }
+  }
+  StringAppend(&str, "  ", 0);
+  zDecl = TokensToString(pFirst, pLast, ";\n", pClass, 2);
+  StringAppend(&str, zDecl, 0);
+  SafeFree(zDecl);
+  pDecl->zExtra = StrDup(StringGet(&str), 0);
+  StringReset(&str);
+  return 0;
+}
+
+/*
+** This routine is called when we see a function or procedure definition.
+** We make an entry in the declaration table that is a prototype for this
+** function or procedure.
+*/
+static int ProcessProcedureDef(Token *pFirst, Token *pLast, int flags){
+  Token *pName;
+  Decl *pDecl;
+  Token *pCode;
+
+  if( pFirst==0 || pLast==0 ){
+    return 0;
+  }
+  if( flags & PS_Method ){
+    if( flags & PS_PPP ){
+      return ProcessMethodDef(pFirst, pLast, flags);
+    }else{
+      return 0;
+    }
+  }
+  if( (flags & PS_Static)!=0 && !proto_static ){
+    return 0;
+  }
+  pCode = pLast;
+  while( pLast && pLast!=pFirst && pLast->zText[0]!=')' ){
+    pLast = pLast->pPrev;
+  }
+  if( pLast==0 || pLast==pFirst || pFirst->pNext==pLast ){
+    fprintf(stderr,"%s:%d: Unrecognized syntax.\n",
+      zFilename, pFirst->nLine);
+    return 1;
+  }
+  if( flags & (PS_Interface|PS_Export|PS_Local) ){
+    fprintf(stderr,"%s:%d: Missing \"inline\" on function or procedure.\n",
+      zFilename, pFirst->nLine);
+    return 1;
+  }
+  pName = FindDeclName(pFirst,pLast);
+  if( pName==0 ){
+    fprintf(stderr,"%s:%d: Malformed function or procedure definition.\n",
+      zFilename, pFirst->nLine);
+    return 1;
+  }
+
+  /*
+  ** At this point we've isolated a procedure declaration between pFirst
+  ** and pLast with the name pName.
+  */
+#ifdef DEBUG
+  if( debugMask & PARSER ){
+    printf("**** Found routine: %.*s on line %d...\n", pName->nText,
+       pName->zText, pFirst->nLine);
+    PrintTokens(pFirst,pLast);
+    printf(";\n");
+  }
+#endif
+  pDecl = CreateDecl(pName->zText,pName->nText);
+  pDecl->pComment = pFirst->pComment;
+  if( pCode && pCode->eType==TT_Braces ){
+    pDecl->tokenCode = *pCode;
+  }
+  DeclSetProperty(pDecl,TY_Subroutine);
+  pDecl->zDecl = TokensToString(pFirst,pLast,";\n",0,0);
+  if( (flags & (PS_Static|PS_Local2))!=0 ){
+    DeclSetProperty(pDecl,DP_Local);
+  }else if( (flags & (PS_Export2))!=0 ){
+    DeclSetProperty(pDecl,DP_Export);
+  }
+
+  if( flags & DP_Cplusplus ){
+    DeclSetProperty(pDecl,DP_Cplusplus);
+  }else{
+    DeclSetProperty(pDecl,DP_ExternCReqd);
+  }
+
+  return 0;
+}
+
+/*
+** This routine is called whenever we see the "inline" keyword.  We
+** need to seek-out the inline function or procedure and make a
+** declaration out of the entire definition.
+*/
+static int ProcessInlineProc(Token *pFirst, int flags, int *pReset){
+  Token *pName;
+  Token *pEnd;
+  Decl *pDecl;
+
+  for(pEnd=pFirst; pEnd; pEnd = pEnd->pNext){
+    if( pEnd->zText[0]=='{' || pEnd->zText[0]==';' ){
+      *pReset = pEnd->zText[0];
+      break;
+    }
+  }
+  if( pEnd==0 ){
+    *pReset = ';';
+    fprintf(stderr,"%s:%d: incomplete inline procedure definition\n",
+      zFilename, pFirst->nLine);
+    return 1;
+  }
+  pName = FindDeclName(pFirst,pEnd);
+  if( pName==0 ){
+    fprintf(stderr,"%s:%d: malformed inline procedure definition\n",
+      zFilename, pFirst->nLine);
+    return 1;
+  }
+
+#ifdef DEBUG
+  if( debugMask & PARSER ){
+    printf("**** Found inline routine: %.*s on line %d...\n",
+       pName->nText, pName->zText, pFirst->nLine);
+    PrintTokens(pFirst,pEnd);
+    printf("\n");
+  }
+#endif
+  pDecl = CreateDecl(pName->zText,pName->nText);
+  pDecl->pComment = pFirst->pComment;
+  DeclSetProperty(pDecl,TY_Subroutine);
+  pDecl->zDecl = TokensToString(pFirst,pEnd,";\n",0,0);
+  if( (flags & (PS_Static|PS_Local|PS_Local2)) ){
+    DeclSetProperty(pDecl,DP_Local);
+  }else if( flags & (PS_Export|PS_Export2) ){
+    DeclSetProperty(pDecl,DP_Export);
+  }
+
+  if( flags & DP_Cplusplus ){
+    DeclSetProperty(pDecl,DP_Cplusplus);
+  }else{
+    DeclSetProperty(pDecl,DP_ExternCReqd);
+  }
+
+  return 0;
+}
+
+/*
+** Determine if the tokens between pFirst and pEnd form a variable
+** definition or a function prototype.  Return TRUE if we are dealing
+** with a variable defintion and FALSE for a prototype.
+**
+** pEnd is the token that ends the object.  It can be either a ';' or
+** a '='.  If it is '=', then assume we have a variable definition.
+**
+** If pEnd is ';', then the determination is more difficult.  We have
+** to search for an occurrence of an ID followed immediately by '('.
+** If found, we have a prototype.  Otherwise we are dealing with a
+** variable definition.
+*/
+static int isVariableDef(Token *pFirst, Token *pEnd){
+  if( pEnd && pEnd->zText[0]=='=' &&
+    (pEnd->pPrev->nText!=8 || strncmp(pEnd->pPrev->zText,"operator",8)!=0)
+  ){
+    return 1;
+  }
+  while( pFirst && pFirst!=pEnd && pFirst->pNext && pFirst->pNext!=pEnd ){
+    if( pFirst->eType==TT_Id && pFirst->pNext->zText[0]=='(' ){
+      return 0;
+    }
+    pFirst = pFirst->pNext;
+  }
+  return 1;
+}
+
+/*
+** Return TRUE if pFirst is the first token of a static assert.
+*/
+static int isStaticAssert(Token *pFirst){
+  if( (pFirst->nText==13 && strncmp(pFirst->zText, "static_assert", 13)==0)
+   || (pFirst->nText==14 && strncmp(pFirst->zText, "_Static_assert", 14)==0)
+  ){
+    return 1;
+  }else{
+    return 0;
+  }
+}
+
+/*
+** This routine is called whenever we encounter a ";" or "=".  The stuff
+** between pFirst and pLast constitutes either a typedef or a global
+** variable definition.  Do the right thing.
+*/
+static int ProcessDecl(Token *pFirst, Token *pEnd, int flags){
+  Token *pName;
+  Decl *pDecl;
+  int isLocal = 0;
+  int isVar;
+  int nErr = 0;
+
+  if( pFirst==0 || pEnd==0 ){
+    return 0;
+  }
+  if( flags & PS_Typedef ){
+    if( (flags & (PS_Export2|PS_Local2))!=0 ){
+      fprintf(stderr,"%s:%d: \"EXPORT\" or \"LOCAL\" ignored before typedef.\n",
+        zFilename, pFirst->nLine);
+      nErr++;
+    }
+    if( (flags & (PS_Interface|PS_Export|PS_Local|DP_Cplusplus))==0 ){
+      /* It is illegal to duplicate a typedef in C (but OK in C++).
+      ** So don't record typedefs that aren't within a C++ file or
+      ** within #if INTERFACE..#endif */
+      return nErr;
+    }
+    if( (flags & (PS_Interface|PS_Export|PS_Local))==0 && proto_static==0 ){
+      /* Ignore typedefs that are not with "#if INTERFACE..#endif" unless
+      ** the "-local" command line option is used. */
+      return nErr;
+    }
+    if( (flags & (PS_Interface|PS_Export))==0 ){
+      /* typedefs are always local, unless within #if INTERFACE..#endif */
+      isLocal = 1;
+    }
+  }else if( flags & (PS_Static|PS_Local2) ){
+    if( proto_static==0 && (flags & PS_Local2)==0 ){
+      /* Don't record static variables unless the "-local" command line
+      ** option was specified or the "LOCAL" keyword is used. */
+      return nErr;
+    }
+    while( pFirst!=0 && pFirst->pNext!=pEnd &&
+       ((pFirst->nText==6 && strncmp(pFirst->zText,"static",6)==0)
+        || (pFirst->nText==5 && strncmp(pFirst->zText,"LOCAL",6)==0))
+    ){
+      /* Lose the initial "static" or local from local variables.
+      ** We'll prepend "extern" later. */
+      pFirst = pFirst->pNext;
+      isLocal = 1;
+    }
+    if( pFirst==0 || !isLocal ){
+      return nErr;
+    }
+  }else if( flags & PS_Method ){
+    /* Methods are declared by their class.  Don't declare separately. */
+    return nErr;
+  }else if( isStaticAssert(pFirst) ){
+    return 0;
+  }
+  isVar =  (flags & (PS_Typedef|PS_Method))==0 && isVariableDef(pFirst,pEnd);
+  if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0
+  && (flags & PS_Extern)==0 ){
+    fprintf(stderr,"%s:%d: Can't define a variable in this context\n",
+      zFilename, pFirst->nLine);
+    nErr++;
+  }
+  pName = FindDeclName(pFirst,pEnd->pPrev);
+  if( pName==0 ){
+    if( pFirst->nText==4 && strncmp(pFirst->zText,"enum",4)==0 ){
+      /* Ignore completely anonymous enums.  See documentation section 3.8.1. */
+      return nErr;
+    }else{
+      fprintf(stderr,"%s:%d: Can't find a name for the object declared here.\n",
+        zFilename, pFirst->nLine);
+      return nErr+1;
+    }
+  }
+
+#ifdef DEBUG
+  if( debugMask & PARSER ){
+    if( flags & PS_Typedef ){
+      printf("**** Found typedef %.*s at line %d...\n",
+        pName->nText, pName->zText, pName->nLine);
+    }else if( isVar ){
+      printf("**** Found variable %.*s at line %d...\n",
+        pName->nText, pName->zText, pName->nLine);
+    }else{
+      printf("**** Found prototype %.*s at line %d...\n",
+        pName->nText, pName->zText, pName->nLine);
+    }
+    PrintTokens(pFirst,pEnd->pPrev);
+    printf(";\n");
+  }
+#endif
+
+  pDecl = CreateDecl(pName->zText,pName->nText);
+  if( (flags & PS_Typedef) ){
+    DeclSetProperty(pDecl, TY_Typedef);
+  }else if( isVar ){
+    DeclSetProperty(pDecl,DP_ExternReqd | TY_Variable);
+    if( !(flags & DP_Cplusplus) ){
+      DeclSetProperty(pDecl,DP_ExternCReqd);
+    }
+  }else{
+    DeclSetProperty(pDecl, TY_Subroutine);
+    if( !(flags & DP_Cplusplus) ){
+      DeclSetProperty(pDecl,DP_ExternCReqd);
+    }
+  }
+  pDecl->pComment = pFirst->pComment;
+  pDecl->zDecl = TokensToString(pFirst,pEnd->pPrev,";\n",0,0);
+  if( isLocal || (flags & (PS_Local|PS_Local2))!=0 ){
+    DeclSetProperty(pDecl,DP_Local);
+  }else if( flags & (PS_Export|PS_Export2) ){
+    DeclSetProperty(pDecl,DP_Export);
+  }
+  if( flags & DP_Cplusplus ){
+    DeclSetProperty(pDecl,DP_Cplusplus);
+  }
+  return nErr;
+}
+
+/*
+** Push an if condition onto the if stack
+*/
+static void PushIfMacro(
+  const char *zPrefix,      /* A prefix, like "define" or "!" */
+  const char *zText,        /* The condition */
+  int nText,                /* Number of characters in zText */
+  int nLine,                /* Line number where this macro occurs */
+  int flags                 /* Either 0, PS_Interface, PS_Export or PS_Local */
+){
+  Ifmacro *pIf;
+  int nByte;
+
+  nByte = sizeof(Ifmacro);
+  if( zText ){
+    if( zPrefix ){
+      nByte += strlen(zPrefix) + 2;
+    }
+    nByte += nText + 1;
+  }
+  pIf = SafeMalloc( nByte );
+  if( zText ){
+    pIf->zCondition = (char*)&pIf[1];
+    if( zPrefix ){
+      sprintf(pIf->zCondition,"%s(%.*s)",zPrefix,nText,zText);
+    }else{
+      sprintf(pIf->zCondition,"%.*s",nText,zText);
+    }
+  }else{
+    pIf->zCondition = 0;
+  }
+  pIf->nLine = nLine;
+  pIf->flags = flags;
+  pIf->pNext = ifStack;
+  ifStack = pIf;
+}
+
+/*
+** This routine is called to handle all preprocessor directives.
+**
+** This routine will recompute the value of *pPresetFlags to be the
+** logical or of all flags on all nested #ifs.  The #ifs that set flags
+** are as follows:
+**
+**        conditional                   flag set
+**        ------------------------      --------------------
+**        #if INTERFACE                 PS_Interface
+**        #if EXPORT_INTERFACE          PS_Export
+**        #if LOCAL_INTERFACE           PS_Local
+**
+** For example, if after processing the preprocessor token given
+** by pToken there is an "#if INTERFACE" on the preprocessor
+** stack, then *pPresetFlags will be set to PS_Interface.
+*/
+static int ParsePreprocessor(Token *pToken, int flags, int *pPresetFlags){
+  const char *zCmd;
+  int nCmd;
+  const char *zArg;
+  int nArg;
+  int nErr = 0;
+  Ifmacro *pIf;
+
+  zCmd = &pToken->zText[1];
+  while( isspace(*zCmd) && *zCmd!='\n' ){
+    zCmd++;
+  }
+  if( !isalpha(*zCmd) ){
+    return 0;
+  }
+  nCmd = 1;
+  while( isalpha(zCmd[nCmd]) ){
+    nCmd++;
+  }
+
+  if( nCmd==5 && strncmp(zCmd,"endif",5)==0 ){
+    /*
+    ** Pop the if stack
+    */
+    pIf = ifStack;
+    if( pIf==0 ){
+      fprintf(stderr,"%s:%d: extra '#endif'.\n",zFilename,pToken->nLine);
+      return 1;
+    }
+    ifStack = pIf->pNext;
+    SafeFree(pIf);
+  }else if( nCmd==6 && strncmp(zCmd,"define",6)==0 ){
+    /*
+    ** Record a #define if we are in PS_Interface or PS_Export
+    */
+    Decl *pDecl;
+    if( !(flags & (PS_Local|PS_Interface|PS_Export)) ){ return 0; }
+    zArg = &zCmd[6];
+    while( *zArg && isspace(*zArg) && *zArg!='\n' ){
+      zArg++;
+    }
+    if( *zArg==0 || *zArg=='\n' ){ return 0; }
+    for(nArg=0; ISALNUM(zArg[nArg]); nArg++){}
+    if( nArg==0 ){ return 0; }
+    pDecl = CreateDecl(zArg,nArg);
+    pDecl->pComment = pToken->pComment;
+    DeclSetProperty(pDecl,TY_Macro);
+    pDecl->zDecl = SafeMalloc( pToken->nText + 2 );
+    sprintf(pDecl->zDecl,"%.*s\n",pToken->nText,pToken->zText);
+    if( flags & PS_Export ){
+      DeclSetProperty(pDecl,DP_Export);
+    }else if( flags & PS_Local ){
+      DeclSetProperty(pDecl,DP_Local);
+    }
+  }else if( nCmd==7 && strncmp(zCmd,"include",7)==0 ){
+    /*
+    ** Record an #include if we are in PS_Interface or PS_Export
+    */
+    Include *pInclude;
+    char *zIf;
+
+    if( !(flags & (PS_Interface|PS_Export)) ){ return 0; }
+    zArg = &zCmd[7];
+    while( *zArg && isspace(*zArg) ){ zArg++; }
+    for(nArg=0; !isspace(zArg[nArg]); nArg++){}
+    if( (zArg[0]=='"' && zArg[nArg-1]!='"')
+      ||(zArg[0]=='<' && zArg[nArg-1]!='>')
+    ){
+      fprintf(stderr,"%s:%d: malformed #include statement.\n",
+        zFilename,pToken->nLine);
+      return 1;
+    }
+    zIf = GetIfString();
+    if( zIf ){
+      pInclude = SafeMalloc( sizeof(Include) + nArg*2 + strlen(zIf) + 10 );
+      pInclude->zFile = (char*)&pInclude[1];
+      pInclude->zLabel = &pInclude->zFile[nArg+1];
+      sprintf(pInclude->zFile,"%.*s",nArg,zArg);
+      sprintf(pInclude->zLabel,"%.*s:%s",nArg,zArg,zIf);
+      pInclude->zIf = &pInclude->zLabel[nArg+1];
+      SafeFree(zIf);
+    }else{
+      pInclude = SafeMalloc( sizeof(Include) + nArg + 1 );
+      pInclude->zFile = (char*)&pInclude[1];
+      sprintf(pInclude->zFile,"%.*s",nArg,zArg);
+      pInclude->zIf = 0;
+      pInclude->zLabel = pInclude->zFile;
+    }
+    pInclude->pNext = includeList;
+    includeList = pInclude;
+  }else if( nCmd==2 && strncmp(zCmd,"if",2)==0 ){
+    /*
+    ** Push an #if.  Watch for the special cases of INTERFACE
+    ** and EXPORT_INTERFACE and LOCAL_INTERFACE
+    */
+    zArg = &zCmd[2];
+    while( *zArg && isspace(*zArg) && *zArg!='\n' ){
+      zArg++;
+    }
+    if( *zArg==0 || *zArg=='\n' ){ return 0; }
+    nArg = pToken->nText + (int)(pToken->zText - zArg);
+    if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
+      PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
+    }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
+      PushIfMacro(0,0,0,pToken->nLine,PS_Export);
+    }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
+      PushIfMacro(0,0,0,pToken->nLine,PS_Local);
+    }else if( nArg==15 && strncmp(zArg,"MAKEHEADERS_STOPLOCAL_INTERFACE",15)==0 ){
+      PushIfMacro(0,0,0,pToken->nLine,PS_Local);
+    }else{
+      PushIfMacro(0,zArg,nArg,pToken->nLine,0);
+    }
+  }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){
+    /*
+    ** Push an #ifdef.
+    */
+    zArg = &zCmd[5];
+    while( *zArg && isspace(*zArg) && *zArg!='\n' ){
+      zArg++;
+    }
+    if( *zArg==0 || *zArg=='\n' ){ return 0; }
+    nArg = pToken->nText + (int)(pToken->zText - zArg);
+    PushIfMacro("defined",zArg,nArg,pToken->nLine,0);
+  }else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){
+    /*
+    ** Push an #ifndef.
+    */
+    zArg = &zCmd[6];
+    while( *zArg && isspace(*zArg) && *zArg!='\n' ){
+      zArg++;
+    }
+    if( *zArg==0 || *zArg=='\n' ){ return 0; }
+    nArg = pToken->nText + (int)(pToken->zText - zArg);
+    PushIfMacro("!defined",zArg,nArg,pToken->nLine,0);
+  }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){
+    /*
+    ** Invert the #if on the top of the stack
+    */
+    if( ifStack==0 ){
+      fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename,
+         pToken->nLine);
+      return 1;
+    }
+    pIf = ifStack;
+    if( pIf->zCondition ){
+      ifStack = ifStack->pNext;
+      PushIfMacro("!",pIf->zCondition,strlen(pIf->zCondition),pIf->nLine,0);
+      SafeFree(pIf);
+    }else{
+      pIf->flags = 0;
+    }
+  }else{
+    /*
+    ** This directive can be safely ignored
+    */
+    return 0;
+  }
+
+  /*
+  ** Recompute the preset flags
+  */
+  *pPresetFlags = 0;
+  for(pIf = ifStack; pIf; pIf=pIf->pNext){
+    *pPresetFlags |= pIf->flags;
+  }
+
+  return nErr;
+}
+
+/*
+** Parse an entire file.  Return the number of errors.
+**
+** pList is a list of tokens in the file.  Whitespace tokens have been
+** eliminated, and text with {...} has been collapsed into a
+** single TT_Brace token.
+**
+** initFlags are a set of parse flags that should always be set for this
+** file.  For .c files this is normally 0.  For .h files it is PS_Interface.
+*/
+static int ParseFile(Token *pList, int initFlags){
+  int nErr = 0;
+  Token *pStart = 0;
+  int flags = initFlags;
+  int presetFlags = initFlags;
+  int resetFlag = 0;
+
+  includeList = 0;
+  while( pList ){
+    switch( pList->eType ){
+    case TT_EOF:
+      goto end_of_loop;
+
+    case TT_Preprocessor:
+      nErr += ParsePreprocessor(pList,flags,&presetFlags);
+      pStart = 0;
+      presetFlags |= initFlags;
+      flags = presetFlags;
+      break;
+
+    case TT_Other:
+      switch( pList->zText[0] ){
+      case ';':
+        nErr += ProcessDecl(pStart,pList,flags);
+        pStart = 0;
+        flags = presetFlags;
+        break;
+
+      case '=':
+        if( pList->pPrev->nText==8
+            && strncmp(pList->pPrev->zText,"operator",8)==0 ){
+          break;
+        }
+        nErr += ProcessDecl(pStart,pList,flags);
+        pStart = 0;
+        while( pList && pList->zText[0]!=';' ){
+          pList = pList->pNext;
+        }
+        if( pList==0 ) goto end_of_loop;
+        flags = presetFlags;
+        break;
+
+      case ':':
+        if( pList->zText[1]==':' ){
+          flags |= PS_Method;
+        }
+        break;
+
+      default:
+        break;
+      }
+      break;
+
+    case TT_Braces:
+      nErr += ProcessProcedureDef(pStart,pList,flags);
+      pStart = 0;
+      flags = presetFlags;
+      break;
+
+    case TT_Id:
+       if( pStart==0 ){
+          pStart = pList;
+          flags = presetFlags;
+       }
+       resetFlag = 0;
+       switch( pList->zText[0] ){
+       case 'c':
+         if( pList->nText==5 && strncmp(pList->zText,"class",5)==0 ){
+           nErr += ProcessTypeDecl(pList,flags,&resetFlag);
+         }
+         break;
+
+       case 'E':
+         if( pList->nText==6 && strncmp(pList->zText,"EXPORT",6)==0 ){
+           flags |= PS_Export2;
+           /* pStart = 0; */
+         }
+         break;
+
+       case 'e':
+         if( pList->nText==4 && strncmp(pList->zText,"enum",4)==0 ){
+           if( pList->pNext && pList->pNext->eType==TT_Braces ){
+             pList = pList->pNext;
+           }else{
+             nErr += ProcessTypeDecl(pList,flags,&resetFlag);
+           }
+         }else if( pList->nText==6 && strncmp(pList->zText,"extern",6)==0 ){
+           pList = pList->pNext;
+           if( pList && pList->nText==3 && strncmp(pList->zText,"\"C\"",3)==0 ){
+             pList = pList->pNext;
+             flags &= ~DP_Cplusplus;
+           }else{
+             flags |= PS_Extern;
+           }
+           pStart = pList;
+         }
+         break;
+
+       case 'i':
+         if( pList->nText==6 && strncmp(pList->zText,"inline",6)==0
+          && (flags & PS_Static)==0
+         ){
+           nErr += ProcessInlineProc(pList,flags,&resetFlag);
+         }
+         break;
+
+       case 'L':
+         if( pList->nText==5 && strncmp(pList->zText,"LOCAL",5)==0 ){
+           flags |= PS_Local2;
+           pStart = pList;
+         }
+         break;
+
+       case 'P':
+         if( pList->nText==6 && strncmp(pList->zText, "PUBLIC",6)==0 ){
+           flags |= PS_Public;
+           pStart = pList;
+         }else if( pList->nText==7 && strncmp(pList->zText, "PRIVATE",7)==0 ){
+           flags |= PS_Private;
+           pStart = pList;
+         }else if( pList->nText==9 && strncmp(pList->zText,"PROTECTED",9)==0 ){
+           flags |= PS_Protected;
+           pStart = pList;
+         }
+         break;
+
+       case 's':
+         if( pList->nText==6 && strncmp(pList->zText,"struct",6)==0 ){
+           if( pList->pNext && pList->pNext->eType==TT_Braces ){
+             pList = pList->pNext;
+           }else{
+             nErr += ProcessTypeDecl(pList,flags,&resetFlag);
+           }
+         }else if( pList->nText==6 && strncmp(pList->zText,"static",6)==0 ){
+           flags |= PS_Static;
+         }
+         break;
+
+       case 't':
+         if( pList->nText==7 && strncmp(pList->zText,"typedef",7)==0 ){
+           flags |= PS_Typedef;
+         }
+         break;
+
+       case 'u':
+         if( pList->nText==5 && strncmp(pList->zText,"union",5)==0 ){
+           if( pList->pNext && pList->pNext->eType==TT_Braces ){
+             pList = pList->pNext;
+           }else{
+             nErr += ProcessTypeDecl(pList,flags,&resetFlag);
+           }
+         }
+         break;
+
+       default:
+         break;
+       }
+       if( resetFlag!=0 ){
+         while( pList && pList->zText[0]!=resetFlag ){
+           pList = pList->pNext;
+         }
+         if( pList==0 ) goto end_of_loop;
+         pStart = 0;
+         flags = presetFlags;
+       }
+       break;
+
+    case TT_String:
+    case TT_Number:
+       break;
+
+    default:
+       pStart = pList;
+       flags = presetFlags;
+       break;
+    }
+    pList = pList->pNext;
+  }
+  end_of_loop:
+
+  /* Verify that all #ifs have a matching "#endif" */
+  while( ifStack ){
+    Ifmacro *pIf = ifStack;
+    ifStack = pIf->pNext;
+    fprintf(stderr,"%s:%d: This '#if' has no '#endif'\n",zFilename,
+      pIf->nLine);
+    SafeFree(pIf);
+  }
+
+  return nErr;
+}
+
+/*
+** If the given Decl object has a non-null zExtra field, then the text
+** of that zExtra field needs to be inserted in the middle of the
+** zDecl field before the last "}" in the zDecl.  This routine does that.
+** If the zExtra is NULL, this routine is a no-op.
+**
+** zExtra holds extra method declarations for classes.  The declarations
+** have to be inserted into the class definition.
+*/
+static void InsertExtraDecl(Decl *pDecl){
+  int i;
+  String str;
+
+  if( pDecl==0 || pDecl->zExtra==0 || pDecl->zDecl==0 ) return;
+  i = strlen(pDecl->zDecl) - 1;
+  while( i>0 && pDecl->zDecl[i]!='}' ){ i--; }
+  StringInit(&str);
+  StringAppend(&str, pDecl->zDecl, i);
+  StringAppend(&str, pDecl->zExtra, 0);
+  StringAppend(&str, &pDecl->zDecl[i], 0);
+  SafeFree(pDecl->zDecl);
+  SafeFree(pDecl->zExtra);
+  pDecl->zDecl = StrDup(StringGet(&str), 0);
+  StringReset(&str);
+  pDecl->zExtra = 0;
+}
+
+/*
+** Reset the DP_Forward and DP_Declared flags on all Decl structures.
+** Set both flags for anything that is tagged as local and isn't
+** in the file zFilename so that it won't be printing in other files.
+*/
+static void ResetDeclFlags(char *zFilename){
+  Decl *pDecl;
+
+  for(pDecl = pDeclFirst; pDecl; pDecl = pDecl->pNext){
+    DeclClearProperty(pDecl,DP_Forward|DP_Declared);
+    if( DeclHasProperty(pDecl,DP_Local) && pDecl->zFile!=zFilename ){
+      DeclSetProperty(pDecl,DP_Forward|DP_Declared);
+    }
+  }
+}
+
+/*
+** Forward declaration of the ScanText() function.
+*/
+static void ScanText(const char*, GenState *pState);
+
+/*
+** The output in pStr is currently within an #if CONTEXT where context
+** is equal to *pzIf.  (*pzIf might be NULL to indicate that we are
+** not within any #if at the moment.)  We are getting ready to output
+** some text that needs to be within the context of "#if NEW" where
+** NEW is zIf.  Make an appropriate change to the context.
+*/
+static void ChangeIfContext(
+  const char *zIf,       /* The desired #if context */
+  GenState *pState       /* Current state of the code generator */
+){
+  if( zIf==0 ){
+    if( pState->zIf==0 ) return;
+    StringAppend(pState->pStr,"#endif\n",0);
+    pState->zIf = 0;
+  }else{
+    if( pState->zIf ){
+      if( strcmp(zIf,pState->zIf)==0 ) return;
+      StringAppend(pState->pStr,"#endif\n",0);
+      pState->zIf = 0;
+    }
+    ScanText(zIf, pState);
+    if( pState->zIf!=0 ){
+      StringAppend(pState->pStr,"#endif\n",0);
+    }
+    StringAppend(pState->pStr,"#if ",0);
+    StringAppend(pState->pStr,zIf,0);
+    StringAppend(pState->pStr,"\n",0);
+    pState->zIf = zIf;
+  }
+}
+
+/*
+** Add to the string pStr a #include of every file on the list of
+** include files pInclude.  The table pTable contains all files that
+** have already been #included at least once.  Don't add any
+** duplicates.  Update pTable with every new #include that is added.
+*/
+static void AddIncludes(
+  Include *pInclude,       /* Write every #include on this list */
+  GenState *pState         /* Current state of the code generator */
+){
+  if( pInclude ){
+    if( pInclude->pNext ){
+      AddIncludes(pInclude->pNext,pState);
+    }
+    if( IdentTableInsert(pState->pTable,pInclude->zLabel,0) ){
+      ChangeIfContext(pInclude->zIf,pState);
+      StringAppend(pState->pStr,"#include ",0);
+      StringAppend(pState->pStr,pInclude->zFile,0);
+      StringAppend(pState->pStr,"\n",1);
+    }
+  }
+}
+
+/*
+** Add to the string pStr a declaration for the object described
+** in pDecl.
+**
+** If pDecl has already been declared in this file, detect that
+** fact and abort early.  Do not duplicate a declaration.
+**
+** If the needFullDecl flag is false and this object has a forward
+** declaration, then supply the forward declaration only.  A later
+** call to CompleteForwardDeclarations() will finish the declaration
+** for us.  But if needFullDecl is true, we must supply the full
+** declaration now.  Some objects do not have a forward declaration.
+** For those objects, we must print the full declaration now.
+**
+** Because it is illegal to duplicate a typedef in C, care is taken
+** to insure that typedefs for the same identifier are only issued once.
+*/
+static void DeclareObject(
+  Decl *pDecl,        /* The thing to be declared */
+  GenState *pState,   /* Current state of the code generator */
+  int needFullDecl    /* Must have the full declaration.  A forward
+                       * declaration isn't enough */
+){
+  Decl *p;               /* The object to be declared */
+  int flag;
+  int isCpp;             /* True if generating C++ */
+  int doneTypedef = 0;   /* True if a typedef has been done for this object */
+
+  /* printf("BEGIN %s of %s\n",needFullDecl?"FULL":"PROTOTYPE",pDecl->zName);*/
+  /*
+  ** For any object that has a forward declaration, go ahead and do the
+  ** forward declaration first.
+  */
+  isCpp = (pState->flags & DP_Cplusplus) != 0;
+  for(p=pDecl; p; p=p->pSameName){
+    if( p->zFwd ){
+      if( !DeclHasProperty(p,DP_Forward) ){
+        DeclSetProperty(p,DP_Forward);
+        if( strncmp(p->zFwd,"typedef",7)==0 ){
+          if( doneTypedef ) continue;
+          doneTypedef = 1;
+        }
+        ChangeIfContext(p->zIf,pState);
+        StringAppend(pState->pStr,isCpp ? p->zFwdCpp : p->zFwd,0);
+      }
+    }
+  }
+
+  /*
+  ** Early out if everything is already suitably declared.
+  **
+  ** This is a very important step because it prevents us from
+  ** executing the code the follows in a recursive call to this
+  ** function with the same value for pDecl.
+  */
+  flag = needFullDecl ? DP_Declared|DP_Forward : DP_Forward;
+  for(p=pDecl; p; p=p->pSameName){
+    if( !DeclHasProperty(p,flag) ) break;
+  }
+  if( p==0 ){
+    return;
+  }
+
+  /*
+  ** Make sure we have all necessary #includes
+  */
+  for(p=pDecl; p; p=p->pSameName){
+    AddIncludes(p->pInclude,pState);
+  }
+
+  /*
+  ** Go ahead an mark everything as being declared, to prevent an
+  ** infinite loop thru the ScanText() function.  At the same time,
+  ** we decide which objects need a full declaration and mark them
+  ** with the DP_Flag bit.  We are only able to use DP_Flag in this
+  ** way because we know we'll never execute this far into this
+  ** function on a recursive call with the same pDecl.  Hence, recursive
+  ** calls to this function (through ScanText()) can never change the
+  ** value of DP_Flag out from under us.
+  */
+  for(p=pDecl; p; p=p->pSameName){
+    if( !DeclHasProperty(p,DP_Declared)
+     && (p->zFwd==0 || needFullDecl)
+     && p->zDecl!=0
+    ){
+      DeclSetProperty(p,DP_Forward|DP_Declared|DP_Flag);
+    }else{
+      DeclClearProperty(p,DP_Flag);
+    }
+  }
+
+  /*
+  ** Call ScanText() recursively (this routine is called from ScanText())
+  ** to include declarations required to come before these declarations.
+  */
+  for(p=pDecl; p; p=p->pSameName){
+    if( DeclHasProperty(p,DP_Flag) ){
+      if( p->zDecl[0]=='#' ){
+        ScanText(&p->zDecl[1],pState);
+      }else{
+        InsertExtraDecl(p);
+        ScanText(p->zDecl,pState);
+      }
+    }
+  }
+
+  /*
+  ** Output the declarations.  Do this in two passes.  First
+  ** output everything that isn't a typedef.  Then go back and
+  ** get the typedefs by the same name.
+  */
+  for(p=pDecl; p; p=p->pSameName){
+    if( DeclHasProperty(p,DP_Flag) && !DeclHasProperty(p,TY_Typedef) ){
+      if( DeclHasAnyProperty(p,TY_Enumeration) ){
+        if( doneTypedef ) continue;
+        doneTypedef = 1;
+      }
+      ChangeIfContext(p->zIf,pState);
+      if( !isCpp && DeclHasAnyProperty(p,DP_ExternReqd) ){
+        StringAppend(pState->pStr,"extern ",0);
+      }else if( isCpp && DeclHasProperty(p,DP_Cplusplus|DP_ExternReqd) ){
+        StringAppend(pState->pStr,"extern ",0);
+      }else if( isCpp && DeclHasAnyProperty(p,DP_ExternCReqd|DP_ExternReqd) ){
+        StringAppend(pState->pStr,"extern \"C\" ",0);
+      }
+      InsertExtraDecl(p);
+      StringAppend(pState->pStr,p->zDecl,0);
+      if( !isCpp && DeclHasProperty(p,DP_Cplusplus) ){
+        fprintf(stderr,
+          "%s: C code ought not reference the C++ object \"%s\"\n",
+          pState->zFilename, p->zName);
+        pState->nErr++;
+      }
+      DeclClearProperty(p,DP_Flag);
+    }
+  }
+  for(p=pDecl; p && !doneTypedef; p=p->pSameName){
+    if( DeclHasProperty(p,DP_Flag) ){
+      /* This has to be a typedef */
+      doneTypedef = 1;
+      ChangeIfContext(p->zIf,pState);
+      InsertExtraDecl(p);
+      StringAppend(pState->pStr,p->zDecl,0);
+    }
+  }
+}
+
+/*
+** This routine scans the input text given, and appends to the
+** string in pState->pStr the text of any declarations that must
+** occur before the text in zText.
+**
+** If an identifier in zText is immediately followed by '*', then
+** only forward declarations are needed for that identifier.  If the
+** identifier name is not followed immediately by '*', we must supply
+** a full declaration.
+*/
+static void ScanText(
+  const char *zText,    /* The input text to be scanned */
+  GenState *pState      /* Current state of the code generator */
+){
+  int nextValid = 0;    /* True is sNext contains valid data */
+  InStream sIn;         /* The input text */
+  Token sToken;         /* The current token being examined */
+  Token sNext;          /* The next non-space token */
+
+  /* printf("BEGIN SCAN TEXT on %s\n", zText); */
+
+  sIn.z = zText;
+  sIn.i = 0;
+  sIn.nLine = 1;
+  while( sIn.z[sIn.i]!=0 ){
+    if( nextValid ){
+      sToken = sNext;
+      nextValid = 0;
+    }else{
+      GetNonspaceToken(&sIn,&sToken);
+    }
+    if( sToken.eType==TT_Id ){
+      int needFullDecl;   /* True if we need to provide the full declaration,
+                          ** not just the forward declaration */
+      Decl *pDecl;        /* The declaration having the name in sToken */
+
+      /*
+      ** See if there is a declaration in the database with the name given
+      ** by sToken.
+      */
+      pDecl = FindDecl(sToken.zText,sToken.nText);
+      if( pDecl==0 ) continue;
+
+      /*
+      ** If we get this far, we've found an identifier that has a
+      ** declaration in the database.  Now see if we the full declaration
+      ** or just a forward declaration.
+      */
+      GetNonspaceToken(&sIn,&sNext);
+      if( sNext.zText[0]=='*' ){
+        needFullDecl = 0;
+      }else{
+        needFullDecl = 1;
+        nextValid = sNext.eType==TT_Id;
+      }
+
+      /*
+      ** Generate the needed declaration.
+      */
+      DeclareObject(pDecl,pState,needFullDecl);
+    }else if( sToken.eType==TT_Preprocessor ){
+      sIn.i -= sToken.nText - 1;
+    }
+  }
+  /* printf("END SCANTEXT\n"); */
+}
+
+/*
+** Provide a full declaration to any object which so far has had only
+** a forward declaration.
+*/
+static void CompleteForwardDeclarations(GenState *pState){
+  Decl *pDecl;
+  int progress;
+
+  do{
+    progress = 0;
+    for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){
+      if( DeclHasProperty(pDecl,DP_Forward)
+       && !DeclHasProperty(pDecl,DP_Declared)
+      ){
+        DeclareObject(pDecl,pState,1);
+        progress = 1;
+        assert( DeclHasProperty(pDecl,DP_Declared) );
+      }
+    }
+  }while( progress );
+}
+
+/*
+** Generate an include file for the given source file.  Return the number
+** of errors encountered.
+**
+** if nolocal_flag is true, then we do not generate declarations for
+** objected marked DP_Local.
+*/
+static int MakeHeader(InFile *pFile, FILE *report, int nolocal_flag){
+  int nErr = 0;
+  GenState sState;
+  String outStr;
+  IdentTable includeTable;
+  Ident *pId;
+  char *zNewVersion;
+  char *zOldVersion;
+
+  if( pFile->zHdr==0 || *pFile->zHdr==0 ) return 0;
+  sState.pStr = &outStr;
+  StringInit(&outStr);
+  StringAppend(&outStr,zTopLine,nTopLine);
+  sState.pTable = &includeTable;
+  memset(&includeTable,0,sizeof(includeTable));
+  sState.zIf = 0;
+  sState.nErr = 0;
+  sState.zFilename = pFile->zSrc;
+  sState.flags = pFile->flags & DP_Cplusplus;
+  ResetDeclFlags(nolocal_flag ? "no" : pFile->zSrc);
+  for(pId = pFile->idTable.pList; pId; pId=pId->pNext){
+    Decl *pDecl = FindDecl(pId->zName,0);
+    if( pDecl ){
+      DeclareObject(pDecl,&sState,1);
+    }
+  }
+  CompleteForwardDeclarations(&sState);
+  ChangeIfContext(0,&sState);
+  nErr += sState.nErr;
+  zOldVersion = ReadFile(pFile->zHdr);
+  zNewVersion = StringGet(&outStr);
+  if( report ) fprintf(report,"%s: ",pFile->zHdr);
+  if( zOldVersion==0 ){
+    if( report ) fprintf(report,"updated\n");
+    if( WriteFile(pFile->zHdr,zNewVersion) ){
+      fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
+      nErr++;
+    }
+  }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){
+    if( report ) fprintf(report,"error!\n");
+    fprintf(stderr,
+       "%s: Can't overwrite this file because it wasn't previously\n"
+       "%*s  generated by 'makeheaders'.\n",
+       pFile->zHdr, (int)strlen(pFile->zHdr), "");
+    nErr++;
+  }else if( strcmp(zOldVersion,zNewVersion)!=0 ){
+    if( report ) fprintf(report,"updated\n");
+    if( WriteFile(pFile->zHdr,zNewVersion) ){
+      fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
+      nErr++;
+    }
+  }else if( report ){
+    fprintf(report,"unchanged\n");
+  }
+  SafeFree(zOldVersion);
+  IdentTableReset(&includeTable);
+  StringReset(&outStr);
+  return nErr;
+}
+
+/*
+** Generate a global header file -- a header file that contains all
+** declarations.  If the forExport flag is true, then only those
+** objects that are exported are included in the header file.
+*/
+static int MakeGlobalHeader(int forExport){
+  GenState sState;
+  String outStr;
+  IdentTable includeTable;
+  Decl *pDecl;
+
+  sState.pStr = &outStr;
+  StringInit(&outStr);
+  /* StringAppend(&outStr,zTopLine,nTopLine); */
+  sState.pTable = &includeTable;
+  memset(&includeTable,0,sizeof(includeTable));
+  sState.zIf = 0;
+  sState.nErr = 0;
+  sState.zFilename = "(all)";
+  sState.flags = 0;
+  ResetDeclFlags(0);
+  for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){
+    if( forExport==0 || DeclHasProperty(pDecl,DP_Export) ){
+      DeclareObject(pDecl,&sState,1);
+    }
+  }
+  ChangeIfContext(0,&sState);
+  printf("%s",StringGet(&outStr));
+  IdentTableReset(&includeTable);
+  StringReset(&outStr);
+  return 0;
+}
+
+#ifdef DEBUG
+/*
+** Return the number of characters in the given string prior to the
+** first newline.
+*/
+static int ClipTrailingNewline(char *z){
+  int n = strlen(z);
+  while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ){ n--; }
+  return n;
+}
+
+/*
+** Dump the entire declaration list for debugging purposes
+*/
+static void DumpDeclList(void){
+  Decl *pDecl;
+
+  for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){
+    printf("**** %s from file %s ****\n",pDecl->zName,pDecl->zFile);
+    if( pDecl->zIf ){
+      printf("If: [%.*s]\n",ClipTrailingNewline(pDecl->zIf),pDecl->zIf);
+    }
+    if( pDecl->zFwd ){
+      printf("Decl: [%.*s]\n",ClipTrailingNewline(pDecl->zFwd),pDecl->zFwd);
+    }
+    if( pDecl->zDecl ){
+      InsertExtraDecl(pDecl);
+      printf("Def: [%.*s]\n",ClipTrailingNewline(pDecl->zDecl),pDecl->zDecl);
+    }
+    if( pDecl->flags ){
+      static struct {
+        int mask;
+        char *desc;
+      } flagSet[] = {
+        { TY_Class,       "class" },
+        { TY_Enumeration, "enum" },
+        { TY_Structure,   "struct" },
+        { TY_Union,       "union" },
+        { TY_Variable,    "variable" },
+        { TY_Subroutine,  "function" },
+        { TY_Typedef,     "typedef" },
+        { TY_Macro,       "macro" },
+        { DP_Export,      "export" },
+        { DP_Local,       "local" },
+        { DP_Cplusplus,   "C++" },
+      };
+      int i;
+      printf("flags:");
+      for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){
+        if( flagSet[i].mask & pDecl->flags ){
+          printf(" %s", flagSet[i].desc);
+        }
+      }
+      printf("\n");
+    }
+    if( pDecl->pInclude ){
+      Include *p;
+      printf("includes:");
+      for(p=pDecl->pInclude; p; p=p->pNext){
+        printf(" %s",p->zFile);
+      }
+      printf("\n");
+    }
+  }
+}
+#endif
+
+/*
+** When the "-doc" command-line option is used, this routine is called
+** to print all of the database information to standard output.
+*/
+static void DocumentationDump(void){
+  Decl *pDecl;
+  static struct {
+    int mask;
+    char flag;
+  } flagSet[] = {
+    { TY_Class,       'c' },
+    { TY_Enumeration, 'e' },
+    { TY_Structure,   's' },
+    { TY_Union,       'u' },
+    { TY_Variable,    'v' },
+    { TY_Subroutine,  'f' },
+    { TY_Typedef,     't' },
+    { TY_Macro,       'm' },
+    { DP_Export,      'x' },
+    { DP_Local,       'l' },
+    { DP_Cplusplus,   '+' },
+  };
+
+  for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){
+    int i;
+    int nLabel = 0;
+    char *zDecl;
+    char zLabel[50];
+    for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){
+      if( DeclHasProperty(pDecl,flagSet[i].mask) ){
+        zLabel[nLabel++] = flagSet[i].flag;
+      }
+    }
+    if( nLabel==0 ) continue;
+    zLabel[nLabel] = 0;
+    InsertExtraDecl(pDecl);
+    zDecl = pDecl->zDecl;
+    if( zDecl==0 ) zDecl = pDecl->zFwd;
+    printf("%s %s %s %p %d %d %d %d %d\n",
+       pDecl->zName,
+       zLabel,
+       pDecl->zFile,
+       pDecl->pComment,
+       pDecl->pComment ? pDecl->pComment->nText+1 : 0,
+       pDecl->zIf ? (int)strlen(pDecl->zIf)+1 : 0,
+       zDecl ? (int)strlen(zDecl) : 0,
+       pDecl->pComment ? pDecl->pComment->nLine : 0,
+       pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0
+    );
+    if( pDecl->pComment ){
+      printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText);
+    }
+    if( pDecl->zIf ){
+      printf("%s\n",pDecl->zIf);
+    }
+    if( zDecl ){
+      printf("%s",zDecl);
+    }
+    if( pDecl->tokenCode.nText ){
+      printf("%.*s\n",pDecl->tokenCode.nText, pDecl->tokenCode.zText);
+    }
+  }
+}
+
+/*
+** Given the complete text of an input file, this routine prints a
+** documentation record for the header comment at the beginning of the
+** file (if the file has a header comment.)
+*/
+void PrintModuleRecord(const char *zFile, const char *zFilename){
+  int i;
+  static int addr = 5;
+  while( isspace(*zFile) ){ zFile++; }
+  if( *zFile!='/' || zFile[1]!='*' ) return;
+  for(i=2; zFile[i] && (zFile[i-1]!='/' || zFile[i-2]!='*'); i++){}
+  if( zFile[i]==0 ) return;
+  printf("%s M %s %d %d 0 0 0 0\n%.*s\n",
+    zFilename, zFilename, addr, i+1, i, zFile);
+  addr += 4;
+}
+
+
+/*
+** Given an input argument to the program, construct a new InFile
+** object.
+*/
+static InFile *CreateInFile(char *zArg, int *pnErr){
+  int nSrc;
+  char *zSrc;
+  InFile *pFile;
+  int i;
+
+  /*
+  ** Get the name of the input file to be scanned.  The input file is
+  ** everything before the first ':' or the whole file if no ':' is seen.
+  **
+  ** Except, on windows, ignore any ':' that occurs as the second character
+  ** since it might be part of the drive specifier.  So really, the ":' has
+  ** to be the 3rd or later character in the name.  This precludes 1-character
+  ** file names, which really should not be a problem.
+  */
+  zSrc = zArg;
+  for(nSrc=2; zSrc[nSrc] && zArg[nSrc]!=':'; nSrc++){}
+  pFile = SafeMalloc( sizeof(InFile) );
+  memset(pFile,0,sizeof(InFile));
+  pFile->zSrc = StrDup(zSrc,nSrc);
+
+  /* Figure out if we are dealing with C or C++ code.  Assume any
+  ** file with ".c" or ".h" is C code and all else is C++.
+  */
+  if( nSrc>2 && zSrc[nSrc-2]=='.' && (zSrc[nSrc-1]=='c' || zSrc[nSrc-1]=='h')){
+    pFile->flags &= ~DP_Cplusplus;
+  }else{
+    pFile->flags |= DP_Cplusplus;
+  }
+
+  /*
+  ** If a separate header file is specified, use it
+  */
+  if( zSrc[nSrc]==':' ){
+    int nHdr;
+    char *zHdr;
+    zHdr = &zSrc[nSrc+1];
+    for(nHdr=0; zHdr[nHdr]; nHdr++){}
+    pFile->zHdr = StrDup(zHdr,nHdr);
+  }
+
+  /* Look for any 'c' or 'C' in the suffix of the file name and change
+  ** that character to 'h' or 'H' respectively.  If no 'c' or 'C' is found,
+  ** then assume we are dealing with a header.
+  */
+  else{
+    int foundC = 0;
+    pFile->zHdr = StrDup(zSrc,nSrc);
+    for(i = nSrc-1; i>0 && pFile->zHdr[i]!='.'; i--){
+      if( pFile->zHdr[i]=='c' ){
+        foundC = 1;
+        pFile->zHdr[i] = 'h';
+      }else if( pFile->zHdr[i]=='C' ){
+        foundC = 1;
+        pFile->zHdr[i] = 'H';
+      }
+    }
+    if( !foundC ){
+      SafeFree(pFile->zHdr);
+      pFile->zHdr = 0;
+    }
+  }
+
+  /*
+  ** If pFile->zSrc contains no 'c' or 'C' in its extension, it
+  ** must be a header file.   In that case, we need to set the
+  ** PS_Interface flag.
+  */
+  pFile->flags |= PS_Interface;
+  for(i=nSrc-1; i>0 && zSrc[i]!='.'; i--){
+    if( zSrc[i]=='c' || zSrc[i]=='C' ){
+      pFile->flags &= ~PS_Interface;
+      break;
+    }
+  }
+
+  /* Done!
+  */
+  return pFile;
+}
+
+/* MS-Windows and MS-DOS both have the following serious OS bug:  the
+** length of a command line is severely restricted.  But this program
+** occasionally requires long command lines.  Hence the following
+** work around.
+**
+** If the parameters "-f FILENAME" appear anywhere on the command line,
+** then the named file is scanned for additional command line arguments.
+** These arguments are substituted in place of the "FILENAME" argument
+** in the original argument list.
+**
+** This first parameter to this routine is the index of the "-f"
+** parameter in the argv[] array.  The argc and argv are passed by
+** pointer so that they can be changed.
+**
+** Parsing of the parameters in the file is very simple.  Parameters
+** can be separated by any amount of white-space (including newlines
+** and carriage returns.)  There are now quoting characters of any
+** kind.  The length of a token is limited to about 1000 characters.
+*/
+static void AddParameters(int index, int *pArgc, char ***pArgv){
+  int argc = *pArgc;      /* The original argc value */
+  char **argv = *pArgv;   /* The original argv value */
+  int newArgc;            /* Value for argc after inserting new arguments */
+  char **zNew = 0;        /* The new argv after this routine is done */
+  char *zFile;            /* Name of the input file */
+  int nNew = 0;           /* Number of new entries in the argv[] file */
+  int nAlloc = 0;         /* Space allocated for zNew[] */
+  int i;                  /* Loop counter */
+  int n;                  /* Number of characters in a new argument */
+  int c;                  /* Next character of input */
+  int startOfLine = 1;    /* True if we are where '#' can start a comment */
+  FILE *in;               /* The input file */
+  char zBuf[1000];        /* A single argument is accumulated here */
+
+  if( index+1==argc ) return;
+  zFile = argv[index+1];
+  in = fopen(zFile,"r");
+  if( in==0 ){
+    fprintf(stderr,"Can't open input file \"%s\"\n",zFile);
+    exit(1);
+  }
+  c = ' ';
+  while( c!=EOF ){
+    while( c!=EOF && isspace(c) ){
+      if( c=='\n' ){
+        startOfLine = 1;
+      }
+      c = getc(in);
+      if( startOfLine && c=='#' ){
+        while( c!=EOF && c!='\n' ){
+          c = getc(in);
+        }
+      }
+    }
+    n = 0;
+    while( c!=EOF && !isspace(c) ){
+      if( n<sizeof(zBuf)-1 ){ zBuf[n++] = c; }
+      startOfLine = 0;
+      c = getc(in);
+    }
+    zBuf[n] = 0;
+    if( n>0 ){
+      nNew++;
+      if( nNew + argc > nAlloc ){
+        if( nAlloc==0 ){
+          nAlloc = 100 + argc;
+          zNew = malloc( sizeof(char*) * nAlloc );
+        }else{
+          nAlloc *= 2;
+          zNew = realloc( zNew, sizeof(char*) * nAlloc );
+        }
+      }
+      if( zNew ){
+        int j = nNew + index;
+        zNew[j] = malloc( n + 1 );
+        if( zNew[j] ){
+          strcpy( zNew[j], zBuf );
+        }
+      }
+    }
+  }
+  newArgc = argc + nNew - 1;
+  for(i=0; i<=index; i++){
+    zNew[i] = argv[i];
+  }
+  for(i=nNew + index + 1; i<newArgc; i++){
+    zNew[i] = argv[i + 1 - nNew];
+  }
+  zNew[newArgc] = 0;
+  *pArgc = newArgc;
+  *pArgv = zNew;
+}
+
+#ifdef NOT_USED
+/*
+** Return the time that the given file was last modified.  If we can't
+** locate the file (because, for example, it doesn't exist), then
+** return 0.
+*/
+static unsigned int ModTime(const char *zFilename){
+  unsigned int mTime = 0;
+  struct stat sStat;
+  if( stat(zFilename,&sStat)==0 ){
+    mTime = sStat.st_mtime;
+  }
+  return mTime;
+}
+#endif
+
+/*
+** Print a usage comment for this program.
+*/
+static void Usage(const char *argv0, const char *argvN){
+  fprintf(stderr,"%s: Illegal argument \"%s\"\n",argv0,argvN);
+  fprintf(stderr,"Usage: %s [options] filename...\n"
+    "Options:\n"
+    "  -h          Generate a single .h to standard output.\n"
+    "  -H          Like -h, but only output EXPORT declarations.\n"
+    "  -v          (verbose) Write status information to the screen.\n"
+    "  -doc        Generate no header files.  Instead, output information\n"
+    "              that can be used by an automatic program documentation\n"
+    "              and cross-reference generator.\n"
+    "  -local      Generate prototypes for \"static\" functions and\n"
+    "              procedures.\n"
+    "  -f FILE     Read additional command-line arguments from the file named\n"
+    "              \"FILE\".\n"
+#ifdef DEBUG
+    "  -! MASK     Set the debugging mask to the number \"MASK\".\n"
+#endif
+    "  --          Treat all subsequent comment-line parameters as filenames,\n"
+    "              even if they begin with \"-\".\n",
+    argv0
+  );
+}
+
+/*
+** The following text contains a few simple #defines that we want
+** to be available to every file.
+*/
+static const char zInit[] =
+  "#define INTERFACE 0\n"
+  "#define EXPORT_INTERFACE 0\n"
+  "#define LOCAL_INTERFACE 0\n"
+  "#define EXPORT\n"
+  "#define LOCAL static\n"
+  "#define PUBLIC\n"
+  "#define PRIVATE\n"
+  "#define PROTECTED\n"
+;
+
+#if TEST==0
+int main(int argc, char **argv){
+  int i;                /* Loop counter */
+  int nErr = 0;         /* Number of errors encountered */
+  Token *pList;         /* List of input tokens for one file */
+  InFile *pFileList = 0;/* List of all input files */
+  InFile *pTail = 0;    /* Last file on the list */
+  InFile *pFile;        /* for looping over the file list */
+  int h_flag = 0;       /* True if -h is present.  Output unified header */
+  int H_flag = 0;       /* True if -H is present.  Output EXPORT header */
+  int v_flag = 0;       /* Verbose */
+  int noMoreFlags;      /* True if -- has been seen. */
+  FILE *report;         /* Send progress reports to this, if not NULL */
+
+  noMoreFlags = 0;
+  for(i=1; i<argc; i++){
+    if( argv[i][0]=='-' && !noMoreFlags ){
+      switch( argv[i][1] ){
+        case 'h':   h_flag = 1;   break;
+        case 'H':   H_flag = 1;   break;
+        case 'v':   v_flag = 1;   break;
+        case 'd':   doc_flag = 1; proto_static = 1; break;
+        case 'l':   proto_static = 1; break;
+        case 'f':   AddParameters(i, &argc, &argv); break;
+        case '-':   noMoreFlags = 1;   break;
+#ifdef DEBUG
+        case '!':   i++;  debugMask = strtol(argv[i],0,0); break;
+#endif
+        default:    Usage(argv[0],argv[i]); return 1;
+      }
+    }else{
+      pFile = CreateInFile(argv[i],&nErr);
+      if( pFile ){
+        if( pFileList ){
+          pTail->pNext = pFile;
+          pTail = pFile;
+        }else{
+          pFileList = pTail = pFile;
+        }
+      }
+    }
+  }
+  if( h_flag && H_flag ){
+    h_flag = 0;
+  }
+  if( v_flag ){
+    report = (h_flag || H_flag) ? stderr : stdout;
+  }else{
+    report = 0;
+  }
+  if( nErr>0 ){
+    return nErr;
+  }
+  for(pFile=pFileList; pFile; pFile=pFile->pNext){
+    char *zFile;
+
+    zFilename = pFile->zSrc;
+    if( zFilename==0 ) continue;
+    zFile = ReadFile(zFilename);
+    if( zFile==0 ){
+      fprintf(stderr,"Can't read input file \"%s\"\n",zFilename);
+      nErr++;
+      continue;
+    }
+    if( strncmp(zFile,zTopLine,nTopLine)==0 ){
+      pFile->zSrc = 0;
+    }else{
+      if( report ) fprintf(report,"Reading %s...\n",zFilename);
+      pList = TokenizeFile(zFile,&pFile->idTable);
+      if( pList ){
+        nErr += ParseFile(pList,pFile->flags);
+        FreeTokenList(pList);
+      }else if( zFile[0]==0 ){
+        fprintf(stderr,"Input file \"%s\" is empty.\n", zFilename);
+        nErr++;
+      }else{
+        fprintf(stderr,"Errors while processing \"%s\"\n", zFilename);
+        nErr++;
+      }
+    }
+    if( !doc_flag ) SafeFree(zFile);
+    if( doc_flag ) PrintModuleRecord(zFile,zFilename);
+  }
+  if( nErr>0 ){
+    return nErr;
+  }
+#ifdef DEBUG
+  if( debugMask & DECL_DUMP ){
+    DumpDeclList();
+    return nErr;
+  }
+#endif
+  if( doc_flag ){
+    DocumentationDump();
+    return nErr;
+  }
+  zFilename = "--internal--";
+  pList = TokenizeFile(zInit,0);
+  if( pList==0 ){
+    return nErr+1;
+  }
+  ParseFile(pList,PS_Interface);
+  FreeTokenList(pList);
+  if( h_flag || H_flag ){
+    nErr += MakeGlobalHeader(H_flag);
+  }else{
+    for(pFile=pFileList; pFile; pFile=pFile->pNext){
+      if( pFile->zSrc==0 ) continue;
+      nErr += MakeHeader(pFile,report,0);
+    }
+  }
+  return nErr;
+}
+#endif
+
+
+
+
+ + + + + + diff --git a/tools/bin/gitadd b/tools/bin/gitadd deleted file mode 100755 index 9355724..0000000 --- a/tools/bin/gitadd +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -x -make dist-clean -git add "$@" - diff --git a/tools/bin/makeheaders b/tools/bin/makeheaders deleted file mode 100755 index a50d9a0..0000000 Binary files a/tools/bin/makeheaders and /dev/null differ diff --git a/tools/bin/setuid_root.sh b/tools/bin/setuid_root.sh deleted file mode 100755 index aedd564..0000000 --- a/tools/bin/setuid_root.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -chown root "$@" && \ -chmod u+rsx,u-w,go+rx-s-w "$@" - diff --git a/tools/bin/tranche b/tools/bin/tranche deleted file mode 100755 index eb63c28..0000000 Binary files a/tools/bin/tranche and /dev/null differ diff --git a/tools/doc/makefile.txt b/tools/doc/makefile.txt deleted file mode 100644 index 722de84..0000000 --- a/tools/doc/makefile.txt +++ /dev/null @@ -1,91 +0,0 @@ -these are the comments from my RT makefile: - -# -# 2010 11 20 TWL Created -# 2011 05 26 TWL Modified to generalize -# 2012 02 23 NLS Add ECHO variable to use on different environnement -# corrected setup macro --> add include directory in the path to copy -# corrected install macro --> change the name of installed library : lib$(LIB)$(LIBSUFFIX) -# changed DOC_DIR directory name to 5_documents -# 2012 02 23 TWL removed LIB variable which is now set from the command line so -# so that all source dirs can use the same makefile -# 2012 02 23 TWL added target make dist_clean which also deletes the 2_makefile_deps file -# 2012 04 11 AWW added creation of temporary disk before each test is run -# 2012 06 05 TWL moved tests and try .cc files to directories. caused rtmake tests to -# dist_clean and make deps -# -# -#---------------------------------------------------------------------------- -# use this makefile to compile and test the code: -# -# for a first time run, or for regression use the following: -# -# $ make setup # makes the directories, though should already exist -# $ make regress -# -# the usual development workflow makes use of these: -# -# $ make deps # only when needed, for example if headers includes change or new files introduced -# $ cd tests; make deps # only when needed -# $ make lib # this makes the local library -# $ make tests # this updates tests and compiles -# $ make clean # deletes the .o files and library to force a recompile -# $ cd 1_tests; make clean -# -# for a release of a component -# -# $ make regress -# $ make install # this will only work if all the tests in 1_tests are passing -# -# before a checkin -# -# $ make dist_clean # will also clean the tests and try directories -# -# .lib.cc c++ files taken as source of object files for local build library -# .exl.cc c++ files taken to have main calls and are linked against local build libary -# .ex.cc c++ files taken to have main calls and are not linked against the local build library -# there are no rules for other files in this makefile -# -# about dependencies -# The makefile has no way of knowing if an edit changed the dependencies. Often they do not -# and it would be unwieldy to make the deps every time. Hence *the programmer* must delete -# the deps file if he has made any changes that change the dependencies. -# -# The makefile will make the 2_makefile_deps if the file is missing. -# -# -# about testing -# -# the name of the directory you run make in is taken to also be: the name of the library, -# the name of the main include file (with a .h added), and the name of the include directory -# where the individual headers are found. It is called LIB -# -# test programs are kept in a subdirectory called 1_tests, and are either .exl.cc, ex.cc, -# .sh files. When 'make tests' target is invoked they are all run. Test executables return 0 -# if the test fails, non-zero otherwise. -# -# to remove a test from the pool move it into the subdirectory in 1_tests, 9_broken, -# 5_more_tests of 5_scratch. broken tests are things that are known but must be fixed -# before a release. 5_more_tests are tests being worked on. 5_scratch is stuff that is -# probably going to be deleted. if there is a 5_deprecated, that is for good stuff but it -# is no longer used for some reason or other. -# -# There is a standard source code template and a -# messaging convention. Also, the names, by convention,are test_xxxx_ where xxx is a -# hexadecimal series nummber. If all the test executables pass the file 1_TESTS_PASSED is -# left in the directory. Otherwise the file 1_TESTS_FAILED is left in the directory. -# -# about release directory -# -# this is set in the ApplicationBase variable by rt_init -# -# after the tests pass stuff might be copied to the release directory using -# -# make install -# -# the release directory must have these subdirectories: -# -# bin documents include src -# -# -# diff --git a/tools/doc/makeheaders-notes.txt b/tools/doc/makeheaders-notes.txt deleted file mode 100644 index 5aebda9..0000000 --- a/tools/doc/makeheaders-notes.txt +++ /dev/null @@ -1,16 +0,0 @@ - -This worked to force the include to be part of the interface: - -#if INTERFACE -#include -#endif - -But this did not: - -#if INTERFACE - #include -#endif - -makeheaders looks to be sensitive to indentation - - diff --git a/tools/doc/makeheaders.html b/tools/doc/makeheaders.html deleted file mode 100644 index 289da58..0000000 --- a/tools/doc/makeheaders.html +++ /dev/null @@ -1,1150 +0,0 @@ - -The Makeheaders Program - -

The Makeheaders Program

- - -

-This document describes makeheaders, -a tool that automatically generates “.h” -files for a C or C++ programming project. -

- - -

Table Of Contents

- - -

1.0 Background

- -

-A piece of C source code can be one of two things: -a declaration or a definition. -A declaration is source text that gives information to the -compiler but doesn't directly result in any code being generated. -A definition is source text that results in executable machine -instructions or initialization data. -(These two terms are sometimes used inconsistently by other authors. -In particular, many people reverse the meanings of these words when -discussing Pascal or Ada code. -The meanings described here are the same as used in the ANSI-C -standards document.) -

- -

-Declarations in C include things such as the following: -

    -
  • Typedefs. -
  • Structure, union and enumeration declarations. -
  • Function and procedure prototypes. -
  • Preprocessor macros and #defines. -
  • extern” variable declarations. -
-

- -

-Definitions in C, on the other hand, include these kinds of things: -

    -
  • Variable definitions. -
  • The bodies of functions and procedures. -
  • Initialization data. -
-

- -

-The distinction between a declaration and a definition is common in -modern software engineering. -Another way of looking at the difference is that the declaration -is the interface and the definition is the implementation. -

- -

-In C programs, it has always been the tradition that declarations are -put in files with the “.h” suffix and definitions are -placed in “.c” files. -The .c files contain “#include” preprocessor statements -that cause the contents of .h files to be included as part of the -source code when the .c file is compiled. -In this way, the .h files define the interface to a subsystem and -the .c files define how the subsystem is implemented. -

- - -

1.1 Problems With The Traditional Approach

- -

-As the art of computer programming continues to advance, and the size -and complexity of programs continues to swell, the traditional C -approach of placing declarations and definitions in separate files begins -to present the programmer with logistics and -maintenance problems. -To wit: -

- -

-

    -

  1. -In large codes with many source files, it becomes difficult to determine -which .h files should be included in which .c files. -

  2. -It is typically the case that a .h file will be forced to include -another .h files, which in turn might include other .h files, -and so forth. -The .c file must be recompiled when any of the .h files in this chain -are altered, but it can be difficult to determine what .h files are found -in the include chain. -A frequent Makefile error is to omit some .h files from a dependency -list even though those files are on the include file chain. -

  3. -Some information is common to both the declaration and the definition of -an object in C, and so must be repeated in both the .h and the .c files -for that object. -In a large project, it can become increasingly difficult to keep the two -files in sync. -

  4. -When a .c file includes a .h file and the .h files changes, the .c file -must be recompiled, even if the part of the .h file that changed is not -actually used by the .c file. -In a large program, it is generally the case that almost every .c file ends up -depending on one or two of the more important .h files, and so when those .h -files change, the entire program must be recompiled. -It also happens that those important .h files tend to be the ones that -change most frequently. -This means that the entire program must be recompiled frequently, -leading to a lengthy modify-compile-test cycle and a corresponding -decrease in programmer productivity. -

  5. -The C programming language requires that declarations depending upon -each other must occur in a particular order. -In a program with complex, interwoven data structures, the correct -declaration order can become very difficult to determine manually, -especially when the declarations involved are spread out over several -files. -
-

- - -

1.2 The Makeheaders Solution

- -

-The makeheaders program is designed to ameliorate the problems associated -with the traditional C programming model by automatically generating -the interface information in the .h files from -interface information contained in other .h files and -from implementation information in the .c files. -When the makeheaders program is run, it scans the source -files for a project, -then generates a series of new .h files, one for each .c file. -The generated .h files contain exactly those declarations required by the -corresponding .c files, no more and no less. -

- -

-The makeheaders programming model overcomes all of the objections to the -traditional C programming model. -

    -

  1. -Because all declarations needed by a .c file are contained in a -single .h file, there is never any question about what .h files -a .c will need to include. If the .c file is named -alpha.c then it must include only the single .h file -named alpha.h. -(The .c file might also use some include files from the standard -library, such as <stdio.h>, but that is another matter.) -

  2. -The generated .h files do not include other .h files, and so there -are no include chains to worry about. -The file alpha.c depends on alpha.h and -nothing more. -

  3. -There is still duplication in the .h and the .c file, but because -the duplicate information is automatically generated, it is no longer -a problem. -Simply rerun makeheaders to resynchronize everything. -

  4. -The generated .h file contains the minimal set of declarations needed -by the .c file. -This means that when something changes, a minimal amount of recompilation -is required to produce an updated executable. -Experience has shown that this gives a dramatic improvement -in programmer productivity by facilitating a rapid modify-compile-test -cycle during development. -

  5. -The makeheaders program automatically sorts declarations into the -correct order, completely eliminating the wearisome and error-prone -task of sorting declarations by hand. -
-

- -

-In addition, the makeheaders program is fast and unintrusive. -It is a simple matter to incorporate makeheaders into a Makefile -so that makeheaders will be run automatically whenever the project -is rebuilt. -And the burden of running makeheaders is light. -It will easily process tens of thousands of lines of source -code per second. -

- - -

2.0 Running The Makeheaders Program

- -

-The makeheaders program is very easy to run. -If you have a collection of C source code and include files in the working -directory, then you can run makeheaders to generate appropriate .h -files using the following command: -

-   makeheaders *.[ch]
-
-That's really all there is to it! -This command will generate one .h file for every .c file. -Any .h files that were generated by a prior run of makeheaders -are ignored, -but manually entered .h files -that contain structure declarations and so forth will be scanned and -the declarations will be copied into the generated .h files as -appropriate. -But if makeheaders sees that the .h file that it has generated is no -different from the .h file it generated last time, it doesn't update -the file. -This prevents the corresponding .c files from having to -be needlessly recompiled. -

- -

-There are several options to the makeheaders program that can -be used to alter its behavior. -The default behavior is to write a single .h file for each .c file and -to give the .h file the same base name as the .c file. -Instead of generating a whole mess of .h files, you can, if you choose, -generate a single big .h file that contains all declarations needed -by all the .c files. Do this using the -h option to makeheaders. -As follows: -

-   makeheaders -h *.[ch] >common.h
-
-With the -h option, the .h file is not actually written to a disk file but -instead appears on standard output, where you are free to redirect it -into the file of your choice. -

- -

-A similar option is -H. Like the lower-case -h option, big -H -generates a single include file on standard output. But unlike -small -h, the big -H only emits prototypes and declarations that -have been designated as “exportable”. -The idea is that -H will generate an include file that defines -the interface to a library. -More will be said about this in section 3.4. -

- -

-Sometimes you want the base name of the .c file and the .h file to -be different. -For example, suppose you want the include file for alpha.c -to be called beta.h. -In this case, you would invoke makeheaders as follows: -

-   makeheaders alpha.c:beta.h
-
-Any time a filename argument contains a colon, the name before the -colon is taken to be the name of the .c file and the name after the -colon is taken to be the name of the .h file. -You can't use the shell's wildcard mechanism with this approach, but that -normally isn't a problem in Makefiles, which is where this stuff -comes in handy. -

- -

-If you want a particular file to be scanned by makeheaders but you -don't want makeheaders to generate a header file for that file, -then you can supply an empty header filename, like this: -

-   makeheaders alpha.c beta.c gamma.c:
-
-In this example, makeheaders will scan the three files named -“alpha.c”, -“beta.c” and -“gamma.c” -but because of the colon on the end of third filename -it will only generate headers for the first two files. -Unfortunately, -it is not possible to get makeheaders to process any file whose -name contains a colon. -

- -

-In a large project, the length of the command line for makeheaders -can become very long. -If the operating system doesn't support long command lines -(example: DOS and Win32) you may not be able to list all of the -input files in the space available. -In that case, you can use the “-f” option followed -by the name of a file to cause makeheaders to read command line -options and filename from the file instead of from the command line. -For example, you might prepare a file named “mkhdr.dat” -that contains text like this: -

-  src/alpha.c:hdr/alpha.h
-  src/beta.c:hdr/beta.h
-  src/gamma.c:hdr/gamma.h
-  ...
-
-Then invoke makeheaders as follows: -
-  makeheaders -f mkhdr.dat
-
-

- -

-The “-local” option causes makeheaders to -generate of prototypes for “static” functions and -procedures. -Such prototypes are normally omitted. -

- -

-Finally, makeheaders also includes a “-doc” option. -This command line option prevents makeheaders from generating any -headers at all. -Instead, makeheaders will write to standard output -information about every definition and declaration that it encounters -in its scan of source files. -The information output includes the type of the definition or -declaration and any comment that preceeds the definition or -declaration. -The output is in a format that can be easily parsed, and is -intended to be read by another program that will generate -documentation about the program. -We'll talk more about this feature later. -

- -

-If you forget what command line options are available, or forget -their exact name, you can invoke makeheaders using an unknown -command line option (like “--help” or -“-?”) -and it will print a summary of the available options on standard -error. -If you need to process a file whose name begins with -“-”, -you can prepend a “./” to its name in order to get it -accepted by the command line parser. -Or, you can insert the special option “--” on the -command line to cause all subsequent command line arguments to be treated as -filenames even if their names begin with “-”. -

- - -

3.0 Preparing Source Files For Use With Makeheaders

- -

-Very little has to be done to prepare source files for use with -makeheaders since makeheaders will read and understand ordinary -C code. -But it is important that you structure your files in a way that -makes sense in the makeheaders context. -This section will describe several typical uses of makeheaders. -

- - -

3.1 The Basic Setup

- -

-The simplest way to use makeheaders is to put all definitions in -one or more .c files and all structure and type declarations in -separate .h files. -The only restriction is that you should take care to chose basenames -for your .h files that are different from the basenames for your -.c files. -Recall that if your .c file is named (for example) -“alpha.c” -makeheaders will attempt to generate a corresponding header file -named “alpha.h”. -For that reason, you don't want to use that name for -any of the .h files you write since that will prevent makeheaders -from generating the .h file automatically. -

- -

-The structure of a .c file intented for use with makeheaders is very -simple. -All you have to do is add a single “#include” to the -top of the file that sources the header file that makeheaders will generate. -Hence, the beginning of a source file named “alpha.c” -might look something like this: -

- -
-   /*
-    * Introductory comment...
-    */
-   #include "alpha.h"
-
-   /* The rest of your code... */
-
- -

-Your manually generated header files require no special attention at all. -Code them as you normally would. -However, makeheaders will work better if you omit the -“#if” statements people often put around the outside of -header files that prevent the files from being included more than once. -For example, to create a header file named “beta.h”, -many people will habitually write the following: - -

-   #ifndef BETA_H
-   #define BETA_H
-
-   /* declarations for beta.h go here */
-
-   #endif
-
- -You can forego this cleverness with makeheaders. -Remember that the header files you write will never really be -included by any C code. -Instead, makeheaders will scan your header files to extract only -those declarations that are needed by individual .c files and then -copy those declarations to the .h files corresponding to the .c files. -Hence, the “#if” wrapper serves no useful purpose. -But it does make makeheaders work harder, forcing it to put -the statements - -
-   #if !defined(BETA_H)
-   #endif
-
- -around every declaration that it copies out of your header file. -No ill effect should come of this, but neither is there any benefit. -

- -

-Having prepared your .c and .h files as described above, you can -cause makeheaders to generate its .h files using the following simple -command: - -

-   makeheaders *.[ch]
-
- -The makeheaders program will scan all of the .c files and all of the -manually written .h files and then automatically generate .h files -corresponding to all .c files. -

- -

-Note that -the wildcard expression used in the above example, -“*.[ch]”, -will expand to include all .h files in the current directory, both -those entered manually be the programmer and others generated automatically -by a prior run of makeheaders. -But that is not a problem. -The makeheaders program will recognize and ignore any files it -has previously generated that show up on its input list. -

- - -

3.2 What Declarations Get Copied

- -

-The following list details all of the code constructs that makeheaders -will extract and place in -the automatically generated .h files: -

- -
    -

  • -When a function is defined in any .c file, a prototype of that function -is placed in the generated .h file of every .c file that -calls the function.

    - -

    If the “static” keyword of C appears at the -beginning of the function definition, the prototype is suppressed. -If you use the “LOCAL” keyword where you would normally -say “static”, then a prototype is generated, but it -will only appear in the single header file that corresponds to the -source file containing the function. For example, if the file -alpha.c contains the following: -

    -  LOCAL int testFunc(void){
    -    return 0;
    -  }
    -
    -Then the header file alpha.h will contain -
    -  #define LOCAL static
    -  LOCAL int testFunc(void);
    -
    -However, no other generated header files will contain a prototype for -testFunc() since the function has only file scope.

    - -

    When the “LOCAL” keyword is used, makeheaders will -also generate a #define for LOCAL, like this: -

    -   #define LOCAL static
    -
    -so that the C compiler will know what it means.

    - -

    If you invoke makeheaders with a “-local” -command-line option, then it treats the “static” -keyword like “LOCAL” and generates prototypes in the -header file that corresponds to the source file containing the function -definition.

    - -

  • -When a global variable is defined in a .c file, an -“extern” -declaration of that variable is placed in the header of every -.c file that uses the variable. -

    - -

  • -When a structure, union or enumeration declaration or a -function prototype or a C++ class declaration appears in a -manually produced .h file, that declaration is copied into the -automatically generated -.h files of all .c files that use the structure, union, enumeration, -function or class. -But declarations that appear in a -.c file are considered private to that .c file and are not copied into -any automatically generated files. -

    - -

  • -All #defines and typedefs that appear in manually produced .h files -are copied into automatically generated .h files as needed. -Similar constructs that appear in .c files are considered private to -those files and are not copied. -

    - -

  • -When a structure, union or enumeration declaration appears in a .h -file, makeheaders will automatically -generate a typedef that allows the declaration to be referenced without -the “struct”, “union” or -“enum” qualifier. -In other words, if makeheaders sees the code: -
    -  struct Examp { /* ... */ };
    -
    -it will automatically generate a corresponding typedef like this: -
    -  typedef struct Examp Examp;
    -
    -

    - -

  • -Makeheaders generates an error message if it encounters a function or -variable definition within a .h file. -The .h files are suppose to contain only interface, not implementation. -C compilers will not enforce this convention, but makeheaders does. -
- -

-As a final note, we observe that automatically generated declarations -are ordered as required by the ANSI-C programming language. -If the declaration of some structure “X” requires a -prior declaration of another structure “Y”, then Y will -appear first in the generated headers. -

- - -

3.3 How To Avoid Having To Write Any Header Files

- -

-In my experience, large projects work better if all of the manually -written code is placed in .c files and all .h files are generated -automatically. -This is slightly different for the traditional C method of placing -the interface in .h files and the implementation in .c files, but -it is a refreshing change that brings a noticable improvement to the -coding experience. -Others, I believe, share this view since I've -noticed recent languages (ex: java, tcl, perl, awk) tend to -support the one-file approach to coding as the only option. -

- -

-The makeheaders program supports putting both -interface and implementation into the same source file. -But you do have to tell makeheaders which part of the source file is the -interface and which part is the implementation. -Makeheaders has to know this in order to be able to figure out whether or -not structures declarations, typedefs, #defines and so forth should -be copied into the generated headers of other source files. -

- -

-You can instruct makeheaders to treat any part of a .c file as if -it were a .h file by enclosing that part of the .c file within: -

-   #if INTERFACE
-   #endif
-
-Thus any structure definitions that appear after the -“#if INTERFACE” but before the corresponding -“#endif” are eligable to be copied into the -automatically generated -.h files of other .c files. -

- -

-If you use the “#if INTERFACE” mechanism in a .c file, -then the generated header for that .c file will contain a line -like this: -

-   #define INTERFACE 0
-
-In other words, the C compiler will never see any of the text that -defines the interface. -But makeheaders will copy all necessary definitions and declarations -into the .h file it generates, so .c files will compile as if the -declarations were really there. -This approach has the advantage that you don't have to worry with -putting the declarations in the correct ANSI-C order -- makeheaders -will do that for you automatically. -

- -

-Note that you don't have to use this approach exclusively. -You can put some declarations in .h files and others within the -“#if INTERFACE” regions of .c files. -Makeheaders treats all declarations alike, no matter where they -come from. -You should also note that a single .c file can contain as many -“#if INTERFACE” regions as desired. -

- - -

3.4 Designating Declarations For Export

- -

-In a large project, one will often construct a hierarchy of -interfaces. -For example, you may have a group of 20 or so files that form -a library used in several other parts of the system. -Each file in this library will present two interfaces. -One interface will be the routines and data structures it is -willing to share with other files in the same library, and the -second interface is those routines and data structures it wishes -to make available to other subsystems. -(The second interface is normally a subset of the first.) -Ordinary C does not provide support for a tiered interface -like this, but makeheaders does. -

- -

-Using makeheaders, it is possible to designate routines and data -structures as being for “export”. -Exported objects are visible not only to other files within the -same library or subassembly but also to other -libraries and subassemblies in the larger program. -By default, makeheaders only makes objects visible to other members -of the same library. -

- -

-That isn't the complete truth, actually. -The semantics of C are such that once an object becomes visible -outside of a single source file, it is also visible to any user -of the library that is made from the source file. -Makeheaders can not prevent outsiders for using non-exported resources, -but it can discourage the practice by refusing to provide prototypes -and declarations for the services it does not want to export. -Thus the only real effect of the making an object exportable is -to include it in the output makeheaders generates when it is run -using the -H command line option. -This is not a perfect solution, but it works well in practice. -

- -

-But trouble quickly arises when we attempt to devise a mechanism for -telling makeheaders which prototypes it should export and which it should -keep local. -The built-in “static” keyword of C works well for -prohibiting prototypes from leaving a single source file, but because C doesn't -support a linkage hierarchy, there is nothing in the C language to help us. -We'll have to invite our own keyword: “EXPORT” -

- -

-Makeheaders allows the EXPORT keyword to precede any function or -procedure definition. -The routine following the EXPORT keyword is then eligable to appear -in the header file generated using the -H command line option. -Note that if a .c file contains the EXPORT keyword, makeheaders will -put the macro -

-   #define EXPORT
-
-in the header file it generates for the .c file so that the EXPORT keyword -will never be seen by the C compiler. -

- -

-But the EXPORT keyword only works for function and procedure definitions. -For structure, union and enum definitions, typedefs, #defines and -class declarations, a second mechanism is used. -Just as any declarations or definition contained within -

-   #if INTERFACE
-   #endif
-
-are visible to all files within the library, any declarations -or definitions within -
-   #if EXPORT_INTERFACE
-   #endif
-
-will become part of the exported interface. -The “#if EXPORT_INTERFACE” mechanism can be used in -either .c or .h files. -(The “#if INTERFACE” can also be used in both .h and -.c files, but since it's use in a .h file would be redundant, we haven't -mentioned it before.) -

- - -

3.5 Local declarations processed by makeheaders

- -

-Structure declarations and typedefs that appear in .c files are normally -ignored by makeheaders. -Such declarations are only intended for use by the source file in which -they appear and so makeheaders doesn't need to copy them into any -generated header files. -We call such declarations “private”. -

- -

-Sometimes it is convenient to have makeheaders sort a sequence -of private declarations into the correct order for us automatically. -Or, we could have static functions and procedures for which we would like -makeheaders to generate prototypes, but the arguments to these -functions and procedures uses private declarations. -In both of these cases, we want makeheaders to be aware of the -private declarations and copy them into the local header file, -but we don't want makeheaders to propagate the -declarations outside of the file in which they are declared. -

- -

-When this situation arises, enclose the private declarations -within -

-  #if LOCAL_INTERFACE
-  #endif
-
-A “LOCAL_INTERFACE” block works very much like the -“INTERFACE” and -“EXPORT_INTERFACE” -blocks described above, except that makeheaders insures that the -objects declared in a LOCAL_INTERFACE are only visible to the -file containing the LOCAL_INTERFACE. -

- - -

3.6 Using Makeheaders With C++ Code

- -

-You can use makeheaders to generate header files for C++ code, in -addition to C. -Makeheaders will recognize and copy both “class” -declarations -and inline function definitions, and it knows not to try to generate -prototypes for methods. -

- -

-In fact, makeheaders is smart enough to be used in projects that employ -a mixture of C and C++. -For example, if a C function is called from within a C++ code module, -makeheaders will know to prepend the text -

-   extern "C"
-
-to the prototype for that function in the C++ header file. -Going the other way, -if you try to call a C++ function from within C, an -appropriate error message is issued, since C++ routines can not -normally be called by C code (due to fact that most C++ compilers -use name mangling to facilitate type-safe linkage.) -

- -

-No special command-line options are required to use makeheaders with -C++ input. Makeheaders will recognize that its source code is C++ -by the suffix on the source code filename. Simple ".c" or ".h" suffixes -are assumed to be ANSI-C. Anything else, including ".cc", ".C" and -".cpp" is assumed to be C++. -The name of the header file generated by makeheaders is derived from -the name of the source file by converting every "c" to "h" and -every "C" to "H" in the suffix of the filename. -Thus the C++ source -file “alpha.cpp” will induce makeheaders to -generate a header file named “alpha.hpp”. -

- -

-Makeheaders augments class definitions by inserting prototypes to -methods where appropriate. If a method definition begins with one -of the special keywords PUBLIC, PROTECTED, or -PRIVATE (in upper-case to distinguish them from the regular -C++ keywords with the same meaning) then a prototype for that -method will be inserted into the class definition. If none of -these keywords appear, then the prototype is not inserted. For -example, in the following code, the constructor is not explicitly -declared in the class definition but makeheaders will add it there -because of the PUBLIC keyword that appears before the constructor -definition. -

- -
-#if INTERFACE
-class Example1 {
-private:
-  int v1;
-};
-#endif
-PUBLIC Example1::Example1(){
-  v1 = 0;
-}
-
- -

-The code above is equivalent to the following: -

- -
-#if INTERFACE
-class Example1 {
-private:
-  int v1;
-public:
-  Example1();
-};
-#endif
-Example1::Example1(){
-  v1 = 0;
-}
-
- -

-The first form is preferred because only a single declaration of -the constructor is required. The second form requires two declarations, -one in the class definition and one on the defintion of the constructor. -

- -

3.6.1 C++ Limitations

- -

-Makeheaders does not understand more recent -C++ syntax such as templates and namespaces. -Perhaps these issues will be addressed in future revisions. -

- - -

3.7 Conditional Compilation

- -

-The makeheaders program understands and tracks the conditional -compilation constructs in the source code files it scans. -Hence, if the following code appears in a source file -

-  #ifdef UNIX
-  #  define WORKS_WELL 1
-  #else
-  #  define WORKS_WELL 0
-  #endif
-
-then the next patch of code will appear in the generated header for -every .c file that uses the WORKS_WELL constant: -
-  #if defined(UNIX)
-  #  define WORKS_WELL 1
-  #endif
-  #if !defined(UNIX)
-  #  define WORKS_WELL 0
-  #endif
-
-The conditional compilation constructs can be nested to any depth. -Makeheaders also recognizes the special case of -
-  #if 0
-  #endif
-
-and treats the enclosed text as a comment. -

- - -

3.8 Caveats

- -

-The makeheaders system is designed to be robust -but it is possible for a devious programmer to fool the system, -usually with unhelpful consequences. -This subsection is a guide to helping you avoid trouble. -

- -

-Makeheaders does not understand the old K&R style of function -and procedure definitions. -It only understands the modern ANSI-C style, and will probably -become very confused if it encounters an old K&R function. -Therefore you should take care to avoid putting K&R function definitions -in your code. -

- -

-Makeheaders does not understand when you define more than one -global variable with the same type separated by a comma. -In other words, makeheaders does not understand this: -

-   int a = 4, b = 5;
-
-The makeheaders program wants every variable to have its own -definition. Like this: -
-   int a = 4;
-   int b = 5;
-
-Notice that this applies to global variables only, not to variables -you declare inside your functions. -Since global variables ought to be exceedingly rare, and since it is -good style to declare them separately anyhow, this restriction is -not seen as a terrible hardship. -

- -

-Makeheaders does not support defining an enumerated or aggregate type in -the same statement as a variable declaration. None of the following -statements work completely: -

-struct {int field;} a;
-struct Tag {int field;} b;
-struct Tag c;
-
-Instead, define types separately from variables: -
-#if INTERFACE
-struct Tag {int field;};
-#endif
-Tag a;
-Tag b; /* No more than one variable per declaration. */
-Tag c; /* So must put each on its own line. */
-
-See 3.2 What Declarations Get Copied for details, -including on the automatic typedef. -

- -

-The makeheaders program processes its source file prior to sending -those files through the C preprocessor. -Hence, if you hide important structure information in preprocessor defines, -makeheaders might not be able to successfully extract the information -it needs from variables, functions and procedure definitions. -For example, if you write this: -

-  #define BEGIN {
-  #define END }
-
-at the beginning of your source file, and then try to create a function -definition like this: -
-  char *StrDup(const char *zSrc)
-    BEGIN
-      /* Code here */
-    END
-
-then makeheaders won't be able to find the end of the function definition -and bad things are likely to happen. -

- -

-For most projects the code constructs that makeheaders cannot -handle are very rare. -As long as you avoid excessive cleverness, makeheaders will -probably be able to figure out what you want and will do the right -thing. -

- -

-Makeheaders has limited understanding of enums. In particular, it does -not realize the significance of enumerated values, so the enum is not -emitted in the header files when its enumerated values are used unless -the name associated with the enum is also used. Moreover, enums can be -completely anonymous, e.g. “enum {X, Y, Z};”. -Makeheaders ignores such enums so they can at least be used within a -single source file. Makeheaders expects you to use #define constants -instead. If you want enum features that #define lacks, and you need the -enum in the interface, bypass makeheaders and write a header file by -hand, or teach makeheaders to emit the enum definition when any of the -enumerated values are used, rather than only when the top-level name (if -any) is used. -

- - -

4.0 Using Makeheaders To Generate Documentation

- -

-Many people have observed the advantages of generating program -documentation directly from the source code: -

    -
  • Less effort is involved. It is easier to write a program than - it is to write a program and a document. -
  • The documentation is more likely to agree with the code. - When documentation is derived directly from the code, or is - contained in comments immediately adjacent to the code, it is much - more likely to be correct than if it is contained in a separate - unrelated file in a different part of the source tree. -
  • Information is kept in only one place. When a change occurs - in the code, it is not necessary to make a corresponding change - in a separate document. Just rerun the documentation generator. -
-The makeheaders program does not generate program documentation itself. -But you can use makeheaders to parse the program source code, extract -the information that is relevant to the documentation and to pass this -information to another tool to do the actual documentation preparation. -

- -

-When makeheaders is run with the “-doc” option, it -emits no header files at all. -Instead, it does a complete dump of its internal tables to standard -output in a form that is easily parsed. -This output can then be used by another program (the implementation -of which is left as an exercise to the reader) that will use the -information to prepare suitable documentation. -

- -

-The “-doc” option causes makeheaders to print -information to standard output about all of the following objects: -

    -
  • C++ class declarations -
  • Structure and union declarations -
  • Enumerations -
  • Typedefs -
  • Procedure and function definitions -
  • Global variables -
  • Preprocessor macros (ex: “#define”) -
-For each of these objects, the following information is output: -
    -
  • The name of the object. -
  • The type of the object. (Structure, typedef, macro, etc.) -
  • Flags to indicate if the declaration is exported (contained within - an EXPORT_INTERFACE block) or local (contained with LOCAL_INTERFACE). -
  • A flag to indicate if the object is declared in a C++ file. -
  • The name of the file in which the object was declared. -
  • The complete text of any block comment that preceeds the declarations. -
  • If the declaration occurred inside a preprocessor conditional - (“#if”) then the text of that conditional is - provided. -
  • The complete text of a declaration for the object. -
-The exact output format will not be described here. -It is simple to understand and parse and should be obvious to -anyone who inspects some sample output. -

- - -

5.0 Compiling The Makeheaders Program

- -

-The source code for makeheaders is a single file of ANSI-C code, -approximately 3000 lines in length. -The program makes only modest demands of the system and C library -and should compile without alteration on most ANSI C compilers -and on most operating systems. -It is known to compile using several variations of GCC for Unix -as well as Cygwin32 and MSVC 5.0 for Win32. -

- - -

6.0 History

- -

-The makeheaders program was first written by D. Richard Hipp -(also the original author of -SQLite and -Fossil) in 1993. -Hipp open-sourced the project immediately, but it never caught -on with any other developers and it continued to be used mostly -by Hipp himself for over a decade. When Hipp was first writing -the Fossil version control system in 2006 and 2007, he used -makeheaders on that project to help simplify the source code. -As the popularity of Fossil increased, the makeheaders -that was incorporated into the Fossil source tree became the -"official" makeheaders implementation. -

- -

-As this paragraph is being composed (2016-11-05), Fossil is the -only project known to Hipp that is still using makeheaders. On -the other hand, makeheaders has served the Fossil project well and -there are no plans remove it. -

- - -

7.0 Summary And Conclusion

- -

-The makeheaders program will automatically generate a minimal header file -for each of a set of C source and header files, and will -generate a composite header file for the entire source file suite, -for either internal or external use. -It can also be used as the parser in an automated program -documentation system. -

- -

-The makeheaders program has been in use since 1994, -in a wide variety of projects under both UNIX and Win32. -In every project where it has been used, makeheaders has proven -to be a very helpful aid -in the construction and maintenance of large C codes. -In at least two cases, makeheaders has facilitated development -of programs that would have otherwise been all but impossible -due to their size and complexity. -

- - diff --git a/tools/lib/bashrc b/tools/lib/bashrc deleted file mode 100755 index 79c23ec..0000000 --- a/tools/lib/bashrc +++ /dev/null @@ -1,20 +0,0 @@ -# - -umask 0077 - -if [ $INSIDE_EMACS ]; then - echo Hello Emacs -fi -export PS1='\n$(/usr/local/bin/Z)\n\u@\h§\w§\n> ' -export PS2='>>' -PATH=~/bin:$PATH - -#temporarily while active in subu code development -export PROJECT_SUBU=~/subu -PATH="$PROJECT_SUBU"/tools/bin:$PATH - -export PS_FORMAT=user:15,pid,%cpu,%mem,vsz,rss,tty,stat,start,time,command - -export EDITOR=emacs - - diff --git a/tools/lib/dot_emacs b/tools/lib/dot_emacs deleted file mode 100755 index 27132b6..0000000 --- a/tools/lib/dot_emacs +++ /dev/null @@ -1,202 +0,0 @@ - -(defun undedicate-window (&optional window) - (interactive) - (set-window-dedicated-p (or window (get-buffer-window)) nil)) - -;; Removing annoying dedicated buffer nonsense -(defun switch-to-buffer! (buffer-or-name &optional norecord force-same-window) - "Like switch-to-buffer but works for dedicated buffers \(though -it will ask first)." - (interactive - (list (read-buffer-to-switch "Switch to buffer: ") nil 'force-same-window)) - (when (and (window-dedicated-p (get-buffer-window)) - (yes-or-no-p "This window is dedicated, undedicate it? ")) - (undedicate-window)) - (switch-to-buffer buffer-or-name norecord force-same-window)) - -(defun toggle-window-dedication (&optional window) - (interactive) - (let ((window (or window (get-buffer-window)))) - (set-window-dedicated-p window (not (window-dedicated-p window))))) - -(global-set-key (kbd "C-x d") 'toggle-window-dedication) - -;; fix 'feature' of broken gdb where it takes control of an -;; emacs window, and locks the user out from switching from it -;; -;; yes and this fix breaks file name completion... -;; -;; (defun set-window-undedicated-p (window flag) -;; "Never set window dedicated." -;; flag) -;; (advice-add 'set-window-dedicated-p :override #'set-window-undedicated-p) -;; -;; Toggle window dedication - - (defun toggle-window-dedicated () - "Toggle whether the current active window is dedicated or not" - (interactive) - (message - (if (let (window (get-buffer-window (current-buffer))) - (set-window-dedicated-p window - (not (window-dedicated-p window)))) - "Window '%s' is dedicated" - "Window '%s' is normal") - (current-buffer))) - - (global-set-key "\C-q" 'toggle-window-dedicated) - -;; (setq ring-bell-function (lambda () -;; (call-process-shell-command -;; "xset led 3; xset -led 3" nil 0 nil))) -;; -;; (setq ring-bell-function nil) - - (setq ring-bell-function - (lambda () - (call-process-shell-command "xset led named 'Scroll Lock'") - (call-process-shell-command "xset -led named 'Scroll Lock'"))) - - -;; preferable to have keys for the characters, but the keyboard is already overloaded .. -;; (define-key key-translation-map (kbd " p") (kbd "¬")) -;; (set-input-method “latin-9-prefix) - - (global-set-key [f1] 'help-command) - (global-set-key "\C-h" 'nil) - (define-key key-translation-map (kbd "M-S") (kbd "§")) - - (global-set-key (kbd "C-x g phi SPC") [?φ]) ; phi for phase - (global-set-key (kbd "C-x g Phi SPC") [?Φ]) - - (global-set-key (kbd "C-x g d SPC") [?δ]) - (global-set-key (kbd "C-x g D SPC") [?Δ]) ; this is 'delta' is not 'increment'! - (global-set-key (kbd "C-x g delta SPC") [?δ]) - (global-set-key (kbd "C-x g Delta SPC") [?Δ]) ; this is 'delta' is not 'increment'! - - - (global-set-key (kbd "C-x g g SPC") [?γ]) - (global-set-key (kbd "C-x g G SPC") [?Γ]) - (global-set-key (kbd "C-x g gamma SPC") [?γ]) - (global-set-key (kbd "C-x g Gamma SPC") [?Γ]) - - (global-set-key (kbd "C-x g l SPC") [?λ]) - (global-set-key (kbd "C-x g L SPC") [?Λ]) - (global-set-key (kbd "C-x g lambda SPC") [?λ]) - (global-set-key (kbd "C-x g Lambda SPC") [?Λ]) - - (global-set-key (kbd "C-x g p SPC") [?π]) - (global-set-key (kbd "C-x g P SPC") [?Π]) - (global-set-key (kbd "C-x g pi SPC") [?π]) - (global-set-key (kbd "C-x g Pi SPC") [?Π]) - - (global-set-key (kbd "C-x g > = SPC") [?≥]) - (global-set-key (kbd "C-x g < = SPC") [?≤]) - (global-set-key (kbd "C-x g ! = SPC") [?≠]) - (global-set-key (kbd "C-x g neq SPC") [?≠]) - - (global-set-key (kbd "C-x g nil SPC") [?∅]) - - (global-set-key (kbd "C-x g not SPC") [?¬]) - - (global-set-key (kbd "C-x g and SPC") [?∧]) - (global-set-key (kbd "C-x g or SPC") [?∨]) - - (global-set-key (kbd "C-x g exists SPC") [?∃]) - (global-set-key (kbd "C-x g all SPC") [?∀]) - - (global-set-key (kbd "C-x g do SPC") [?⟳]) ; do - (global-set-key (kbd "C-x g rb SPC") [?◨]) - (global-set-key (kbd "C-x g lb SPC") [?◧]) - - (global-set-key (kbd "C-x g cont SPC") [?➜]) ; continue - (global-set-key (kbd "C-x g thread SPC") [?☥]) ; thread - - (global-set-key (kbd "C-x g in SPC") [?∈]) ; set membership - - - -;; lisp -;; - (setq lisp-indent-offset 2) - (setq inferior-lisp-program "sbcl") - - (modify-syntax-entry ?[ "(]" lisp-mode-syntax-table) - (modify-syntax-entry ?] ")[" lisp-mode-syntax-table) - (modify-syntax-entry ?{ "(}" lisp-mode-syntax-table) - (modify-syntax-entry ?} "){" lisp-mode-syntax-table) - -;; get the pwd in shell mode from the prompt rather than guessing by -;; watching the commands typed .. yes! now shell variables and source -;; scripts will work -;; in bashrc: export PS1='\n$(/usr/local/bin/Z)\u@\h§\w§\n> ' -;; - (add-hook 'shell-mode-hook - (lambda () - (shell-dirtrack-mode -1) - (dirtrack-mode 1))) - - (add-hook 'dirtrack-directory-change-hook - (lambda () - (message default-directory))) - - (setq dirtrack-list '("§\\(.*\\)§\n> " 1)) - -;; use a backrevs dir rather than leaving ~file droppings everywhere -;; - (setq backup-directory-alist `(("." . "~/emacs_backrevs"))) - (setq backup-by-copying t) - -;; stop the 'tab' character polution -;; - (setq-default indent-tabs-mode nil) - -;; turn off the poison C-z key. Use C-x C-z or the command suspend-emacs -;; - (global-set-key (kbd "C-z") nil) - -;; truncate rather than wrapping lines (use horizontal scroll to see to the right) -;; - (set-default 'truncate-lines t) - (setq truncate-partial-width-windows nil) - (setq-default fill-column 80) - -;; recover some window real estate -;; c-x mode-line to toggle the mode-line on and off -;; - (defun mode-line () "toggles the modeline on and off" - (interactive) - (setq mode-line-format - (if (equal mode-line-format nil) - (default-value 'mode-line-format)) ) - (redraw-display)) - - (tool-bar-mode -1) - (menu-bar-mode -1) - - -(put 'upcase-region 'disabled nil) -(put 'narrow-to-region 'disabled nil) -(put 'downcase-region 'disabled nil) -(put 'set-goal-column 'disabled nil) - - -(custom-set-variables - ;; custom-set-variables was added by Custom. - ;; If you edit it by hand, you could mess it up, so be careful. - ;; Your init file should contain only one such instance. - ;; If there is more than one, they won't work right. - '(ansi-color-names-vector - ["#212526" "#ff4b4b" "#b4fa70" "#fce94f" "#729fcf" "#e090d7" "#8cc4ff" "#eeeeec"]) - '(custom-enabled-themes (quote (wheatgrass))) - '(geiser-racket-binary "racket") - '(send-mail-function (quote smtpmail-send-it)) - '(tool-bar-mode nil)) -(custom-set-faces - ;; custom-set-faces was added by Custom. - ;; If you edit it by hand, you could mess it up, so be careful. - ;; Your init file should contain only one such instance. - ;; If there is more than one, they won't work right. - '(default ((t (:family "DejaVu Sans Mono" :foundry "PfEd" :slant normal :weight bold :height 98 :width normal))))) - -(put 'erase-buffer 'disabled nil) diff --git a/tools/lib/makefile_cc b/tools/lib/makefile_cc deleted file mode 100755 index bcf2475..0000000 --- a/tools/lib/makefile_cc +++ /dev/null @@ -1,168 +0,0 @@ - -#should add test that incdir is not pwd before deleting the include file in -#dist-clean currently make is looking for module.h while the include file is -#always module.lib.h so the lib include is not getting clobbered - -SHELL=/bin/bash - -# these are the sources edited by the programmer -C_SOURCE_LIB= $(wildcard $(SRCDIR)/*.lib.c) -C_SOURCE_CLI= $(wildcard $(SRCDIR)/*.cli.c) -CC_SOURCE_LIB= $(wildcard $(SRCDIR)/*.lib.cc) -CC_SOURCE_CLI= $(wildcard $(SRCDIR)/*.cli.cc) - -#remove the suffix to get base name -C_BASE_LIB= $(sort $(patsubst %.lib.c, %, $(notdir $(C_SOURCE_LIB)))) -C_BASE_CLI= $(sort $(patsubst %.cli.c, %, $(notdir $(C_SOURCE_CLI)))) -CC_BASE_LIB= $(sort $(patsubst %.lib.cc, %, $(notdir $(CC_SOURCE_LIB)))) -CC_BASE_CLI= $(sort $(patsubst %.cli.cc, %, $(notdir $(CC_SOURCE_CLI)))) - -# two sets of object files, one for the lib, and one for the command line interface progs -OBJECT_LIB= $(patsubst %, $(TMPDIR)/%.lib.o, $(C_BASE_LIB) $(CC_BASE_LIB)) -OBJECT_CLI= $(patsubst %, $(TMPDIR)/%.cli.o, $(C_BASE_CLI) $(CC_BASE_CLI)) - -# executables are made from CLI sources -EXEC= $(patsubst %, $(EXECDIR)/%, $(C_BASE_CLI) $(CC_BASE_CLI)) - -#otherwise make provides default values for these -C= -CC= - -DEPFILE=$(TMPDIR)/makefile-dep -LIBFILE=$(LIBDIR)/lib$(MODULE).a -INCFILE=$(INCDIR)/$(MODULE).h - --include makefile-flags - -# a single space literal, for example if you wanted to subsitute commas to -# spaces: $(subst $(space),;,$(string)) we ran into this out of a need to send -# multiple separate command arguments to a shell script from one variable value -blank := -space :=$(blank) $(blank) - -.PHONY: all -all: version dep lib exec - -.PHONY: version -version: - @echo makefile version 4.0 - @echo "PWD: " $(PWD) - @echo "MAKEFILE_LIST: " $(MAKEFILE_LIST) - @echo "C: " $(C) - @echo "CFLAGS: " $(CFLAGS) - @echo "CC: " $(CC) - @echo "CCFLAGS: " $(CCFLAGS) - @echo "LINKFLAGS: " $(LINKFLAGS) - -.PHONY: info -info: - @echo "DEPRDIR: " $(DEPRDIR) - @echo "DOCDIR: " $(DOCDIR) - @echo "EXECDIR: " $(EXECDIR) - @echo "INCDIR: " $(INCDIR) - @echo "LIBDIR: " $(LIBDIR) - @echo "TESTDIR: " $(TESTDIR) - @echo "TMPDIR: " $(TMPDIR) - @echo "TOOLSDIR: " $(TOOLSDIR) - @echo "TRYDIR: " $(TRYDIR) - @echo "DEPFILE: " $(DEPFILE) - @echo "LIBFILE: " $(LIBFILE) - @echo "INCFILE: " $(INCFILE) - @echo "C_SOURCE_LIB: " $(C_SOURCE_LIB) - @echo "C_SOURCE_CLI: " $(C_SOURCE_CLI) - @echo "CC_SOURCE_LIB: " $(CC_SOURCE_LIB) - @echo "CC_SOURCE_CLI: " $(CC_SOURCE_CLI) - @echo "C_BASE_LIB: " $(C_BASE_LIB) - @echo "C_BASE_CLI: " $(C_BASE_CLI) - @echo "CC_BASE_LIB: " $(CC_BASE_LIB) - @echo "CC_BASE_CLI: " $(CC_BASE_CLI) - @echo "OBJECT_LIB: " $(OBJECT_LIB) - @echo "OBJECT_CLI: " $(OBJECT_CLI) - @echo "EXEC: " $(EXEC) - -# should be safe to run this in an already setup or partially setup directory -.PHONY: setup -setup: - [ ! -e $(DEPRDIR) ] && mkdir $(DEPRDIR) || true - [ ! -e $(DOCDIR) ] && mkdir $(DOCDIR) || true - [ ! -e $(EXECDIR) ] && mkdir $(EXECDIR) || true - [ ! -e $(INCDIR) ] && mkdir $(INCDIR) || true - [ ! -e $(LIBDIR) ] && mkdir $(LIBDIR) || true - [ ! -e $(SRCDIR) ] && mkdir $(SRCDIR) || true - [ ! -e $(TESTDIR) ] && mkdir $(TESTDIR) || true - [ ! -e $(TMPDIR) ] && mkdir $(TMPDIR) || true - [ ! -e $(TRYDIR) ] && mkdir $(TRYDIR) || true - -#better to put the dependency generation into a loop on the source file and specify -MT for the target -#also should be redirecting only stdout so stderr messages will be displayed >>1 depfile -.PHONY: dep -dep: - @if [ -z "$(CC)" ]; then\ - if [ -z "$C()" ]; then\ - echo "No compiler specified";\ - exit 1;\ - else\ - echo "C compiler only deps" ;\ - $(C) $(CFLAGS) -MM $(C_SOURCE_LIB) $(C_SOURCE_CLI) | sed 's|^.*\.o|$(TMPDIR)/&|' > $(DEPFILE);\ - echo "deps for C linking";\ - for i in $(C_BASE_CLI) ; do\ - $(ECHO) >> $(DEPFILE);\ - $(ECHO) "$(EXECDIR)/$$i : $(TMPDIR)/$$i.cli.o $(LIBFILE)" >> $(DEPFILE);\ - $(ECHO) " $(C) -o $(EXECDIR)/$$i $(TMPDIR)/$$i.cli.o $(LINKFLAGS)" >> $(DEPFILE);\ - done;\ - fi;\ - else\ - $(CC) $(CCFLAGS) -MM $(CC_SOURCE_LIB) $(CC_SOURCE_CLI) | sed 's|^.*\.o|$(TMPDIR)/&|' > $(DEPFILE);\ - if [ -z "$C()" ]; then\ - echo "CC compiler only deps" ;\ - else\ - echo "CC and C mixed compile deps" ;\ - $(C) $(CFLAGS) -MM $(C_SOURCE_LIB) $(C_SOURCE_CLI) | sed 's|^.*\.o|$(TMPDIR)/&|' > $(DEPFILE);\ - fi;\ - echo "deps for CC linking";\ - for i in $(CC_BASE_CLI) $(C_BASE_CLI) ; do\ - $(ECHO) >> $(DEPFILE);\ - $(ECHO) "$(EXECDIR)/$$i : $(TMPDIR)/$$i.cli.o $(LIBFILE)" >> $(DEPFILE);\ - $(ECHO) " $(CC) -o $(EXECDIR)/$$i $(TMPDIR)/$$i.cli.o $(LINKFLAGS)" >> $(DEPFILE);\ - done;\ - fi - -.PHONY: lib -lib: $(LIBFILE) - -$(LIBFILE): $(OBJECT_LIB) - ar rcs $(LIBFILE) $(OBJECT_LIB) - -.PHONY: exec -exec: $(LIBFILE) - make sub_exec - -.PHONY: sub_exec -sub_exec: $(EXEC) - -.PHONY: stage -stage: - [ -f $(LIBFILE) ] && cp $(LIBFILE) $(PROJECT_SUBU)/stage/lib || true - [ -f $(INCFILE) ] && cp $(INCFILE) $(PROJECT_SUBU)/stage/include || true - [ $(shell ls -A $(EXECDIR)) ] && cp $(EXECDIR)/* $(PROJECT_SUBU)/stage/bin || true - -.PHONY: clean -clean: - for i in $(wildcard tmp/*); do rm $$i || true; done - -.PHONY: dist-clean -dist-clean: clean - for i in $(EXEC); do [ -e $$i ] && rm $$i || true; done - rm $(INCFILE) || true - rm $(LIBFILE) || true - --include $(DEPFILE) - -# recipes - -$(TMPDIR)/%.o : $(SRCDIR)/%.c - $(C) $(CFLAGS) -o $@ -c $< - -$(TMPDIR)/%.o : $(SRCDIR)/%.cc - $(CC) $(CCFLAGS) -o $@ -c $< - diff --git a/tools/lib/makefile_cc.~82b14679df98a141dbf8dac2f51f30d5fc84863b~ b/tools/lib/makefile_cc.~82b14679df98a141dbf8dac2f51f30d5fc84863b~ deleted file mode 100644 index 6de582b..0000000 --- a/tools/lib/makefile_cc.~82b14679df98a141dbf8dac2f51f30d5fc84863b~ +++ /dev/null @@ -1,113 +0,0 @@ - -SHELL=/bin/bash - --include 0_makefile-flags - -DEPSFILE=$(TMPDIR)/makefile_deps - -# a single space literal, for example if you wanted to subsitute commas to -# spaces: $(subst $(space),;,$(string)) we ran into this out of a need to send -# multiple separate command arguments to a shell script from one variable value -blank := -space :=$(blank) $(blank) - -# files used by the compiler -SOURCES_LIB= $(wildcard *.lib.c) -SOURCES_CLI= $(wildcard *.cli.c) -SOURCES= $(SOURCES_LIB) $(SOURCES_CLI) - -HFILES = $(wildcard *.lib.h) $(wildcard *.cli.h) - -OBJECTS_LIB= $(patsubst %.c, %.o, $(SOURCES_LIB)) -OBJECTS_CLI= $(patsubst %.c, %.o, $(SOURCES_CLI)) -OBJECTS= $(OBJECTS_LIB) $(OBJECTS_CLI) - -EXECS= $(sort $(patsubst %.cli.c, %, $(wildcard *.cli.c))) - -all: version deps lib execs - -version: - @echo makefile version 3.0 - @echo "PWD: " $(PWD) - @echo "MAKEFILE_LIST: " $(MAKEFILE_LIST) - @echo "CC: " $(CC) - @echo "CFLAGS: " $(CFLAGS) - @echo "LINKFLAGS: " $(LINKFLAGS) - -info: - @echo "DEPDIR: " $(DEPDIR) - @echo "DOCDIR: " $(DOCDIR) - @echo "EXECSDIR: " $(EXECSDIR) - @echo "INCDIR: " $(INCDIR) - @echo "LIBDIR: " $(LIBDIR) - @echo "TESTDIR: " $(TESTDIR) - @echo "TMPDIR: " $(TMPDIR) - @echo "TOOLSDIR: " $(TOOLSDIR) - @echo "TRYDIR: " $(TRYDIR) - @echo "DEPSFILE: " $(DEPSFILE) - @echo "LIBFILE: " $(LIBFILE) - @echo "SOURCES_LIB: " $(SOURCES_LIB) - @echo "SOURCES_CLI: " $(SOURCES_CLI) - @echo "SOURCES: " $(SOURCES) - @echo "HFILES: " $(HFILES) - @echo "OBJECTS_LIB: " $(OBJECTS_LIB) - @echo "OBJECTS_CLI: " $(OBJECTS_CLI) - @echo "OBJECTS: " $(OBJECTS) - @echo "EXECS: " $(EXECS) - -# should be safe to run this in an already setup or partially setup directory -setup: - if [ ! -e $(DEPRDIR) ]; then mkdir $(DEPRDIR); fi - if [ ! -e $(DOCDIR) ]; then mkdir $(DOCDIR); fi - if [ ! -e $(EXECSDIR) ]; then mkdir $(EXECSDIR); fi - if [ ! -e $(INCDIR) ]; then mkdir $(INCDIR); fi - if [ ! -e $(LIBDIR) ]; then mkdir $(LIBDIR); fi - if [ ! -e $(TESTDIR) ]; then mkdir $(TESTDIR); fi - if [ ! -e $(TMPDIR) ]; then mkdir $(TMPDIR); fi - if [ ! -e $(TRYDIR) ]; then mkdir $(TRYDIR); fi - -deps: - $(CC) $(CFLAGS) -MM $(SOURCES) 1> $(DEPSFILE) - for i in $(EXECS) ; do\ - $(ECHO) >> $(DEPSFILE);\ - $(ECHO) "$(EXECSDIR)/$$i : $$i.cli.o $(LIBDIR)/$(LIBFILE)" >> $(DEPSFILE);\ - $(ECHO) " $(CC) -o $(EXECSDIR)/$$i $$i.cli.o $(LINKFLAGS)" >> $(DEPSFILE);\ - done - -lib: - if [ ! -e $(DEPSFILE) ]; then make deps; fi - make sub_lib - -sub_lib: $(LIBDIR)/$(LIBFILE) - - -execs: $(LIBDIR)/$(LIBFILE) - if [ ! -e $(DEPSFILE) ]; then make deps; fi - make sub_execs - -sub_execs: $(patsubst %, $(EXECSDIR)/%, $(EXECS)) - -stage: - if [ -f $(LIBDIR)/$(LIBFILE) ]; then cp $(LIBDIR)/$(LIBFILE) $(PROJECT_SUBU)/stage/lib; fi - if [ -f $(INCDIR)/$(INCFILE) ]; then cp $(INCDIR)/$(INCFILE) $(PROJECT_SUBU)/stage/include; fi - cp $(EXECSDIR)/* $(PROJECT_SUBU)/stage/bin - -clean: - for i in $(wildcard *~); do mv $$i $(TMPDIR); done - for i in $(wildcard *.lib.o) $(wildcard *.cli.o); do rm $$i; done - for i in $(EXECS); do if [ -e $(EXECSDIR)/$$i ]; then rm $(EXECSDIR)/$$i; fi; done - if [ -f $(LIBDIR)/$(LIBFILE) ]; then rm $(LIBDIR)/$(LIBFILE); fi - if [ -f $(DEPSFILE) ]; then rm $(DEPSFILE); fi - -# -$(LIBDIR)/$(LIBFILE) : $(OBJECTS_LIB) - ar rcs $(LIBDIR)/$(LIBFILE) $(OBJECTS_LIB) - --include $(DEPSFILE) - -# recipe for making object files: -# -%.o : %.c - $(CC) $(CFLAGS) -c $< - - diff --git a/tools/lib/makefile_trc b/tools/lib/makefile_trc deleted file mode 100644 index 1edc8b3..0000000 --- a/tools/lib/makefile_trc +++ /dev/null @@ -1,188 +0,0 @@ - -# We should read the tranche sources and see what files they output, and then -# build the deps accordingly, but this makefile is more primitive approach. -# Here the programmer must have one output .c file or .cc file per input tranche -# file and it must be name .{lib,cli}.{c,cc} and the {.c,.cc} output -# tranche must be named .trc.{c,cc} - -# will re-orgnaize the directory structure .. will name the src-da .. etc. to the -# name of the module, then have a source dir inside that has only the stuff -# programmer's edit, then get rid of the numbers - -SHELL=/bin/bash - -#LIDBIR, EXECDIR, HDIR hold the make results that might later be staged -#$(PWD) is the directory that make was called from, this is already build in -#set to dot to use the same directory as the source code -#leave blank to ommit -DEPRDIR=deprecated -DOCDIR=doc -EXECDIR=exec -INCDIR=include -LIBDIR=lib -SRCDIR=src -TESTDIR=test -TMPDIR=tmp -TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) -TRYDIR=try - -LIBFILE=$(LIBDIR)/lib$(MODULE).a -INCFILE=$(MODULE).h - -# these are the sources edited by the programmer -C_SOURCE_LIB= $(wildcard $(SRCDIR)/*.lib.c) -C_SOURCE_CLI= $(wildcard $(SRCDIR)/*.cli.c) -CC_SOURCE_LIB= $(wildcard $(SRCDIR)/*.lib.cc) -CC_SOURCE_CLI= $(wildcard $(SRCDIR)/*.cli.cc) - -#remove the suffix to get base name -C_BASE_LIB= $(sort $(patsubst %.lib.c, %, $(notdir $(C_SOURCE_LIB)))) -C_BASE_CLI= $(sort $(patsubst %.cli.c, %, $(notdir $(C_SOURCE_CLI)))) -CC_BASE_LIB= $(sort $(patsubst %.lib.cc, %, $(notdir $(CC_SOURCE_LIB)))) -CC_BASE_CLI= $(sort $(patsubst %.cli.cc, %, $(notdir $(CC_SOURCE_CLI)))) - -# the progreammer must name the c and cc tranches accordingly: -C_TRC_LIB= $(patasubst %, $(TMPDIR)/%.trc.c, C_BASE_LIB) -C_TRC_CLI= $(patasubst %, $(TMPDIR)/%.trc.c, C_BASE_CLI) -CC_TRC_LIB= $(patasubst %, $(TMPDIR)/%.trc.cc, CC_BASE_LIB) -CC_TRC_CLI= $(patasubst %, $(TMPDIR)/%.trc.cc, CC_BASE_CLI) - -# two sets of object files, one for the lib, and one for the command line interface progs -OBJECT_LIB= $(patsubst %, $(TMDIR)/%.o, $(C_BASE_LIB) $(CC_BASE_LIB)) -OBJECT_CLI= $(patsubst %, $(TMDIR)/%.o, $(C_BASE_CLI) $(CC_BASE_CLI)) - -# executables are made from CLI sources -EXEC= (patsubst %, $(EXECDIR)/%, $(C_BASE_CLI) $(CC_BASE_CLI)) - -#otherwise make provides default values for these -C= -CC= - --include makefile-flags - -DEPFILE=$(TMPDIR)/makefile_dep - -# a single space literal, for example if you wanted to subsitute commas to -# spaces: $(subst $(space),;,$(string)) we ran into this out of a need to send -# multiple separate command arguments to a shell script from one variable value -blank := -space :=$(blank) $(blank) - -all: version deps lib execs - -version: - @echo makefile version 3.0 - @echo "PWD: " $(PWD) - @echo "MAKEFILE_LIST: " $(MAKEFILE_LIST) - @echo "C: " $(C) - @echo "CFLAGS: " $(CFLAGS) - @echo "CC: " $(CC) - @echo "CCFLAGS: " $(CCFLAGS) - @echo "LINKFLAGS: " $(LINKFLAGS) - -info: - @echo "DEPDIR: " $(DEPDIR) - @echo "DOCDIR: " $(DOCDIR) - @echo "EXECSDIR: " $(EXECSDIR) - @echo "INCDIR: " $(INCDIR) - @echo "LIBDIR: " $(LIBDIR) - @echo "TESTDIR: " $(TESTDIR) - @echo "TMPDIR: " $(TMPDIR) - @echo "TOOLSDIR: " $(TOOLSDIR) - @echo "TRYDIR: " $(TRYDIR) - @echo "DEPSFILE: " $(DEPSFILE) - @echo "LIBFILE: " $(LIBFILE) - @echo "TRC_TRC: " $(TRC_TRC) - @echo "C_BASE_LIB: " $(C_BASE_LIB) - @echo "C_BASE_CLI: " $(C_BASE_CLI) - @echo "CC_BASE_LIB: " $(CC_BASE_LIB) - @echo "CC_BASE_CLI: " $(CC_BASE_CLI) - -# should be safe to run this in an already setup or partially setup directory -setup: - if [ ! -e $(DEPRDIR) ]; then mkdir $(DEPRDIR); fi - if [ ! -e $(DOCDIR) ]; then mkdir $(DOCDIR); fi - if [ ! -e $(EXECSDIR) ]; then mkdir $(EXECSDIR); fi - if [ ! -e $(INCDIR) ]; then mkdir $(INCDIR); fi - if [ ! -e $(LIBDIR) ]; then mkdir $(LIBDIR); fi - if [ ! -e $(SRCDIR) ]; then mkdir $(SRCDIR); fi - if [ ! -e $(TESTDIR) ]; then mkdir $(TESTDIR); fi - if [ ! -e $(TMPDIR) ]; then mkdir $(TMPDIR); fi - if [ ! -e $(TRYDIR) ]; then mkdir $(TRYDIR); fi - -dep: $(C_TRC_LIB) $(C_TRC_CLI) $(CC_TRC_LIB) $(CC_TRC_CLI) - if [ -z "$(CC)" ]; then\ - if [ -z "$C()" ]; then\ - echo "No compiler specified";\ - exit 1;\ - else\ - echo "C compiler only deps" ;\ - $(C) $(CFLAGS) -MM $(C_TRC_LIB) $(C_TRC_CLI) 1> $(DEPSFILE);\ - echo "deps for C linking";\ - for i in $(EXECS) ; do\ - $(ECHO) >> $(DEPSFILE);\ - $(ECHO) "$(EXECSDIR)/$$i : $$i.cli.o $(LIBDIR)/$(LIBFILE)" >> $(DEPSFILE);\ - $(ECHO) " $(C) -o $(EXECSDIR)/$$i $$i.cli.o $(LINKFLAGS)" >> $(DEPSFILE);\ - done;\ - fi;\ - else\ - $(CC) $(CCFLAGS) -MM $(CC_TRC_LIB) $(CC_TRC_CLI) 1> $(DEPSFILE);\ - if [ -z "$C()" ]; then\ - echo "CC compiler only deps" ;\ - else\ - echo "CC and C mixed compile deps" ;\ - $(C) $(CFLAGS) -MM $(C_TRC_LIB) $(C_TRC_CLI) 1>> $(DEPSFILE);\ - fi;\ - echo "deps for CC linking";\ - for i in $(EXECS) ; do\ - $(ECHO) >> $(DEPSFILE);\ - $(ECHO) "$(EXECSDIR)/$$i : $$i.cli.o $(LIBDIR)/$(LIBFILE)" >> $(DEPSFILE);\ - $(ECHO) " $(CC) -o $(EXECSDIR)/$$i $$i.cli.o $(LINKFLAGS)" >> $(DEPSFILE);\ - done;\ - fi - -lib: - make $(LIBDIR)/$(LIBFILE) - -$(LIBDIR)/$(LIBFILE) : $(OBJECTS_LIB) - ar rcs $(LIBDIR)/$(LIBFILE) $(OBJECTS_LIB) - -exec: $(LIBDIR)/$(LIBFILE) - make sub_exec - -sub_exec: $(patsubst %, $(EXECSDIR)/%, $(EXECS)) - -stage: - if [ -f $(LIBDIR)/$(LIBFILE) ]; then cp $(LIBDIR)/$(LIBFILE) $(PROJECT_SUBU)/stage/lib; fi - if [ -f $(INCDIR)/$(INCFILE) ]; then cp $(INCDIR)/$(INCFILE) $(PROJECT_SUBU)/stage/include; fi - -cp $(EXECSDIR)/* $(PROJECT_SUBU)/stage/bin - -clean: - for i in $(wildcard *~); do mv $$i $(TMPDIR); done - for i in $(wildcard *.lib.o) $(wildcard *.cli.o); do rm $$i; done - for i in $(EXECS); do if [ -e $(EXECSDIR)/$$i ]; then rm $(EXECSDIR)/$$i; fi; done - if [ -f $(LIBDIR)/$(LIBFILE) ]; then rm $(LIBDIR)/$(LIBFILE); fi - if [ -f $(DEPSFILE) ]; then rm $(DEPSFILE); fi - --include $(DEPSFILE) - -# recipes -# -%.trc.c : %.lib.c - $(TRANCHE) $< - -%.trc.c : %.cli.c - $(TRANCHE) $< - -%.trc.cc : %.lib.cc - $(TRANCHE) $< - -%.trc.cc : %.cli.cc - $(TRANCHE) $< - -%.o : %.c - $(C) $(CFLAGS) -c $< - -%.o : %.cc - $(CC) $(CCFLAGS) -c $< - diff --git a/tools/src/makeheaders.c b/tools/src/makeheaders.c deleted file mode 100644 index b58e787..0000000 --- a/tools/src/makeheaders.c +++ /dev/null @@ -1,3739 +0,0 @@ - - - - - - -Fossil: File Content - - - - -
-

Fossil

File Content
- -
- -
-
- - - -
- -
-

Latest version of file 'src/makeheaders.c':

-
    -
  • File -src/makeheaders.c -— part of check-in -[8cecc544] -at -2018-11-02 15:21:54 -on branch trunk -— Enhance makeheaders so that it is able to deal with static_assert() statements. -(These do not come up in Fossil itself. This check-in is in response to use -of Makeheaders by external projects.) - (user: -drh -size: 100011) -[more...] -
-
-
-
-/*
-** This program is free software; you can redistribute it and/or
-** modify it under the terms of the Simplified BSD License (also
-** known as the "2-Clause License" or "FreeBSD License".)
-**
-** Copyright 1993 D. Richard Hipp. All rights reserved.
-**
-** Redistribution and use in source and binary forms, with or
-** without modification, are permitted provided that the following
-** conditions are met:
-**
-**   1. Redistributions of source code must retain the above copyright
-**      notice, this list of conditions and the following disclaimer.
-**
-**   2. Redistributions in binary form must reproduce the above copyright
-**      notice, this list of conditions and the following disclaimer in
-**      the documentation and/or other materials provided with the
-**      distribution.
-**
-** This software is provided "as is" and any express or implied warranties,
-** including, but not limited to, the implied warranties of merchantability
-** and fitness for a particular purpose are disclaimed.  In no event shall
-** the author or contributors be liable for any direct, indirect, incidental,
-** special, exemplary, or consequential damages (including, but not limited
-** to, procurement of substitute goods or services; loss of use, data or
-** profits; or business interruption) however caused and on any theory of
-** liability, whether in contract, strict liability, or tort (including
-** negligence or otherwise) arising in any way out of the use of this
-** software, even if advised of the possibility of such damage.
-**
-** This program is distributed in the hope that it will be useful,
-** but without any warranty; without even the implied warranty of
-** merchantability or fitness for a particular purpose.
-** appropriate header files.
-*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <memory.h>
-#include <sys/stat.h>
-#include <assert.h>
-#include <string.h>
-
-#if defined( __MINGW32__) ||  defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__)
-#  ifndef WIN32
-#    define WIN32
-#  endif
-#else
-# include <unistd.h>
-#endif
-
-/*
-** Macros for debugging.
-*/
-#ifdef DEBUG
-static int debugMask = 0;
-# define debug0(F,M)       if( (F)&debugMask ){ fprintf(stderr,M); }
-# define debug1(F,M,A)     if( (F)&debugMask ){ fprintf(stderr,M,A); }
-# define debug2(F,M,A,B)   if( (F)&debugMask ){ fprintf(stderr,M,A,B); }
-# define debug3(F,M,A,B,C) if( (F)&debugMask ){ fprintf(stderr,M,A,B,C); }
-# define PARSER      0x00000001
-# define DECL_DUMP   0x00000002
-# define TOKENIZER   0x00000004
-#else
-# define debug0(Flags, Format)
-# define debug1(Flags, Format, A)
-# define debug2(Flags, Format, A, B)
-# define debug3(Flags, Format, A, B, C)
-#endif
-
-/*
-** The following macros are purely for the purpose of testing this
-** program on itself.  They don't really contribute to the code.
-*/
-#define INTERFACE 1
-#define EXPORT_INTERFACE 1
-#define EXPORT
-
-/*
-** Each token in a source file is represented by an instance of
-** the following structure.  Tokens are collected onto a list.
-*/
-typedef struct Token Token;
-struct Token {
-  const char *zText;      /* The text of the token */
-  int nText;              /* Number of characters in the token's text */
-  int eType;              /* The type of this token */
-  int nLine;              /* The line number on which the token starts */
-  Token *pComment;        /* Most recent block comment before this token */
-  Token *pNext;           /* Next token on the list */
-  Token *pPrev;           /* Previous token on the list */
-};
-
-/*
-** During tokenization, information about the state of the input
-** stream is held in an instance of the following structure
-*/
-typedef struct InStream InStream;
-struct InStream {
-  const char *z;          /* Complete text of the input */
-  int i;                  /* Next character to read from the input */
-  int nLine;              /* The line number for character z[i] */
-};
-
-/*
-** Each declaration in the C or C++ source files is parsed out and stored as
-** an instance of the following structure.
-**
-** A "forward declaration" is a declaration that an object exists that
-** doesn't tell about the objects structure.  A typical forward declaration
-** is:
-**
-**          struct Xyzzy;
-**
-** Not every object has a forward declaration.  If it does, thought, the
-** forward declaration will be contained in the zFwd field for C and
-** the zFwdCpp for C++.  The zDecl field contains the complete
-** declaration text.
-*/
-typedef struct Decl Decl;
-struct Decl {
-  char *zName;       /* Name of the object being declared.  The appearance
-                     ** of this name is a source file triggers the declaration
-                     ** to be added to the header for that file. */
-  const char *zFile; /* File from which extracted.  */
-  char *zIf;         /* Surround the declaration with this #if */
-  char *zFwd;        /* A forward declaration.  NULL if there is none. */
-  char *zFwdCpp;     /* Use this forward declaration for C++. */
-  char *zDecl;       /* A full declaration of this object */
-  char *zExtra;      /* Extra declaration text inserted into class objects */
-  int extraType;     /* Last public:, protected: or private: in zExtraDecl */
-  struct Include *pInclude;   /* #includes that come before this declaration */
-  int flags;         /* See the "Properties" below */
-  Token *pComment;   /* A block comment associated with this declaration */
-  Token tokenCode;   /* Implementation of functions and procedures */
-  Decl *pSameName;   /* Next declaration with the same "zName" */
-  Decl *pSameHash;   /* Next declaration with same hash but different zName */
-  Decl *pNext;       /* Next declaration with a different name */
-};
-
-/*
-** Properties associated with declarations.
-**
-** DP_Forward and DP_Declared are used during the generation of a single
-** header file in order to prevent duplicate declarations and definitions.
-** DP_Forward is set after the object has been given a forward declaration
-** and DP_Declared is set after the object gets a full declarations.
-** (Example:  A forward declaration is "typedef struct Abc Abc;" and the
-** full declaration is "struct Abc { int a; float b; };".)
-**
-** The DP_Export and DP_Local flags are more permanent.  They mark objects
-** that have EXPORT scope and LOCAL scope respectively.  If both of these
-** marks are missing, then the object has library scope.  The meanings of
-** the scopes are as follows:
-**
-**    LOCAL scope         The object is only usable within the file in
-**                        which it is declared.
-**
-**    library scope       The object is visible and usable within other
-**                        files in the same project.  By if the project is
-**                        a library, then the object is not visible to users
-**                        of the library.  (i.e. the object does not appear
-**                        in the output when using the -H option.)
-**
-**    EXPORT scope        The object is visible and usable everywhere.
-**
-** The DP_Flag is a temporary use flag that is used during processing to
-** prevent an infinite loop.  It's use is localized.
-**
-** The DP_Cplusplus, DP_ExternCReqd and DP_ExternReqd flags are permanent
-** and are used to specify what type of declaration the object requires.
-*/
-#define DP_Forward      0x001   /* Has a forward declaration in this file */
-#define DP_Declared     0x002   /* Has a full declaration in this file */
-#define DP_Export       0x004   /* Export this declaration */
-#define DP_Local        0x008   /* Declare in its home file only */
-#define DP_Flag         0x010   /* Use to mark a subset of a Decl list
-                                ** for special processing */
-#define DP_Cplusplus    0x020   /* Has C++ linkage and cannot appear in a
-                                ** C header file */
-#define DP_ExternCReqd  0x040   /* Prepend 'extern "C"' in a C++ header.
-                                ** Prepend nothing in a C header */
-#define DP_ExternReqd   0x080   /* Prepend 'extern "C"' in a C++ header if
-                                ** DP_Cplusplus is not also set. If DP_Cplusplus
-                                ** is set or this is a C header then
-                                ** prepend 'extern' */
-
-/*
-** Convenience macros for dealing with declaration properties
-*/
-#define DeclHasProperty(D,P)    (((D)->flags&(P))==(P))
-#define DeclHasAnyProperty(D,P) (((D)->flags&(P))!=0)
-#define DeclSetProperty(D,P)    (D)->flags |= (P)
-#define DeclClearProperty(D,P)  (D)->flags &= ~(P)
-
-/*
-** These are state properties of the parser.  Each of the values is
-** distinct from the DP_ values above so that both can be used in
-** the same "flags" field.
-**
-** Be careful not to confuse PS_Export with DP_Export or
-** PS_Local with DP_Local.  Their names are similar, but the meanings
-** of these flags are very different.
-*/
-#define PS_Extern        0x000800    /* "extern" has been seen */
-#define PS_Export        0x001000    /* If between "#if EXPORT_INTERFACE"
-                                     ** and "#endif" */
-#define PS_Export2       0x002000    /* If "EXPORT" seen */
-#define PS_Typedef       0x004000    /* If "typedef" has been seen */
-#define PS_Static        0x008000    /* If "static" has been seen */
-#define PS_Interface     0x010000    /* If within #if INTERFACE..#endif */
-#define PS_Method        0x020000    /* If "::" token has been seen */
-#define PS_Local         0x040000    /* If within #if LOCAL_INTERFACE..#endif */
-#define PS_Local2        0x080000    /* If "LOCAL" seen. */
-#define PS_Public        0x100000    /* If "PUBLIC" seen. */
-#define PS_Protected     0x200000    /* If "PROTECTED" seen. */
-#define PS_Private       0x400000    /* If "PRIVATE" seen. */
-#define PS_PPP           0x700000    /* If any of PUBLIC, PRIVATE, PROTECTED */
-
-/*
-** The following set of flags are ORed into the "flags" field of
-** a Decl in order to identify what type of object is being
-** declared.
-*/
-#define TY_Class         0x00100000
-#define TY_Subroutine    0x00200000
-#define TY_Macro         0x00400000
-#define TY_Typedef       0x00800000
-#define TY_Variable      0x01000000
-#define TY_Structure     0x02000000
-#define TY_Union         0x04000000
-#define TY_Enumeration   0x08000000
-#define TY_Defunct       0x10000000  /* Used to erase a declaration */
-
-/*
-** Each nested #if (or #ifdef or #ifndef) is stored in a stack of
-** instances of the following structure.
-*/
-typedef struct Ifmacro Ifmacro;
-struct Ifmacro {
-  int nLine;         /* Line number where this macro occurs */
-  char *zCondition;  /* Text of the condition for this macro */
-  Ifmacro *pNext;    /* Next down in the stack */
-  int flags;         /* Can hold PS_Export, PS_Interface or PS_Local flags */
-};
-
-/*
-** When parsing a file, we need to keep track of what other files have
-** be #include-ed.  For each #include found, we create an instance of
-** the following structure.
-*/
-typedef struct Include Include;
-struct Include {
-  char *zFile;       /* The name of file include.  Includes "" or <> */
-  char *zIf;         /* If not NULL, #include should be enclosed in #if */
-  char *zLabel;      /* A unique label used to test if this #include has
-                      * appeared already in a file or not */
-  Include *pNext;    /* Previous include file, or NULL if this is the first */
-};
-
-/*
-** Identifiers found in a source file that might be used later to provoke
-** the copying of a declaration into the corresponding header file are
-** stored in a hash table as instances of the following structure.
-*/
-typedef struct Ident Ident;
-struct Ident {
-  char *zName;        /* The text of this identifier */
-  Ident *pCollide;    /* Next identifier with the same hash */
-  Ident *pNext;       /* Next identifier in a list of them all */
-};
-
-/*
-** A complete table of identifiers is stored in an instance of
-** the next structure.
-*/
-#define IDENT_HASH_SIZE 2237
-typedef struct IdentTable IdentTable;
-struct IdentTable {
-  Ident *pList;                     /* List of all identifiers in this table */
-  Ident *apTable[IDENT_HASH_SIZE];  /* The hash table */
-};
-
-/*
-** The following structure holds all information for a single
-** source file named on the command line of this program.
-*/
-typedef struct InFile InFile;
-struct InFile {
-  char *zSrc;              /* Name of input file */
-  char *zHdr;              /* Name of the generated .h file for this input.
-                           ** Will be NULL if input is to be scanned only */
-  int flags;               /* One or more DP_, PS_ and/or TY_ flags */
-  InFile *pNext;           /* Next input file in the list of them all */
-  IdentTable idTable;      /* All identifiers in this input file */
-};
-
-/*
-** An unbounded string is able to grow without limit.  We use these
-** to construct large in-memory strings from lots of smaller components.
-*/
-typedef struct String String;
-struct String {
-  int nAlloc;      /* Number of bytes allocated */
-  int nUsed;       /* Number of bytes used (not counting null terminator) */
-  char *zText;     /* Text of the string */
-};
-
-/*
-** The following structure contains a lot of state information used
-** while generating a .h file.  We put the information in this structure
-** and pass around a pointer to this structure, rather than pass around
-** all of the information separately.  This helps reduce the number of
-** arguments to generator functions.
-*/
-typedef struct GenState GenState;
-struct GenState {
-  String *pStr;          /* Write output to this string */
-  IdentTable *pTable;    /* A table holding the zLabel of every #include that
-                          * has already been generated.  Used to avoid
-                          * generating duplicate #includes. */
-  const char *zIf;       /* If not NULL, then we are within a #if with
-                          * this argument. */
-  int nErr;              /* Number of errors */
-  const char *zFilename; /* Name of the source file being scanned */
-  int flags;             /* Various flags (DP_ and PS_ flags above) */
-};
-
-/*
-** The following text line appears at the top of every file generated
-** by this program.  By recognizing this line, the program can be sure
-** never to read a file that it generated itself.
-**
-** The "#undef INTERFACE" part is a hack to work around a name collision
-** in MSVC 2008.
-*/
-const char zTopLine[] =
-  "/* \aThis file was automatically generated.  Do not edit! */\n"
-  "#undef INTERFACE\n";
-#define nTopLine (sizeof(zTopLine)-1)
-
-/*
-** The name of the file currently being parsed.
-*/
-static const char *zFilename;
-
-/*
-** The stack of #if macros for the file currently being parsed.
-*/
-static Ifmacro *ifStack = 0;
-
-/*
-** A list of all files that have been #included so far in a file being
-** parsed.
-*/
-static Include *includeList = 0;
-
-/*
-** The last block comment seen.
-*/
-static Token *blockComment = 0;
-
-/*
-** The following flag is set if the -doc flag appears on the
-** command line.
-*/
-static int doc_flag = 0;
-
-/*
-** If the following flag is set, then makeheaders will attempt to
-** generate prototypes for static functions and procedures.
-*/
-static int proto_static = 0;
-
-/*
-** A list of all declarations.  The list is held together using the
-** pNext field of the Decl structure.
-*/
-static Decl *pDeclFirst;    /* First on the list */
-static Decl *pDeclLast;     /* Last on the list */
-
-/*
-** A hash table of all declarations
-*/
-#define DECL_HASH_SIZE 3371
-static Decl *apTable[DECL_HASH_SIZE];
-
-/*
-** The TEST macro must be defined to something.  Make sure this is the
-** case.
-*/
-#ifndef TEST
-# define TEST 0
-#endif
-
-#ifdef NOT_USED
-/*
-** We do our own assertion macro so that we can have more control
-** over debugging.
-*/
-#define Assert(X)    if(!(X)){ CantHappen(__LINE__); }
-#define CANT_HAPPEN  CantHappen(__LINE__)
-static void CantHappen(int iLine){
-  fprintf(stderr,"Assertion failed on line %d\n",iLine);
-  *(char*)1 = 0;  /* Force a core-dump */
-}
-#endif
-
-/*
-** Memory allocation functions that are guaranteed never to return NULL.
-*/
-static void *SafeMalloc(int nByte){
-  void *p = malloc( nByte );
-  if( p==0 ){
-    fprintf(stderr,"Out of memory.  Can't allocate %d bytes.\n",nByte);
-    exit(1);
-  }
-  return p;
-}
-static void SafeFree(void *pOld){
-  if( pOld ){
-    free(pOld);
-  }
-}
-static void *SafeRealloc(void *pOld, int nByte){
-  void *p;
-  if( pOld==0 ){
-    p = SafeMalloc(nByte);
-  }else{
-    p = realloc(pOld, nByte);
-    if( p==0 ){
-      fprintf(stderr,
-        "Out of memory.  Can't enlarge an allocation to %d bytes\n",nByte);
-      exit(1);
-    }
-  }
-  return p;
-}
-static char *StrDup(const char *zSrc, int nByte){
-  char *zDest;
-  if( nByte<=0 ){
-    nByte = strlen(zSrc);
-  }
-  zDest = SafeMalloc( nByte + 1 );
-  strncpy(zDest,zSrc,nByte);
-  zDest[nByte] = 0;
-  return zDest;
-}
-
-/*
-** Return TRUE if the character X can be part of an identifier
-*/
-#define ISALNUM(X)  ((X)=='_' || isalnum(X))
-
-/*
-** Routines for dealing with unbounded strings.
-*/
-static void StringInit(String *pStr){
-  pStr->nAlloc = 0;
-  pStr->nUsed = 0;
-  pStr->zText = 0;
-}
-static void StringReset(String *pStr){
-  SafeFree(pStr->zText);
-  StringInit(pStr);
-}
-static void StringAppend(String *pStr, const char *zText, int nByte){
-  if( nByte<=0 ){
-    nByte = strlen(zText);
-  }
-  if( pStr->nUsed + nByte >= pStr->nAlloc ){
-    if( pStr->nAlloc==0 ){
-      pStr->nAlloc = nByte + 100;
-      pStr->zText = SafeMalloc( pStr->nAlloc );
-    }else{
-      pStr->nAlloc = pStr->nAlloc*2 + nByte;
-      pStr->zText = SafeRealloc(pStr->zText, pStr->nAlloc);
-    }
-  }
-  strncpy(&pStr->zText[pStr->nUsed],zText,nByte);
-  pStr->nUsed += nByte;
-  pStr->zText[pStr->nUsed] = 0;
-}
-#define StringGet(S) ((S)->zText?(S)->zText:"")
-
-/*
-** Compute a hash on a string.  The number returned is a non-negative
-** value between 0 and 2**31 - 1
-*/
-static int Hash(const char *z, int n){
-  int h = 0;
-  if( n<=0 ){
-    n = strlen(z);
-  }
-  while( n-- ){
-    h = h ^ (h<<5) ^ *z++;
-  }
-  return h & 0x7fffffff;
-}
-
-/*
-** Given an identifier name, try to find a declaration for that
-** identifier in the hash table.  If found, return a pointer to
-** the Decl structure.  If not found, return 0.
-*/
-static Decl *FindDecl(const char *zName, int len){
-  int h;
-  Decl *p;
-
-  if( len<=0 ){
-    len = strlen(zName);
-  }
-  h = Hash(zName,len) % DECL_HASH_SIZE;
-  p = apTable[h];
-  while( p && (strncmp(p->zName,zName,len)!=0 || p->zName[len]!=0) ){
-    p = p->pSameHash;
-  }
-  return p;
-}
-
-/*
-** Install the given declaration both in the hash table and on
-** the list of all declarations.
-*/
-static void InstallDecl(Decl *pDecl){
-  int h;
-  Decl *pOther;
-
-  h = Hash(pDecl->zName,0) % DECL_HASH_SIZE;
-  pOther = apTable[h];
-  while( pOther && strcmp(pDecl->zName,pOther->zName)!=0 ){
-    pOther = pOther->pSameHash;
-  }
-  if( pOther ){
-    pDecl->pSameName = pOther->pSameName;
-    pOther->pSameName = pDecl;
-  }else{
-    pDecl->pSameName = 0;
-    pDecl->pSameHash = apTable[h];
-    apTable[h] = pDecl;
-  }
-  pDecl->pNext = 0;
-  if( pDeclFirst==0 ){
-    pDeclFirst = pDeclLast = pDecl;
-  }else{
-    pDeclLast->pNext = pDecl;
-    pDeclLast = pDecl;
-  }
-}
-
-/*
-** Look at the current ifStack.  If anything declared at the current
-** position must be surrounded with
-**
-**      #if   STUFF
-**      #endif
-**
-** Then this routine computes STUFF and returns a pointer to it.  Memory
-** to hold the value returned is obtained from malloc().
-*/
-static char *GetIfString(void){
-  Ifmacro *pIf;
-  char *zResult = 0;
-  int hasIf = 0;
-  String str;
-
-  for(pIf = ifStack; pIf; pIf=pIf->pNext){
-    if( pIf->zCondition==0 || *pIf->zCondition==0 ) continue;
-    if( !hasIf ){
-      hasIf = 1;
-      StringInit(&str);
-    }else{
-      StringAppend(&str," && ",4);
-    }
-    StringAppend(&str,pIf->zCondition,0);
-  }
-  if( hasIf ){
-    zResult = StrDup(StringGet(&str),0);
-    StringReset(&str);
-  }else{
-    zResult = 0;
-  }
-  return zResult;
-}
-
-/*
-** Create a new declaration and put it in the hash table.  Also
-** return a pointer to it so that we can fill in the zFwd and zDecl
-** fields, and so forth.
-*/
-static Decl *CreateDecl(
-  const char *zName,       /* Name of the object being declared. */
-  int nName                /* Length of the name */
-){
-  Decl *pDecl;
-
-  pDecl = SafeMalloc( sizeof(Decl) + nName + 1);
-  memset(pDecl,0,sizeof(Decl));
-  pDecl->zName = (char*)&pDecl[1];
-  sprintf(pDecl->zName,"%.*s",nName,zName);
-  pDecl->zFile = zFilename;
-  pDecl->pInclude = includeList;
-  pDecl->zIf = GetIfString();
-  InstallDecl(pDecl);
-  return pDecl;
-}
-
-/*
-** Insert a new identifier into an table of identifiers.  Return TRUE if
-** a new identifier was inserted and return FALSE if the identifier was
-** already in the table.
-*/
-static int IdentTableInsert(
-  IdentTable *pTable,       /* The table into which we will insert */
-  const char *zId,          /* Name of the identifiers */
-  int nId                   /* Length of the identifier name */
-){
-  int h;
-  Ident *pId;
-
-  if( nId<=0 ){
-    nId = strlen(zId);
-  }
-  h = Hash(zId,nId) % IDENT_HASH_SIZE;
-  for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){
-    if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){
-      /* printf("Already in table: %.*s\n",nId,zId); */
-      return 0;
-    }
-  }
-  pId = SafeMalloc( sizeof(Ident) + nId + 1 );
-  pId->zName = (char*)&pId[1];
-  sprintf(pId->zName,"%.*s",nId,zId);
-  pId->pNext = pTable->pList;
-  pTable->pList = pId;
-  pId->pCollide = pTable->apTable[h];
-  pTable->apTable[h] = pId;
-  /* printf("Add to table: %.*s\n",nId,zId); */
-  return 1;
-}
-
-/*
-** Check to see if the given value is in the given IdentTable.  Return
-** true if it is and false if it is not.
-*/
-static int IdentTableTest(
-  IdentTable *pTable,       /* The table in which to search */
-  const char *zId,          /* Name of the identifiers */
-  int nId                   /* Length of the identifier name */
-){
-  int h;
-  Ident *pId;
-
-  if( nId<=0 ){
-    nId = strlen(zId);
-  }
-  h = Hash(zId,nId) % IDENT_HASH_SIZE;
-  for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){
-    if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){
-      return 1;
-    }
-  }
-  return 0;
-}
-
-/*
-** Remove every identifier from the given table.   Reset the table to
-** its initial state.
-*/
-static void IdentTableReset(IdentTable *pTable){
-  Ident *pId, *pNext;
-
-  for(pId = pTable->pList; pId; pId = pNext){
-    pNext = pId->pNext;
-    SafeFree(pId);
-  }
-  memset(pTable,0,sizeof(IdentTable));
-}
-
-#ifdef DEBUG
-/*
-** Print the name of every identifier in the given table, one per line
-*/
-static void IdentTablePrint(IdentTable *pTable, FILE *pOut){
-  Ident *pId;
-
-  for(pId = pTable->pList; pId; pId = pId->pNext){
-    fprintf(pOut,"%s\n",pId->zName);
-  }
-}
-#endif
-
-/*
-** Read an entire file into memory.  Return a pointer to the memory.
-**
-** The memory is obtained from SafeMalloc and must be freed by the
-** calling function.
-**
-** If the read fails for any reason, 0 is returned.
-*/
-static char *ReadFile(const char *zFilename){
-  struct stat sStat;
-  FILE *pIn;
-  char *zBuf;
-  int n;
-
-  if( stat(zFilename,&sStat)!=0
-#ifndef WIN32
-    || !S_ISREG(sStat.st_mode)
-#endif
-  ){
-    return 0;
-  }
-  pIn = fopen(zFilename,"r");
-  if( pIn==0 ){
-    return 0;
-  }
-  zBuf = SafeMalloc( sStat.st_size + 1 );
-  n = fread(zBuf,1,sStat.st_size,pIn);
-  zBuf[n] = 0;
-  fclose(pIn);
-  return zBuf;
-}
-
-/*
-** Write the contents of a string into a file.  Return the number of
-** errors
-*/
-static int WriteFile(const char *zFilename, const char *zOutput){
-  FILE *pOut;
-  pOut = fopen(zFilename,"w");
-  if( pOut==0 ){
-    return 1;
-  }
-  fwrite(zOutput,1,strlen(zOutput),pOut);
-  fclose(pOut);
-  return 0;
-}
-
-/*
-** Major token types
-*/
-#define TT_Space           1   /* Contiguous white space */
-#define TT_Id              2   /* An identifier */
-#define TT_Preprocessor    3   /* Any C preprocessor directive */
-#define TT_Comment         4   /* Either C or C++ style comment */
-#define TT_Number          5   /* Any numeric constant */
-#define TT_String          6   /* String or character constants. ".." or '.' */
-#define TT_Braces          7   /* All text between { and a matching } */
-#define TT_EOF             8   /* End of file */
-#define TT_Error           9   /* An error condition */
-#define TT_BlockComment    10  /* A C-Style comment at the left margin that
-                                * spans multiple lines */
-#define TT_Other           0   /* None of the above */
-
-/*
-** Get a single low-level token from the input file.  Update the
-** file pointer so that it points to the first character beyond the
-** token.
-**
-** A "low-level token" is any token except TT_Braces.  A TT_Braces token
-** consists of many smaller tokens and is assembled by a routine that
-** calls this one.
-**
-** The function returns the number of errors.  An error is an
-** unterminated string or character literal or an unterminated
-** comment.
-**
-** Profiling shows that this routine consumes about half the
-** CPU time on a typical run of makeheaders.
-*/
-static int GetToken(InStream *pIn, Token *pToken){
-  int i;
-  const char *z;
-  int cStart;
-  int c;
-  int startLine;   /* Line on which a structure begins */
-  int nlisc = 0;   /* True if there is a new-line in a ".." or '..' */
-  int nErr = 0;    /* Number of errors seen */
-
-  z = pIn->z;
-  i = pIn->i;
-  pToken->nLine = pIn->nLine;
-  pToken->zText = &z[i];
-  switch( z[i] ){
-    case 0:
-      pToken->eType = TT_EOF;
-      pToken->nText = 0;
-      break;
-
-    case '#':
-      if( i==0 || z[i-1]=='\n' || (i>1 && z[i-1]=='\r' && z[i-2]=='\n')){
-        /* We found a preprocessor statement */
-        pToken->eType = TT_Preprocessor;
-        i++;
-        while( z[i]!=0 && z[i]!='\n' ){
-          if( z[i]=='\\' ){
-            i++;
-            if( z[i]=='\n' ) pIn->nLine++;
-          }
-          i++;
-        }
-        pToken->nText = i - pIn->i;
-      }else{
-        /* Just an operator */
-        pToken->eType = TT_Other;
-        pToken->nText = 1;
-      }
-      break;
-
-    case ' ':
-    case '\t':
-    case '\r':
-    case '\f':
-    case '\n':
-      while( isspace(z[i]) ){
-        if( z[i]=='\n' ) pIn->nLine++;
-        i++;
-      }
-      pToken->eType = TT_Space;
-      pToken->nText = i - pIn->i;
-      break;
-
-    case '\\':
-      pToken->nText = 2;
-      pToken->eType = TT_Other;
-      if( z[i+1]=='\n' ){
-        pIn->nLine++;
-        pToken->eType = TT_Space;
-      }else if( z[i+1]==0 ){
-        pToken->nText = 1;
-      }
-      break;
-
-    case '\'':
-    case '\"':
-      cStart = z[i];
-      startLine = pIn->nLine;
-      do{
-        i++;
-        c = z[i];
-        if( c=='\n' ){
-          if( !nlisc ){
-            fprintf(stderr,
-              "%s:%d: (warning) Newline in string or character literal.\n",
-              zFilename, pIn->nLine);
-            nlisc = 1;
-          }
-          pIn->nLine++;
-        }
-        if( c=='\\' ){
-          i++;
-          c = z[i];
-          if( c=='\n' ){
-            pIn->nLine++;
-          }
-        }else if( c==cStart ){
-          i++;
-          c = 0;
-        }else if( c==0 ){
-          fprintf(stderr, "%s:%d: Unterminated string or character literal.\n",
-             zFilename, startLine);
-          nErr++;
-        }
-      }while( c );
-      pToken->eType = TT_String;
-      pToken->nText = i - pIn->i;
-      break;
-
-    case '/':
-      if( z[i+1]=='/' ){
-        /* C++ style comment */
-        while( z[i] && z[i]!='\n' ){ i++; }
-        pToken->eType = TT_Comment;
-        pToken->nText = i - pIn->i;
-      }else if( z[i+1]=='*' ){
-        /* C style comment */
-        int isBlockComment = i==0 || z[i-1]=='\n';
-        i += 2;
-        startLine = pIn->nLine;
-        while( z[i] && (z[i]!='*' || z[i+1]!='/') ){
-          if( z[i]=='\n' ){
-            pIn->nLine++;
-            if( isBlockComment ){
-              if( z[i+1]=='*' || z[i+2]=='*' ){
-                 isBlockComment = 2;
-              }else{
-                 isBlockComment = 0;
-              }
-            }
-          }
-          i++;
-        }
-        if( z[i] ){
-          i += 2;
-        }else{
-          isBlockComment = 0;
-          fprintf(stderr,"%s:%d: Unterminated comment\n",
-            zFilename, startLine);
-          nErr++;
-        }
-        pToken->eType = isBlockComment==2 ? TT_BlockComment : TT_Comment;
-        pToken->nText = i - pIn->i;
-      }else{
-        /* A divide operator */
-        pToken->eType = TT_Other;
-        pToken->nText = 1 + (z[i+1]=='+');
-      }
-      break;
-
-    case '0':
-      if( z[i+1]=='x' || z[i+1]=='X' ){
-        /* A hex constant */
-        i += 2;
-        while( isxdigit(z[i]) ){ i++; }
-      }else{
-        /* An octal constant */
-        while( isdigit(z[i]) ){ i++; }
-      }
-      pToken->eType = TT_Number;
-      pToken->nText = i - pIn->i;
-      break;
-
-    case '1': case '2': case '3': case '4':
-    case '5': case '6': case '7': case '8': case '9':
-      while( isdigit(z[i]) ){ i++; }
-      if( (c=z[i])=='.' ){
-         i++;
-         while( isdigit(z[i]) ){ i++; }
-         c = z[i];
-         if( c=='e' || c=='E' ){
-           i++;
-           if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; }
-           while( isdigit(z[i]) ){ i++; }
-           c = z[i];
-         }
-         if( c=='f' || c=='F' || c=='l' || c=='L' ){ i++; }
-      }else if( c=='e' || c=='E' ){
-         i++;
-         if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; }
-         while( isdigit(z[i]) ){ i++; }
-      }else if( c=='L' || c=='l' ){
-         i++;
-         c = z[i];
-         if( c=='u' || c=='U' ){ i++; }
-      }else if( c=='u' || c=='U' ){
-         i++;
-         c = z[i];
-         if( c=='l' || c=='L' ){ i++; }
-      }
-      pToken->eType = TT_Number;
-      pToken->nText = i - pIn->i;
-      break;
-
-    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
-    case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
-    case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
-    case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B':
-    case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I':
-    case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P':
-    case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W':
-    case 'X': case 'Y': case 'Z': case '_':
-      while( isalnum(z[i]) || z[i]=='_' ){ i++; };
-      pToken->eType = TT_Id;
-      pToken->nText = i - pIn->i;
-      break;
-
-    case ':':
-      pToken->eType = TT_Other;
-      pToken->nText = 1 + (z[i+1]==':');
-      break;
-
-    case '=':
-    case '<':
-    case '>':
-    case '+':
-    case '-':
-    case '*':
-    case '%':
-    case '^':
-    case '&':
-    case '|':
-      pToken->eType = TT_Other;
-      pToken->nText = 1 + (z[i+1]=='=');
-      break;
-
-    default:
-      pToken->eType = TT_Other;
-      pToken->nText = 1;
-      break;
-  }
-  pIn->i += pToken->nText;
-  return nErr;
-}
-
-/*
-** This routine recovers the next token from the input file which is
-** not a space or a comment or any text between an "#if 0" and "#endif".
-**
-** This routine returns the number of errors encountered.  An error
-** is an unterminated token or unmatched "#if 0".
-**
-** Profiling shows that this routine uses about a quarter of the
-** CPU time in a typical run.
-*/
-static int GetNonspaceToken(InStream *pIn, Token *pToken){
-  int nIf = 0;
-  int inZero = 0;
-  const char *z;
-  int value;
-  int startLine;
-  int nErr = 0;
-
-  startLine = pIn->nLine;
-  while( 1 ){
-    nErr += GetToken(pIn,pToken);
-    /* printf("%04d: Type=%d nIf=%d [%.*s]\n",
-       pToken->nLine,pToken->eType,nIf,pToken->nText,
-       pToken->eType!=TT_Space ? pToken->zText : "<space>"); */
-    pToken->pComment = blockComment;
-    switch( pToken->eType ){
-      case TT_Comment:          /*0123456789 12345678 */
-       if( strncmp(pToken->zText, "/*MAKEHEADERS-STOP", 18)==0 ) return nErr;
-       break;
-
-      case TT_Space:
-        break;
-
-      case TT_BlockComment:
-        if( doc_flag ){
-          blockComment = SafeMalloc( sizeof(Token) );
-          *blockComment = *pToken;
-        }
-        break;
-
-      case TT_EOF:
-        if( nIf ){
-          fprintf(stderr,"%s:%d: Unterminated \"#if\"\n",
-             zFilename, startLine);
-          nErr++;
-        }
-        return nErr;
-
-      case TT_Preprocessor:
-        z = &pToken->zText[1];
-        while( *z==' ' || *z=='\t' ) z++;
-        if( sscanf(z,"if %d",&value)==1 && value==0 ){
-          nIf++;
-          inZero = 1;
-        }else if( inZero ){
-          if( strncmp(z,"if",2)==0 ){
-            nIf++;
-          }else if( strncmp(z,"endif",5)==0 ){
-            nIf--;
-            if( nIf==0 ) inZero = 0;
-          }
-        }else{
-          return nErr;
-        }
-        break;
-
-      default:
-        if( !inZero ){
-          return nErr;
-        }
-        break;
-    }
-  }
-  /* NOT REACHED */
-}
-
-/*
-** This routine looks for identifiers (strings of contiguous alphanumeric
-** characters) within a preprocessor directive and adds every such string
-** found to the given identifier table
-*/
-static void FindIdentifiersInMacro(Token *pToken, IdentTable *pTable){
-  Token sToken;
-  InStream sIn;
-  int go = 1;
-
-  sIn.z = pToken->zText;
-  sIn.i = 1;
-  sIn.nLine = 1;
-  while( go && sIn.i < pToken->nText ){
-    GetToken(&sIn,&sToken);
-    switch( sToken.eType ){
-      case TT_Id:
-        IdentTableInsert(pTable,sToken.zText,sToken.nText);
-        break;
-
-      case TT_EOF:
-        go = 0;
-        break;
-
-      default:
-        break;
-    }
-  }
-}
-
-/*
-** This routine gets the next token.  Everything contained within
-** {...} is collapsed into a single TT_Braces token.  Whitespace is
-** omitted.
-**
-** If pTable is not NULL, then insert every identifier seen into the
-** IdentTable.  This includes any identifiers seen inside of {...}.
-**
-** The number of errors encountered is returned.  An error is an
-** unterminated token.
-*/
-static int GetBigToken(InStream *pIn, Token *pToken, IdentTable *pTable){
-  const char *zStart;
-  int iStart;
-  int nBrace;
-  int c;
-  int nLine;
-  int nErr;
-
-  nErr = GetNonspaceToken(pIn,pToken);
-  switch( pToken->eType ){
-    case TT_Id:
-      if( pTable!=0 ){
-        IdentTableInsert(pTable,pToken->zText,pToken->nText);
-      }
-      return nErr;
-
-    case TT_Preprocessor:
-      if( pTable!=0 ){
-        FindIdentifiersInMacro(pToken,pTable);
-      }
-      return nErr;
-
-    case TT_Other:
-      if( pToken->zText[0]=='{' ) break;
-      return nErr;
-
-    default:
-      return nErr;
-  }
-
-  iStart = pIn->i;
-  zStart = pToken->zText;
-  nLine = pToken->nLine;
-  nBrace = 1;
-  while( nBrace ){
-    nErr += GetNonspaceToken(pIn,pToken);
-    /* printf("%04d: nBrace=%d [%.*s]\n",pToken->nLine,nBrace,
-       pToken->nText,pToken->zText); */
-    switch( pToken->eType ){
-      case TT_EOF:
-        fprintf(stderr,"%s:%d: Unterminated \"{\"\n",
-           zFilename, nLine);
-        nErr++;
-        pToken->eType = TT_Error;
-        return nErr;
-
-      case TT_Id:
-        if( pTable ){
-          IdentTableInsert(pTable,pToken->zText,pToken->nText);
-        }
-        break;
-
-      case TT_Preprocessor:
-        if( pTable!=0 ){
-          FindIdentifiersInMacro(pToken,pTable);
-        }
-        break;
-
-      case TT_Other:
-        if( (c = pToken->zText[0])=='{' ){
-          nBrace++;
-        }else if( c=='}' ){
-          nBrace--;
-        }
-        break;
-
-      default:
-        break;
-    }
-  }
-  pToken->eType = TT_Braces;
-  pToken->nText = 1 + pIn->i - iStart;
-  pToken->zText = zStart;
-  pToken->nLine = nLine;
-  return nErr;
-}
-
-/*
-** This routine frees up a list of Tokens.  The pComment tokens are
-** not cleared by this.  So we leak a little memory when using the -doc
-** option.  So what.
-*/
-static void FreeTokenList(Token *pList){
-  Token *pNext;
-  while( pList ){
-    pNext = pList->pNext;
-    SafeFree(pList);
-    pList = pNext;
-  }
-}
-
-/*
-** Tokenize an entire file.  Return a pointer to the list of tokens.
-**
-** Space for each token is obtained from a separate malloc() call.  The
-** calling function is responsible for freeing this space.
-**
-** If pTable is not NULL, then fill the table with all identifiers seen in
-** the input file.
-*/
-static Token *TokenizeFile(const char *zFile, IdentTable *pTable){
-  InStream sIn;
-  Token *pFirst = 0, *pLast = 0, *pNew;
-  int nErr = 0;
-
-  sIn.z = zFile;
-  sIn.i = 0;
-  sIn.nLine = 1;
-  blockComment = 0;
-
-  while( sIn.z[sIn.i]!=0 ){
-    pNew = SafeMalloc( sizeof(Token) );
-    nErr += GetBigToken(&sIn,pNew,pTable);
-    debug3(TOKENIZER, "Token on line %d: [%.*s]\n",
-       pNew->nLine, pNew->nText<50 ? pNew->nText : 50, pNew->zText);
-    if( pFirst==0 ){
-      pFirst = pLast = pNew;
-      pNew->pPrev = 0;
-    }else{
-      pLast->pNext = pNew;
-      pNew->pPrev = pLast;
-      pLast = pNew;
-    }
-    if( pNew->eType==TT_EOF ) break;
-  }
-  if( pLast ) pLast->pNext = 0;
-  blockComment = 0;
-  if( nErr ){
-    FreeTokenList(pFirst);
-    pFirst = 0;
-  }
-
-  return pFirst;
-}
-
-#if TEST==1
-/*
-** Use the following routine to test or debug the tokenizer.
-*/
-void main(int argc, char **argv){
-  char *zFile;
-  Token *pList, *p;
-  IdentTable sTable;
-
-  if( argc!=2 ){
-    fprintf(stderr,"Usage: %s filename\n",*argv);
-    exit(1);
-  }
-  memset(&sTable,0,sizeof(sTable));
-  zFile = ReadFile(argv[1]);
-  if( zFile==0 ){
-    fprintf(stderr,"Can't read file \"%s\"\n",argv[1]);
-    exit(1);
-  }
-  pList = TokenizeFile(zFile,&sTable);
-  for(p=pList; p; p=p->pNext){
-    int j;
-    switch( p->eType ){
-      case TT_Space:
-        printf("%4d: Space\n",p->nLine);
-        break;
-      case TT_Id:
-        printf("%4d: Id           %.*s\n",p->nLine,p->nText,p->zText);
-        break;
-      case TT_Preprocessor:
-        printf("%4d: Preprocessor %.*s\n",p->nLine,p->nText,p->zText);
-        break;
-      case TT_Comment:
-        printf("%4d: Comment\n",p->nLine);
-        break;
-      case TT_BlockComment:
-        printf("%4d: Block Comment\n",p->nLine);
-        break;
-      case TT_Number:
-        printf("%4d: Number       %.*s\n",p->nLine,p->nText,p->zText);
-        break;
-      case TT_String:
-        printf("%4d: String       %.*s\n",p->nLine,p->nText,p->zText);
-        break;
-      case TT_Other:
-        printf("%4d: Other        %.*s\n",p->nLine,p->nText,p->zText);
-        break;
-      case TT_Braces:
-        for(j=0; j<p->nText && j<30 && p->zText[j]!='\n'; j++){}
-        printf("%4d: Braces       %.*s...}\n",p->nLine,j,p->zText);
-        break;
-      case TT_EOF:
-        printf("%4d: End of file\n",p->nLine);
-        break;
-      default:
-        printf("%4d: type %d\n",p->nLine,p->eType);
-        break;
-    }
-  }
-  FreeTokenList(pList);
-  SafeFree(zFile);
-  IdentTablePrint(&sTable,stdout);
-}
-#endif
-
-#ifdef DEBUG
-/*
-** For debugging purposes, write out a list of tokens.
-*/
-static void PrintTokens(Token *pFirst, Token *pLast){
-  int needSpace = 0;
-  int c;
-
-  pLast = pLast->pNext;
-  while( pFirst!=pLast ){
-    switch( pFirst->eType ){
-      case TT_Preprocessor:
-        printf("\n%.*s\n",pFirst->nText,pFirst->zText);
-        needSpace = 0;
-        break;
-
-      case TT_Id:
-      case TT_Number:
-        printf("%s%.*s", needSpace ? " " : "", pFirst->nText, pFirst->zText);
-        needSpace = 1;
-        break;
-
-      default:
-        c = pFirst->zText[0];
-        printf("%s%.*s",
-          (needSpace && (c=='*' || c=='{')) ? " " : "",
-          pFirst->nText, pFirst->zText);
-        needSpace = pFirst->zText[0]==',';
-        break;
-    }
-    pFirst = pFirst->pNext;
-  }
-}
-#endif
-
-/*
-** Convert a sequence of tokens into a string and return a pointer
-** to that string.  Space to hold the string is obtained from malloc()
-** and must be freed by the calling function.
-**
-** Certain keywords (EXPORT, PRIVATE, PUBLIC, PROTECTED) are always
-** skipped.
-**
-** If pSkip!=0 then skip over nSkip tokens beginning with pSkip.
-**
-** If zTerm!=0 then append the text to the end.
-*/
-static char *TokensToString(
-  Token *pFirst,    /* First token in the string */
-  Token *pLast,     /* Last token in the string */
-  char *zTerm,      /* Terminate the string with this text if not NULL */
-  Token *pSkip,     /* Skip this token if not NULL */
-  int nSkip         /* Skip a total of this many tokens */
-){
-  char *zReturn;
-  String str;
-  int needSpace = 0;
-  int c;
-  int iSkip = 0;
-  int skipOne = 0;
-
-  StringInit(&str);
-  pLast = pLast->pNext;
-  while( pFirst!=pLast ){
-    if( pFirst==pSkip ){ iSkip = nSkip; }
-    if( iSkip>0 ){
-      iSkip--;
-      pFirst=pFirst->pNext;
-      continue;
-    }
-    switch( pFirst->eType ){
-      case TT_Preprocessor:
-        StringAppend(&str,"\n",1);
-        StringAppend(&str,pFirst->zText,pFirst->nText);
-        StringAppend(&str,"\n",1);
-        needSpace = 0;
-        break;
-
-      case TT_Id:
-        switch( pFirst->zText[0] ){
-          case 'E':
-            if( pFirst->nText==6 && strncmp(pFirst->zText,"EXPORT",6)==0 ){
-              skipOne = 1;
-            }
-            break;
-          case 'P':
-            switch( pFirst->nText ){
-              case 6:  skipOne = !strncmp(pFirst->zText,"PUBLIC", 6);    break;
-              case 7:  skipOne = !strncmp(pFirst->zText,"PRIVATE",7);    break;
-              case 9:  skipOne = !strncmp(pFirst->zText,"PROTECTED",9);  break;
-              default: break;
-            }
-            break;
-          default:
-            break;
-        }
-        if( skipOne ){
-          pFirst = pFirst->pNext;
-          continue;
-        }
-        /* Fall thru to the next case */
-      case TT_Number:
-        if( needSpace ){
-          StringAppend(&str," ",1);
-        }
-        StringAppend(&str,pFirst->zText,pFirst->nText);
-        needSpace = 1;
-        break;
-
-      default:
-        c = pFirst->zText[0];
-        if( needSpace && (c=='*' || c=='{') ){
-          StringAppend(&str," ",1);
-        }
-        StringAppend(&str,pFirst->zText,pFirst->nText);
-        /* needSpace = pFirst->zText[0]==','; */
-        needSpace = 0;
-        break;
-    }
-    pFirst = pFirst->pNext;
-  }
-  if( zTerm && *zTerm ){
-    StringAppend(&str,zTerm,strlen(zTerm));
-  }
-  zReturn = StrDup(StringGet(&str),0);
-  StringReset(&str);
-  return zReturn;
-}
-
-/*
-** This routine is called when we see one of the keywords "struct",
-** "enum", "union" or "class".  This might be the beginning of a
-** type declaration.  This routine will process the declaration and
-** remove the declaration tokens from the input stream.
-**
-** If this is a type declaration that is immediately followed by a
-** semicolon (in other words it isn't also a variable definition)
-** then set *pReset to ';'.  Otherwise leave *pReset at 0.  The
-** *pReset flag causes the parser to skip ahead to the next token
-** that begins with the value placed in the *pReset flag, if that
-** value is different from 0.
-*/
-static int ProcessTypeDecl(Token *pList, int flags, int *pReset){
-  Token *pName, *pEnd;
-  Decl *pDecl;
-  String str;
-  int need_to_collapse = 1;
-  int type = 0;
-
-  *pReset = 0;
-  if( pList==0 || pList->pNext==0 || pList->pNext->eType!=TT_Id ){
-    return 0;
-  }
-  pName = pList->pNext;
-
-  /* Catch the case of "struct Foo;" and skip it. */
-  if( pName->pNext && pName->pNext->zText[0]==';' ){
-    *pReset = ';';
-    return 0;
-  }
-
-  for(pEnd=pName->pNext; pEnd && pEnd->eType!=TT_Braces; pEnd=pEnd->pNext){
-    switch( pEnd->zText[0] ){
-      case '(':
-      case ')':
-      case '*':
-      case '[':
-      case '=':
-      case ';':
-        return 0;
-    }
-  }
-  if( pEnd==0 ){
-    return 0;
-  }
-
-  /*
-  ** At this point, we know we have a type declaration that is bounded
-  ** by pList and pEnd and has the name pName.
-  */
-
-  /*
-  ** If the braces are followed immediately by a semicolon, then we are
-  ** dealing a type declaration only.  There is not variable definition
-  ** following the type declaration.  So reset...
-  */
-  if( pEnd->pNext==0 || pEnd->pNext->zText[0]==';' ){
-    *pReset = ';';
-    need_to_collapse = 0;
-  }else{
-    need_to_collapse = 1;
-  }
-
-  if( proto_static==0 && (flags & (PS_Local|PS_Export|PS_Interface))==0 ){
-    /* Ignore these objects unless they are explicitly declared as interface,
-    ** or unless the "-local" command line option was specified. */
-    *pReset = ';';
-    return 0;
-  }
-
-#ifdef DEBUG
-  if( debugMask & PARSER ){
-    printf("**** Found type: %.*s %.*s...\n",
-      pList->nText, pList->zText, pName->nText, pName->zText);
-    PrintTokens(pList,pEnd);
-    printf(";\n");
-  }
-#endif
-
-  /*
-  ** Create a new Decl object for this definition.  Actually, if this
-  ** is a C++ class definition, then the Decl object might already exist,
-  ** so check first for that case before creating a new one.
-  */
-  switch( *pList->zText ){
-    case 'c':  type = TY_Class;        break;
-    case 's':  type = TY_Structure;    break;
-    case 'e':  type = TY_Enumeration;  break;
-    case 'u':  type = TY_Union;        break;
-    default:   /* Can't Happen */      break;
-  }
-  if( type!=TY_Class ){
-    pDecl = 0;
-  }else{
-    pDecl = FindDecl(pName->zText, pName->nText);
-    if( pDecl && (pDecl->flags & type)!=type ) pDecl = 0;
-  }
-  if( pDecl==0 ){
-    pDecl = CreateDecl(pName->zText,pName->nText);
-  }
-  if( (flags & PS_Static) || !(flags & (PS_Interface|PS_Export)) ){
-    DeclSetProperty(pDecl,DP_Local);
-  }
-  DeclSetProperty(pDecl,type);
-
-  /* The object has a full declaration only if it is contained within
-  ** "#if INTERFACE...#endif" or "#if EXPORT_INTERFACE...#endif" or
-  ** "#if LOCAL_INTERFACE...#endif".  Otherwise, we only give it a
-  ** forward declaration.
-  */
-  if( flags & (PS_Local | PS_Export | PS_Interface)  ){
-    pDecl->zDecl = TokensToString(pList,pEnd,";\n",0,0);
-  }else{
-    pDecl->zDecl = 0;
-  }
-  pDecl->pComment = pList->pComment;
-  StringInit(&str);
-  StringAppend(&str,"typedef ",0);
-  StringAppend(&str,pList->zText,pList->nText);
-  StringAppend(&str," ",0);
-  StringAppend(&str,pName->zText,pName->nText);
-  StringAppend(&str," ",0);
-  StringAppend(&str,pName->zText,pName->nText);
-  StringAppend(&str,";\n",2);
-  pDecl->zFwd = StrDup(StringGet(&str),0);
-  StringReset(&str);
-  StringInit(&str);
-  StringAppend(&str,pList->zText,pList->nText);
-  StringAppend(&str," ",0);
-  StringAppend(&str,pName->zText,pName->nText);
-  StringAppend(&str,";\n",2);
-  pDecl->zFwdCpp = StrDup(StringGet(&str),0);
-  StringReset(&str);
-  if( flags & PS_Export ){
-    DeclSetProperty(pDecl,DP_Export);
-  }else if( flags & PS_Local ){
-    DeclSetProperty(pDecl,DP_Local);
-  }
-
-  /* Here's something weird.  ANSI-C doesn't allow a forward declaration
-  ** of an enumeration.  So we have to build the typedef into the
-  ** definition.
-  */
-  if( pDecl->zDecl && DeclHasProperty(pDecl, TY_Enumeration) ){
-    StringInit(&str);
-    StringAppend(&str,pDecl->zDecl,0);
-    StringAppend(&str,pDecl->zFwd,0);
-    SafeFree(pDecl->zDecl);
-    SafeFree(pDecl->zFwd);
-    pDecl->zFwd = 0;
-    pDecl->zDecl = StrDup(StringGet(&str),0);
-    StringReset(&str);
-  }
-
-  if( pName->pNext->zText[0]==':' ){
-    DeclSetProperty(pDecl,DP_Cplusplus);
-  }
-  if( pName->nText==5 && strncmp(pName->zText,"class",5)==0 ){
-    DeclSetProperty(pDecl,DP_Cplusplus);
-  }
-
-  /*
-  ** Remove all but pList and pName from the input stream.
-  */
-  if( need_to_collapse ){
-    while( pEnd!=pName ){
-      Token *pPrev = pEnd->pPrev;
-      pPrev->pNext = pEnd->pNext;
-      pEnd->pNext->pPrev = pPrev;
-      SafeFree(pEnd);
-      pEnd = pPrev;
-    }
-  }
-  return 0;
-}
-
-/*
-** Given a list of tokens that declare something (a function, procedure,
-** variable or typedef) find the token which contains the name of the
-** thing being declared.
-**
-** Algorithm:
-**
-**   The name is:
-**
-**     1.  The first identifier that is followed by a "[", or
-**
-**     2.  The first identifier that is followed by a "(" where the
-**         "(" is followed by another identifier, or
-**
-**     3.  The first identifier followed by "::", or
-**
-**     4.  If none of the above, then the last identifier.
-**
-**   In all of the above, certain reserved words (like "char") are
-**   not considered identifiers.
-*/
-static Token *FindDeclName(Token *pFirst, Token *pLast){
-  Token *pName = 0;
-  Token *p;
-  int c;
-
-  if( pFirst==0 || pLast==0 ){
-    return 0;
-  }
-  pLast = pLast->pNext;
-  for(p=pFirst; p && p!=pLast; p=p->pNext){
-    if( p->eType==TT_Id ){
-      static IdentTable sReserved;
-      static int isInit = 0;
-      static const char *aWords[] = { "char", "class",
-       "const", "double", "enum", "extern", "EXPORT", "ET_PROC",
-       "float", "int", "long",
-       "PRIVATE", "PROTECTED", "PUBLIC",
-       "register", "static", "struct", "sizeof", "signed", "typedef",
-       "union", "volatile", "virtual", "void", };
-
-      if( !isInit ){
-        int i;
-        for(i=0; i<sizeof(aWords)/sizeof(aWords[0]); i++){
-          IdentTableInsert(&sReserved,aWords[i],0);
-        }
-        isInit = 1;
-      }
-      if( !IdentTableTest(&sReserved,p->zText,p->nText) ){
-        pName = p;
-      }
-    }else if( p==pFirst ){
-      continue;
-    }else if( (c=p->zText[0])=='[' && pName ){
-      break;
-    }else if( c=='(' && p->pNext && p->pNext->eType==TT_Id && pName ){
-      break;
-    }else if( c==':' && p->zText[1]==':' && pName ){
-      break;
-    }
-  }
-  return pName;
-}
-
-/*
-** This routine is called when we see a method for a class that begins
-** with the PUBLIC, PRIVATE, or PROTECTED keywords.  Such methods are
-** added to their class definitions.
-*/
-static int ProcessMethodDef(Token *pFirst, Token *pLast, int flags){
-  Token *pClass;
-  char *zDecl;
-  Decl *pDecl;
-  String str;
-  int type;
-
-  pLast = pLast->pPrev;
-  while( pFirst->zText[0]=='P' ){
-    int rc = 1;
-    switch( pFirst->nText ){
-      case 6:  rc = strncmp(pFirst->zText,"PUBLIC",6); break;
-      case 7:  rc = strncmp(pFirst->zText,"PRIVATE",7); break;
-      case 9:  rc = strncmp(pFirst->zText,"PROTECTED",9); break;
-      default:  break;
-    }
-    if( rc ) break;
-    pFirst = pFirst->pNext;
-  }
-  pClass = FindDeclName(pFirst,pLast);
-  if( pClass==0 ){
-    fprintf(stderr,"%s:%d: Unable to find the class name for this method\n",
-       zFilename, pFirst->nLine);
-    return 1;
-  }
-  pDecl = FindDecl(pClass->zText, pClass->nText);
-  if( pDecl==0 || (pDecl->flags & TY_Class)!=TY_Class ){
-    pDecl = CreateDecl(pClass->zText, pClass->nText);
-    DeclSetProperty(pDecl, TY_Class);
-  }
-  StringInit(&str);
-  if( pDecl->zExtra ){
-    StringAppend(&str, pDecl->zExtra, 0);
-    SafeFree(pDecl->zExtra);
-    pDecl->zExtra = 0;
-  }
-  type = flags & PS_PPP;
-  if( pDecl->extraType!=type ){
-    if( type & PS_Public ){
-      StringAppend(&str, "public:\n", 0);
-      pDecl->extraType = PS_Public;
-    }else if( type & PS_Protected ){
-      StringAppend(&str, "protected:\n", 0);
-      pDecl->extraType = PS_Protected;
-    }else if( type & PS_Private ){
-      StringAppend(&str, "private:\n", 0);
-      pDecl->extraType = PS_Private;
-    }
-  }
-  StringAppend(&str, "  ", 0);
-  zDecl = TokensToString(pFirst, pLast, ";\n", pClass, 2);
-  StringAppend(&str, zDecl, 0);
-  SafeFree(zDecl);
-  pDecl->zExtra = StrDup(StringGet(&str), 0);
-  StringReset(&str);
-  return 0;
-}
-
-/*
-** This routine is called when we see a function or procedure definition.
-** We make an entry in the declaration table that is a prototype for this
-** function or procedure.
-*/
-static int ProcessProcedureDef(Token *pFirst, Token *pLast, int flags){
-  Token *pName;
-  Decl *pDecl;
-  Token *pCode;
-
-  if( pFirst==0 || pLast==0 ){
-    return 0;
-  }
-  if( flags & PS_Method ){
-    if( flags & PS_PPP ){
-      return ProcessMethodDef(pFirst, pLast, flags);
-    }else{
-      return 0;
-    }
-  }
-  if( (flags & PS_Static)!=0 && !proto_static ){
-    return 0;
-  }
-  pCode = pLast;
-  while( pLast && pLast!=pFirst && pLast->zText[0]!=')' ){
-    pLast = pLast->pPrev;
-  }
-  if( pLast==0 || pLast==pFirst || pFirst->pNext==pLast ){
-    fprintf(stderr,"%s:%d: Unrecognized syntax.\n",
-      zFilename, pFirst->nLine);
-    return 1;
-  }
-  if( flags & (PS_Interface|PS_Export|PS_Local) ){
-    fprintf(stderr,"%s:%d: Missing \"inline\" on function or procedure.\n",
-      zFilename, pFirst->nLine);
-    return 1;
-  }
-  pName = FindDeclName(pFirst,pLast);
-  if( pName==0 ){
-    fprintf(stderr,"%s:%d: Malformed function or procedure definition.\n",
-      zFilename, pFirst->nLine);
-    return 1;
-  }
-
-  /*
-  ** At this point we've isolated a procedure declaration between pFirst
-  ** and pLast with the name pName.
-  */
-#ifdef DEBUG
-  if( debugMask & PARSER ){
-    printf("**** Found routine: %.*s on line %d...\n", pName->nText,
-       pName->zText, pFirst->nLine);
-    PrintTokens(pFirst,pLast);
-    printf(";\n");
-  }
-#endif
-  pDecl = CreateDecl(pName->zText,pName->nText);
-  pDecl->pComment = pFirst->pComment;
-  if( pCode && pCode->eType==TT_Braces ){
-    pDecl->tokenCode = *pCode;
-  }
-  DeclSetProperty(pDecl,TY_Subroutine);
-  pDecl->zDecl = TokensToString(pFirst,pLast,";\n",0,0);
-  if( (flags & (PS_Static|PS_Local2))!=0 ){
-    DeclSetProperty(pDecl,DP_Local);
-  }else if( (flags & (PS_Export2))!=0 ){
-    DeclSetProperty(pDecl,DP_Export);
-  }
-
-  if( flags & DP_Cplusplus ){
-    DeclSetProperty(pDecl,DP_Cplusplus);
-  }else{
-    DeclSetProperty(pDecl,DP_ExternCReqd);
-  }
-
-  return 0;
-}
-
-/*
-** This routine is called whenever we see the "inline" keyword.  We
-** need to seek-out the inline function or procedure and make a
-** declaration out of the entire definition.
-*/
-static int ProcessInlineProc(Token *pFirst, int flags, int *pReset){
-  Token *pName;
-  Token *pEnd;
-  Decl *pDecl;
-
-  for(pEnd=pFirst; pEnd; pEnd = pEnd->pNext){
-    if( pEnd->zText[0]=='{' || pEnd->zText[0]==';' ){
-      *pReset = pEnd->zText[0];
-      break;
-    }
-  }
-  if( pEnd==0 ){
-    *pReset = ';';
-    fprintf(stderr,"%s:%d: incomplete inline procedure definition\n",
-      zFilename, pFirst->nLine);
-    return 1;
-  }
-  pName = FindDeclName(pFirst,pEnd);
-  if( pName==0 ){
-    fprintf(stderr,"%s:%d: malformed inline procedure definition\n",
-      zFilename, pFirst->nLine);
-    return 1;
-  }
-
-#ifdef DEBUG
-  if( debugMask & PARSER ){
-    printf("**** Found inline routine: %.*s on line %d...\n",
-       pName->nText, pName->zText, pFirst->nLine);
-    PrintTokens(pFirst,pEnd);
-    printf("\n");
-  }
-#endif
-  pDecl = CreateDecl(pName->zText,pName->nText);
-  pDecl->pComment = pFirst->pComment;
-  DeclSetProperty(pDecl,TY_Subroutine);
-  pDecl->zDecl = TokensToString(pFirst,pEnd,";\n",0,0);
-  if( (flags & (PS_Static|PS_Local|PS_Local2)) ){
-    DeclSetProperty(pDecl,DP_Local);
-  }else if( flags & (PS_Export|PS_Export2) ){
-    DeclSetProperty(pDecl,DP_Export);
-  }
-
-  if( flags & DP_Cplusplus ){
-    DeclSetProperty(pDecl,DP_Cplusplus);
-  }else{
-    DeclSetProperty(pDecl,DP_ExternCReqd);
-  }
-
-  return 0;
-}
-
-/*
-** Determine if the tokens between pFirst and pEnd form a variable
-** definition or a function prototype.  Return TRUE if we are dealing
-** with a variable defintion and FALSE for a prototype.
-**
-** pEnd is the token that ends the object.  It can be either a ';' or
-** a '='.  If it is '=', then assume we have a variable definition.
-**
-** If pEnd is ';', then the determination is more difficult.  We have
-** to search for an occurrence of an ID followed immediately by '('.
-** If found, we have a prototype.  Otherwise we are dealing with a
-** variable definition.
-*/
-static int isVariableDef(Token *pFirst, Token *pEnd){
-  if( pEnd && pEnd->zText[0]=='=' &&
-    (pEnd->pPrev->nText!=8 || strncmp(pEnd->pPrev->zText,"operator",8)!=0)
-  ){
-    return 1;
-  }
-  while( pFirst && pFirst!=pEnd && pFirst->pNext && pFirst->pNext!=pEnd ){
-    if( pFirst->eType==TT_Id && pFirst->pNext->zText[0]=='(' ){
-      return 0;
-    }
-    pFirst = pFirst->pNext;
-  }
-  return 1;
-}
-
-/*
-** Return TRUE if pFirst is the first token of a static assert.
-*/
-static int isStaticAssert(Token *pFirst){
-  if( (pFirst->nText==13 && strncmp(pFirst->zText, "static_assert", 13)==0)
-   || (pFirst->nText==14 && strncmp(pFirst->zText, "_Static_assert", 14)==0)
-  ){
-    return 1;
-  }else{
-    return 0;
-  }
-}
-
-/*
-** This routine is called whenever we encounter a ";" or "=".  The stuff
-** between pFirst and pLast constitutes either a typedef or a global
-** variable definition.  Do the right thing.
-*/
-static int ProcessDecl(Token *pFirst, Token *pEnd, int flags){
-  Token *pName;
-  Decl *pDecl;
-  int isLocal = 0;
-  int isVar;
-  int nErr = 0;
-
-  if( pFirst==0 || pEnd==0 ){
-    return 0;
-  }
-  if( flags & PS_Typedef ){
-    if( (flags & (PS_Export2|PS_Local2))!=0 ){
-      fprintf(stderr,"%s:%d: \"EXPORT\" or \"LOCAL\" ignored before typedef.\n",
-        zFilename, pFirst->nLine);
-      nErr++;
-    }
-    if( (flags & (PS_Interface|PS_Export|PS_Local|DP_Cplusplus))==0 ){
-      /* It is illegal to duplicate a typedef in C (but OK in C++).
-      ** So don't record typedefs that aren't within a C++ file or
-      ** within #if INTERFACE..#endif */
-      return nErr;
-    }
-    if( (flags & (PS_Interface|PS_Export|PS_Local))==0 && proto_static==0 ){
-      /* Ignore typedefs that are not with "#if INTERFACE..#endif" unless
-      ** the "-local" command line option is used. */
-      return nErr;
-    }
-    if( (flags & (PS_Interface|PS_Export))==0 ){
-      /* typedefs are always local, unless within #if INTERFACE..#endif */
-      isLocal = 1;
-    }
-  }else if( flags & (PS_Static|PS_Local2) ){
-    if( proto_static==0 && (flags & PS_Local2)==0 ){
-      /* Don't record static variables unless the "-local" command line
-      ** option was specified or the "LOCAL" keyword is used. */
-      return nErr;
-    }
-    while( pFirst!=0 && pFirst->pNext!=pEnd &&
-       ((pFirst->nText==6 && strncmp(pFirst->zText,"static",6)==0)
-        || (pFirst->nText==5 && strncmp(pFirst->zText,"LOCAL",6)==0))
-    ){
-      /* Lose the initial "static" or local from local variables.
-      ** We'll prepend "extern" later. */
-      pFirst = pFirst->pNext;
-      isLocal = 1;
-    }
-    if( pFirst==0 || !isLocal ){
-      return nErr;
-    }
-  }else if( flags & PS_Method ){
-    /* Methods are declared by their class.  Don't declare separately. */
-    return nErr;
-  }else if( isStaticAssert(pFirst) ){
-    return 0;
-  }
-  isVar =  (flags & (PS_Typedef|PS_Method))==0 && isVariableDef(pFirst,pEnd);
-  if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0
-  && (flags & PS_Extern)==0 ){
-    fprintf(stderr,"%s:%d: Can't define a variable in this context\n",
-      zFilename, pFirst->nLine);
-    nErr++;
-  }
-  pName = FindDeclName(pFirst,pEnd->pPrev);
-  if( pName==0 ){
-    if( pFirst->nText==4 && strncmp(pFirst->zText,"enum",4)==0 ){
-      /* Ignore completely anonymous enums.  See documentation section 3.8.1. */
-      return nErr;
-    }else{
-      fprintf(stderr,"%s:%d: Can't find a name for the object declared here.\n",
-        zFilename, pFirst->nLine);
-      return nErr+1;
-    }
-  }
-
-#ifdef DEBUG
-  if( debugMask & PARSER ){
-    if( flags & PS_Typedef ){
-      printf("**** Found typedef %.*s at line %d...\n",
-        pName->nText, pName->zText, pName->nLine);
-    }else if( isVar ){
-      printf("**** Found variable %.*s at line %d...\n",
-        pName->nText, pName->zText, pName->nLine);
-    }else{
-      printf("**** Found prototype %.*s at line %d...\n",
-        pName->nText, pName->zText, pName->nLine);
-    }
-    PrintTokens(pFirst,pEnd->pPrev);
-    printf(";\n");
-  }
-#endif
-
-  pDecl = CreateDecl(pName->zText,pName->nText);
-  if( (flags & PS_Typedef) ){
-    DeclSetProperty(pDecl, TY_Typedef);
-  }else if( isVar ){
-    DeclSetProperty(pDecl,DP_ExternReqd | TY_Variable);
-    if( !(flags & DP_Cplusplus) ){
-      DeclSetProperty(pDecl,DP_ExternCReqd);
-    }
-  }else{
-    DeclSetProperty(pDecl, TY_Subroutine);
-    if( !(flags & DP_Cplusplus) ){
-      DeclSetProperty(pDecl,DP_ExternCReqd);
-    }
-  }
-  pDecl->pComment = pFirst->pComment;
-  pDecl->zDecl = TokensToString(pFirst,pEnd->pPrev,";\n",0,0);
-  if( isLocal || (flags & (PS_Local|PS_Local2))!=0 ){
-    DeclSetProperty(pDecl,DP_Local);
-  }else if( flags & (PS_Export|PS_Export2) ){
-    DeclSetProperty(pDecl,DP_Export);
-  }
-  if( flags & DP_Cplusplus ){
-    DeclSetProperty(pDecl,DP_Cplusplus);
-  }
-  return nErr;
-}
-
-/*
-** Push an if condition onto the if stack
-*/
-static void PushIfMacro(
-  const char *zPrefix,      /* A prefix, like "define" or "!" */
-  const char *zText,        /* The condition */
-  int nText,                /* Number of characters in zText */
-  int nLine,                /* Line number where this macro occurs */
-  int flags                 /* Either 0, PS_Interface, PS_Export or PS_Local */
-){
-  Ifmacro *pIf;
-  int nByte;
-
-  nByte = sizeof(Ifmacro);
-  if( zText ){
-    if( zPrefix ){
-      nByte += strlen(zPrefix) + 2;
-    }
-    nByte += nText + 1;
-  }
-  pIf = SafeMalloc( nByte );
-  if( zText ){
-    pIf->zCondition = (char*)&pIf[1];
-    if( zPrefix ){
-      sprintf(pIf->zCondition,"%s(%.*s)",zPrefix,nText,zText);
-    }else{
-      sprintf(pIf->zCondition,"%.*s",nText,zText);
-    }
-  }else{
-    pIf->zCondition = 0;
-  }
-  pIf->nLine = nLine;
-  pIf->flags = flags;
-  pIf->pNext = ifStack;
-  ifStack = pIf;
-}
-
-/*
-** This routine is called to handle all preprocessor directives.
-**
-** This routine will recompute the value of *pPresetFlags to be the
-** logical or of all flags on all nested #ifs.  The #ifs that set flags
-** are as follows:
-**
-**        conditional                   flag set
-**        ------------------------      --------------------
-**        #if INTERFACE                 PS_Interface
-**        #if EXPORT_INTERFACE          PS_Export
-**        #if LOCAL_INTERFACE           PS_Local
-**
-** For example, if after processing the preprocessor token given
-** by pToken there is an "#if INTERFACE" on the preprocessor
-** stack, then *pPresetFlags will be set to PS_Interface.
-*/
-static int ParsePreprocessor(Token *pToken, int flags, int *pPresetFlags){
-  const char *zCmd;
-  int nCmd;
-  const char *zArg;
-  int nArg;
-  int nErr = 0;
-  Ifmacro *pIf;
-
-  zCmd = &pToken->zText[1];
-  while( isspace(*zCmd) && *zCmd!='\n' ){
-    zCmd++;
-  }
-  if( !isalpha(*zCmd) ){
-    return 0;
-  }
-  nCmd = 1;
-  while( isalpha(zCmd[nCmd]) ){
-    nCmd++;
-  }
-
-  if( nCmd==5 && strncmp(zCmd,"endif",5)==0 ){
-    /*
-    ** Pop the if stack
-    */
-    pIf = ifStack;
-    if( pIf==0 ){
-      fprintf(stderr,"%s:%d: extra '#endif'.\n",zFilename,pToken->nLine);
-      return 1;
-    }
-    ifStack = pIf->pNext;
-    SafeFree(pIf);
-  }else if( nCmd==6 && strncmp(zCmd,"define",6)==0 ){
-    /*
-    ** Record a #define if we are in PS_Interface or PS_Export
-    */
-    Decl *pDecl;
-    if( !(flags & (PS_Local|PS_Interface|PS_Export)) ){ return 0; }
-    zArg = &zCmd[6];
-    while( *zArg && isspace(*zArg) && *zArg!='\n' ){
-      zArg++;
-    }
-    if( *zArg==0 || *zArg=='\n' ){ return 0; }
-    for(nArg=0; ISALNUM(zArg[nArg]); nArg++){}
-    if( nArg==0 ){ return 0; }
-    pDecl = CreateDecl(zArg,nArg);
-    pDecl->pComment = pToken->pComment;
-    DeclSetProperty(pDecl,TY_Macro);
-    pDecl->zDecl = SafeMalloc( pToken->nText + 2 );
-    sprintf(pDecl->zDecl,"%.*s\n",pToken->nText,pToken->zText);
-    if( flags & PS_Export ){
-      DeclSetProperty(pDecl,DP_Export);
-    }else if( flags & PS_Local ){
-      DeclSetProperty(pDecl,DP_Local);
-    }
-  }else if( nCmd==7 && strncmp(zCmd,"include",7)==0 ){
-    /*
-    ** Record an #include if we are in PS_Interface or PS_Export
-    */
-    Include *pInclude;
-    char *zIf;
-
-    if( !(flags & (PS_Interface|PS_Export)) ){ return 0; }
-    zArg = &zCmd[7];
-    while( *zArg && isspace(*zArg) ){ zArg++; }
-    for(nArg=0; !isspace(zArg[nArg]); nArg++){}
-    if( (zArg[0]=='"' && zArg[nArg-1]!='"')
-      ||(zArg[0]=='<' && zArg[nArg-1]!='>')
-    ){
-      fprintf(stderr,"%s:%d: malformed #include statement.\n",
-        zFilename,pToken->nLine);
-      return 1;
-    }
-    zIf = GetIfString();
-    if( zIf ){
-      pInclude = SafeMalloc( sizeof(Include) + nArg*2 + strlen(zIf) + 10 );
-      pInclude->zFile = (char*)&pInclude[1];
-      pInclude->zLabel = &pInclude->zFile[nArg+1];
-      sprintf(pInclude->zFile,"%.*s",nArg,zArg);
-      sprintf(pInclude->zLabel,"%.*s:%s",nArg,zArg,zIf);
-      pInclude->zIf = &pInclude->zLabel[nArg+1];
-      SafeFree(zIf);
-    }else{
-      pInclude = SafeMalloc( sizeof(Include) + nArg + 1 );
-      pInclude->zFile = (char*)&pInclude[1];
-      sprintf(pInclude->zFile,"%.*s",nArg,zArg);
-      pInclude->zIf = 0;
-      pInclude->zLabel = pInclude->zFile;
-    }
-    pInclude->pNext = includeList;
-    includeList = pInclude;
-  }else if( nCmd==2 && strncmp(zCmd,"if",2)==0 ){
-    /*
-    ** Push an #if.  Watch for the special cases of INTERFACE
-    ** and EXPORT_INTERFACE and LOCAL_INTERFACE
-    */
-    zArg = &zCmd[2];
-    while( *zArg && isspace(*zArg) && *zArg!='\n' ){
-      zArg++;
-    }
-    if( *zArg==0 || *zArg=='\n' ){ return 0; }
-    nArg = pToken->nText + (int)(pToken->zText - zArg);
-    if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
-      PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
-    }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
-      PushIfMacro(0,0,0,pToken->nLine,PS_Export);
-    }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
-      PushIfMacro(0,0,0,pToken->nLine,PS_Local);
-    }else if( nArg==15 && strncmp(zArg,"MAKEHEADERS_STOPLOCAL_INTERFACE",15)==0 ){
-      PushIfMacro(0,0,0,pToken->nLine,PS_Local);
-    }else{
-      PushIfMacro(0,zArg,nArg,pToken->nLine,0);
-    }
-  }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){
-    /*
-    ** Push an #ifdef.
-    */
-    zArg = &zCmd[5];
-    while( *zArg && isspace(*zArg) && *zArg!='\n' ){
-      zArg++;
-    }
-    if( *zArg==0 || *zArg=='\n' ){ return 0; }
-    nArg = pToken->nText + (int)(pToken->zText - zArg);
-    PushIfMacro("defined",zArg,nArg,pToken->nLine,0);
-  }else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){
-    /*
-    ** Push an #ifndef.
-    */
-    zArg = &zCmd[6];
-    while( *zArg && isspace(*zArg) && *zArg!='\n' ){
-      zArg++;
-    }
-    if( *zArg==0 || *zArg=='\n' ){ return 0; }
-    nArg = pToken->nText + (int)(pToken->zText - zArg);
-    PushIfMacro("!defined",zArg,nArg,pToken->nLine,0);
-  }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){
-    /*
-    ** Invert the #if on the top of the stack
-    */
-    if( ifStack==0 ){
-      fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename,
-         pToken->nLine);
-      return 1;
-    }
-    pIf = ifStack;
-    if( pIf->zCondition ){
-      ifStack = ifStack->pNext;
-      PushIfMacro("!",pIf->zCondition,strlen(pIf->zCondition),pIf->nLine,0);
-      SafeFree(pIf);
-    }else{
-      pIf->flags = 0;
-    }
-  }else{
-    /*
-    ** This directive can be safely ignored
-    */
-    return 0;
-  }
-
-  /*
-  ** Recompute the preset flags
-  */
-  *pPresetFlags = 0;
-  for(pIf = ifStack; pIf; pIf=pIf->pNext){
-    *pPresetFlags |= pIf->flags;
-  }
-
-  return nErr;
-}
-
-/*
-** Parse an entire file.  Return the number of errors.
-**
-** pList is a list of tokens in the file.  Whitespace tokens have been
-** eliminated, and text with {...} has been collapsed into a
-** single TT_Brace token.
-**
-** initFlags are a set of parse flags that should always be set for this
-** file.  For .c files this is normally 0.  For .h files it is PS_Interface.
-*/
-static int ParseFile(Token *pList, int initFlags){
-  int nErr = 0;
-  Token *pStart = 0;
-  int flags = initFlags;
-  int presetFlags = initFlags;
-  int resetFlag = 0;
-
-  includeList = 0;
-  while( pList ){
-    switch( pList->eType ){
-    case TT_EOF:
-      goto end_of_loop;
-
-    case TT_Preprocessor:
-      nErr += ParsePreprocessor(pList,flags,&presetFlags);
-      pStart = 0;
-      presetFlags |= initFlags;
-      flags = presetFlags;
-      break;
-
-    case TT_Other:
-      switch( pList->zText[0] ){
-      case ';':
-        nErr += ProcessDecl(pStart,pList,flags);
-        pStart = 0;
-        flags = presetFlags;
-        break;
-
-      case '=':
-        if( pList->pPrev->nText==8
-            && strncmp(pList->pPrev->zText,"operator",8)==0 ){
-          break;
-        }
-        nErr += ProcessDecl(pStart,pList,flags);
-        pStart = 0;
-        while( pList && pList->zText[0]!=';' ){
-          pList = pList->pNext;
-        }
-        if( pList==0 ) goto end_of_loop;
-        flags = presetFlags;
-        break;
-
-      case ':':
-        if( pList->zText[1]==':' ){
-          flags |= PS_Method;
-        }
-        break;
-
-      default:
-        break;
-      }
-      break;
-
-    case TT_Braces:
-      nErr += ProcessProcedureDef(pStart,pList,flags);
-      pStart = 0;
-      flags = presetFlags;
-      break;
-
-    case TT_Id:
-       if( pStart==0 ){
-          pStart = pList;
-          flags = presetFlags;
-       }
-       resetFlag = 0;
-       switch( pList->zText[0] ){
-       case 'c':
-         if( pList->nText==5 && strncmp(pList->zText,"class",5)==0 ){
-           nErr += ProcessTypeDecl(pList,flags,&resetFlag);
-         }
-         break;
-
-       case 'E':
-         if( pList->nText==6 && strncmp(pList->zText,"EXPORT",6)==0 ){
-           flags |= PS_Export2;
-           /* pStart = 0; */
-         }
-         break;
-
-       case 'e':
-         if( pList->nText==4 && strncmp(pList->zText,"enum",4)==0 ){
-           if( pList->pNext && pList->pNext->eType==TT_Braces ){
-             pList = pList->pNext;
-           }else{
-             nErr += ProcessTypeDecl(pList,flags,&resetFlag);
-           }
-         }else if( pList->nText==6 && strncmp(pList->zText,"extern",6)==0 ){
-           pList = pList->pNext;
-           if( pList && pList->nText==3 && strncmp(pList->zText,"\"C\"",3)==0 ){
-             pList = pList->pNext;
-             flags &= ~DP_Cplusplus;
-           }else{
-             flags |= PS_Extern;
-           }
-           pStart = pList;
-         }
-         break;
-
-       case 'i':
-         if( pList->nText==6 && strncmp(pList->zText,"inline",6)==0
-          && (flags & PS_Static)==0
-         ){
-           nErr += ProcessInlineProc(pList,flags,&resetFlag);
-         }
-         break;
-
-       case 'L':
-         if( pList->nText==5 && strncmp(pList->zText,"LOCAL",5)==0 ){
-           flags |= PS_Local2;
-           pStart = pList;
-         }
-         break;
-
-       case 'P':
-         if( pList->nText==6 && strncmp(pList->zText, "PUBLIC",6)==0 ){
-           flags |= PS_Public;
-           pStart = pList;
-         }else if( pList->nText==7 && strncmp(pList->zText, "PRIVATE",7)==0 ){
-           flags |= PS_Private;
-           pStart = pList;
-         }else if( pList->nText==9 && strncmp(pList->zText,"PROTECTED",9)==0 ){
-           flags |= PS_Protected;
-           pStart = pList;
-         }
-         break;
-
-       case 's':
-         if( pList->nText==6 && strncmp(pList->zText,"struct",6)==0 ){
-           if( pList->pNext && pList->pNext->eType==TT_Braces ){
-             pList = pList->pNext;
-           }else{
-             nErr += ProcessTypeDecl(pList,flags,&resetFlag);
-           }
-         }else if( pList->nText==6 && strncmp(pList->zText,"static",6)==0 ){
-           flags |= PS_Static;
-         }
-         break;
-
-       case 't':
-         if( pList->nText==7 && strncmp(pList->zText,"typedef",7)==0 ){
-           flags |= PS_Typedef;
-         }
-         break;
-
-       case 'u':
-         if( pList->nText==5 && strncmp(pList->zText,"union",5)==0 ){
-           if( pList->pNext && pList->pNext->eType==TT_Braces ){
-             pList = pList->pNext;
-           }else{
-             nErr += ProcessTypeDecl(pList,flags,&resetFlag);
-           }
-         }
-         break;
-
-       default:
-         break;
-       }
-       if( resetFlag!=0 ){
-         while( pList && pList->zText[0]!=resetFlag ){
-           pList = pList->pNext;
-         }
-         if( pList==0 ) goto end_of_loop;
-         pStart = 0;
-         flags = presetFlags;
-       }
-       break;
-
-    case TT_String:
-    case TT_Number:
-       break;
-
-    default:
-       pStart = pList;
-       flags = presetFlags;
-       break;
-    }
-    pList = pList->pNext;
-  }
-  end_of_loop:
-
-  /* Verify that all #ifs have a matching "#endif" */
-  while( ifStack ){
-    Ifmacro *pIf = ifStack;
-    ifStack = pIf->pNext;
-    fprintf(stderr,"%s:%d: This '#if' has no '#endif'\n",zFilename,
-      pIf->nLine);
-    SafeFree(pIf);
-  }
-
-  return nErr;
-}
-
-/*
-** If the given Decl object has a non-null zExtra field, then the text
-** of that zExtra field needs to be inserted in the middle of the
-** zDecl field before the last "}" in the zDecl.  This routine does that.
-** If the zExtra is NULL, this routine is a no-op.
-**
-** zExtra holds extra method declarations for classes.  The declarations
-** have to be inserted into the class definition.
-*/
-static void InsertExtraDecl(Decl *pDecl){
-  int i;
-  String str;
-
-  if( pDecl==0 || pDecl->zExtra==0 || pDecl->zDecl==0 ) return;
-  i = strlen(pDecl->zDecl) - 1;
-  while( i>0 && pDecl->zDecl[i]!='}' ){ i--; }
-  StringInit(&str);
-  StringAppend(&str, pDecl->zDecl, i);
-  StringAppend(&str, pDecl->zExtra, 0);
-  StringAppend(&str, &pDecl->zDecl[i], 0);
-  SafeFree(pDecl->zDecl);
-  SafeFree(pDecl->zExtra);
-  pDecl->zDecl = StrDup(StringGet(&str), 0);
-  StringReset(&str);
-  pDecl->zExtra = 0;
-}
-
-/*
-** Reset the DP_Forward and DP_Declared flags on all Decl structures.
-** Set both flags for anything that is tagged as local and isn't
-** in the file zFilename so that it won't be printing in other files.
-*/
-static void ResetDeclFlags(char *zFilename){
-  Decl *pDecl;
-
-  for(pDecl = pDeclFirst; pDecl; pDecl = pDecl->pNext){
-    DeclClearProperty(pDecl,DP_Forward|DP_Declared);
-    if( DeclHasProperty(pDecl,DP_Local) && pDecl->zFile!=zFilename ){
-      DeclSetProperty(pDecl,DP_Forward|DP_Declared);
-    }
-  }
-}
-
-/*
-** Forward declaration of the ScanText() function.
-*/
-static void ScanText(const char*, GenState *pState);
-
-/*
-** The output in pStr is currently within an #if CONTEXT where context
-** is equal to *pzIf.  (*pzIf might be NULL to indicate that we are
-** not within any #if at the moment.)  We are getting ready to output
-** some text that needs to be within the context of "#if NEW" where
-** NEW is zIf.  Make an appropriate change to the context.
-*/
-static void ChangeIfContext(
-  const char *zIf,       /* The desired #if context */
-  GenState *pState       /* Current state of the code generator */
-){
-  if( zIf==0 ){
-    if( pState->zIf==0 ) return;
-    StringAppend(pState->pStr,"#endif\n",0);
-    pState->zIf = 0;
-  }else{
-    if( pState->zIf ){
-      if( strcmp(zIf,pState->zIf)==0 ) return;
-      StringAppend(pState->pStr,"#endif\n",0);
-      pState->zIf = 0;
-    }
-    ScanText(zIf, pState);
-    if( pState->zIf!=0 ){
-      StringAppend(pState->pStr,"#endif\n",0);
-    }
-    StringAppend(pState->pStr,"#if ",0);
-    StringAppend(pState->pStr,zIf,0);
-    StringAppend(pState->pStr,"\n",0);
-    pState->zIf = zIf;
-  }
-}
-
-/*
-** Add to the string pStr a #include of every file on the list of
-** include files pInclude.  The table pTable contains all files that
-** have already been #included at least once.  Don't add any
-** duplicates.  Update pTable with every new #include that is added.
-*/
-static void AddIncludes(
-  Include *pInclude,       /* Write every #include on this list */
-  GenState *pState         /* Current state of the code generator */
-){
-  if( pInclude ){
-    if( pInclude->pNext ){
-      AddIncludes(pInclude->pNext,pState);
-    }
-    if( IdentTableInsert(pState->pTable,pInclude->zLabel,0) ){
-      ChangeIfContext(pInclude->zIf,pState);
-      StringAppend(pState->pStr,"#include ",0);
-      StringAppend(pState->pStr,pInclude->zFile,0);
-      StringAppend(pState->pStr,"\n",1);
-    }
-  }
-}
-
-/*
-** Add to the string pStr a declaration for the object described
-** in pDecl.
-**
-** If pDecl has already been declared in this file, detect that
-** fact and abort early.  Do not duplicate a declaration.
-**
-** If the needFullDecl flag is false and this object has a forward
-** declaration, then supply the forward declaration only.  A later
-** call to CompleteForwardDeclarations() will finish the declaration
-** for us.  But if needFullDecl is true, we must supply the full
-** declaration now.  Some objects do not have a forward declaration.
-** For those objects, we must print the full declaration now.
-**
-** Because it is illegal to duplicate a typedef in C, care is taken
-** to insure that typedefs for the same identifier are only issued once.
-*/
-static void DeclareObject(
-  Decl *pDecl,        /* The thing to be declared */
-  GenState *pState,   /* Current state of the code generator */
-  int needFullDecl    /* Must have the full declaration.  A forward
-                       * declaration isn't enough */
-){
-  Decl *p;               /* The object to be declared */
-  int flag;
-  int isCpp;             /* True if generating C++ */
-  int doneTypedef = 0;   /* True if a typedef has been done for this object */
-
-  /* printf("BEGIN %s of %s\n",needFullDecl?"FULL":"PROTOTYPE",pDecl->zName);*/
-  /*
-  ** For any object that has a forward declaration, go ahead and do the
-  ** forward declaration first.
-  */
-  isCpp = (pState->flags & DP_Cplusplus) != 0;
-  for(p=pDecl; p; p=p->pSameName){
-    if( p->zFwd ){
-      if( !DeclHasProperty(p,DP_Forward) ){
-        DeclSetProperty(p,DP_Forward);
-        if( strncmp(p->zFwd,"typedef",7)==0 ){
-          if( doneTypedef ) continue;
-          doneTypedef = 1;
-        }
-        ChangeIfContext(p->zIf,pState);
-        StringAppend(pState->pStr,isCpp ? p->zFwdCpp : p->zFwd,0);
-      }
-    }
-  }
-
-  /*
-  ** Early out if everything is already suitably declared.
-  **
-  ** This is a very important step because it prevents us from
-  ** executing the code the follows in a recursive call to this
-  ** function with the same value for pDecl.
-  */
-  flag = needFullDecl ? DP_Declared|DP_Forward : DP_Forward;
-  for(p=pDecl; p; p=p->pSameName){
-    if( !DeclHasProperty(p,flag) ) break;
-  }
-  if( p==0 ){
-    return;
-  }
-
-  /*
-  ** Make sure we have all necessary #includes
-  */
-  for(p=pDecl; p; p=p->pSameName){
-    AddIncludes(p->pInclude,pState);
-  }
-
-  /*
-  ** Go ahead an mark everything as being declared, to prevent an
-  ** infinite loop thru the ScanText() function.  At the same time,
-  ** we decide which objects need a full declaration and mark them
-  ** with the DP_Flag bit.  We are only able to use DP_Flag in this
-  ** way because we know we'll never execute this far into this
-  ** function on a recursive call with the same pDecl.  Hence, recursive
-  ** calls to this function (through ScanText()) can never change the
-  ** value of DP_Flag out from under us.
-  */
-  for(p=pDecl; p; p=p->pSameName){
-    if( !DeclHasProperty(p,DP_Declared)
-     && (p->zFwd==0 || needFullDecl)
-     && p->zDecl!=0
-    ){
-      DeclSetProperty(p,DP_Forward|DP_Declared|DP_Flag);
-    }else{
-      DeclClearProperty(p,DP_Flag);
-    }
-  }
-
-  /*
-  ** Call ScanText() recursively (this routine is called from ScanText())
-  ** to include declarations required to come before these declarations.
-  */
-  for(p=pDecl; p; p=p->pSameName){
-    if( DeclHasProperty(p,DP_Flag) ){
-      if( p->zDecl[0]=='#' ){
-        ScanText(&p->zDecl[1],pState);
-      }else{
-        InsertExtraDecl(p);
-        ScanText(p->zDecl,pState);
-      }
-    }
-  }
-
-  /*
-  ** Output the declarations.  Do this in two passes.  First
-  ** output everything that isn't a typedef.  Then go back and
-  ** get the typedefs by the same name.
-  */
-  for(p=pDecl; p; p=p->pSameName){
-    if( DeclHasProperty(p,DP_Flag) && !DeclHasProperty(p,TY_Typedef) ){
-      if( DeclHasAnyProperty(p,TY_Enumeration) ){
-        if( doneTypedef ) continue;
-        doneTypedef = 1;
-      }
-      ChangeIfContext(p->zIf,pState);
-      if( !isCpp && DeclHasAnyProperty(p,DP_ExternReqd) ){
-        StringAppend(pState->pStr,"extern ",0);
-      }else if( isCpp && DeclHasProperty(p,DP_Cplusplus|DP_ExternReqd) ){
-        StringAppend(pState->pStr,"extern ",0);
-      }else if( isCpp && DeclHasAnyProperty(p,DP_ExternCReqd|DP_ExternReqd) ){
-        StringAppend(pState->pStr,"extern \"C\" ",0);
-      }
-      InsertExtraDecl(p);
-      StringAppend(pState->pStr,p->zDecl,0);
-      if( !isCpp && DeclHasProperty(p,DP_Cplusplus) ){
-        fprintf(stderr,
-          "%s: C code ought not reference the C++ object \"%s\"\n",
-          pState->zFilename, p->zName);
-        pState->nErr++;
-      }
-      DeclClearProperty(p,DP_Flag);
-    }
-  }
-  for(p=pDecl; p && !doneTypedef; p=p->pSameName){
-    if( DeclHasProperty(p,DP_Flag) ){
-      /* This has to be a typedef */
-      doneTypedef = 1;
-      ChangeIfContext(p->zIf,pState);
-      InsertExtraDecl(p);
-      StringAppend(pState->pStr,p->zDecl,0);
-    }
-  }
-}
-
-/*
-** This routine scans the input text given, and appends to the
-** string in pState->pStr the text of any declarations that must
-** occur before the text in zText.
-**
-** If an identifier in zText is immediately followed by '*', then
-** only forward declarations are needed for that identifier.  If the
-** identifier name is not followed immediately by '*', we must supply
-** a full declaration.
-*/
-static void ScanText(
-  const char *zText,    /* The input text to be scanned */
-  GenState *pState      /* Current state of the code generator */
-){
-  int nextValid = 0;    /* True is sNext contains valid data */
-  InStream sIn;         /* The input text */
-  Token sToken;         /* The current token being examined */
-  Token sNext;          /* The next non-space token */
-
-  /* printf("BEGIN SCAN TEXT on %s\n", zText); */
-
-  sIn.z = zText;
-  sIn.i = 0;
-  sIn.nLine = 1;
-  while( sIn.z[sIn.i]!=0 ){
-    if( nextValid ){
-      sToken = sNext;
-      nextValid = 0;
-    }else{
-      GetNonspaceToken(&sIn,&sToken);
-    }
-    if( sToken.eType==TT_Id ){
-      int needFullDecl;   /* True if we need to provide the full declaration,
-                          ** not just the forward declaration */
-      Decl *pDecl;        /* The declaration having the name in sToken */
-
-      /*
-      ** See if there is a declaration in the database with the name given
-      ** by sToken.
-      */
-      pDecl = FindDecl(sToken.zText,sToken.nText);
-      if( pDecl==0 ) continue;
-
-      /*
-      ** If we get this far, we've found an identifier that has a
-      ** declaration in the database.  Now see if we the full declaration
-      ** or just a forward declaration.
-      */
-      GetNonspaceToken(&sIn,&sNext);
-      if( sNext.zText[0]=='*' ){
-        needFullDecl = 0;
-      }else{
-        needFullDecl = 1;
-        nextValid = sNext.eType==TT_Id;
-      }
-
-      /*
-      ** Generate the needed declaration.
-      */
-      DeclareObject(pDecl,pState,needFullDecl);
-    }else if( sToken.eType==TT_Preprocessor ){
-      sIn.i -= sToken.nText - 1;
-    }
-  }
-  /* printf("END SCANTEXT\n"); */
-}
-
-/*
-** Provide a full declaration to any object which so far has had only
-** a forward declaration.
-*/
-static void CompleteForwardDeclarations(GenState *pState){
-  Decl *pDecl;
-  int progress;
-
-  do{
-    progress = 0;
-    for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){
-      if( DeclHasProperty(pDecl,DP_Forward)
-       && !DeclHasProperty(pDecl,DP_Declared)
-      ){
-        DeclareObject(pDecl,pState,1);
-        progress = 1;
-        assert( DeclHasProperty(pDecl,DP_Declared) );
-      }
-    }
-  }while( progress );
-}
-
-/*
-** Generate an include file for the given source file.  Return the number
-** of errors encountered.
-**
-** if nolocal_flag is true, then we do not generate declarations for
-** objected marked DP_Local.
-*/
-static int MakeHeader(InFile *pFile, FILE *report, int nolocal_flag){
-  int nErr = 0;
-  GenState sState;
-  String outStr;
-  IdentTable includeTable;
-  Ident *pId;
-  char *zNewVersion;
-  char *zOldVersion;
-
-  if( pFile->zHdr==0 || *pFile->zHdr==0 ) return 0;
-  sState.pStr = &outStr;
-  StringInit(&outStr);
-  StringAppend(&outStr,zTopLine,nTopLine);
-  sState.pTable = &includeTable;
-  memset(&includeTable,0,sizeof(includeTable));
-  sState.zIf = 0;
-  sState.nErr = 0;
-  sState.zFilename = pFile->zSrc;
-  sState.flags = pFile->flags & DP_Cplusplus;
-  ResetDeclFlags(nolocal_flag ? "no" : pFile->zSrc);
-  for(pId = pFile->idTable.pList; pId; pId=pId->pNext){
-    Decl *pDecl = FindDecl(pId->zName,0);
-    if( pDecl ){
-      DeclareObject(pDecl,&sState,1);
-    }
-  }
-  CompleteForwardDeclarations(&sState);
-  ChangeIfContext(0,&sState);
-  nErr += sState.nErr;
-  zOldVersion = ReadFile(pFile->zHdr);
-  zNewVersion = StringGet(&outStr);
-  if( report ) fprintf(report,"%s: ",pFile->zHdr);
-  if( zOldVersion==0 ){
-    if( report ) fprintf(report,"updated\n");
-    if( WriteFile(pFile->zHdr,zNewVersion) ){
-      fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
-      nErr++;
-    }
-  }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){
-    if( report ) fprintf(report,"error!\n");
-    fprintf(stderr,
-       "%s: Can't overwrite this file because it wasn't previously\n"
-       "%*s  generated by 'makeheaders'.\n",
-       pFile->zHdr, (int)strlen(pFile->zHdr), "");
-    nErr++;
-  }else if( strcmp(zOldVersion,zNewVersion)!=0 ){
-    if( report ) fprintf(report,"updated\n");
-    if( WriteFile(pFile->zHdr,zNewVersion) ){
-      fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
-      nErr++;
-    }
-  }else if( report ){
-    fprintf(report,"unchanged\n");
-  }
-  SafeFree(zOldVersion);
-  IdentTableReset(&includeTable);
-  StringReset(&outStr);
-  return nErr;
-}
-
-/*
-** Generate a global header file -- a header file that contains all
-** declarations.  If the forExport flag is true, then only those
-** objects that are exported are included in the header file.
-*/
-static int MakeGlobalHeader(int forExport){
-  GenState sState;
-  String outStr;
-  IdentTable includeTable;
-  Decl *pDecl;
-
-  sState.pStr = &outStr;
-  StringInit(&outStr);
-  /* StringAppend(&outStr,zTopLine,nTopLine); */
-  sState.pTable = &includeTable;
-  memset(&includeTable,0,sizeof(includeTable));
-  sState.zIf = 0;
-  sState.nErr = 0;
-  sState.zFilename = "(all)";
-  sState.flags = 0;
-  ResetDeclFlags(0);
-  for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){
-    if( forExport==0 || DeclHasProperty(pDecl,DP_Export) ){
-      DeclareObject(pDecl,&sState,1);
-    }
-  }
-  ChangeIfContext(0,&sState);
-  printf("%s",StringGet(&outStr));
-  IdentTableReset(&includeTable);
-  StringReset(&outStr);
-  return 0;
-}
-
-#ifdef DEBUG
-/*
-** Return the number of characters in the given string prior to the
-** first newline.
-*/
-static int ClipTrailingNewline(char *z){
-  int n = strlen(z);
-  while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ){ n--; }
-  return n;
-}
-
-/*
-** Dump the entire declaration list for debugging purposes
-*/
-static void DumpDeclList(void){
-  Decl *pDecl;
-
-  for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){
-    printf("**** %s from file %s ****\n",pDecl->zName,pDecl->zFile);
-    if( pDecl->zIf ){
-      printf("If: [%.*s]\n",ClipTrailingNewline(pDecl->zIf),pDecl->zIf);
-    }
-    if( pDecl->zFwd ){
-      printf("Decl: [%.*s]\n",ClipTrailingNewline(pDecl->zFwd),pDecl->zFwd);
-    }
-    if( pDecl->zDecl ){
-      InsertExtraDecl(pDecl);
-      printf("Def: [%.*s]\n",ClipTrailingNewline(pDecl->zDecl),pDecl->zDecl);
-    }
-    if( pDecl->flags ){
-      static struct {
-        int mask;
-        char *desc;
-      } flagSet[] = {
-        { TY_Class,       "class" },
-        { TY_Enumeration, "enum" },
-        { TY_Structure,   "struct" },
-        { TY_Union,       "union" },
-        { TY_Variable,    "variable" },
-        { TY_Subroutine,  "function" },
-        { TY_Typedef,     "typedef" },
-        { TY_Macro,       "macro" },
-        { DP_Export,      "export" },
-        { DP_Local,       "local" },
-        { DP_Cplusplus,   "C++" },
-      };
-      int i;
-      printf("flags:");
-      for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){
-        if( flagSet[i].mask & pDecl->flags ){
-          printf(" %s", flagSet[i].desc);
-        }
-      }
-      printf("\n");
-    }
-    if( pDecl->pInclude ){
-      Include *p;
-      printf("includes:");
-      for(p=pDecl->pInclude; p; p=p->pNext){
-        printf(" %s",p->zFile);
-      }
-      printf("\n");
-    }
-  }
-}
-#endif
-
-/*
-** When the "-doc" command-line option is used, this routine is called
-** to print all of the database information to standard output.
-*/
-static void DocumentationDump(void){
-  Decl *pDecl;
-  static struct {
-    int mask;
-    char flag;
-  } flagSet[] = {
-    { TY_Class,       'c' },
-    { TY_Enumeration, 'e' },
-    { TY_Structure,   's' },
-    { TY_Union,       'u' },
-    { TY_Variable,    'v' },
-    { TY_Subroutine,  'f' },
-    { TY_Typedef,     't' },
-    { TY_Macro,       'm' },
-    { DP_Export,      'x' },
-    { DP_Local,       'l' },
-    { DP_Cplusplus,   '+' },
-  };
-
-  for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){
-    int i;
-    int nLabel = 0;
-    char *zDecl;
-    char zLabel[50];
-    for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){
-      if( DeclHasProperty(pDecl,flagSet[i].mask) ){
-        zLabel[nLabel++] = flagSet[i].flag;
-      }
-    }
-    if( nLabel==0 ) continue;
-    zLabel[nLabel] = 0;
-    InsertExtraDecl(pDecl);
-    zDecl = pDecl->zDecl;
-    if( zDecl==0 ) zDecl = pDecl->zFwd;
-    printf("%s %s %s %p %d %d %d %d %d\n",
-       pDecl->zName,
-       zLabel,
-       pDecl->zFile,
-       pDecl->pComment,
-       pDecl->pComment ? pDecl->pComment->nText+1 : 0,
-       pDecl->zIf ? (int)strlen(pDecl->zIf)+1 : 0,
-       zDecl ? (int)strlen(zDecl) : 0,
-       pDecl->pComment ? pDecl->pComment->nLine : 0,
-       pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0
-    );
-    if( pDecl->pComment ){
-      printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText);
-    }
-    if( pDecl->zIf ){
-      printf("%s\n",pDecl->zIf);
-    }
-    if( zDecl ){
-      printf("%s",zDecl);
-    }
-    if( pDecl->tokenCode.nText ){
-      printf("%.*s\n",pDecl->tokenCode.nText, pDecl->tokenCode.zText);
-    }
-  }
-}
-
-/*
-** Given the complete text of an input file, this routine prints a
-** documentation record for the header comment at the beginning of the
-** file (if the file has a header comment.)
-*/
-void PrintModuleRecord(const char *zFile, const char *zFilename){
-  int i;
-  static int addr = 5;
-  while( isspace(*zFile) ){ zFile++; }
-  if( *zFile!='/' || zFile[1]!='*' ) return;
-  for(i=2; zFile[i] && (zFile[i-1]!='/' || zFile[i-2]!='*'); i++){}
-  if( zFile[i]==0 ) return;
-  printf("%s M %s %d %d 0 0 0 0\n%.*s\n",
-    zFilename, zFilename, addr, i+1, i, zFile);
-  addr += 4;
-}
-
-
-/*
-** Given an input argument to the program, construct a new InFile
-** object.
-*/
-static InFile *CreateInFile(char *zArg, int *pnErr){
-  int nSrc;
-  char *zSrc;
-  InFile *pFile;
-  int i;
-
-  /*
-  ** Get the name of the input file to be scanned.  The input file is
-  ** everything before the first ':' or the whole file if no ':' is seen.
-  **
-  ** Except, on windows, ignore any ':' that occurs as the second character
-  ** since it might be part of the drive specifier.  So really, the ":' has
-  ** to be the 3rd or later character in the name.  This precludes 1-character
-  ** file names, which really should not be a problem.
-  */
-  zSrc = zArg;
-  for(nSrc=2; zSrc[nSrc] && zArg[nSrc]!=':'; nSrc++){}
-  pFile = SafeMalloc( sizeof(InFile) );
-  memset(pFile,0,sizeof(InFile));
-  pFile->zSrc = StrDup(zSrc,nSrc);
-
-  /* Figure out if we are dealing with C or C++ code.  Assume any
-  ** file with ".c" or ".h" is C code and all else is C++.
-  */
-  if( nSrc>2 && zSrc[nSrc-2]=='.' && (zSrc[nSrc-1]=='c' || zSrc[nSrc-1]=='h')){
-    pFile->flags &= ~DP_Cplusplus;
-  }else{
-    pFile->flags |= DP_Cplusplus;
-  }
-
-  /*
-  ** If a separate header file is specified, use it
-  */
-  if( zSrc[nSrc]==':' ){
-    int nHdr;
-    char *zHdr;
-    zHdr = &zSrc[nSrc+1];
-    for(nHdr=0; zHdr[nHdr]; nHdr++){}
-    pFile->zHdr = StrDup(zHdr,nHdr);
-  }
-
-  /* Look for any 'c' or 'C' in the suffix of the file name and change
-  ** that character to 'h' or 'H' respectively.  If no 'c' or 'C' is found,
-  ** then assume we are dealing with a header.
-  */
-  else{
-    int foundC = 0;
-    pFile->zHdr = StrDup(zSrc,nSrc);
-    for(i = nSrc-1; i>0 && pFile->zHdr[i]!='.'; i--){
-      if( pFile->zHdr[i]=='c' ){
-        foundC = 1;
-        pFile->zHdr[i] = 'h';
-      }else if( pFile->zHdr[i]=='C' ){
-        foundC = 1;
-        pFile->zHdr[i] = 'H';
-      }
-    }
-    if( !foundC ){
-      SafeFree(pFile->zHdr);
-      pFile->zHdr = 0;
-    }
-  }
-
-  /*
-  ** If pFile->zSrc contains no 'c' or 'C' in its extension, it
-  ** must be a header file.   In that case, we need to set the
-  ** PS_Interface flag.
-  */
-  pFile->flags |= PS_Interface;
-  for(i=nSrc-1; i>0 && zSrc[i]!='.'; i--){
-    if( zSrc[i]=='c' || zSrc[i]=='C' ){
-      pFile->flags &= ~PS_Interface;
-      break;
-    }
-  }
-
-  /* Done!
-  */
-  return pFile;
-}
-
-/* MS-Windows and MS-DOS both have the following serious OS bug:  the
-** length of a command line is severely restricted.  But this program
-** occasionally requires long command lines.  Hence the following
-** work around.
-**
-** If the parameters "-f FILENAME" appear anywhere on the command line,
-** then the named file is scanned for additional command line arguments.
-** These arguments are substituted in place of the "FILENAME" argument
-** in the original argument list.
-**
-** This first parameter to this routine is the index of the "-f"
-** parameter in the argv[] array.  The argc and argv are passed by
-** pointer so that they can be changed.
-**
-** Parsing of the parameters in the file is very simple.  Parameters
-** can be separated by any amount of white-space (including newlines
-** and carriage returns.)  There are now quoting characters of any
-** kind.  The length of a token is limited to about 1000 characters.
-*/
-static void AddParameters(int index, int *pArgc, char ***pArgv){
-  int argc = *pArgc;      /* The original argc value */
-  char **argv = *pArgv;   /* The original argv value */
-  int newArgc;            /* Value for argc after inserting new arguments */
-  char **zNew = 0;        /* The new argv after this routine is done */
-  char *zFile;            /* Name of the input file */
-  int nNew = 0;           /* Number of new entries in the argv[] file */
-  int nAlloc = 0;         /* Space allocated for zNew[] */
-  int i;                  /* Loop counter */
-  int n;                  /* Number of characters in a new argument */
-  int c;                  /* Next character of input */
-  int startOfLine = 1;    /* True if we are where '#' can start a comment */
-  FILE *in;               /* The input file */
-  char zBuf[1000];        /* A single argument is accumulated here */
-
-  if( index+1==argc ) return;
-  zFile = argv[index+1];
-  in = fopen(zFile,"r");
-  if( in==0 ){
-    fprintf(stderr,"Can't open input file \"%s\"\n",zFile);
-    exit(1);
-  }
-  c = ' ';
-  while( c!=EOF ){
-    while( c!=EOF && isspace(c) ){
-      if( c=='\n' ){
-        startOfLine = 1;
-      }
-      c = getc(in);
-      if( startOfLine && c=='#' ){
-        while( c!=EOF && c!='\n' ){
-          c = getc(in);
-        }
-      }
-    }
-    n = 0;
-    while( c!=EOF && !isspace(c) ){
-      if( n<sizeof(zBuf)-1 ){ zBuf[n++] = c; }
-      startOfLine = 0;
-      c = getc(in);
-    }
-    zBuf[n] = 0;
-    if( n>0 ){
-      nNew++;
-      if( nNew + argc > nAlloc ){
-        if( nAlloc==0 ){
-          nAlloc = 100 + argc;
-          zNew = malloc( sizeof(char*) * nAlloc );
-        }else{
-          nAlloc *= 2;
-          zNew = realloc( zNew, sizeof(char*) * nAlloc );
-        }
-      }
-      if( zNew ){
-        int j = nNew + index;
-        zNew[j] = malloc( n + 1 );
-        if( zNew[j] ){
-          strcpy( zNew[j], zBuf );
-        }
-      }
-    }
-  }
-  newArgc = argc + nNew - 1;
-  for(i=0; i<=index; i++){
-    zNew[i] = argv[i];
-  }
-  for(i=nNew + index + 1; i<newArgc; i++){
-    zNew[i] = argv[i + 1 - nNew];
-  }
-  zNew[newArgc] = 0;
-  *pArgc = newArgc;
-  *pArgv = zNew;
-}
-
-#ifdef NOT_USED
-/*
-** Return the time that the given file was last modified.  If we can't
-** locate the file (because, for example, it doesn't exist), then
-** return 0.
-*/
-static unsigned int ModTime(const char *zFilename){
-  unsigned int mTime = 0;
-  struct stat sStat;
-  if( stat(zFilename,&sStat)==0 ){
-    mTime = sStat.st_mtime;
-  }
-  return mTime;
-}
-#endif
-
-/*
-** Print a usage comment for this program.
-*/
-static void Usage(const char *argv0, const char *argvN){
-  fprintf(stderr,"%s: Illegal argument \"%s\"\n",argv0,argvN);
-  fprintf(stderr,"Usage: %s [options] filename...\n"
-    "Options:\n"
-    "  -h          Generate a single .h to standard output.\n"
-    "  -H          Like -h, but only output EXPORT declarations.\n"
-    "  -v          (verbose) Write status information to the screen.\n"
-    "  -doc        Generate no header files.  Instead, output information\n"
-    "              that can be used by an automatic program documentation\n"
-    "              and cross-reference generator.\n"
-    "  -local      Generate prototypes for \"static\" functions and\n"
-    "              procedures.\n"
-    "  -f FILE     Read additional command-line arguments from the file named\n"
-    "              \"FILE\".\n"
-#ifdef DEBUG
-    "  -! MASK     Set the debugging mask to the number \"MASK\".\n"
-#endif
-    "  --          Treat all subsequent comment-line parameters as filenames,\n"
-    "              even if they begin with \"-\".\n",
-    argv0
-  );
-}
-
-/*
-** The following text contains a few simple #defines that we want
-** to be available to every file.
-*/
-static const char zInit[] =
-  "#define INTERFACE 0\n"
-  "#define EXPORT_INTERFACE 0\n"
-  "#define LOCAL_INTERFACE 0\n"
-  "#define EXPORT\n"
-  "#define LOCAL static\n"
-  "#define PUBLIC\n"
-  "#define PRIVATE\n"
-  "#define PROTECTED\n"
-;
-
-#if TEST==0
-int main(int argc, char **argv){
-  int i;                /* Loop counter */
-  int nErr = 0;         /* Number of errors encountered */
-  Token *pList;         /* List of input tokens for one file */
-  InFile *pFileList = 0;/* List of all input files */
-  InFile *pTail = 0;    /* Last file on the list */
-  InFile *pFile;        /* for looping over the file list */
-  int h_flag = 0;       /* True if -h is present.  Output unified header */
-  int H_flag = 0;       /* True if -H is present.  Output EXPORT header */
-  int v_flag = 0;       /* Verbose */
-  int noMoreFlags;      /* True if -- has been seen. */
-  FILE *report;         /* Send progress reports to this, if not NULL */
-
-  noMoreFlags = 0;
-  for(i=1; i<argc; i++){
-    if( argv[i][0]=='-' && !noMoreFlags ){
-      switch( argv[i][1] ){
-        case 'h':   h_flag = 1;   break;
-        case 'H':   H_flag = 1;   break;
-        case 'v':   v_flag = 1;   break;
-        case 'd':   doc_flag = 1; proto_static = 1; break;
-        case 'l':   proto_static = 1; break;
-        case 'f':   AddParameters(i, &argc, &argv); break;
-        case '-':   noMoreFlags = 1;   break;
-#ifdef DEBUG
-        case '!':   i++;  debugMask = strtol(argv[i],0,0); break;
-#endif
-        default:    Usage(argv[0],argv[i]); return 1;
-      }
-    }else{
-      pFile = CreateInFile(argv[i],&nErr);
-      if( pFile ){
-        if( pFileList ){
-          pTail->pNext = pFile;
-          pTail = pFile;
-        }else{
-          pFileList = pTail = pFile;
-        }
-      }
-    }
-  }
-  if( h_flag && H_flag ){
-    h_flag = 0;
-  }
-  if( v_flag ){
-    report = (h_flag || H_flag) ? stderr : stdout;
-  }else{
-    report = 0;
-  }
-  if( nErr>0 ){
-    return nErr;
-  }
-  for(pFile=pFileList; pFile; pFile=pFile->pNext){
-    char *zFile;
-
-    zFilename = pFile->zSrc;
-    if( zFilename==0 ) continue;
-    zFile = ReadFile(zFilename);
-    if( zFile==0 ){
-      fprintf(stderr,"Can't read input file \"%s\"\n",zFilename);
-      nErr++;
-      continue;
-    }
-    if( strncmp(zFile,zTopLine,nTopLine)==0 ){
-      pFile->zSrc = 0;
-    }else{
-      if( report ) fprintf(report,"Reading %s...\n",zFilename);
-      pList = TokenizeFile(zFile,&pFile->idTable);
-      if( pList ){
-        nErr += ParseFile(pList,pFile->flags);
-        FreeTokenList(pList);
-      }else if( zFile[0]==0 ){
-        fprintf(stderr,"Input file \"%s\" is empty.\n", zFilename);
-        nErr++;
-      }else{
-        fprintf(stderr,"Errors while processing \"%s\"\n", zFilename);
-        nErr++;
-      }
-    }
-    if( !doc_flag ) SafeFree(zFile);
-    if( doc_flag ) PrintModuleRecord(zFile,zFilename);
-  }
-  if( nErr>0 ){
-    return nErr;
-  }
-#ifdef DEBUG
-  if( debugMask & DECL_DUMP ){
-    DumpDeclList();
-    return nErr;
-  }
-#endif
-  if( doc_flag ){
-    DocumentationDump();
-    return nErr;
-  }
-  zFilename = "--internal--";
-  pList = TokenizeFile(zInit,0);
-  if( pList==0 ){
-    return nErr+1;
-  }
-  ParseFile(pList,PS_Interface);
-  FreeTokenList(pList);
-  if( h_flag || H_flag ){
-    nErr += MakeGlobalHeader(H_flag);
-  }else{
-    for(pFile=pFileList; pFile; pFile=pFile->pNext){
-      if( pFile->zSrc==0 ) continue;
-      nErr += MakeHeader(pFile,report,0);
-    }
-  }
-  return nErr;
-}
-#endif
-
-
-
-
- - - - - - diff --git a/tranche/deprecated/0_makefile b/tranche/deprecated/0_makefile deleted file mode 100644 index b8c7b9d..0000000 --- a/tranche/deprecated/0_makefile +++ /dev/null @@ -1,40 +0,0 @@ -# src/0_makefile - -SHELL=/bin/bash - --include 0_makefile_flags - -SUID_TOOL=$(TOOLSDIR)/bin/setuid_root.sh -MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_cc - -SOURCES=$(wildcard *.c) -HFILES=$(wildcard *.h) - -all: version deps lib execs - -version: - $(MAKE) $@ - @echo "SUID_TOOL: " $(SUID_TOOL) - -deps: - makeheaders $(SOURCES) $(HFILES) - sed -i '/^ *int *main *(.*)/d' *.h - $(MAKE) $@ - -execs: - $(MAKE) $@ - @echo "-> $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all" - cat $(SUID_TOOL) - @echo -n "Are you sure? [y/N] " && read ans && [ $${ans:-N} == y ] - sudo $(SUID_TOOL) $(EXECSDIR)/subu-mk-0 $(EXECSDIR)/subu-rm-0 $(EXECSDIR)/subu-bind-all - -clean: - $(MAKE) $@ - for i in $(HFILES); do rm $$i; done - -%:: - $(MAKE) $@ - - - - diff --git a/tranche/deprecated/0_makefile-flags b/tranche/deprecated/0_makefile-flags deleted file mode 100644 index 0da8b61..0000000 --- a/tranche/deprecated/0_makefile-flags +++ /dev/null @@ -1,31 +0,0 @@ - -# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. -ECHO= echo -#ECHO= echo -e - -# directories used by this makefile, these could all be set to dot for -# the simplest source directory structure - -#LIDBIR, EXECSDIR, HDIR hold the make results that might later be staged -#$(PWD) is the directory that make was called from, this is already build in -#set to dot to use the same directory as the source code -#leave blank to ommit -DEPRDIR=1_deprecated -DOCDIR=1_doc -EXECSDIR=1_execs -HDIR=1_headers -LIBDIR=1_lib -TESTDIR=1_tests -TMPDIR=1_tmp -TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) -TRYDIR=1_try - - -# compiler and flags -CC=gcc -CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB -#CFLAGS=-std=gnu11 -fPIC -I. -Werror -LINKFLAGS=-L1_lib -lsubu -lsqlite3 - -LIBFILE=$(LIBDIR)/libsubu.a - diff --git a/tranche/doc/todo.txt b/tranche/doc/todo.txt deleted file mode 100644 index 1494e66..0000000 --- a/tranche/doc/todo.txt +++ /dev/null @@ -1,11 +0,0 @@ - -2019-03-22T13:37:23Z -1. indentation - - the first non blank line after the #tranche keyword sets the indentation - level for the following text. When echoing to the output file - remove that many leading spaces. - - Hmm, or allow options among the tranche parameters, -indent 3 or - -indent ' '. - diff --git a/tranche/makefile b/tranche/makefile deleted file mode 100644 index 4c20d5e..0000000 --- a/tranche/makefile +++ /dev/null @@ -1,22 +0,0 @@ -# tranche/makefile - -SHELL=/bin/bash - --include makefile-flags - -.PHONY: all version deps lib exec -all: version deps lib exec - -lib: - $(MAKE) $@ - cp src/tranche.lib.h include/tranche.h - -exec: - $(MAKE) $@ - -%:: - $(MAKE) $@ - - - - diff --git a/tranche/makefile-flags b/tranche/makefile-flags deleted file mode 100644 index 68f6296..0000000 --- a/tranche/makefile-flags +++ /dev/null @@ -1,31 +0,0 @@ -MODULE=tranche - -# some versions of Linux need a -e option others complain if there is a -e .. and it isn't the binary for echo .. -ECHO= echo -#ECHO= echo -e - -# directories used by this makefile, these could all be set to dot for -# the simplest source directory structure - -#LIDBIR, EXECDIR, INCDIR hold the make results that might later be staged -#$(PWD) is the directory that make was called from, this is already build in -#set to dot to use the same directory as the source code -#leave blank to ommit -DEPRDIR=deprecated -DOCDIR=doc -EXECDIR=exec -INCDIR=include -LIBDIR=lib -SRCDIR=src -TESTDIR=test -TMPDIR=tmp -TOOLSDIR=$(realpath $(PROJECT_SUBU)/tools) -TRYDIR=try - -C=gcc -CFLAGS=-std=gnu11 -fPIC -I. -I../stage/include -ggdb -Werror -DDEBUG -DDEBUGDB -#CFLAGS=-std=gnu11 -fPIC -I. -Werror -LINKFLAGS=-Llib -L../stage/lib/ -lda -ltranche - -MAKE=/usr/bin/make --no-print-directory -f $(PROJECT_SUBU)/tools/lib/makefile_cc -#MAKE=/usr/bin/make -f $(PROJECT_SUBU)/tools/lib/makefile_cc diff --git a/tranche/src/tranche.cli.c b/tranche/src/tranche.cli.c deleted file mode 100644 index 8df4337..0000000 --- a/tranche/src/tranche.cli.c +++ /dev/null @@ -1,25 +0,0 @@ - -#include -#include -#include -#include "tranche.lib.h" - -int main(int argc, char **argv, char **envp){ - if(argc != 2){ - fprintf(stderr, "usage: %s \n",argv[0]); - return 1; - } - FILE *file = fopen(argv[1], "r"); - if(!file){ - fprintf(stderr,"could not open file %s\n", argv[1]); - return 2; - } - Da targets; - da_alloc(&targets, sizeof(int)); - int fd = STDOUT_FILENO; - da_push(&targets, &fd); - tranche_send(file, &targets); - da_free(&targets); - fclose(file); - return 0; -} diff --git a/tranche/src/tranche.lib.c b/tranche/src/tranche.lib.c deleted file mode 100644 index 9b824d3..0000000 --- a/tranche/src/tranche.lib.c +++ /dev/null @@ -1,146 +0,0 @@ -/* -The purpose of this tools is to facilitate putting prototypes (declarations) next -to implementations (definitions) in a single source file of a C/C++ programs. - -Splits a single source file into multiple files. Scans through the single -source file looking for lines of the form: - - #tranche-begin filename ... - -With the # as the first non-space character on the line, and only filename -following the tag. Upon finding such a line, copies all following lines into the -listed files, until reaching the end marker: - - #tranche-end - -A next improvement of this file would be to support variables to be passed in -for the file names. As it stands, changing the file name requires editing -the source file. - -Files are opened for create or append, so opening to the same file will append -to the end. - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -//-------------------------------------------------------------------------------- -// parsing - -char newline = '\n'; -char terminator = 0; - -char tranche_begin_tag[] = "#tranche"; -size_t tranche_begin_tag_len = 8; - -char tranche_end_tag[] = "#tranche-end"; -size_t tranche_end_tag_len = 12; - -// given a line -// returns beginning of file name list -static char *is_tranche_begin(char *pt){ - while( *pt && isspace(*pt) ) pt++; - if(!*pt) return NULL; - if( strncmp(pt, tranche_begin_tag, tranche_begin_tag_len) ) return NULL; - return pt + tranche_begin_tag_len; -} - -static char *is_tranche_end(char *pt){ - while( *pt && isspace(*pt) ) pt++; - if(!*pt) return NULL; - if( strncmp(pt, tranche_end_tag, tranche_end_tag_len) ) return NULL; - return pt + tranche_end_tag_len; -} - -static bool parse_file_list(Da *file_names, char *pt0){ - char *pt1; - while( *pt0 && isspace(*pt0) ) pt0++; - pt1 = pt0; - while( *pt0 ){ - while( *pt1 && !isspace(*pt1) ) pt1++; - char *file_name = strndup(pt0, pt1 - pt0); - da_push(file_names, &file_name); - while( *pt1 && isspace(*pt1) ) pt1++; - pt0 = pt1; - } -} - - - -//-------------------------------------------------------------------------------- -// da_map calls - -static void tranche_open_fd(void *fnp, void *closure){ - char *file_name = *(char **)fnp; - Da *fdap = (Da *)closure; - int fd = open(file_name, O_WRONLY | O_APPEND | O_CREAT, 0666); - if(fd == -1){ - fprintf(stderr, "Could not open file %s\n", file_name); - return; - } - da_push(fdap, &fd); - return; -} -static void tranche_open_fds(Da *fnap, Da *fdap){ - da_map(fnap, tranche_open_fd, fdap); -} - -static void tranche_close_fd(void *fdp, void *closure){ - close(*(int *)fdp); -} -static void tranche_close_fds(Da *fdap){ - da_map(fdap, tranche_close_fd, NULL); - da_rewind(fdap); -} - -static void tranche_puts(void *fdp, void *string){ - write(*(int *)fdp, string, strlen(string)); -} -static void tranche_puts_all(Da *fdap, char *string){ - da_map(fdap, tranche_puts, string); -} - - - -//-------------------------------------------------------------------------------- -// we have a little problem if the user tries to tranche two things to the same file .. -int tranche_send(FILE *src, Da *arg_fdap){ - char *pt; - Da line; - Da file_name_arr; - Da fda; - da_alloc(&line, sizeof(char)); - da_alloc(&file_name_arr, sizeof(char *)); - da_alloc(&fda, sizeof(int)); - - while( !feof(src) ){ - da_fgets(&line, src); - if( is_tranche_end(line.base) ) break; - pt = is_tranche_begin(line.base); - if(pt){ // then this line is the start of a nested tranche block - parse_file_list(&file_name_arr, pt); - tranche_open_fds(&file_name_arr, &fda); - da_free_elements(&file_name_arr); - tranche_send(src, &fda); - tranche_close_fds(&fda); - }else{ - da_pop(&line, NULL); // pop the terminating zero - da_push(&line, &newline); - da_push(&line, &terminator); - tranche_puts_all(arg_fdap, line.base); - } - da_rewind(&line); - } - - da_free(&line); - da_free(&file_name_arr); - da_free(&fda); - return 0; -} diff --git a/tranche/src/tranche.lib.h b/tranche/src/tranche.lib.h deleted file mode 100644 index 27990a6..0000000 --- a/tranche/src/tranche.lib.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef TRANCHE_LIB_H -#define TRANCHE_LIB_H - -int tranche_send(FILE *src, Da *arg_fds); - - - -#endif diff --git a/tranche/test/test1.dat b/tranche/test/test1.dat deleted file mode 100644 index b03df3f..0000000 --- a/tranche/test/test1.dat +++ /dev/null @@ -1,23 +0,0 @@ - -#tranche test11.dat test12.dat -The little red hen said to Mick, no thank you not today sir. -And then all the barnes animals shouted out in glee. -No more misery! -#tranche test13.dat -apple banana pear -kiwi -#tranche-end -cows -and cats -#tranche-end - -the between space - -#tranche test14.dat -int float if while -do -function -#tranche-end - -#tranche test15.dat -#tranche-end \ No newline at end of file diff --git a/tranche/test/test1.sh b/tranche/test/test1.sh deleted file mode 100644 index c1140ca..0000000 --- a/tranche/test/test1.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -x -./tranche test1.dat >test1stdout.dat -diff test11.dat test11.dat.expected -diff test12.dat test12.dat.expected -diff test13.dat test13.dat.expected -diff test14.dat test14.dat.expected -diff test15.dat test15.dat.expected -diff test1stdout.dat test1stdout.dat.expected -rm test11.dat test12.dat test13.dat test14.dat test15.dat test1stdout.dat - diff --git a/tranche/test/test11.dat.expected b/tranche/test/test11.dat.expected deleted file mode 100644 index 2c2904a..0000000 --- a/tranche/test/test11.dat.expected +++ /dev/null @@ -1,5 +0,0 @@ -The little red hen said to Mick, no thank you not today sir. -And then all the barnes animals shouted out in glee. -No more misery! -cows -and cats diff --git a/tranche/test/test12.dat.expected b/tranche/test/test12.dat.expected deleted file mode 100644 index 2c2904a..0000000 --- a/tranche/test/test12.dat.expected +++ /dev/null @@ -1,5 +0,0 @@ -The little red hen said to Mick, no thank you not today sir. -And then all the barnes animals shouted out in glee. -No more misery! -cows -and cats diff --git a/tranche/test/test13.dat.expected b/tranche/test/test13.dat.expected deleted file mode 100644 index 81fb20c..0000000 --- a/tranche/test/test13.dat.expected +++ /dev/null @@ -1,2 +0,0 @@ -apple banana pear -kiwi diff --git a/tranche/test/test14.dat.expected b/tranche/test/test14.dat.expected deleted file mode 100644 index 0d8b89b..0000000 --- a/tranche/test/test14.dat.expected +++ /dev/null @@ -1,3 +0,0 @@ -int float if while -do -function diff --git a/tranche/test/test15.dat.expected b/tranche/test/test15.dat.expected deleted file mode 100644 index e69de29..0000000 diff --git a/tranche/test/test1stdout.dat.expected b/tranche/test/test1stdout.dat.expected deleted file mode 100644 index 4e519ff..0000000 --- a/tranche/test/test1stdout.dat.expected +++ /dev/null @@ -1,5 +0,0 @@ - - -the between space - - diff --git a/tranche/test/test2.c.expected b/tranche/test/test2.c.expected deleted file mode 100644 index a4876a1..0000000 --- a/tranche/test/test2.c.expected +++ /dev/null @@ -1,10 +0,0 @@ - - -#include "test2.h" - - -int f(int x){ - return x; -} - - diff --git a/tranche/test/test2.h.expected b/tranche/test/test2.h.expected deleted file mode 100644 index fdc4d72..0000000 --- a/tranche/test/test2.h.expected +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef TEST2_H -#define TEST2_H -int f(int x); -#endif diff --git a/tranche/test/test2.trc.c b/tranche/test/test2.trc.c deleted file mode 100644 index ff7d696..0000000 --- a/tranche/test/test2.trc.c +++ /dev/null @@ -1,23 +0,0 @@ - -#tranche test2.c - -#tranche test2.h -#ifndef TEST2_H -#define TEST2_H -#tranche-end - -#include "test2.h" - -#tranche test2.h -int f(int x); -#tranche-end - -int f(int x){ - return x; -} - -#tranche test2.h -#endif -#tranche-end - -#tranche-end diff --git a/tranche/test/tranche b/tranche/test/tranche deleted file mode 120000 index acf4a6f..0000000 --- a/tranche/test/tranche +++ /dev/null @@ -1 +0,0 @@ -../1_execs/tranche \ No newline at end of file