+++ /dev/null
-# 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) $@
-
-
-
-
+++ /dev/null
-
-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
-
-
+++ /dev/null
-/*
-Dynamic Array
-
-*/
-
-#include "da.lib.h"
-
-#include<stdlib.h>
-#include<stdbool.h>
-#include<string.h>
-
-//--------------------------------------------------------------------------------
-// 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;
-}
+++ /dev/null
-#ifndef DA_LIB_H
-#define DA_LIB_H
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdio.h>
-
-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
-
+++ /dev/null
-# da/test
-
-SHELL=/bin/bash
-
--include makefile-flags
-
-.PHONY: all
-all: version deps lib exec
-
-%::
- $(MAKE) $@
-
-
-
-
+++ /dev/null
-./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
+++ /dev/null
-
-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
+++ /dev/null
-passed all 4 tests
+++ /dev/null
-this is a test
-ends without a newline
-(setq mode-require-final-newline nil)
\ No newline at end of file
+++ /dev/null
-
-#include <stdio.h>
-#include <stdbool.h>
-#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;
-}
+++ /dev/null
-/*
-Tests for Da.
-
-*/
-
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-#include <da.h>
-
-#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;
-}
-
-
-
-
+++ /dev/null
-#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
+++ /dev/null
-# 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) $@
-
-
-
-
+++ /dev/null
-
-# 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
-
+++ /dev/null
-
-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.
+++ /dev/null
-
-#include "dbprintf.lib.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-
-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;
-}
+++ /dev/null
-# src-dispatch/makefile
-
-SHELL=/bin/bash
-
--include makefile-flags
-
-all: version dep lib
-
-%::
- $(MAKE) $@
-
-
-
-
+++ /dev/null
-
-# 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
-
+++ /dev/null
-#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 <sys/types.h>
-#include <unistd.h>
-
-#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 <wait.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-
-#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
#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
--- /dev/null
+# 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) $@
+
+
+
+
--- /dev/null
+
+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
+
+
--- /dev/null
+/*
+Dynamic Array
+
+*/
+
+#include "da.lib.h"
+
+#include<stdlib.h>
+#include<stdbool.h>
+#include<string.h>
+
+//--------------------------------------------------------------------------------
+// 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;
+}
--- /dev/null
+#ifndef DA_LIB_H
+#define DA_LIB_H
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+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
+
--- /dev/null
+# da/test
+
+SHELL=/bin/bash
+
+-include makefile-flags
+
+.PHONY: all
+all: version deps lib exec
+
+%::
+ $(MAKE) $@
+
+
+
+
--- /dev/null
+./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
--- /dev/null
+
+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
--- /dev/null
+passed all 4 tests
--- /dev/null
+this is a test
+ends without a newline
+(setq mode-require-final-newline nil)
\ No newline at end of file
--- /dev/null
+
+#include <stdio.h>
+#include <stdbool.h>
+#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;
+}
--- /dev/null
+/*
+Tests for Da.
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <da.h>
+
+#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;
+}
+
+
+
+
--- /dev/null
+#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
--- /dev/null
+# 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) $@
+
+
+
+
--- /dev/null
+
+# 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
+
--- /dev/null
+
+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.
--- /dev/null
+
+#include "dbprintf.lib.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+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;
+}
--- /dev/null
+# src-dispatch/makefile
+
+SHELL=/bin/bash
+
+-include makefile-flags
+
+all: version dep lib
+
+%::
+ $(MAKE) $@
+
+
+
+
--- /dev/null
+
+# 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
+
--- /dev/null
+#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 <sys/types.h>
+#include <unistd.h>
+
+#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 <wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#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
--- /dev/null
+#ifndef DA_LIB_H
+#define DA_LIB_H
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+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
+
--- /dev/null
+#ifndef TRANCHE_LIB_H
+#define TRANCHE_LIB_H
+
+int tranche_send(FILE *src, Da *arg_fds);
+
+
+
+#endif
--- /dev/null
+# 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) $@
+
+
+
+
--- /dev/null
+
+# 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
+
--- /dev/null
+/*
+ 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 <sys/types.h>
+#include <unistd.h>
+#include <wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+
+
+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;
+ }
+}
--- /dev/null
+/*
+ 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 <wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#if INTERFACE
+#include <sys/types.h>
+#include <unistd.h>
+#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;
+ }
+}
+
+
--- /dev/null
+/*
+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 <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+
+#if INTERFACE
+#include <sys/types.h>
+#include <pwd.h>
+#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;
+ }}}
+
--- /dev/null
+/*
+ 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 <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#if INTERFACE
+#include <stdbool.h>
+#include <errno.h>
+#include <sqlite3.h>
+#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;
+}
--- /dev/null
+/*
+Set or get a new maximum subu number. Currently doesn't do the setting part.
+
+*/
+#include "subudb-number-next.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+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;
+}
--- /dev/null
+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.
--- /dev/null
+# 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) $@
+
+
+
+
--- /dev/null
+
+# 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
+
--- /dev/null
+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
--- /dev/null
+/*
+Tests for da.
+
+*/
+
+#include <da.cli.h>
+#include <stdbool.h>
+
+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;
+
+}
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <stdlib.h>
+#include <stdbool.h>
+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
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+int dbprintf(const char *format,...);
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <sys/types.h>
+#include <unistd.h>
+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
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <stdbool.h>
+#include <errno.h>
+#include <sqlite3.h>
+int subu_bind_all(char **mess,sqlite3 *db);
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
+#define SUBU_ERR_ARG_CNT 1
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <stdbool.h>
+#include <errno.h>
+#include <sqlite3.h>
+int subu_bind(char **mess,char *masteru_name,char *subu_username,char *subuhome);
+#define SUBU_ERR_ARG_CNT 1
--- /dev/null
+/* \aThis 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
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <stdbool.h>
+#include <errno.h>
+#include <sqlite3.h>
+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
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <stdbool.h>
+#include <errno.h>
+#include <sqlite3.h>
+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
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+typedef unsigned int uint;
+#include <sqlite3.h>
+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 <stdbool.h>
+#include <errno.h>
+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 <sys/types.h>
+#include <unistd.h>
+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 <stdlib.h>
+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
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int db_commit(sqlite3 *db);
+int db_rollback(sqlite3 *db);
+int subudb_schema(sqlite3 *db);
+int db_begin(sqlite3 *db);
+#include <stdbool.h>
+#include <errno.h>
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_number_get(sqlite3 *db,int *n);
+int subudb_number_set(sqlite3 *db,int n);
+#include <stdbool.h>
+#include <errno.h>
+#define SUBU_ERR_N 14
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
+#define SUBU_ERR_ARG_CNT 1
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_Masteru_Subu_get_subu_username(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username);
+#include <stdbool.h>
+#include <errno.h>
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
+#define SUBU_ERR_ARG_CNT 1
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_Masteru_Subu_put(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
+#include <stdbool.h>
+#include <errno.h>
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
+#include <stdbool.h>
+#include <errno.h>
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+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 <stdbool.h>
+#include <errno.h>
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
+#define SUBU_ERR_ARG_CNT 1
--- /dev/null
+/* \aThis file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
+#include <stdlib.h>
+#include <stdbool.h>
+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
--- /dev/null
+/*
+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
--- /dev/null
+/*
+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
--- /dev/null
+/*
+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 <stdio.h>
+
+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;
+}
--- /dev/null
+
+#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/";
--- /dev/null
+/*
+The db file is maintained in SQLite
+
+Because user names of are of limited length, subu user names are always named _s<number>.
+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 <sqlite3.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+//--------------------------------------------------------------------------------
+// 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;
+}
--- /dev/null
+/*
+mount all the subu user directories into master's subuland
+uses unmount to undo this
+
+*/
+#include "subu-bind-all.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}
--- /dev/null
+/*
+mount a subu user directory into master's subuland
+uses unmount to undo this
+
+*/
+#include "subu-bind.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}
--- /dev/null
+/*
+ subu-mk-0 command
+
+*/
+#include "subu-mk-0.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+
+}
--- /dev/null
+/*
+ subu-mk-0 command
+
+*/
+#include "subu-rm-0.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+ }
+}
--- /dev/null
+/*
+ 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<number>, calls useradd to creat
+ the new uswer account, and enters the relationship between masteru, subu, and
+ s<number> in the db file. It is this relation in the db file that
+ associates the subuname with the s<number> user.
+
+ subu-rm-0 uses userdel to delete the related s<number> 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 <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#if INTERFACE
+#include <stdbool.h>
+#include <errno.h>
+#include <sqlite3.h>
+#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);
+}
--- /dev/null
+/*
+This command initializes the db file.
+
+*/
+#include "subudb-init.cli.h"
+#include <stdio.h>
+
+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;
+}
--- /dev/null
+/*
+Set or get a new maximum subu number. Currently doesn't do the setting part.
+
+*/
+#include "subudb-number.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+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;
+
+}
--- /dev/null
+/*
+get the username from the db file
+for testing subudb_Masteru_Subu_get_subu_username
+
+*/
+#include "subudb-rel-get.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}
--- /dev/null
+/*
+puts a relation in the masteru/subu table
+
+*/
+#include "subudb-rel-put.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}
--- /dev/null
+/*
+puts a relation in the masteru/subu table
+
+*/
+#include "subudb-rel-rm.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+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;
+}
--- /dev/null
+/*
+Set or get a new maximum subu number. Currently doesn't do the setting part.
+
+*/
+#include "subudb-subus.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+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;
+
+}
--- /dev/null
+#!/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=<shell>][owner=<owner-username>] [subu=]<subu-username>
+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)
--- /dev/null
+# 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) $@
+
+
+
+
--- /dev/null
+
+# 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
+
--- /dev/null
+
+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 ' '.
+
--- /dev/null
+# 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) $@
+
+
+
+
--- /dev/null
+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
--- /dev/null
+
+#include <stdio.h>
+#include <unistd.h>
+#include <da.h>
+#include "tranche.lib.h"
+
+int main(int argc, char **argv, char **envp){
+ if(argc != 2){
+ fprintf(stderr, "usage: %s <source-file>\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;
+}
--- /dev/null
+/*
+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 <da.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+//--------------------------------------------------------------------------------
+// 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;
+}
--- /dev/null
+#ifndef TRANCHE_LIB_H
+#define TRANCHE_LIB_H
+
+int tranche_send(FILE *src, Da *arg_fds);
+
+
+
+#endif
--- /dev/null
+
+#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
--- /dev/null
+#!/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
+
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+apple banana pear
+kiwi
--- /dev/null
+int float if while
+do
+function
--- /dev/null
+
+
+the between space
+
+
--- /dev/null
+
+
+#include "test2.h"
+
+
+int f(int x){
+ return x;
+}
+
+
--- /dev/null
+#ifndef TEST2_H
+#define TEST2_H
+int f(int x);
+#endif
--- /dev/null
+
+#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
--- /dev/null
+../1_execs/tranche
\ No newline at end of file
+++ /dev/null
-#ifndef DA_LIB_H
-#define DA_LIB_H
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdio.h>
-
-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
-
+++ /dev/null
-#ifndef TRANCHE_LIB_H
-#define TRANCHE_LIB_H
-
-int tranche_send(FILE *src, Da *arg_fds);
-
-
-
-#endif
+++ /dev/null
-# 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) $@
-
-
-
-
+++ /dev/null
-
-# 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
-
+++ /dev/null
-/*
- 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 <sys/types.h>
-#include <unistd.h>
-#include <wait.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-
-
-
-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;
- }
-}
+++ /dev/null
-/*
- 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 <wait.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-
-#if INTERFACE
-#include <sys/types.h>
-#include <unistd.h>
-#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;
- }
-}
-
-
+++ /dev/null
-/*
-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 <sys/types.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-
-#if INTERFACE
-#include <sys/types.h>
-#include <pwd.h>
-#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;
- }}}
-
+++ /dev/null
-/*
- 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 <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <pwd.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#if INTERFACE
-#include <stdbool.h>
-#include <errno.h>
-#include <sqlite3.h>
-#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;
-}
+++ /dev/null
-/*
-Set or get a new maximum subu number. Currently doesn't do the setting part.
-
-*/
-#include "subudb-number-next.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-
-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;
-}
+++ /dev/null
-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.
+++ /dev/null
-# 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) $@
-
-
-
-
+++ /dev/null
-
-# 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
-
+++ /dev/null
-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
+++ /dev/null
-/*
-Tests for da.
-
-*/
-
-#include <da.cli.h>
-#include <stdbool.h>
-
-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;
-
-}
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <stdlib.h>
-#include <stdbool.h>
-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
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-int dbprintf(const char *format,...);
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <sys/types.h>
-#include <unistd.h>
-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
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <stdbool.h>
-#include <errno.h>
-#include <sqlite3.h>
-int subu_bind_all(char **mess,sqlite3 *db);
-#define SUBU_ERR_DB_FILE 8
-extern char DB_File[];
-#define SUBU_ERR_ARG_CNT 1
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <stdbool.h>
-#include <errno.h>
-#include <sqlite3.h>
-int subu_bind(char **mess,char *masteru_name,char *subu_username,char *subuhome);
-#define SUBU_ERR_ARG_CNT 1
+++ /dev/null
-/* \aThis 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
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <stdbool.h>
-#include <errno.h>
-#include <sqlite3.h>
-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
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <stdbool.h>
-#include <errno.h>
-#include <sqlite3.h>
-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
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-typedef unsigned int uint;
-#include <sqlite3.h>
-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 <stdbool.h>
-#include <errno.h>
-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 <sys/types.h>
-#include <unistd.h>
-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 <stdlib.h>
-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
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <sqlite3.h>
-int db_commit(sqlite3 *db);
-int db_rollback(sqlite3 *db);
-int subudb_schema(sqlite3 *db);
-int db_begin(sqlite3 *db);
-#include <stdbool.h>
-#include <errno.h>
-#define SUBU_ERR_DB_FILE 8
-extern char DB_File[];
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <sqlite3.h>
-int subudb_number_get(sqlite3 *db,int *n);
-int subudb_number_set(sqlite3 *db,int n);
-#include <stdbool.h>
-#include <errno.h>
-#define SUBU_ERR_N 14
-#define SUBU_ERR_DB_FILE 8
-extern char DB_File[];
-#define SUBU_ERR_ARG_CNT 1
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <sqlite3.h>
-int subudb_Masteru_Subu_get_subu_username(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username);
-#include <stdbool.h>
-#include <errno.h>
-#define SUBU_ERR_DB_FILE 8
-extern char DB_File[];
-#define SUBU_ERR_ARG_CNT 1
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <sqlite3.h>
-int subudb_Masteru_Subu_put(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
-#include <stdbool.h>
-#include <errno.h>
-#define SUBU_ERR_DB_FILE 8
-extern char DB_File[];
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <sqlite3.h>
-int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
-#include <stdbool.h>
-#include <errno.h>
-#define SUBU_ERR_DB_FILE 8
-extern char DB_File[];
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <sqlite3.h>
-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 <stdbool.h>
-#include <errno.h>
-#define SUBU_ERR_DB_FILE 8
-extern char DB_File[];
-#define SUBU_ERR_ARG_CNT 1
+++ /dev/null
-/* \aThis file was automatically generated. Do not edit! */
-#undef INTERFACE
-#include <sqlite3.h>
-int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
-#include <stdlib.h>
-#include <stdbool.h>
-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
+++ /dev/null
-/*
-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
+++ /dev/null
-/*
-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
+++ /dev/null
-/*
-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 <stdio.h>
-
-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;
-}
+++ /dev/null
-
-#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/";
+++ /dev/null
-/*
-The db file is maintained in SQLite
-
-Because user names of are of limited length, subu user names are always named _s<number>.
-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 <sqlite3.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdbool.h>
-
-//--------------------------------------------------------------------------------
-// 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;
-}
+++ /dev/null
-/*
-mount all the subu user directories into master's subuland
-uses unmount to undo this
-
-*/
-#include "subu-bind-all.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-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;
-}
+++ /dev/null
-/*
-mount a subu user directory into master's subuland
-uses unmount to undo this
-
-*/
-#include "subu-bind.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-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;
-}
+++ /dev/null
-/*
- subu-mk-0 command
-
-*/
-#include "subu-mk-0.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-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;
-
-}
+++ /dev/null
-/*
- subu-mk-0 command
-
-*/
-#include "subu-rm-0.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-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;
- }
-}
+++ /dev/null
-/*
- 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<number>, calls useradd to creat
- the new uswer account, and enters the relationship between masteru, subu, and
- s<number> in the db file. It is this relation in the db file that
- associates the subuname with the s<number> user.
-
- subu-rm-0 uses userdel to delete the related s<number> 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 <sys/types.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <pwd.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#if INTERFACE
-#include <stdbool.h>
-#include <errno.h>
-#include <sqlite3.h>
-#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);
-}
+++ /dev/null
-/*
-This command initializes the db file.
-
-*/
-#include "subudb-init.cli.h"
-#include <stdio.h>
-
-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;
-}
+++ /dev/null
-/*
-Set or get a new maximum subu number. Currently doesn't do the setting part.
-
-*/
-#include "subudb-number.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-
-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;
-
-}
+++ /dev/null
-/*
-get the username from the db file
-for testing subudb_Masteru_Subu_get_subu_username
-
-*/
-#include "subudb-rel-get.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-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;
-}
+++ /dev/null
-/*
-puts a relation in the masteru/subu table
-
-*/
-#include "subudb-rel-put.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-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;
-}
+++ /dev/null
-/*
-puts a relation in the masteru/subu table
-
-*/
-#include "subudb-rel-rm.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-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;
-}
+++ /dev/null
-/*
-Set or get a new maximum subu number. Currently doesn't do the setting part.
-
-*/
-#include "subudb-subus.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-
-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;
-
-}
+++ /dev/null
-#!/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=<shell>][owner=<owner-username>] [subu=]<subu-username>
-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)
--- /dev/null
+./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)
--- /dev/null
+#!/bin/bash
+set -x
+make dist-clean
+git add "$@"
+
--- /dev/null
+#!/bin/bash
+chown root "$@" && \
+chmod u+rsx,u-w,go+rx-s-w "$@"
+
--- /dev/null
+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
+#
+#
+#
--- /dev/null
+
+This worked to force the include to be part of the interface:
+
+#if INTERFACE
+#include <sqlite3.h>
+#endif
+
+But this did not:
+
+#if INTERFACE
+ #include <sqlite3.h>
+#endif
+
+makeheaders looks to be sensitive to indentation
+
+
--- /dev/null
+<html>
+<head><title>The Makeheaders Program</title></head>
+<body bgcolor=white>
+<h1 align=center>The Makeheaders Program</h1>
+
+
+<p>
+This document describes <em>makeheaders</em>,
+a tool that automatically generates “<code>.h</code>”
+files for a C or C++ programming project.
+</p>
+
+
+<h2>Table Of Contents</h2>
+
+<ul>
+<li><a href="#H0002">1,0 Background</a>
+<ul>
+<li><a href="#H0003">1.1 Problems With The Traditional Approach</a>
+
+<li><a href="#H0004">1.2 The Makeheaders Solution</a>
+</ul>
+<li><a href="#H0005">2.0 Running The Makeheaders Program</a>
+
+<li><a href="#H0006">3.0 Preparing Source Files For Use With Makeheaders</a>
+<ul>
+<li><a href="#H0007">3.1 The Basic Setup</a>
+
+<li><a href="#H0008">3.2 What Declarations Get Copied</a>
+
+<li><a href="#H0009">3.3 How To Avoid Having To Write Any Header Files</a>
+
+<li><a href="#H0010">3.4 Designating Declarations For Export</a>
+
+<li><a href="#H0011">3.5 Local declarations processed by makeheaders</a>
+
+<li><a href="#H0012">3.6 Using Makeheaders With C++ Code</a>
+
+<li><a href="#H0013">3.7 Conditional Compilation</a>
+
+<li><a href="#H0014">3.8 Caveats</a>
+</ul>
+<li><a href="#H0015">4.0 Using Makeheaders To Generate Documentation</a>
+
+<li><a href="#H0016">5.0 Compiling The Makeheaders Program</a>
+
+<li><a href="#H0017">6.0 History</a>
+
+<li><a href="#H0018">7.0 Summary And Conclusion</a>
+</ul><a name="H0002"></a>
+<h2>1.0 Background</h2>
+
+<p>
+A piece of C source code can be one of two things:
+a <em>declaration</em> or a <em>definition</em>.
+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.)
+</p>
+
+<p>
+Declarations in C include things such as the following:
+<ul>
+<li> Typedefs.
+<li> Structure, union and enumeration declarations.
+<li> Function and procedure prototypes.
+<li> Preprocessor macros and #defines.
+<li> “<code>extern</code>” variable declarations.
+</ul>
+</p>
+
+<p>
+Definitions in C, on the other hand, include these kinds of things:
+<ul>
+<li> Variable definitions.
+<li> The bodies of functions and procedures.
+<li> Initialization data.
+</ul>
+</p>
+
+<p>
+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 <em>interface</em> and the definition is the <em>implementation</em>.
+</p>
+
+<p>
+In C programs, it has always been the tradition that declarations are
+put in files with the “<code>.h</code>” suffix and definitions are
+placed in “<code>.c</code>” files.
+The .c files contain “<code>#include</code>” 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.
+</p>
+
+<a name="H0003"></a>
+<h3>1.1 Problems With The Traditional Approach</h3>
+
+<p>
+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:
+</p>
+
+<p>
+<ol>
+<p><li>
+In large codes with many source files, it becomes difficult to determine
+which .h files should be included in which .c files.
+<p><li>
+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.
+<p><li>
+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.
+<p><li>
+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.
+<p><li>
+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.
+</ol>
+</p>
+
+<a name="H0004"></a>
+<h3>1.2 The Makeheaders Solution</h3>
+
+<p>
+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.
+</p>
+
+<p>
+The makeheaders programming model overcomes all of the objections to the
+traditional C programming model.
+<ol>
+<p><li>
+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
+<code>alpha.c</code> then it must include only the single .h file
+named <code>alpha.h</code>.
+(The .c file might also use some include files from the standard
+library, such as <code><stdio.h></code>, but that is another matter.)
+<p><li>
+The generated .h files do not include other .h files, and so there
+are no include chains to worry about.
+The file <code>alpha.c</code> depends on <code>alpha.h</code> and
+nothing more.
+<p><li>
+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.
+<p><li>
+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.
+<p><li>
+The makeheaders program automatically sorts declarations into the
+correct order, completely eliminating the wearisome and error-prone
+task of sorting declarations by hand.
+</ol>
+<p>
+
+<p>
+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.
+</p>
+
+<a name="H0005"></a>
+<h2>2.0 Running The Makeheaders Program</h2>
+
+<p>
+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:
+<pre>
+ makeheaders *.[ch]
+</pre>
+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.
+</p>
+
+<p>
+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:
+<pre>
+ makeheaders -h *.[ch] >common.h
+</pre>
+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.
+</p>
+
+<p>
+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.
+</p>
+
+<p>
+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 <code>alpha.c</code>
+to be called <code>beta.h</code>.
+In this case, you would invoke makeheaders as follows:
+<pre>
+ makeheaders alpha.c:beta.h
+</pre>
+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.
+</p>
+
+<p>
+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:
+<pre>
+ makeheaders alpha.c beta.c gamma.c:
+</pre>
+In this example, makeheaders will scan the three files named
+“<code>alpha.c</code>”,
+“<code>beta.c</code>” and
+“<code>gamma.c</code>”
+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.
+</p>
+
+<p>
+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 “<code>-f</code>” 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 “<code>mkhdr.dat</code>”
+that contains text like this:
+<pre>
+ src/alpha.c:hdr/alpha.h
+ src/beta.c:hdr/beta.h
+ src/gamma.c:hdr/gamma.h
+ ...
+</pre>
+Then invoke makeheaders as follows:
+<pre>
+ makeheaders -f mkhdr.dat
+</pre>
+</p>
+
+<p>
+The “<code>-local</code>” option causes makeheaders to
+generate of prototypes for “<code>static</code>” functions and
+procedures.
+Such prototypes are normally omitted.
+</p>
+
+<p>
+Finally, makeheaders also includes a “<code>-doc</code>” 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.
+</p>
+
+<p>
+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 “<code>--help</code>” or
+“<code>-?</code>”)
+and it will print a summary of the available options on standard
+error.
+If you need to process a file whose name begins with
+“<code>-</code>”,
+you can prepend a “<code>./</code>” to its name in order to get it
+accepted by the command line parser.
+Or, you can insert the special option “<code>--</code>” on the
+command line to cause all subsequent command line arguments to be treated as
+filenames even if their names begin with “<code>-</code>”.
+</p>
+
+<a name="H0006"></a>
+<h2>3.0 Preparing Source Files For Use With Makeheaders</h2>
+
+<p>
+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.
+</p>
+
+<a name="H0007"></a>
+<h3>3.1 The Basic Setup</h3>
+
+<p>
+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)
+“<code>alpha.c</code>”
+makeheaders will attempt to generate a corresponding header file
+named “<code>alpha.h</code>”.
+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.
+</p>
+
+<p>
+The structure of a .c file intented for use with makeheaders is very
+simple.
+All you have to do is add a single “<code>#include</code>” to the
+top of the file that sources the header file that makeheaders will generate.
+Hence, the beginning of a source file named “<code>alpha.c</code>”
+might look something like this:
+</p>
+
+<pre>
+ /*
+ * Introductory comment...
+ */
+ #include "alpha.h"
+
+ /* The rest of your code... */
+</pre>
+
+<p>
+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
+“<code>#if</code>” 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 “<code>beta.h</code>”,
+many people will habitually write the following:
+
+<pre>
+ #ifndef BETA_H
+ #define BETA_H
+
+ /* declarations for beta.h go here */
+
+ #endif
+</pre>
+
+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 “<code>#if</code>” wrapper serves no useful purpose.
+But it does make makeheaders work harder, forcing it to put
+the statements
+
+<pre>
+ #if !defined(BETA_H)
+ #endif
+</pre>
+
+around every declaration that it copies out of your header file.
+No ill effect should come of this, but neither is there any benefit.
+</p>
+
+<p>
+Having prepared your .c and .h files as described above, you can
+cause makeheaders to generate its .h files using the following simple
+command:
+
+<pre>
+ makeheaders *.[ch]
+</pre>
+
+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.
+</p>
+
+<p>
+Note that
+the wildcard expression used in the above example,
+“<code>*.[ch]</code>”,
+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.
+</p>
+
+<a name="H0008"></a>
+<h3>3.2 What Declarations Get Copied</h3>
+
+<p>
+The following list details all of the code constructs that makeheaders
+will extract and place in
+the automatically generated .h files:
+</p>
+
+<ul>
+<p><li>
+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.</p>
+
+<P>If the “<code>static</code>” keyword of C appears at the
+beginning of the function definition, the prototype is suppressed.
+If you use the “<code>LOCAL</code>” keyword where you would normally
+say “<code>static</code>”, 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
+<code>alpha.c</code> contains the following:
+<pre>
+ LOCAL int testFunc(void){
+ return 0;
+ }
+</pre>
+Then the header file <code>alpha.h</code> will contain
+<pre>
+ #define LOCAL static
+ LOCAL int testFunc(void);
+</pre>
+However, no other generated header files will contain a prototype for
+<code>testFunc()</code> since the function has only file scope.</p>
+
+<p>When the “<code>LOCAL</code>” keyword is used, makeheaders will
+also generate a #define for LOCAL, like this:
+<pre>
+ #define LOCAL static
+</pre>
+so that the C compiler will know what it means.</p>
+
+<p>If you invoke makeheaders with a “<code>-local</code>”
+command-line option, then it treats the “<code>static</code>”
+keyword like “<code>LOCAL</code>” and generates prototypes in the
+header file that corresponds to the source file containing the function
+definition.</p>
+
+<p><li>
+When a global variable is defined in a .c file, an
+“<code>extern</code>”
+declaration of that variable is placed in the header of every
+.c file that uses the variable.
+</p>
+
+<p><li>
+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.
+</p>
+
+<p><li>
+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.
+</p>
+
+<p><li>
+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 “<code>struct</code>”, “<code>union</code>” or
+“<code>enum</code>” qualifier.
+In other words, if makeheaders sees the code:
+<pre>
+ struct Examp { /* ... */ };
+</pre>
+it will automatically generate a corresponding typedef like this:
+<pre>
+ typedef struct Examp Examp;
+</pre>
+</p>
+
+<p><li>
+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.
+</ul>
+
+<p>
+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 “<code>X</code>” requires a
+prior declaration of another structure “<code>Y</code>”, then Y will
+appear first in the generated headers.
+</p>
+
+<a name="H0009"></a>
+<h3>3.3 How To Avoid Having To Write Any Header Files</h3>
+
+<p>
+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.
+</p>
+
+<p>
+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.
+</p>
+
+<p>
+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:
+<pre>
+ #if INTERFACE
+ #endif
+</pre>
+Thus any structure definitions that appear after the
+“<code>#if INTERFACE</code>” but before the corresponding
+“<code>#endif</code>” are eligable to be copied into the
+automatically generated
+.h files of other .c files.
+</p>
+
+<p>
+If you use the “<code>#if INTERFACE</code>” mechanism in a .c file,
+then the generated header for that .c file will contain a line
+like this:
+<pre>
+ #define INTERFACE 0
+</pre>
+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.
+</p>
+
+<p>
+Note that you don't have to use this approach exclusively.
+You can put some declarations in .h files and others within the
+“<code>#if INTERFACE</code>” 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
+“<code>#if INTERFACE</code>” regions as desired.
+</p>
+
+<a name="H0010"></a>
+<h3>3.4 Designating Declarations For Export</h3>
+
+<p>
+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.
+</p>
+
+<p>
+Using makeheaders, it is possible to designate routines and data
+structures as being for “<code>export</code>”.
+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.
+</p>
+
+<p>
+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.
+</p>
+
+<p>
+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 “<code>static</code>” 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: “<code>EXPORT</code>”
+</p>
+
+<p>
+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
+<pre>
+ #define EXPORT
+</pre>
+in the header file it generates for the .c file so that the EXPORT keyword
+will never be seen by the C compiler.
+</p>
+
+<p>
+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
+<pre>
+ #if INTERFACE
+ #endif
+</pre>
+are visible to all files within the library, any declarations
+or definitions within
+<pre>
+ #if EXPORT_INTERFACE
+ #endif
+</pre>
+will become part of the exported interface.
+The “<code>#if EXPORT_INTERFACE</code>” mechanism can be used in
+either .c or .h files.
+(The “<code>#if INTERFACE</code>” 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.)
+</p>
+
+<a name="H0011"></a>
+<h3>3.5 Local declarations processed by makeheaders</h3>
+
+<p>
+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 “<code>private</code>”.
+</p>
+
+<p>
+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.
+</p>
+
+<p>
+When this situation arises, enclose the private declarations
+within
+<pre>
+ #if LOCAL_INTERFACE
+ #endif
+</pre>
+A “<code>LOCAL_INTERFACE</code>” block works very much like the
+“<code>INTERFACE</code>” and
+“<code>EXPORT_INTERFACE</code>”
+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.
+</p>
+
+<a name="H0012"></a>
+<h3>3.6 Using Makeheaders With C++ Code</h3>
+
+<p>
+You can use makeheaders to generate header files for C++ code, in
+addition to C.
+Makeheaders will recognize and copy both “<code>class</code>”
+declarations
+and inline function definitions, and it knows not to try to generate
+prototypes for methods.
+</p>
+
+<p>
+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
+<pre>
+ extern "C"
+</pre>
+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.)
+</p>
+
+<p>
+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 “<code>alpha.cpp</code>” will induce makeheaders to
+generate a header file named “<code>alpha.hpp</code>”.
+</p>
+
+<p>
+Makeheaders augments class definitions by inserting prototypes to
+methods where appropriate. If a method definition begins with one
+of the special keywords <b>PUBLIC</b>, <b>PROTECTED</b>, or
+<b>PRIVATE</b> (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.
+</p>
+
+<blockquote><pre>
+#if INTERFACE
+class Example1 {
+private:
+ int v1;
+};
+#endif
+PUBLIC Example1::Example1(){
+ v1 = 0;
+}
+</pre></blockquote>
+
+<p>
+The code above is equivalent to the following:
+</p>
+
+<blockquote><pre>
+#if INTERFACE
+class Example1 {
+private:
+ int v1;
+public:
+ Example1();
+};
+#endif
+Example1::Example1(){
+ v1 = 0;
+}
+</pre></blockquote>
+
+<p>
+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.
+</p>
+
+<h4>3.6.1 C++ Limitations</h4>
+
+<p>
+Makeheaders does not understand more recent
+C++ syntax such as templates and namespaces.
+Perhaps these issues will be addressed in future revisions.
+</p>
+
+<a name="H0013"></a>
+<h3>3.7 Conditional Compilation</h3>
+
+<p>
+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
+<pre>
+ #ifdef UNIX
+ # define WORKS_WELL 1
+ #else
+ # define WORKS_WELL 0
+ #endif
+</pre>
+then the next patch of code will appear in the generated header for
+every .c file that uses the WORKS_WELL constant:
+<pre>
+ #if defined(UNIX)
+ # define WORKS_WELL 1
+ #endif
+ #if !defined(UNIX)
+ # define WORKS_WELL 0
+ #endif
+</pre>
+The conditional compilation constructs can be nested to any depth.
+Makeheaders also recognizes the special case of
+<pre>
+ #if 0
+ #endif
+</pre>
+and treats the enclosed text as a comment.
+</p>
+
+<a name="H0014"></a>
+<h3>3.8 Caveats</h3>
+
+<p>
+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.
+</p>
+
+<p>
+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.
+</p>
+
+<p>
+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:
+<pre>
+ int a = 4, b = 5;
+</pre>
+The makeheaders program wants every variable to have its own
+definition. Like this:
+<pre>
+ int a = 4;
+ int b = 5;
+</pre>
+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.
+</p>
+
+<p>
+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:
+<pre>
+struct {int field;} a;
+struct Tag {int field;} b;
+struct Tag c;
+</pre>
+Instead, define types separately from variables:
+<pre>
+#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. */
+</pre>
+See <a href="#H0008">3.2 What Declarations Get Copied</a> for details,
+including on the automatic typedef.
+</p>
+
+<p>
+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:
+<pre>
+ #define BEGIN {
+ #define END }
+</pre>
+at the beginning of your source file, and then try to create a function
+definition like this:
+<pre>
+ char *StrDup(const char *zSrc)
+ BEGIN
+ /* Code here */
+ END
+</pre>
+then makeheaders won't be able to find the end of the function definition
+and bad things are likely to happen.
+</p>
+
+<p>
+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.
+</p>
+
+<p>
+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. “<code>enum {X, Y, Z};</code>”.
+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.
+</p>
+
+<a name="H0015"></a>
+<h2>4.0 Using Makeheaders To Generate Documentation</h2>
+
+<p>
+Many people have observed the advantages of generating program
+documentation directly from the source code:
+<ul>
+<li> Less effort is involved. It is easier to write a program than
+ it is to write a program and a document.
+<li> 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.
+<li> 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.
+</ul>
+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.
+</p>
+
+<p>
+When makeheaders is run with the “<code>-doc</code>” 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.
+</p>
+
+<p>
+The “<code>-doc</code>” option causes makeheaders to print
+information to standard output about all of the following objects:
+<ul>
+<li> C++ class declarations
+<li> Structure and union declarations
+<li> Enumerations
+<li> Typedefs
+<li> Procedure and function definitions
+<li> Global variables
+<li> Preprocessor macros (ex: “<code>#define</code>”)
+</ul>
+For each of these objects, the following information is output:
+<ul>
+<li> The name of the object.
+<li> The type of the object. (Structure, typedef, macro, etc.)
+<li> Flags to indicate if the declaration is exported (contained within
+ an EXPORT_INTERFACE block) or local (contained with LOCAL_INTERFACE).
+<li> A flag to indicate if the object is declared in a C++ file.
+<li> The name of the file in which the object was declared.
+<li> The complete text of any block comment that preceeds the declarations.
+<li> If the declaration occurred inside a preprocessor conditional
+ (“<code>#if</code>”) then the text of that conditional is
+ provided.
+<li> The complete text of a declaration for the object.
+</ul>
+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.
+</p>
+
+<a name="H0016"></a>
+<h2>5.0 Compiling The Makeheaders Program</h2>
+
+<p>
+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.
+</p>
+
+<a name="H0017"></a>
+<h2>6.0 History</h2>
+
+<p>
+The makeheaders program was first written by D. Richard Hipp
+(also the original author of
+<a href="https://sqlite.org/">SQLite</a> and
+<a href="https://www.fossil-scm.org/">Fossil</a>) 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.
+</p>
+
+<p>
+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.
+</p>
+
+<a name="H0018"></a>
+<h2>7.0 Summary And Conclusion</h2>
+
+<p>
+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.
+</p>
+
+<p>
+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.
+</p>
+</body>
+</html>
--- /dev/null
+#
+
+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
+
+
--- /dev/null
+
+(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 "<f9> 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)
--- /dev/null
+
+#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 $<
+
--- /dev/null
+
+# 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 <base>.{lib,cli}.{c,cc} and the {.c,.cc} output
+# tranche must be named <base>.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 $<
+
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<base href="https://fossil-scm.org/fossil/doc/8cecc544/src/makeheaders.c" />
+<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: ; script-src 'self' 'nonce-e2f4709a3deeafd8212401c84e2a082e1fbd3068470984f2' ; style-src 'self' 'unsafe-inline'" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Fossil: File Content</title>
+<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="/fossil/timeline.rss" />
+<link rel="stylesheet" href="/fossil/style.css?id=9eb7de39" type="text/css" media="screen" />
+</head>
+<body>
+<div class="header">\r
+ <div class="title"><h1>Fossil</h1>File Content</div>\r
+ <div class="status"><a href='/fossil/login'>Login</a>
+</div>\r
+</div>\r
+<div class="mainmenu">\r
+<a id='hbbtn' href='#'>☰</a><a href='/fossil/doc/trunk/www/index.wiki' class=''>Home</a>
+<a href='/fossil/timeline' class=''>Timeline</a>
+<a href='/fossil/doc/trunk/www/permutedindex.html' class=''>Docs</a>
+<a href='https://fossil-scm.org/forum'>Forum</a><a href='/fossil/uv/download.html' class='desktoponly'>Download</a>
+</div>\r
+<div id='hbdrop'></div>\r
+<form id='f01' method='GET' action='/fossil/file'>
+<input type='hidden' name='udc' value='1'>
+<div class="submenu">
+<a class="label" href="/fossil/artifact/49c76a69">Artifact</a>
+<a class="label" href="/fossil/timeline?n=200&uf=49c76a6973d579ff0b346e5f73182fa72dd797cbb07e8b20612849dc2adef85d">Check-ins Using</a>
+<a class="label" href="/fossil/raw/src/makeheaders.c?name=49c76a6973d579ff0b346e5f73182fa72dd797cbb07e8b20612849dc2adef85d">Download</a>
+<a class="label" href="/fossil/hexdump?name=49c76a6973d579ff0b346e5f73182fa72dd797cbb07e8b20612849dc2adef85d">Hex</a>
+<label class='submenuctrl submenuckbox'><input type='checkbox' name='ln' id='submenuctrl-0' >Line Numbers</label>
+</div>
+<input type="hidden" name="name" value="src/makeheaders.c">
+</form>
+<script src='/fossil/builtin/menu.js?id=6f60cb38'></script>
+<div class="content">
+<h2>Latest version of file 'src/makeheaders.c':</h2>
+<ul>
+<li>File
+<a data-href='/fossil/finfo?name=src/makeheaders.c&m=49c76a6973d579ff' href='/fossil/honeypot'>src/makeheaders.c</a>
+— part of check-in
+<span class="timelineHistDsp">[8cecc544]</span>
+at
+2018-11-02 15:21:54
+on branch <a data-href='/fossil/timeline?r=trunk' href='/fossil/honeypot'>trunk</a>
+— 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)
+<a data-href='/fossil/whatis/49c76a6973d579ff' href='/fossil/honeypot'>[more...]</a>
+</ul>
+<hr />
+<blockquote>
+<pre>
+/*
+** 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
+
+</pre>
+</blockquote>
+</div>
+<div class="footer">\r
+This page was generated in about\r
+0.007s by\r
+Fossil 2.9 [6f60cb3881] 2019-02-27 14:34:30\r
+</div>\r
+<script nonce="e2f4709a3deeafd8212401c84e2a082e1fbd3068470984f2">\r
+/*\r
+** Copyright © 2018 Warren Young\r
+**\r
+** This program is free software; you can redistribute it and/or\r
+** modify it under the terms of the Simplified BSD License (also\r
+** known as the "2-Clause License" or "FreeBSD License".)\r
+**\r
+** This program is distributed in the hope that it will be useful,\r
+** but without any warranty; without even the implied warranty of\r
+** merchantability or fitness for a particular purpose.\r
+**\r
+** Contact: wyoung on the Fossil forum, https://fossil-scm.org/forum/\r
+**\r
+*******************************************************************************\r
+**\r
+** This file contains the JS code specific to the Fossil default skin.\r
+** Currently, the only thing this does is handle clicks on its hamburger\r
+** menu button.\r
+*/\r
+(function() {\r
+ var hbButton = document.getElementById("hbbtn");\r
+ if (!hbButton) return; // no hamburger button\r
+ if (!document.addEventListener) {\r
+ // Turn the button into a link to the sitemap for incompatible browsers.\r
+ hbButton.href = "/fossil/sitemap";\r
+ return;\r
+ }\r
+ var panel = document.getElementById("hbdrop");\r
+ if (!panel) return; // site admin might've nuked it\r
+ if (!panel.style) return; // shouldn't happen, but be sure\r
+ var panelBorder = panel.style.border;\r
+ var panelInitialized = false; // reset if browser window is resized\r
+ var panelResetBorderTimerID = 0; // used to cancel post-animation tasks\r
+\r
+ // Disable animation if this browser doesn't support CSS transitions.\r
+ //\r
+ // We need this ugly calling form for old browsers that don't allow\r
+ // panel.style.hasOwnProperty('transition'); catering to old browsers\r
+ // is the whole point here.\r
+ var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");\r
+\r
+ // The duration of the animation can be overridden from the default skin\r
+ // header.txt by setting the "data-anim-ms" attribute of the panel.\r
+ var animMS = panel.getAttribute("data-anim-ms");\r
+ if (animMS) { // not null or empty string, parse it\r
+ animMS = parseInt(animMS);\r
+ if (isNaN(animMS) || animMS == 0)\r
+ animate = false; // disable animation if non-numeric or zero\r
+ else if (animMS < 0)\r
+ animMS = 400; // set default animation duration if negative\r
+ }\r
+ else // attribute is null or empty string, use default\r
+ animMS = 400;\r
+\r
+ // Calculate panel height despite its being hidden at call time.\r
+ // Based on https://stackoverflow.com/a/29047447/142454\r
+ var panelHeight; // computed on first panel display\r
+ function calculatePanelHeight() {\r
+\r
+ // Clear the max-height CSS property in case the panel size is recalculated\r
+ // after the browser window was resized.\r
+ panel.style.maxHeight = '';\r
+\r
+ // Get initial panel styles so we can restore them below.\r
+ var es = window.getComputedStyle(panel),\r
+ edis = es.display,\r
+ epos = es.position,\r
+ evis = es.visibility;\r
+\r
+ // Restyle the panel so we can measure its height while invisible.\r
+ panel.style.visibility = 'hidden';\r
+ panel.style.position = 'absolute';\r
+ panel.style.display = 'block';\r
+ panelHeight = panel.offsetHeight + 'px';\r
+\r
+ // Revert styles now that job is done.\r
+ panel.style.display = edis;\r
+ panel.style.position = epos;\r
+ panel.style.visibility = evis;\r
+ }\r
+\r
+ // Show the panel by changing the panel height, which kicks off the\r
+ // slide-open/closed transition set up in the XHR onload handler.\r
+ //\r
+ // Schedule the change for a near-future time in case this is the\r
+ // first call, where the div was initially invisible. If we were\r
+ // to change the panel's visibility and height at the same time\r
+ // instead, that would prevent the browser from seeing the height\r
+ // change as a state transition, so it'd skip the CSS transition:\r
+ //\r
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples\r
+ function showPanel() {\r
+ // Cancel the timer to remove the panel border after the closing animation,\r
+ // otherwise double-clicking the hamburger button with the panel opened will\r
+ // remove the borders from the (closed and immediately reopened) panel.\r
+ if (panelResetBorderTimerID) {\r
+ clearTimeout(panelResetBorderTimerID);\r
+ panelResetBorderTimerID = 0;\r
+ }\r
+ if (animate) {\r
+ if (!panelInitialized) {\r
+ panelInitialized = true;\r
+ // Set up a CSS transition to animate the panel open and\r
+ // closed. Only needs to be done once per page load.\r
+ // Based on https://stackoverflow.com/a/29047447/142454\r
+ calculatePanelHeight();\r
+ panel.style.transition = 'max-height ' + animMS +\r
+ 'ms ease-in-out';\r
+ panel.style.overflowY = 'hidden';\r
+ panel.style.maxHeight = '0';\r
+ }\r
+ setTimeout(function() {\r
+ panel.style.maxHeight = panelHeight;\r
+ panel.style.border = panelBorder;\r
+ }, 40); // 25ms is insufficient with Firefox 62\r
+ }\r
+ panel.style.display = 'block';\r
+ document.addEventListener('keydown',panelKeydown,/* useCapture == */true);\r
+ document.addEventListener('click',panelClick,false);\r
+ }\r
+\r
+ var panelKeydown = function(event) {\r
+ var key = event.which || event.keyCode;\r
+ if (key == 27) {\r
+ event.stopPropagation(); // ignore other keydown handlers\r
+ panelToggle(true);\r
+ }\r
+ };\r
+\r
+ var panelClick = function(event) {\r
+ if (!panel.contains(event.target)) {\r
+ // Call event.preventDefault() to have clicks outside the opened panel\r
+ // just close the panel, and swallow clicks on links or form elements.\r
+ //event.preventDefault();\r
+ panelToggle(true);\r
+ }\r
+ };\r
+\r
+ // Return true if the panel is showing.\r
+ function panelShowing() {\r
+ if (animate) {\r
+ return panel.style.maxHeight == panelHeight;\r
+ }\r
+ else {\r
+ return panel.style.display == 'block';\r
+ }\r
+ }\r
+\r
+ // Check if the specified HTML element has any child elements. Note that plain\r
+ // text nodes, comments, and any spaces (presentational or not) are ignored.\r
+ function hasChildren(element) {\r
+ var childElement = element.firstChild;\r
+ while (childElement) {\r
+ if (childElement.nodeType == 1) // Node.ELEMENT_NODE == 1\r
+ return true;\r
+ childElement = childElement.nextSibling;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ // Reset the state of the panel to uninitialized if the browser window is\r
+ // resized, so the dimensions are recalculated the next time it's opened.\r
+ window.addEventListener('resize',function(event) {\r
+ panelInitialized = false;\r
+ },false);\r
+\r
+ // Click handler for the hamburger button.\r
+ hbButton.addEventListener('click',function(event) {\r
+ // Break the event handler chain, or the handler for document → click\r
+ // (about to be installed) may already be triggered by the current event.\r
+ event.stopPropagation();\r
+ event.preventDefault(); // prevent browser from acting on <a> click\r
+ panelToggle(false);\r
+ },false);\r
+\r
+ function panelToggle(suppressAnimation) {\r
+ if (panelShowing()) {\r
+ document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);\r
+ document.removeEventListener('click',panelClick,false);\r
+ // Transition back to hidden state.\r
+ if (animate) {\r
+ if (suppressAnimation) {\r
+ var transition = panel.style.transition;\r
+ panel.style.transition = '';\r
+ panel.style.maxHeight = '0';\r
+ panel.style.border = 'none';\r
+ setTimeout(function() {\r
+ // Make sure CSS transition won't take effect now, so restore it\r
+ // asynchronously. Outer variable 'transition' still valid here.\r
+ panel.style.transition = transition;\r
+ }, 40); // 25ms is insufficient with Firefox 62\r
+ }\r
+ else {\r
+ panel.style.maxHeight = '0';\r
+ panelResetBorderTimerID = setTimeout(function() {\r
+ // Browsers show a 1px high border line when maxHeight == 0,\r
+ // our "hidden" state, so hide the borders in that state, too.\r
+ panel.style.border = 'none';\r
+ panelResetBorderTimerID = 0; // clear ID of completed timer\r
+ }, animMS);\r
+ }\r
+ }\r
+ else {\r
+ panel.style.display = 'none';\r
+ }\r
+ }\r
+ else {\r
+ if (!hasChildren(panel)) {\r
+ // Only get the sitemap once per page load: it isn't likely to\r
+ // change on us.\r
+ var xhr = new XMLHttpRequest();\r
+ xhr.onload = function() {\r
+ var doc = xhr.responseXML;\r
+ if (doc) {\r
+ var sm = doc.querySelector("ul#sitemap");\r
+ if (sm && xhr.status == 200) {\r
+ // Got sitemap. Insert it into the drop-down panel.\r
+ panel.innerHTML = sm.outerHTML;\r
+ // Display the panel\r
+ showPanel();\r
+ }\r
+ }\r
+ // else, can't parse response as HTML or XML\r
+ }\r
+ xhr.open("GET", "/fossil/sitemap?popup"); // note the TH1 substitution!\r
+ xhr.responseType = "document";\r
+ xhr.send();\r
+ }\r
+ else {\r
+ showPanel(); // just show what we built above\r
+ }\r
+ }\r
+ }\r
+})();\r
+\r
+</script>\r
+<script id='href-data' type='application/json'>{"delay":10,"mouseover":0}</script>
+<script nonce="e2f4709a3deeafd8212401c84e2a082e1fbd3068470984f2">
+function setAllHrefs(){
+var anchors = document.getElementsByTagName("a");
+for(var i=0; i<anchors.length; i++){
+var j = anchors[i];
+if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
+}
+var forms = document.getElementsByTagName("form");
+for(var i=0; i<forms.length; i++){
+var j = forms[i];
+if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
+}
+}
+function antiRobotDefense(){
+var x = document.getElementById("href-data");
+var jx = x.textContent || x.innerText;
+var g = JSON.parse(jx);
+var isOperaMini =
+Object.prototype.toString.call(window.operamini)==="[object OperaMini]";
+if(g.mouseover && !isOperaMini){
+document.getElementByTagName("body")[0].onmousemove=function(){
+setTimeout(setAllHrefs, g.delay);
+}
+}else{
+setTimeout(setAllHrefs, g.delay);
+}
+}
+antiRobotDefense()
+</script>
+</body>
+</html>
+++ /dev/null
-#!/bin/bash
-set -x
-make dist-clean
-git add "$@"
-
+++ /dev/null
-#!/bin/bash
-chown root "$@" && \
-chmod u+rsx,u-w,go+rx-s-w "$@"
-
+++ /dev/null
-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
-#
-#
-#
+++ /dev/null
-
-This worked to force the include to be part of the interface:
-
-#if INTERFACE
-#include <sqlite3.h>
-#endif
-
-But this did not:
-
-#if INTERFACE
- #include <sqlite3.h>
-#endif
-
-makeheaders looks to be sensitive to indentation
-
-
+++ /dev/null
-<html>
-<head><title>The Makeheaders Program</title></head>
-<body bgcolor=white>
-<h1 align=center>The Makeheaders Program</h1>
-
-
-<p>
-This document describes <em>makeheaders</em>,
-a tool that automatically generates “<code>.h</code>”
-files for a C or C++ programming project.
-</p>
-
-
-<h2>Table Of Contents</h2>
-
-<ul>
-<li><a href="#H0002">1,0 Background</a>
-<ul>
-<li><a href="#H0003">1.1 Problems With The Traditional Approach</a>
-
-<li><a href="#H0004">1.2 The Makeheaders Solution</a>
-</ul>
-<li><a href="#H0005">2.0 Running The Makeheaders Program</a>
-
-<li><a href="#H0006">3.0 Preparing Source Files For Use With Makeheaders</a>
-<ul>
-<li><a href="#H0007">3.1 The Basic Setup</a>
-
-<li><a href="#H0008">3.2 What Declarations Get Copied</a>
-
-<li><a href="#H0009">3.3 How To Avoid Having To Write Any Header Files</a>
-
-<li><a href="#H0010">3.4 Designating Declarations For Export</a>
-
-<li><a href="#H0011">3.5 Local declarations processed by makeheaders</a>
-
-<li><a href="#H0012">3.6 Using Makeheaders With C++ Code</a>
-
-<li><a href="#H0013">3.7 Conditional Compilation</a>
-
-<li><a href="#H0014">3.8 Caveats</a>
-</ul>
-<li><a href="#H0015">4.0 Using Makeheaders To Generate Documentation</a>
-
-<li><a href="#H0016">5.0 Compiling The Makeheaders Program</a>
-
-<li><a href="#H0017">6.0 History</a>
-
-<li><a href="#H0018">7.0 Summary And Conclusion</a>
-</ul><a name="H0002"></a>
-<h2>1.0 Background</h2>
-
-<p>
-A piece of C source code can be one of two things:
-a <em>declaration</em> or a <em>definition</em>.
-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.)
-</p>
-
-<p>
-Declarations in C include things such as the following:
-<ul>
-<li> Typedefs.
-<li> Structure, union and enumeration declarations.
-<li> Function and procedure prototypes.
-<li> Preprocessor macros and #defines.
-<li> “<code>extern</code>” variable declarations.
-</ul>
-</p>
-
-<p>
-Definitions in C, on the other hand, include these kinds of things:
-<ul>
-<li> Variable definitions.
-<li> The bodies of functions and procedures.
-<li> Initialization data.
-</ul>
-</p>
-
-<p>
-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 <em>interface</em> and the definition is the <em>implementation</em>.
-</p>
-
-<p>
-In C programs, it has always been the tradition that declarations are
-put in files with the “<code>.h</code>” suffix and definitions are
-placed in “<code>.c</code>” files.
-The .c files contain “<code>#include</code>” 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.
-</p>
-
-<a name="H0003"></a>
-<h3>1.1 Problems With The Traditional Approach</h3>
-
-<p>
-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:
-</p>
-
-<p>
-<ol>
-<p><li>
-In large codes with many source files, it becomes difficult to determine
-which .h files should be included in which .c files.
-<p><li>
-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.
-<p><li>
-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.
-<p><li>
-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.
-<p><li>
-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.
-</ol>
-</p>
-
-<a name="H0004"></a>
-<h3>1.2 The Makeheaders Solution</h3>
-
-<p>
-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.
-</p>
-
-<p>
-The makeheaders programming model overcomes all of the objections to the
-traditional C programming model.
-<ol>
-<p><li>
-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
-<code>alpha.c</code> then it must include only the single .h file
-named <code>alpha.h</code>.
-(The .c file might also use some include files from the standard
-library, such as <code><stdio.h></code>, but that is another matter.)
-<p><li>
-The generated .h files do not include other .h files, and so there
-are no include chains to worry about.
-The file <code>alpha.c</code> depends on <code>alpha.h</code> and
-nothing more.
-<p><li>
-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.
-<p><li>
-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.
-<p><li>
-The makeheaders program automatically sorts declarations into the
-correct order, completely eliminating the wearisome and error-prone
-task of sorting declarations by hand.
-</ol>
-<p>
-
-<p>
-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.
-</p>
-
-<a name="H0005"></a>
-<h2>2.0 Running The Makeheaders Program</h2>
-
-<p>
-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:
-<pre>
- makeheaders *.[ch]
-</pre>
-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.
-</p>
-
-<p>
-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:
-<pre>
- makeheaders -h *.[ch] >common.h
-</pre>
-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.
-</p>
-
-<p>
-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.
-</p>
-
-<p>
-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 <code>alpha.c</code>
-to be called <code>beta.h</code>.
-In this case, you would invoke makeheaders as follows:
-<pre>
- makeheaders alpha.c:beta.h
-</pre>
-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.
-</p>
-
-<p>
-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:
-<pre>
- makeheaders alpha.c beta.c gamma.c:
-</pre>
-In this example, makeheaders will scan the three files named
-“<code>alpha.c</code>”,
-“<code>beta.c</code>” and
-“<code>gamma.c</code>”
-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.
-</p>
-
-<p>
-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 “<code>-f</code>” 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 “<code>mkhdr.dat</code>”
-that contains text like this:
-<pre>
- src/alpha.c:hdr/alpha.h
- src/beta.c:hdr/beta.h
- src/gamma.c:hdr/gamma.h
- ...
-</pre>
-Then invoke makeheaders as follows:
-<pre>
- makeheaders -f mkhdr.dat
-</pre>
-</p>
-
-<p>
-The “<code>-local</code>” option causes makeheaders to
-generate of prototypes for “<code>static</code>” functions and
-procedures.
-Such prototypes are normally omitted.
-</p>
-
-<p>
-Finally, makeheaders also includes a “<code>-doc</code>” 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.
-</p>
-
-<p>
-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 “<code>--help</code>” or
-“<code>-?</code>”)
-and it will print a summary of the available options on standard
-error.
-If you need to process a file whose name begins with
-“<code>-</code>”,
-you can prepend a “<code>./</code>” to its name in order to get it
-accepted by the command line parser.
-Or, you can insert the special option “<code>--</code>” on the
-command line to cause all subsequent command line arguments to be treated as
-filenames even if their names begin with “<code>-</code>”.
-</p>
-
-<a name="H0006"></a>
-<h2>3.0 Preparing Source Files For Use With Makeheaders</h2>
-
-<p>
-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.
-</p>
-
-<a name="H0007"></a>
-<h3>3.1 The Basic Setup</h3>
-
-<p>
-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)
-“<code>alpha.c</code>”
-makeheaders will attempt to generate a corresponding header file
-named “<code>alpha.h</code>”.
-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.
-</p>
-
-<p>
-The structure of a .c file intented for use with makeheaders is very
-simple.
-All you have to do is add a single “<code>#include</code>” to the
-top of the file that sources the header file that makeheaders will generate.
-Hence, the beginning of a source file named “<code>alpha.c</code>”
-might look something like this:
-</p>
-
-<pre>
- /*
- * Introductory comment...
- */
- #include "alpha.h"
-
- /* The rest of your code... */
-</pre>
-
-<p>
-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
-“<code>#if</code>” 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 “<code>beta.h</code>”,
-many people will habitually write the following:
-
-<pre>
- #ifndef BETA_H
- #define BETA_H
-
- /* declarations for beta.h go here */
-
- #endif
-</pre>
-
-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 “<code>#if</code>” wrapper serves no useful purpose.
-But it does make makeheaders work harder, forcing it to put
-the statements
-
-<pre>
- #if !defined(BETA_H)
- #endif
-</pre>
-
-around every declaration that it copies out of your header file.
-No ill effect should come of this, but neither is there any benefit.
-</p>
-
-<p>
-Having prepared your .c and .h files as described above, you can
-cause makeheaders to generate its .h files using the following simple
-command:
-
-<pre>
- makeheaders *.[ch]
-</pre>
-
-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.
-</p>
-
-<p>
-Note that
-the wildcard expression used in the above example,
-“<code>*.[ch]</code>”,
-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.
-</p>
-
-<a name="H0008"></a>
-<h3>3.2 What Declarations Get Copied</h3>
-
-<p>
-The following list details all of the code constructs that makeheaders
-will extract and place in
-the automatically generated .h files:
-</p>
-
-<ul>
-<p><li>
-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.</p>
-
-<P>If the “<code>static</code>” keyword of C appears at the
-beginning of the function definition, the prototype is suppressed.
-If you use the “<code>LOCAL</code>” keyword where you would normally
-say “<code>static</code>”, 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
-<code>alpha.c</code> contains the following:
-<pre>
- LOCAL int testFunc(void){
- return 0;
- }
-</pre>
-Then the header file <code>alpha.h</code> will contain
-<pre>
- #define LOCAL static
- LOCAL int testFunc(void);
-</pre>
-However, no other generated header files will contain a prototype for
-<code>testFunc()</code> since the function has only file scope.</p>
-
-<p>When the “<code>LOCAL</code>” keyword is used, makeheaders will
-also generate a #define for LOCAL, like this:
-<pre>
- #define LOCAL static
-</pre>
-so that the C compiler will know what it means.</p>
-
-<p>If you invoke makeheaders with a “<code>-local</code>”
-command-line option, then it treats the “<code>static</code>”
-keyword like “<code>LOCAL</code>” and generates prototypes in the
-header file that corresponds to the source file containing the function
-definition.</p>
-
-<p><li>
-When a global variable is defined in a .c file, an
-“<code>extern</code>”
-declaration of that variable is placed in the header of every
-.c file that uses the variable.
-</p>
-
-<p><li>
-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.
-</p>
-
-<p><li>
-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.
-</p>
-
-<p><li>
-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 “<code>struct</code>”, “<code>union</code>” or
-“<code>enum</code>” qualifier.
-In other words, if makeheaders sees the code:
-<pre>
- struct Examp { /* ... */ };
-</pre>
-it will automatically generate a corresponding typedef like this:
-<pre>
- typedef struct Examp Examp;
-</pre>
-</p>
-
-<p><li>
-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.
-</ul>
-
-<p>
-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 “<code>X</code>” requires a
-prior declaration of another structure “<code>Y</code>”, then Y will
-appear first in the generated headers.
-</p>
-
-<a name="H0009"></a>
-<h3>3.3 How To Avoid Having To Write Any Header Files</h3>
-
-<p>
-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.
-</p>
-
-<p>
-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.
-</p>
-
-<p>
-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:
-<pre>
- #if INTERFACE
- #endif
-</pre>
-Thus any structure definitions that appear after the
-“<code>#if INTERFACE</code>” but before the corresponding
-“<code>#endif</code>” are eligable to be copied into the
-automatically generated
-.h files of other .c files.
-</p>
-
-<p>
-If you use the “<code>#if INTERFACE</code>” mechanism in a .c file,
-then the generated header for that .c file will contain a line
-like this:
-<pre>
- #define INTERFACE 0
-</pre>
-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.
-</p>
-
-<p>
-Note that you don't have to use this approach exclusively.
-You can put some declarations in .h files and others within the
-“<code>#if INTERFACE</code>” 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
-“<code>#if INTERFACE</code>” regions as desired.
-</p>
-
-<a name="H0010"></a>
-<h3>3.4 Designating Declarations For Export</h3>
-
-<p>
-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.
-</p>
-
-<p>
-Using makeheaders, it is possible to designate routines and data
-structures as being for “<code>export</code>”.
-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.
-</p>
-
-<p>
-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.
-</p>
-
-<p>
-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 “<code>static</code>” 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: “<code>EXPORT</code>”
-</p>
-
-<p>
-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
-<pre>
- #define EXPORT
-</pre>
-in the header file it generates for the .c file so that the EXPORT keyword
-will never be seen by the C compiler.
-</p>
-
-<p>
-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
-<pre>
- #if INTERFACE
- #endif
-</pre>
-are visible to all files within the library, any declarations
-or definitions within
-<pre>
- #if EXPORT_INTERFACE
- #endif
-</pre>
-will become part of the exported interface.
-The “<code>#if EXPORT_INTERFACE</code>” mechanism can be used in
-either .c or .h files.
-(The “<code>#if INTERFACE</code>” 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.)
-</p>
-
-<a name="H0011"></a>
-<h3>3.5 Local declarations processed by makeheaders</h3>
-
-<p>
-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 “<code>private</code>”.
-</p>
-
-<p>
-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.
-</p>
-
-<p>
-When this situation arises, enclose the private declarations
-within
-<pre>
- #if LOCAL_INTERFACE
- #endif
-</pre>
-A “<code>LOCAL_INTERFACE</code>” block works very much like the
-“<code>INTERFACE</code>” and
-“<code>EXPORT_INTERFACE</code>”
-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.
-</p>
-
-<a name="H0012"></a>
-<h3>3.6 Using Makeheaders With C++ Code</h3>
-
-<p>
-You can use makeheaders to generate header files for C++ code, in
-addition to C.
-Makeheaders will recognize and copy both “<code>class</code>”
-declarations
-and inline function definitions, and it knows not to try to generate
-prototypes for methods.
-</p>
-
-<p>
-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
-<pre>
- extern "C"
-</pre>
-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.)
-</p>
-
-<p>
-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 “<code>alpha.cpp</code>” will induce makeheaders to
-generate a header file named “<code>alpha.hpp</code>”.
-</p>
-
-<p>
-Makeheaders augments class definitions by inserting prototypes to
-methods where appropriate. If a method definition begins with one
-of the special keywords <b>PUBLIC</b>, <b>PROTECTED</b>, or
-<b>PRIVATE</b> (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.
-</p>
-
-<blockquote><pre>
-#if INTERFACE
-class Example1 {
-private:
- int v1;
-};
-#endif
-PUBLIC Example1::Example1(){
- v1 = 0;
-}
-</pre></blockquote>
-
-<p>
-The code above is equivalent to the following:
-</p>
-
-<blockquote><pre>
-#if INTERFACE
-class Example1 {
-private:
- int v1;
-public:
- Example1();
-};
-#endif
-Example1::Example1(){
- v1 = 0;
-}
-</pre></blockquote>
-
-<p>
-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.
-</p>
-
-<h4>3.6.1 C++ Limitations</h4>
-
-<p>
-Makeheaders does not understand more recent
-C++ syntax such as templates and namespaces.
-Perhaps these issues will be addressed in future revisions.
-</p>
-
-<a name="H0013"></a>
-<h3>3.7 Conditional Compilation</h3>
-
-<p>
-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
-<pre>
- #ifdef UNIX
- # define WORKS_WELL 1
- #else
- # define WORKS_WELL 0
- #endif
-</pre>
-then the next patch of code will appear in the generated header for
-every .c file that uses the WORKS_WELL constant:
-<pre>
- #if defined(UNIX)
- # define WORKS_WELL 1
- #endif
- #if !defined(UNIX)
- # define WORKS_WELL 0
- #endif
-</pre>
-The conditional compilation constructs can be nested to any depth.
-Makeheaders also recognizes the special case of
-<pre>
- #if 0
- #endif
-</pre>
-and treats the enclosed text as a comment.
-</p>
-
-<a name="H0014"></a>
-<h3>3.8 Caveats</h3>
-
-<p>
-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.
-</p>
-
-<p>
-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.
-</p>
-
-<p>
-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:
-<pre>
- int a = 4, b = 5;
-</pre>
-The makeheaders program wants every variable to have its own
-definition. Like this:
-<pre>
- int a = 4;
- int b = 5;
-</pre>
-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.
-</p>
-
-<p>
-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:
-<pre>
-struct {int field;} a;
-struct Tag {int field;} b;
-struct Tag c;
-</pre>
-Instead, define types separately from variables:
-<pre>
-#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. */
-</pre>
-See <a href="#H0008">3.2 What Declarations Get Copied</a> for details,
-including on the automatic typedef.
-</p>
-
-<p>
-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:
-<pre>
- #define BEGIN {
- #define END }
-</pre>
-at the beginning of your source file, and then try to create a function
-definition like this:
-<pre>
- char *StrDup(const char *zSrc)
- BEGIN
- /* Code here */
- END
-</pre>
-then makeheaders won't be able to find the end of the function definition
-and bad things are likely to happen.
-</p>
-
-<p>
-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.
-</p>
-
-<p>
-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. “<code>enum {X, Y, Z};</code>”.
-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.
-</p>
-
-<a name="H0015"></a>
-<h2>4.0 Using Makeheaders To Generate Documentation</h2>
-
-<p>
-Many people have observed the advantages of generating program
-documentation directly from the source code:
-<ul>
-<li> Less effort is involved. It is easier to write a program than
- it is to write a program and a document.
-<li> 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.
-<li> 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.
-</ul>
-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.
-</p>
-
-<p>
-When makeheaders is run with the “<code>-doc</code>” 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.
-</p>
-
-<p>
-The “<code>-doc</code>” option causes makeheaders to print
-information to standard output about all of the following objects:
-<ul>
-<li> C++ class declarations
-<li> Structure and union declarations
-<li> Enumerations
-<li> Typedefs
-<li> Procedure and function definitions
-<li> Global variables
-<li> Preprocessor macros (ex: “<code>#define</code>”)
-</ul>
-For each of these objects, the following information is output:
-<ul>
-<li> The name of the object.
-<li> The type of the object. (Structure, typedef, macro, etc.)
-<li> Flags to indicate if the declaration is exported (contained within
- an EXPORT_INTERFACE block) or local (contained with LOCAL_INTERFACE).
-<li> A flag to indicate if the object is declared in a C++ file.
-<li> The name of the file in which the object was declared.
-<li> The complete text of any block comment that preceeds the declarations.
-<li> If the declaration occurred inside a preprocessor conditional
- (“<code>#if</code>”) then the text of that conditional is
- provided.
-<li> The complete text of a declaration for the object.
-</ul>
-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.
-</p>
-
-<a name="H0016"></a>
-<h2>5.0 Compiling The Makeheaders Program</h2>
-
-<p>
-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.
-</p>
-
-<a name="H0017"></a>
-<h2>6.0 History</h2>
-
-<p>
-The makeheaders program was first written by D. Richard Hipp
-(also the original author of
-<a href="https://sqlite.org/">SQLite</a> and
-<a href="https://www.fossil-scm.org/">Fossil</a>) 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.
-</p>
-
-<p>
-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.
-</p>
-
-<a name="H0018"></a>
-<h2>7.0 Summary And Conclusion</h2>
-
-<p>
-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.
-</p>
-
-<p>
-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.
-</p>
-</body>
-</html>
+++ /dev/null
-#
-
-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
-
-
+++ /dev/null
-
-(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 "<f9> 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)
+++ /dev/null
-
-#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 $<
-
+++ /dev/null
-
-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 $<
-
-
+++ /dev/null
-
-# 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 <base>.{lib,cli}.{c,cc} and the {.c,.cc} output
-# tranche must be named <base>.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 $<
-
+++ /dev/null
-<!DOCTYPE html>
-<html>
-<head>
-<base href="https://fossil-scm.org/fossil/doc/8cecc544/src/makeheaders.c" />
-<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: ; script-src 'self' 'nonce-e2f4709a3deeafd8212401c84e2a082e1fbd3068470984f2' ; style-src 'self' 'unsafe-inline'" />
-<meta name="viewport" content="width=device-width, initial-scale=1.0">
-<title>Fossil: File Content</title>
-<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="/fossil/timeline.rss" />
-<link rel="stylesheet" href="/fossil/style.css?id=9eb7de39" type="text/css" media="screen" />
-</head>
-<body>
-<div class="header">\r
- <div class="title"><h1>Fossil</h1>File Content</div>\r
- <div class="status"><a href='/fossil/login'>Login</a>
-</div>\r
-</div>\r
-<div class="mainmenu">\r
-<a id='hbbtn' href='#'>☰</a><a href='/fossil/doc/trunk/www/index.wiki' class=''>Home</a>
-<a href='/fossil/timeline' class=''>Timeline</a>
-<a href='/fossil/doc/trunk/www/permutedindex.html' class=''>Docs</a>
-<a href='https://fossil-scm.org/forum'>Forum</a><a href='/fossil/uv/download.html' class='desktoponly'>Download</a>
-</div>\r
-<div id='hbdrop'></div>\r
-<form id='f01' method='GET' action='/fossil/file'>
-<input type='hidden' name='udc' value='1'>
-<div class="submenu">
-<a class="label" href="/fossil/artifact/49c76a69">Artifact</a>
-<a class="label" href="/fossil/timeline?n=200&uf=49c76a6973d579ff0b346e5f73182fa72dd797cbb07e8b20612849dc2adef85d">Check-ins Using</a>
-<a class="label" href="/fossil/raw/src/makeheaders.c?name=49c76a6973d579ff0b346e5f73182fa72dd797cbb07e8b20612849dc2adef85d">Download</a>
-<a class="label" href="/fossil/hexdump?name=49c76a6973d579ff0b346e5f73182fa72dd797cbb07e8b20612849dc2adef85d">Hex</a>
-<label class='submenuctrl submenuckbox'><input type='checkbox' name='ln' id='submenuctrl-0' >Line Numbers</label>
-</div>
-<input type="hidden" name="name" value="src/makeheaders.c">
-</form>
-<script src='/fossil/builtin/menu.js?id=6f60cb38'></script>
-<div class="content">
-<h2>Latest version of file 'src/makeheaders.c':</h2>
-<ul>
-<li>File
-<a data-href='/fossil/finfo?name=src/makeheaders.c&m=49c76a6973d579ff' href='/fossil/honeypot'>src/makeheaders.c</a>
-— part of check-in
-<span class="timelineHistDsp">[8cecc544]</span>
-at
-2018-11-02 15:21:54
-on branch <a data-href='/fossil/timeline?r=trunk' href='/fossil/honeypot'>trunk</a>
-— 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)
-<a data-href='/fossil/whatis/49c76a6973d579ff' href='/fossil/honeypot'>[more...]</a>
-</ul>
-<hr />
-<blockquote>
-<pre>
-/*
-** 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
-
-</pre>
-</blockquote>
-</div>
-<div class="footer">\r
-This page was generated in about\r
-0.007s by\r
-Fossil 2.9 [6f60cb3881] 2019-02-27 14:34:30\r
-</div>\r
-<script nonce="e2f4709a3deeafd8212401c84e2a082e1fbd3068470984f2">\r
-/*\r
-** Copyright © 2018 Warren Young\r
-**\r
-** This program is free software; you can redistribute it and/or\r
-** modify it under the terms of the Simplified BSD License (also\r
-** known as the "2-Clause License" or "FreeBSD License".)\r
-**\r
-** This program is distributed in the hope that it will be useful,\r
-** but without any warranty; without even the implied warranty of\r
-** merchantability or fitness for a particular purpose.\r
-**\r
-** Contact: wyoung on the Fossil forum, https://fossil-scm.org/forum/\r
-**\r
-*******************************************************************************\r
-**\r
-** This file contains the JS code specific to the Fossil default skin.\r
-** Currently, the only thing this does is handle clicks on its hamburger\r
-** menu button.\r
-*/\r
-(function() {\r
- var hbButton = document.getElementById("hbbtn");\r
- if (!hbButton) return; // no hamburger button\r
- if (!document.addEventListener) {\r
- // Turn the button into a link to the sitemap for incompatible browsers.\r
- hbButton.href = "/fossil/sitemap";\r
- return;\r
- }\r
- var panel = document.getElementById("hbdrop");\r
- if (!panel) return; // site admin might've nuked it\r
- if (!panel.style) return; // shouldn't happen, but be sure\r
- var panelBorder = panel.style.border;\r
- var panelInitialized = false; // reset if browser window is resized\r
- var panelResetBorderTimerID = 0; // used to cancel post-animation tasks\r
-\r
- // Disable animation if this browser doesn't support CSS transitions.\r
- //\r
- // We need this ugly calling form for old browsers that don't allow\r
- // panel.style.hasOwnProperty('transition'); catering to old browsers\r
- // is the whole point here.\r
- var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");\r
-\r
- // The duration of the animation can be overridden from the default skin\r
- // header.txt by setting the "data-anim-ms" attribute of the panel.\r
- var animMS = panel.getAttribute("data-anim-ms");\r
- if (animMS) { // not null or empty string, parse it\r
- animMS = parseInt(animMS);\r
- if (isNaN(animMS) || animMS == 0)\r
- animate = false; // disable animation if non-numeric or zero\r
- else if (animMS < 0)\r
- animMS = 400; // set default animation duration if negative\r
- }\r
- else // attribute is null or empty string, use default\r
- animMS = 400;\r
-\r
- // Calculate panel height despite its being hidden at call time.\r
- // Based on https://stackoverflow.com/a/29047447/142454\r
- var panelHeight; // computed on first panel display\r
- function calculatePanelHeight() {\r
-\r
- // Clear the max-height CSS property in case the panel size is recalculated\r
- // after the browser window was resized.\r
- panel.style.maxHeight = '';\r
-\r
- // Get initial panel styles so we can restore them below.\r
- var es = window.getComputedStyle(panel),\r
- edis = es.display,\r
- epos = es.position,\r
- evis = es.visibility;\r
-\r
- // Restyle the panel so we can measure its height while invisible.\r
- panel.style.visibility = 'hidden';\r
- panel.style.position = 'absolute';\r
- panel.style.display = 'block';\r
- panelHeight = panel.offsetHeight + 'px';\r
-\r
- // Revert styles now that job is done.\r
- panel.style.display = edis;\r
- panel.style.position = epos;\r
- panel.style.visibility = evis;\r
- }\r
-\r
- // Show the panel by changing the panel height, which kicks off the\r
- // slide-open/closed transition set up in the XHR onload handler.\r
- //\r
- // Schedule the change for a near-future time in case this is the\r
- // first call, where the div was initially invisible. If we were\r
- // to change the panel's visibility and height at the same time\r
- // instead, that would prevent the browser from seeing the height\r
- // change as a state transition, so it'd skip the CSS transition:\r
- //\r
- // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples\r
- function showPanel() {\r
- // Cancel the timer to remove the panel border after the closing animation,\r
- // otherwise double-clicking the hamburger button with the panel opened will\r
- // remove the borders from the (closed and immediately reopened) panel.\r
- if (panelResetBorderTimerID) {\r
- clearTimeout(panelResetBorderTimerID);\r
- panelResetBorderTimerID = 0;\r
- }\r
- if (animate) {\r
- if (!panelInitialized) {\r
- panelInitialized = true;\r
- // Set up a CSS transition to animate the panel open and\r
- // closed. Only needs to be done once per page load.\r
- // Based on https://stackoverflow.com/a/29047447/142454\r
- calculatePanelHeight();\r
- panel.style.transition = 'max-height ' + animMS +\r
- 'ms ease-in-out';\r
- panel.style.overflowY = 'hidden';\r
- panel.style.maxHeight = '0';\r
- }\r
- setTimeout(function() {\r
- panel.style.maxHeight = panelHeight;\r
- panel.style.border = panelBorder;\r
- }, 40); // 25ms is insufficient with Firefox 62\r
- }\r
- panel.style.display = 'block';\r
- document.addEventListener('keydown',panelKeydown,/* useCapture == */true);\r
- document.addEventListener('click',panelClick,false);\r
- }\r
-\r
- var panelKeydown = function(event) {\r
- var key = event.which || event.keyCode;\r
- if (key == 27) {\r
- event.stopPropagation(); // ignore other keydown handlers\r
- panelToggle(true);\r
- }\r
- };\r
-\r
- var panelClick = function(event) {\r
- if (!panel.contains(event.target)) {\r
- // Call event.preventDefault() to have clicks outside the opened panel\r
- // just close the panel, and swallow clicks on links or form elements.\r
- //event.preventDefault();\r
- panelToggle(true);\r
- }\r
- };\r
-\r
- // Return true if the panel is showing.\r
- function panelShowing() {\r
- if (animate) {\r
- return panel.style.maxHeight == panelHeight;\r
- }\r
- else {\r
- return panel.style.display == 'block';\r
- }\r
- }\r
-\r
- // Check if the specified HTML element has any child elements. Note that plain\r
- // text nodes, comments, and any spaces (presentational or not) are ignored.\r
- function hasChildren(element) {\r
- var childElement = element.firstChild;\r
- while (childElement) {\r
- if (childElement.nodeType == 1) // Node.ELEMENT_NODE == 1\r
- return true;\r
- childElement = childElement.nextSibling;\r
- }\r
- return false;\r
- }\r
-\r
- // Reset the state of the panel to uninitialized if the browser window is\r
- // resized, so the dimensions are recalculated the next time it's opened.\r
- window.addEventListener('resize',function(event) {\r
- panelInitialized = false;\r
- },false);\r
-\r
- // Click handler for the hamburger button.\r
- hbButton.addEventListener('click',function(event) {\r
- // Break the event handler chain, or the handler for document → click\r
- // (about to be installed) may already be triggered by the current event.\r
- event.stopPropagation();\r
- event.preventDefault(); // prevent browser from acting on <a> click\r
- panelToggle(false);\r
- },false);\r
-\r
- function panelToggle(suppressAnimation) {\r
- if (panelShowing()) {\r
- document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);\r
- document.removeEventListener('click',panelClick,false);\r
- // Transition back to hidden state.\r
- if (animate) {\r
- if (suppressAnimation) {\r
- var transition = panel.style.transition;\r
- panel.style.transition = '';\r
- panel.style.maxHeight = '0';\r
- panel.style.border = 'none';\r
- setTimeout(function() {\r
- // Make sure CSS transition won't take effect now, so restore it\r
- // asynchronously. Outer variable 'transition' still valid here.\r
- panel.style.transition = transition;\r
- }, 40); // 25ms is insufficient with Firefox 62\r
- }\r
- else {\r
- panel.style.maxHeight = '0';\r
- panelResetBorderTimerID = setTimeout(function() {\r
- // Browsers show a 1px high border line when maxHeight == 0,\r
- // our "hidden" state, so hide the borders in that state, too.\r
- panel.style.border = 'none';\r
- panelResetBorderTimerID = 0; // clear ID of completed timer\r
- }, animMS);\r
- }\r
- }\r
- else {\r
- panel.style.display = 'none';\r
- }\r
- }\r
- else {\r
- if (!hasChildren(panel)) {\r
- // Only get the sitemap once per page load: it isn't likely to\r
- // change on us.\r
- var xhr = new XMLHttpRequest();\r
- xhr.onload = function() {\r
- var doc = xhr.responseXML;\r
- if (doc) {\r
- var sm = doc.querySelector("ul#sitemap");\r
- if (sm && xhr.status == 200) {\r
- // Got sitemap. Insert it into the drop-down panel.\r
- panel.innerHTML = sm.outerHTML;\r
- // Display the panel\r
- showPanel();\r
- }\r
- }\r
- // else, can't parse response as HTML or XML\r
- }\r
- xhr.open("GET", "/fossil/sitemap?popup"); // note the TH1 substitution!\r
- xhr.responseType = "document";\r
- xhr.send();\r
- }\r
- else {\r
- showPanel(); // just show what we built above\r
- }\r
- }\r
- }\r
-})();\r
-\r
-</script>\r
-<script id='href-data' type='application/json'>{"delay":10,"mouseover":0}</script>
-<script nonce="e2f4709a3deeafd8212401c84e2a082e1fbd3068470984f2">
-function setAllHrefs(){
-var anchors = document.getElementsByTagName("a");
-for(var i=0; i<anchors.length; i++){
-var j = anchors[i];
-if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
-}
-var forms = document.getElementsByTagName("form");
-for(var i=0; i<forms.length; i++){
-var j = forms[i];
-if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
-}
-}
-function antiRobotDefense(){
-var x = document.getElementById("href-data");
-var jx = x.textContent || x.innerText;
-var g = JSON.parse(jx);
-var isOperaMini =
-Object.prototype.toString.call(window.operamini)==="[object OperaMini]";
-if(g.mouseover && !isOperaMini){
-document.getElementByTagName("body")[0].onmousemove=function(){
-setTimeout(setAllHrefs, g.delay);
-}
-}else{
-setTimeout(setAllHrefs, g.delay);
-}
-}
-antiRobotDefense()
-</script>
-</body>
-</html>
+++ /dev/null
-# 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) $@
-
-
-
-
+++ /dev/null
-
-# 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
-
+++ /dev/null
-
-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 ' '.
-
+++ /dev/null
-# 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) $@
-
-
-
-
+++ /dev/null
-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
+++ /dev/null
-
-#include <stdio.h>
-#include <unistd.h>
-#include <da.h>
-#include "tranche.lib.h"
-
-int main(int argc, char **argv, char **envp){
- if(argc != 2){
- fprintf(stderr, "usage: %s <source-file>\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;
-}
+++ /dev/null
-/*
-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 <da.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-//--------------------------------------------------------------------------------
-// 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;
-}
+++ /dev/null
-#ifndef TRANCHE_LIB_H
-#define TRANCHE_LIB_H
-
-int tranche_send(FILE *src, Da *arg_fds);
-
-
-
-#endif
+++ /dev/null
-
-#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
+++ /dev/null
-#!/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
-
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-apple banana pear
-kiwi
+++ /dev/null
-int float if while
-do
-function
+++ /dev/null
-
-
-the between space
-
-
+++ /dev/null
-
-
-#include "test2.h"
-
-
-int f(int x){
- return x;
-}
-
-
+++ /dev/null
-#ifndef TEST2_H
-#define TEST2_H
-int f(int x);
-#endif
+++ /dev/null
-
-#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
+++ /dev/null
-../1_execs/tranche
\ No newline at end of file