From: Thomas Walker Lynch
Date: Tue, 12 Mar 2019 17:47:50 +0000 (+0100)
Subject: checkpoint
X-Git-Url: https://git.reasoningtechnology.com/style/static/gitweb.css?a=commitdiff_plain;h=f2cca4f60e07298a5e4c11ad163cb65403b98b04;p=subu
checkpoint
---
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..de56908
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+
+__pycache__/
+tmp/
+.*
+!.gitignore
+
+
diff --git a/7_makefile b/7_makefile
new file mode 100755
index 0000000..24dfafb
--- /dev/null
+++ b/7_makefile
@@ -0,0 +1,28 @@
+#subdirectories=$(shell /usr/bin/find . -maxdepth 1 -printf "%f " | sed y/\./\ /)
+#subdirectories=src src/1_tests src/1_try
+subdirectories=src
+
+all :
+ $(foreach dir, $(subdirectories), \
+ if [ -f $(dir)/makefile ]; then \
+ make -C $(dir) all && make -C $(dir) install; \
+ fi;\
+ )
+
+clean :
+ $(foreach dir, $(subdirectories), \
+ if [ -f ./$(dir)/makefile ]; then \
+ make -C $(dir) clean; \
+ fi;\
+ )
+
+dist_clean :
+ $(foreach dir, $(subdirectories), \
+ if [ -f ./$(dir)/makefile ]; then \
+ make -C $(dir) dist_clean; \
+ fi;\
+ )
+
+
+
+
diff --git a/bin/gitadd b/bin/gitadd
deleted file mode 100755
index 7097d77..0000000
--- a/bin/gitadd
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-set -x
-make clean
-git add "$@"
-
diff --git a/doc/dir-structure.txt b/doc/dir-structure.txt
new file mode 100644
index 0000000..668b909
--- /dev/null
+++ b/doc/dir-structure.txt
@@ -0,0 +1,57 @@
+
+~ is the project directory
+
+
+--------------------------------------------------------------------------------
+~/tools
+
+Some tools are normally installed on the system. Others are local. Local tools
+are typically standard tools that have been customized for the project or are
+unusual to the project.
+
+ ~/tools/bin is for the executables of locally used tools.
+ ~/tools/lib is locally used libraries.
+ ~/tools/doc is for collected docs on tools
+
+typically a developer will want ~/tools/bin in his search path
+
+--------------------------------------------------------------------------------
+~/staged
+
+These are make file build targets. They represent either intermediate or
+finished executables and libraries. When a project has multiple src
+directories and they use each other's work product for the builds, the
+items in the staged area are those that are used by each src directory.
+
+Typically the install target copies material in the staging area to
+system locations.
+
+--------------------------------------------------------------------------------
+~/src
+
+The prefixed numbered directories are overhead for building the src code.
+
+src/1_tests these are programs that typically must pass before the compiled
+code may be staged. Tests typically make use of the library in 2_lib and
+the header files in 2_include.
+
+src/1_try a free area for the programmer to try things out. For example when
+learning a language. Programs and code left in this directory might be interesting
+to newcomers.
+
+src/2_bin these are cli executables that have been created by the makefile
+and will be staged after testing
+
+src/2_include interface header files to be released with the lib. These might
+be different from the build header files found in the src dir.
+
+src/2_lib holds libraries being tested. Currently each src directory only
+builds one library.
+
+3_documents these are for developers working on src. A user manual might
+be under development.
+
+
+
+
+
diff --git a/makefile b/makefile
deleted file mode 100755
index f77a99c..0000000
--- a/makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-#subdirectories=$(shell /usr/bin/find . -maxdepth 1 -printf "%f " | sed y/\./\ /)
-subdirectories=src
-
-all :
- $(foreach dir, $(subdirectories), \
- if [ -f $(dir)/makefile ]; then \
- make -C $(dir) all && make -C $(dir) install; \
- fi;\
- )
-
-clean :
- $(foreach dir, $(subdirectories), \
- if [ -f ./$(dir)/makefile ]; then \
- make -C $(dir) clean; \
- fi;\
- )
diff --git a/src/1_try/mh_main_prob/command1.c b/src/1_try/mh_main_prob/command1.c
deleted file mode 100644
index 637df55..0000000
--- a/src/1_try/mh_main_prob/command1.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "command1.h"
-#include
-int main(int argc, char **argv){
- printf("command1 %d\n", f());
- return 0;
-}
diff --git a/src/1_try/mh_main_prob/command2.c b/src/1_try/mh_main_prob/command2.c
deleted file mode 100644
index 5d8c612..0000000
--- a/src/1_try/mh_main_prob/command2.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "command2.h"
-#include
-int main(int argc, char **argv){
- printf("command2 %d\n", f() + argc);
- return 0;
-}
diff --git a/src/1_try/mh_main_prob/just_fun.c b/src/1_try/mh_main_prob/just_fun.c
deleted file mode 100644
index 67625f4..0000000
--- a/src/1_try/mh_main_prob/just_fun.c
+++ /dev/null
@@ -1,4 +0,0 @@
-#include "just_fun.h"
-int f(){
- return 5;
-}
diff --git a/src/1_try/mh_main_prob/transcript1.txt b/src/1_try/mh_main_prob/transcript1.txt
deleted file mode 100644
index c5511fe..0000000
--- a/src/1_try/mh_main_prob/transcript1.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Various commmand files each with its own main for testing a library. makeheaders gets confused and puts all the
-declarations in the headers, leading to a failure.
-
-
-> ls
-command1.c command2.c just_fun.c
-> cat just_fun.c
-#include "just_fun.h"
-int f(){
- return 5;
-}
-> cat command1.c
-#include "command1.h"
-#include
-int main(){
- printf("command1 %d\n", f());
- return 0;
-}
-> cat command2.c
-#include "command2.h"
-#include
-int main(int argc, char **argv){
- printf("command2 %d\n", f() + argc);
- return 0;
-}
-> makeheaders *.c
-> gcc -o command1 command1.c
-command1.c: In function âmainâ:
-command1.c:3:1: error: number of arguments doesnât match prototype
- int main(){
- ^~~
-In file included from command1.c:1:
-command1.h:5:5: error: prototype declaration
- int main(int argc,char **argv);
- ^~~~
->
diff --git a/src/1_try/mh_main_prob/transcript2.txt b/src/1_try/mh_main_prob/transcript2.txt
deleted file mode 100644
index 77ec819..0000000
--- a/src/1_try/mh_main_prob/transcript2.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Making each main call static so it won't be in the header. gcc can't find main.
-
-> cat command1.c
-#include "command1.h"
-#include
-static int main(){
- printf("command1 %d\n", f());
- return 0;
-}
-> cat command2.c
-#include "command2.h"
-#include
-static int main(int argc, char **argv){
- printf("command2 %d\n", f() + argc);
- return 0;
-}
-> gcc -o command1 command1.c just_fun.c
-/usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o: in function `_start':
-(.text+0x24): undefined reference to `main'
-collect2: error: ld returned 1 exit status
diff --git a/src/1_try/mh_main_prob/transcript3.txt b/src/1_try/mh_main_prob/transcript3.txt
deleted file mode 100644
index b3a00b7..0000000
--- a/src/1_try/mh_main_prob/transcript3.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This time making each main definition have the same prototype. Still end up with multiple main declarations,
-it is just that they agree.
-
-> rm *.h
-> makeheaders *.c
-> cat command1.c
-#include "command1.h"
-#include
-int main(int argc, char **argv){
- printf("command1 %d\n", f());
- return 0;
-}
-> cat command1.h
-/* This file was automatically generated. Do not edit! */
-#undef INTERFACE
-int f();
-int main(int argc,char **argv);
-int main(int argc,char **argv);
-> cat command2.c
-#include "command2.h"
-#include
-int main(int argc, char **argv){
- printf("command2 %d\n", f() + argc);
- return 0;
-}
-> gcc -o command1 command1.c just_fun.c
-> .. worked
diff --git a/src/2_bin/setuid_root.sh b/src/2_bin/setuid_root.sh
deleted file mode 100755
index bea2d00..0000000
--- a/src/2_bin/setuid_root.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-# must be run under sudo
-# be sure to cat it before running it, better yet just type this in manually
-#
-
-chown root $1 && \
-chmod u+rsx,u-w,go+rx-s-w $1
-
diff --git a/src/2_doc/README.txt b/src/2_doc/README.txt
new file mode 100644
index 0000000..a9a2b21
--- /dev/null
+++ b/src/2_doc/README.txt
@@ -0,0 +1,23 @@
+
+
+filename.tag.extension
+
+extension:
+ .c for C source
+ .cc for C++ source
+ .h for C header file
+ .hh for C++ header file
+ .o an object file
+
+tag:
+ .lib. The resulting .o file to be placed in release library and is part of the
+ programming interface.
+ .aux. The resulting.o file not directly part of the programming interface, but
+ it might be called by functions that are.
+ .cli. The source file has a main call and is to be relased as part of the command line interface
+ .loc. file has a main call to be made into a local uitlity function
+
+We carry the source file tag and extension to the .o file. We do not put tags
+nor extensions on command line executables.
+
+local_common.h should be included in all source files
diff --git a/src/2_doc/makefile.txt b/src/2_doc/makefile.txt
new file mode 100644
index 0000000..722de84
--- /dev/null
+++ b/src/2_doc/makefile.txt
@@ -0,0 +1,91 @@
+these are the comments from my RT makefile:
+
+#
+# 2010 11 20 TWL Created
+# 2011 05 26 TWL Modified to generalize
+# 2012 02 23 NLS Add ECHO variable to use on different environnement
+# corrected setup macro --> add include directory in the path to copy
+# corrected install macro --> change the name of installed library : lib$(LIB)$(LIBSUFFIX)
+# changed DOC_DIR directory name to 5_documents
+# 2012 02 23 TWL removed LIB variable which is now set from the command line so
+# so that all source dirs can use the same makefile
+# 2012 02 23 TWL added target make dist_clean which also deletes the 2_makefile_deps file
+# 2012 04 11 AWW added creation of temporary disk before each test is run
+# 2012 06 05 TWL moved tests and try .cc files to directories. caused rtmake tests to
+# dist_clean and make deps
+#
+#
+#----------------------------------------------------------------------------
+# use this makefile to compile and test the code:
+#
+# for a first time run, or for regression use the following:
+#
+# $ make setup # makes the directories, though should already exist
+# $ make regress
+#
+# the usual development workflow makes use of these:
+#
+# $ make deps # only when needed, for example if headers includes change or new files introduced
+# $ cd tests; make deps # only when needed
+# $ make lib # this makes the local library
+# $ make tests # this updates tests and compiles
+# $ make clean # deletes the .o files and library to force a recompile
+# $ cd 1_tests; make clean
+#
+# for a release of a component
+#
+# $ make regress
+# $ make install # this will only work if all the tests in 1_tests are passing
+#
+# before a checkin
+#
+# $ make dist_clean # will also clean the tests and try directories
+#
+# .lib.cc c++ files taken as source of object files for local build library
+# .exl.cc c++ files taken to have main calls and are linked against local build libary
+# .ex.cc c++ files taken to have main calls and are not linked against the local build library
+# there are no rules for other files in this makefile
+#
+# about dependencies
+# The makefile has no way of knowing if an edit changed the dependencies. Often they do not
+# and it would be unwieldy to make the deps every time. Hence *the programmer* must delete
+# the deps file if he has made any changes that change the dependencies.
+#
+# The makefile will make the 2_makefile_deps if the file is missing.
+#
+#
+# about testing
+#
+# the name of the directory you run make in is taken to also be: the name of the library,
+# the name of the main include file (with a .h added), and the name of the include directory
+# where the individual headers are found. It is called LIB
+#
+# test programs are kept in a subdirectory called 1_tests, and are either .exl.cc, ex.cc,
+# .sh files. When 'make tests' target is invoked they are all run. Test executables return 0
+# if the test fails, non-zero otherwise.
+#
+# to remove a test from the pool move it into the subdirectory in 1_tests, 9_broken,
+# 5_more_tests of 5_scratch. broken tests are things that are known but must be fixed
+# before a release. 5_more_tests are tests being worked on. 5_scratch is stuff that is
+# probably going to be deleted. if there is a 5_deprecated, that is for good stuff but it
+# is no longer used for some reason or other.
+#
+# There is a standard source code template and a
+# messaging convention. Also, the names, by convention,are test_xxxx_ where xxx is a
+# hexadecimal series nummber. If all the test executables pass the file 1_TESTS_PASSED is
+# left in the directory. Otherwise the file 1_TESTS_FAILED is left in the directory.
+#
+# about release directory
+#
+# this is set in the ApplicationBase variable by rt_init
+#
+# after the tests pass stuff might be copied to the release directory using
+#
+# make install
+#
+# the release directory must have these subdirectories:
+#
+# bin documents include src
+#
+#
+#
diff --git a/src/2_doc/makeheaders.txt b/src/2_doc/makeheaders.txt
new file mode 100644
index 0000000..5aebda9
--- /dev/null
+++ b/src/2_doc/makeheaders.txt
@@ -0,0 +1,16 @@
+
+This worked to force the include to be part of the interface:
+
+#if INTERFACE
+#include
+#endif
+
+But this did not:
+
+#if INTERFACE
+ #include
+#endif
+
+makeheaders looks to be sensitive to indentation
+
+
diff --git a/src/2_doc/sqlite.txt b/src/2_doc/sqlite.txt
new file mode 100644
index 0000000..b7f8cbb
--- /dev/null
+++ b/src/2_doc/sqlite.txt
@@ -0,0 +1,15 @@
+
+1.
+ This sql:
+
+ char *sql =
+ "BEGIN TRANSACTION;"
+ "UPDATE Key_Int SET value = value + 1 WHERE key = 'max_subu_number';"
+ "SELECT value FROM Key_Int WHERE key = 'max_subu_number';"
+ "COMMIT;"
+ ;
+
+ with sqlite_exec, the call back is called with the data from the select.
+
+ with sqlite_prepare_v2, sqlite_step just returns SQLITE_DONE, and we never
+ get to see our data from the select.
diff --git a/src/2_doc/to_do.txt b/src/2_doc/to_do.txt
new file mode 100644
index 0000000..eebf52a
--- /dev/null
+++ b/src/2_doc/to_do.txt
@@ -0,0 +1,19 @@
+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.
+
+
diff --git a/src/3_documents/README.txt b/src/3_documents/README.txt
deleted file mode 100644
index a9a2b21..0000000
--- a/src/3_documents/README.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-filename.tag.extension
-
-extension:
- .c for C source
- .cc for C++ source
- .h for C header file
- .hh for C++ header file
- .o an object file
-
-tag:
- .lib. The resulting .o file to be placed in release library and is part of the
- programming interface.
- .aux. The resulting.o file not directly part of the programming interface, but
- it might be called by functions that are.
- .cli. The source file has a main call and is to be relased as part of the command line interface
- .loc. file has a main call to be made into a local uitlity function
-
-We carry the source file tag and extension to the .o file. We do not put tags
-nor extensions on command line executables.
-
-local_common.h should be included in all source files
diff --git a/src/3_documents/makefile.txt b/src/3_documents/makefile.txt
deleted file mode 100644
index 722de84..0000000
--- a/src/3_documents/makefile.txt
+++ /dev/null
@@ -1,91 +0,0 @@
-these are the comments from my RT makefile:
-
-#
-# 2010 11 20 TWL Created
-# 2011 05 26 TWL Modified to generalize
-# 2012 02 23 NLS Add ECHO variable to use on different environnement
-# corrected setup macro --> add include directory in the path to copy
-# corrected install macro --> change the name of installed library : lib$(LIB)$(LIBSUFFIX)
-# changed DOC_DIR directory name to 5_documents
-# 2012 02 23 TWL removed LIB variable which is now set from the command line so
-# so that all source dirs can use the same makefile
-# 2012 02 23 TWL added target make dist_clean which also deletes the 2_makefile_deps file
-# 2012 04 11 AWW added creation of temporary disk before each test is run
-# 2012 06 05 TWL moved tests and try .cc files to directories. caused rtmake tests to
-# dist_clean and make deps
-#
-#
-#----------------------------------------------------------------------------
-# use this makefile to compile and test the code:
-#
-# for a first time run, or for regression use the following:
-#
-# $ make setup # makes the directories, though should already exist
-# $ make regress
-#
-# the usual development workflow makes use of these:
-#
-# $ make deps # only when needed, for example if headers includes change or new files introduced
-# $ cd tests; make deps # only when needed
-# $ make lib # this makes the local library
-# $ make tests # this updates tests and compiles
-# $ make clean # deletes the .o files and library to force a recompile
-# $ cd 1_tests; make clean
-#
-# for a release of a component
-#
-# $ make regress
-# $ make install # this will only work if all the tests in 1_tests are passing
-#
-# before a checkin
-#
-# $ make dist_clean # will also clean the tests and try directories
-#
-# .lib.cc c++ files taken as source of object files for local build library
-# .exl.cc c++ files taken to have main calls and are linked against local build libary
-# .ex.cc c++ files taken to have main calls and are not linked against the local build library
-# there are no rules for other files in this makefile
-#
-# about dependencies
-# The makefile has no way of knowing if an edit changed the dependencies. Often they do not
-# and it would be unwieldy to make the deps every time. Hence *the programmer* must delete
-# the deps file if he has made any changes that change the dependencies.
-#
-# The makefile will make the 2_makefile_deps if the file is missing.
-#
-#
-# about testing
-#
-# the name of the directory you run make in is taken to also be: the name of the library,
-# the name of the main include file (with a .h added), and the name of the include directory
-# where the individual headers are found. It is called LIB
-#
-# test programs are kept in a subdirectory called 1_tests, and are either .exl.cc, ex.cc,
-# .sh files. When 'make tests' target is invoked they are all run. Test executables return 0
-# if the test fails, non-zero otherwise.
-#
-# to remove a test from the pool move it into the subdirectory in 1_tests, 9_broken,
-# 5_more_tests of 5_scratch. broken tests are things that are known but must be fixed
-# before a release. 5_more_tests are tests being worked on. 5_scratch is stuff that is
-# probably going to be deleted. if there is a 5_deprecated, that is for good stuff but it
-# is no longer used for some reason or other.
-#
-# There is a standard source code template and a
-# messaging convention. Also, the names, by convention,are test_xxxx_ where xxx is a
-# hexadecimal series nummber. If all the test executables pass the file 1_TESTS_PASSED is
-# left in the directory. Otherwise the file 1_TESTS_FAILED is left in the directory.
-#
-# about release directory
-#
-# this is set in the ApplicationBase variable by rt_init
-#
-# after the tests pass stuff might be copied to the release directory using
-#
-# make install
-#
-# the release directory must have these subdirectories:
-#
-# bin documents include src
-#
-#
-#
diff --git a/src/3_documents/makeheaders.txt b/src/3_documents/makeheaders.txt
deleted file mode 100644
index 5aebda9..0000000
--- a/src/3_documents/makeheaders.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-
-This worked to force the include to be part of the interface:
-
-#if INTERFACE
-#include
-#endif
-
-But this did not:
-
-#if INTERFACE
- #include
-#endif
-
-makeheaders looks to be sensitive to indentation
-
-
diff --git a/src/3_documents/sqlite.txt b/src/3_documents/sqlite.txt
deleted file mode 100644
index b7f8cbb..0000000
--- a/src/3_documents/sqlite.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-
-1.
- This sql:
-
- char *sql =
- "BEGIN TRANSACTION;"
- "UPDATE Key_Int SET value = value + 1 WHERE key = 'max_subu_number';"
- "SELECT value FROM Key_Int WHERE key = 'max_subu_number';"
- "COMMIT;"
- ;
-
- with sqlite_exec, the call back is called with the data from the select.
-
- with sqlite_prepare_v2, sqlite_step just returns SQLITE_DONE, and we never
- get to see our data from the select.
diff --git a/src/3_to_do.txt b/src/3_to_do.txt
deleted file mode 100644
index eebf52a..0000000
--- a/src/3_to_do.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-2019-02-05T23:14:40Z
- error can cause subu-mk-0 to leave the creating of a subu in an intermediate
- state. Rather than bailing on some of the errors we need to clean up instead.
- Perhaps the yet to be written subu-rm program will be resilent enough to do
- more general cleanup.
-
-2019-02-23T18:56:31Z
- need to modify subu-init to take a configuration file name argument instead of
- using a global variabel value. might want to add arguments to other subu
- commands also
-
-2019-03-11T13:48:03Z
- in subu.lib.c append cascading rmdir failure mess to useradd failure mess
-
-2019-03-11T13:48:03Z
- want to add subu-type to masteru_subu(), I imagine there will be static,
- permanent, and temporary subu types.
-
-
diff --git a/src/5_deprecated/subudb-number-next.cli.c b/src/5_deprecated/subudb-number-next.cli.c
new file mode 100644
index 0000000..3373173
--- /dev/null
+++ b/src/5_deprecated/subudb-number-next.cli.c
@@ -0,0 +1,45 @@
+/*
+Set or get a new maximum subu number. Currently doesn't do the setting part.
+
+*/
+#include "subudb-number-next.cli.h"
+#include
+#include
+#include
+
+int main(int argc, char **argv){
+
+ if( argc != 2 ){
+ fprintf(stderr, "usage: %s masteru_name \n",argv[0]);
+ return SUBU_ERR_ARG_CNT;
+ }
+ char *masteru_name = argv[1];
+
+ int rc;
+ sqlite3 *db;
+ rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( rc != SQLITE_OK ){
+ sqlite3_close(db);
+ fprintf(stderr, "error exit, could not open db file\n");
+ return SUBU_ERR_DB_FILE;
+ }
+
+ // read and print the current max
+ char *mess;
+ int n;
+ rc = subudb_number_next(db, masteru_name, &n, &mess);
+ if( rc == SQLITE_DONE ){
+ printf("%d\n", n);
+ }else{
+ fprintf(stderr, "subudb_number_next indicates failure by returning %d\n",rc);
+ fprintf(stderr, "and issues message, %s\n", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return SUBU_ERR_DB_FILE;
+ }
+ rc = sqlite3_close(db);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+}
diff --git a/src/5_scratch/common.lib.h b/src/5_scratch/common.lib.h
index 2454943..cfdc520 100644
--- a/src/5_scratch/common.lib.h
+++ b/src/5_scratch/common.lib.h
@@ -2,8 +2,8 @@
#undef INTERFACE
extern char Subuland_Extension[];
typedef unsigned int uint;
-extern uint First_Max_Subu_number;
+extern uint First_Max_Subunumber;
extern uint Subuhome_Perms;
-extern char Config_File[];
+extern char DB_File[];
#define BUG_SSS_CACHE_RUID 1
#define INTERFACE 0
diff --git a/src/5_scratch/subu-common.lib.h b/src/5_scratch/subu-common.lib.h
new file mode 100644
index 0000000..cfdc520
--- /dev/null
+++ b/src/5_scratch/subu-common.lib.h
@@ -0,0 +1,9 @@
+/* This file was automatically generated. Do not edit! */
+#undef INTERFACE
+extern char Subuland_Extension[];
+typedef unsigned int uint;
+extern uint First_Max_Subunumber;
+extern uint Subuhome_Perms;
+extern char DB_File[];
+#define BUG_SSS_CACHE_RUID 1
+#define INTERFACE 0
diff --git a/src/5_scratch/subu-config.lib.c.~ceea6e7d697546c47f7736b72e7fb60b15c104de~ b/src/5_scratch/subu-config.lib.c.~ceea6e7d697546c47f7736b72e7fb60b15c104de~
new file mode 100644
index 0000000..de7bcbb
--- /dev/null
+++ b/src/5_scratch/subu-config.lib.c.~ceea6e7d697546c47f7736b72e7fb60b15c104de~
@@ -0,0 +1,147 @@
+/*
+The config file is maintained in SQLite
+
+Because user names of are of limited length, subu user names are always named _s.
+A separate table translates the numbers into the subu names.
+
+The first argument is the biggest subu number in the system, or one minus an
+starting point for subu numbering.
+
+currently a unit converted to base 10 will always fit in a 21 bit buffer.
+
+*/
+#include "subu-config.lib.h"
+
+#if INTERFACE
+#include
+#define ERR_CONFIG_FILE 1
+#endif
+
+#include
+#include
+#include
+
+//--------------------------------------------------------------------------------
+int subudb_schema(sqlite3 *db, uint max_subu_number){
+ char max_subu_number_string[32];
+ uint max_subu_number_string_len = snprintf(max_subu_number_string, 32, "%u", max_subu_number);
+ if( max_subu_number_string_len >= 32 ){
+ fprintf(stderr, "error exit, max_subu_number too big to deal with\n");
+ return ERR_CONFIG_FILE;
+ }
+ char sql1[] = "CREATE TABLE Masteru_Subu(masteru_name TEXT, subuname TEXT, subu_username TEXT); ";
+ char sql2[] = "CREATE TABLE Key_Int(key TEXT, value INT); ";
+
+ char sql3_1[] = "INSERT INTO Key_Int VALUES( 'max_subu_number', ";
+ char sql3_2[] = " ); ";
+ char sql3_len = strlen(sql3_1) + max_subu_number_string_len + strlen(sql3_2) + 1;
+ char sql3[sql3_len];
+ strcpy(sql3, sql3_1);
+ strcpy(sql3 + strlen(sql3_1), max_subu_number_string);
+ strcpy(sql3 + strlen(sql3_1) + max_subu_number_string_len, sql3_2);
+
+ char sql[strlen(sql1) + strlen(sql2) + strlen(sql3) + 1];
+ strcpy(sql, sql1);
+ strcpy(sql + strlen(sql1), sql2);
+ strcpy(sql + strlen(sql1) + strlen(sql2), sql3);
+
+ return sqlite3_exec(db, sql, NULL, NULL, NULL);
+}
+
+//--------------------------------------------------------------------------------
+
+// the call back for subu_number_next, note also 3_doc/sqlite3.txt
+static int subu_number_extract(void *nsp, int colcnt, char **colvals, char **colnames){
+ if(colcnt >= 1){
+ *(char **)nsp = strdup( colvals[0] );
+ return 0;
+ }
+ return -1;
+}
+int subu_number_next(sqlite3 *db, char **nsp, char **mess){
+ 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;";
+ int rc = sqlite3_exec(db, sql, subu_number_extract, (void *)nsp, mess);
+ return rc;
+}
+int subu_number_get(sqlite3 *db, int *n){
+ char *sql = "SELECT value FROM Key_Int WHERE key = 'max_subu_number';";
+ 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);
+ }else{
+ sqlite3_finalize(stmt);
+ return rc; // woops this needs to return an error!, be sure it is not SQLITE_DONE
+ }
+ rc = sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+ return rc;
+}
+int subu_number_set(sqlite3 *db, int n){
+ char *sql = "UPDATE Key_Int SET value = ?1 WHERE key = 'max_subu_number';";
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+ sqlite3_bind_int(stmt, 1, n);
+ int rc = sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+ return rc;
+}
+
+
+//--------------------------------------------------------------------------------
+// put relation into Masteru_Subu table
+int subu_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);
+ return rc;
+}
+
+//--------------------------------------------------------------------------------
+int subu_Masteru_Subu_get(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);
+ sqlite3_finalize(stmt);
+ return rc;
+}
+
+//--------------------------------------------------------------------------------
+int subu_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);
+ return rc;
+}
diff --git a/src/5_scratch/subu-mk-0.cli.h b/src/5_scratch/subu-mk-0.cli.h
index 56f6cf6..487b509 100644
--- a/src/5_scratch/subu-mk-0.cli.h
+++ b/src/5_scratch/subu-mk-0.cli.h
@@ -5,6 +5,6 @@
#include
void subu_err(char *fname,int err,char *mess);
int subu_mk_0(char **mess,sqlite3 *db,char *subuname);
-#define SUBU_ERR_CONFIG_FILE 8
-extern char Config_File[];
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
#define SUBU_ERR_ARG_CNT 1
diff --git a/src/5_scratch/subu-rm-0.cli.h b/src/5_scratch/subu-rm-0.cli.h
index 56f6cf6..070bfe8 100644
--- a/src/5_scratch/subu-rm-0.cli.h
+++ b/src/5_scratch/subu-rm-0.cli.h
@@ -4,7 +4,7 @@
#include
#include
void subu_err(char *fname,int err,char *mess);
-int subu_mk_0(char **mess,sqlite3 *db,char *subuname);
-#define SUBU_ERR_CONFIG_FILE 8
-extern char Config_File[];
+int subu_rm_0(char **mess,sqlite3 *db,char *subuname);
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
#define SUBU_ERR_ARG_CNT 1
diff --git a/src/5_scratch/subu.lib.h b/src/5_scratch/subu.lib.h
index 199daab..45c0b81 100644
--- a/src/5_scratch/subu.lib.h
+++ b/src/5_scratch/subu.lib.h
@@ -1,12 +1,12 @@
/* This file was automatically generated. Do not edit! */
#undef INTERFACE
#include
-int subu_rm_masteru_subu(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
-int subu_get_masteru_subu(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username);
+int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
+int subudb_Masteru_Subu_get(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username);
#include
#include
int subu_rm_0(char **mess,sqlite3 *db,char *subuname);
-int subu_put_masteru_subu(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);
#include
#include
int dispatch_exec(char **argv,char **envp);
@@ -14,13 +14,19 @@ int dispatch_exec(char **argv,char **envp);
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);
+int db_commit(sqlite3 *db);
+int subudb_number_init(sqlite3 *db,char *masteru_name,int n);
+typedef unsigned int uint;
+extern uint First_Max_Subunumber;
+int db_rollback(sqlite3 *db);
+int subudb_number_set(sqlite3 *db,char *masteru_name,int n);
+int subudb_number_get(sqlite3 *db,char *masteru_name,int *n);
+int db_begin(sqlite3 *db);
int dbprintf(const char *format,...);
int subu_mk_0(char **mess,sqlite3 *db,char *subuname);
extern char Subuland_Extension[];
-int subu_number_next(sqlite3 *db,char **nsp,char **mess);
-typedef unsigned int uint;
extern uint Subuhome_Perms;
-extern char Config_File[];
+extern char DB_File[];
void subu_err(char *fname,int err,char *mess);
#define SUBU_ERR_N 14
#define SUBU_ERR_CONFIG_SUBU_NOT_FOUND 13
@@ -28,7 +34,7 @@ void subu_err(char *fname,int err,char *mess);
#define SUBU_ERR_FAILED_USERADD 11
#define SUBU_ERR_BUG_SSS 10
#define SUBU_ERR_SUBUHOME_EXISTS 9
-#define SUBU_ERR_CONFIG_FILE 8
+#define SUBU_ERR_DB_FILE 8
#define SUBU_ERR_MASTERU_HOMELESS 7
#define SUBU_ERR_SUBUNAME_MALFORMED 6
#define SUBU_ERR_RMDIR_SUBUHOME 5
diff --git a/src/5_scratch/subudb-init.cli.h b/src/5_scratch/subudb-init.cli.h
new file mode 100644
index 0000000..36fa2c9
--- /dev/null
+++ b/src/5_scratch/subudb-init.cli.h
@@ -0,0 +1,8 @@
+/* This file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include
+int subudb_schema(sqlite3 *db);
+#include
+#include
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
diff --git a/src/5_scratch/subudb-number-next.cli.h b/src/5_scratch/subudb-number-next.cli.h
new file mode 100644
index 0000000..2c295dc
--- /dev/null
+++ b/src/5_scratch/subudb-number-next.cli.h
@@ -0,0 +1,9 @@
+/* This file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include
+int subudb_number_next(sqlite3 *db,char *masteru_name,int *n,char **mess);
+#include
+#include
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
+#define SUBU_ERR_ARG_CNT 1
diff --git a/src/5_scratch/subudb-number.cli.h b/src/5_scratch/subudb-number.cli.h
new file mode 100644
index 0000000..3e4e351
--- /dev/null
+++ b/src/5_scratch/subudb-number.cli.h
@@ -0,0 +1,11 @@
+/* This file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include
+int subudb_number_get(sqlite3 *db,char *masteru_name,int *n);
+int subudb_number_set(sqlite3 *db,char *masteru_name,int n);
+#include
+#include
+#define SUBU_ERR_N 14
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
+#define SUBU_ERR_ARG_CNT 1
diff --git a/src/5_scratch/subudb-rel-get.cli.h b/src/5_scratch/subudb-rel-get.cli.h
new file mode 100644
index 0000000..23c41b1
--- /dev/null
+++ b/src/5_scratch/subudb-rel-get.cli.h
@@ -0,0 +1,9 @@
+/* This file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include
+int subudb_Masteru_Subu_get(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username);
+#include
+#include
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
+#define SUBU_ERR_ARG_CNT 1
diff --git a/src/5_scratch/subudb-rel-put.cli.h b/src/5_scratch/subudb-rel-put.cli.h
new file mode 100644
index 0000000..243b3a9
--- /dev/null
+++ b/src/5_scratch/subudb-rel-put.cli.h
@@ -0,0 +1,8 @@
+/* This file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include
+int subudb_Masteru_Subu_put(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
+#include
+#include
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
diff --git a/src/5_scratch/subudb-rel-rm.cli.h b/src/5_scratch/subudb-rel-rm.cli.h
new file mode 100644
index 0000000..595427f
--- /dev/null
+++ b/src/5_scratch/subudb-rel-rm.cli.h
@@ -0,0 +1,8 @@
+/* This file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include
+int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
+#include
+#include
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
diff --git a/src/5_scratch/subudb.lib.h b/src/5_scratch/subudb.lib.h
new file mode 100644
index 0000000..1c854b3
--- /dev/null
+++ b/src/5_scratch/subudb.lib.h
@@ -0,0 +1,15 @@
+/* This file was automatically generated. Do not edit! */
+#undef INTERFACE
+#include
+int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
+int subudb_Masteru_Subu_get(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_rm(sqlite3 *db,char *masteru_name);
+int subudb_number_set(sqlite3 *db,char *masteru_name,int n);
+int subudb_number_get(sqlite3 *db,char *masteru_name,int *n);
+int subudb_number_init(sqlite3 *db,char *masteru_name,int n);
+int subudb_schema(sqlite3 *db);
+int db_rollback(sqlite3 *db);
+int db_commit(sqlite3 *db);
+int db_begin(sqlite3 *db);
+#define INTERFACE 0
diff --git a/src/7_makefile b/src/7_makefile
new file mode 100755
index 0000000..c16155b
--- /dev/null
+++ b/src/7_makefile
@@ -0,0 +1,169 @@
+
+# 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)
+
+# 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
+
+SHELL=/bin/bash
+SCRATCHDIR= 5_scratch # clean and others put things here
+CC=gcc
+CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB
+#CFLAGS=-std=gnu11 -fPIC -I. -Werror
+LIB=2_lib/libsubu.a
+LINKFLAGS=-L2_lib -lsubu -lsqlite3
+
+#these are the source files that exist
+SOURCES_LIB= $(wildcard *.lib.c)
+SOURCES_CLI= $(wildcard *.cli.c)
+SOURCES= $(SOURCES_LIB) $(SOURCES_CLI)
+
+#these are the object files to be made
+OBJECTS_LIB= $(patsubst %.c, %.o, $(SOURCES_LIB))
+OBJECTS_CLI= $(patsubst %.c, %.o, $(SOURCES_CLI))
+OBJECTS= $(OBJECTS_LIB) $(OBJECTS_CLI)
+
+#these are the header files that exist, makeheaders will want to see them
+HFILES = $(wildcard *.lib.h) $(wildcard *.cli.h)
+
+# sort causes compiles to go in lexical order by file name, this is used to order the tests e.g.
+EXECS= $(sort $(patsubst %.cli.c, %, $(wildcard *.cli.c)))
+
+SUID=$(realpath ../tools/bin/setuid_root.sh)
+
+
+all: version deps lib execs
+
+lists:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo "SOURCES_LIB: " $(SOURCES_LIB)
+ @echo "SOURCES_CLI: " $(SOURCES_CLI)
+ @echo "SOURCES: " $(SOURCES)
+ @echo "OBJECTS_LIB: " $(OBJECTS_LIB)
+ @echo "OBJECTS_CLI: " $(OBJECTS_CLI)
+ @echo "OBJECTS: " $(OBJECTS)
+ @echo "HFILES: " $(HFILES)
+ @echo "EXECS: " $(EXECS)
+ @echo '______end make $@_____'
+
+version:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ @echo makefile version 2.0
+ @echo "CC: " $(CC)
+ @echo "CFLAGS: " $(CFLAGS)
+ @echo "LINKFLAGS: " $(LINKFLAGS)
+ @echo "LIB: " $(LIB)
+ @echo "SUID: " $(SUID)
+ @echo '______end make $@_____'
+
+# safe to run this in an already setup or partially setup directory
+setup:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ if [ ! -e $(SCRATCHDIR) ]; then mkdir $(SCRATCHDIR); fi
+ if [ ! -e 1_tests ]; then mkdir 1_tests; fi
+ if [ ! -e 1_try ]; then mkdir 1_try; fi
+ if [ ! -e 2_bin ]; then mkdir 2_bin; fi
+ if [ ! -e 2_lib ]; then mkdir 2_lib; fi
+ if [ ! -e 2_doc ]; then mkdir 2_doc; fi
+ if [ ! -e 2_include ]; then mkdir 2_include; fi
+ if [ ! -e 5_deprecated ]; then mkdir 5_deprecated; fi
+ if [ ! -e 5_scratch ]; then mkdir 5_scratch; fi
+ @echo '______end make $@_____'
+
+
+deps:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ makeheaders $(SOURCES) $(HFILES)
+ sed -i '/^ *int *main *(.*)/d' *.h
+ $(CC) $(CFLAGS) -MM $(SOURCES) 1> 7_makefile_deps
+ for i in $(EXECS) ; do\
+ $(ECHO) >> 7_makefile_deps;\
+ $(ECHO) "2_bin/$$i : $$i.cli.o $(LIB)" >> 7_makefile_deps;\
+ $(ECHO) " $(CC) -o 2_bin/$$i $$i.cli.o $(LINKFLAGS)" >> 7_makefile_deps;\
+ done
+ @echo '______end make $@_____'
+
+lib:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ if [ ! -e 7_makefile_deps ]; then make deps; fi
+ make sub_lib
+ @echo '______end make $@_____'
+
+sub_lib: $(LIB)
+
+
+execs: $(LIB)
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ if [ ! -e 7_makefile_deps ]; then make deps; fi
+ make sub_execs
+ @echo "-> $(SUID) 2_bin/subu-mk-0 2_bin/subu-rm-0"
+ cat $(SUID)
+ @echo -n "Are you sure? [y/N] " && read ans && [ $${ans:-N} == y ]
+ sudo $(SUID) 2_bin/subu-mk-0 2_bin/subu-rm-0
+ @echo '______end make $@_____'
+
+sub_execs: $(patsubst %, 2_bin/%, $(EXECS))
+
+#not ready yet
+install: all
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ @if[ ! -e 1_tests_passed ]; then echo "can't install as tests have not passed"; fi
+ @test -e test_passed
+ for i in $(BIN); do cp $$i $(RT_BASE)/bin; done
+ cp $(LIB) $(RT_BASE)/lib
+ cp $(APPLICATION).h $(RT_BASE)/include
+ if [ -d $(APPLICATION) ]; then cp $(APPLICATION)/*.h $(RT_BASE)/include/$(APPLICATION); fi
+ @echo '______end make $@_____'
+
+# not written yet
+# copies stuff from the src dir to the stage dirs
+# stage:
+
+clean:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ if [ -f subudb ]; then rm subudb; fi
+ for i in $(wildcard *~); do mv $$i $(SCRATCHDIR); done
+ for i in $(wildcard *.lib.o) $(wildcard *.cli.o); do rm $$i; done
+ for i in $(HFILES); do mv $$i 5_scratch; done # just in case someone wrote a header file
+ for i in $(EXECS); do if [ -e 2_bin/$$i ]; then rm 2_bin/$$i; fi; done
+ if [ -f $(LIB) ]; then rm $(LIB); fi
+ if [ -f 7_makefile_deps ]; then rm 7_makefile_deps; fi
+ @echo '______end make $@_____'
+
+
+# not ready ...
+# dist_clean is used to clean thing up before doing a checkin, hg add should be safe after a dist_clean
+# dist_clean will recurse into the include directory = $(APPLICATION), tests, and try if they are present
+#
+dist-clean:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ make clean
+ if [ -d $(APPLICATION) ]; then cd $(APPLICATION); make clean; fi
+ if [ -d 1_tests ]; then cd 1_tests; make dist_clean; fi
+ if [ -d 1_try ] ; then cd 1_try; make dist_clean; fi
+ @echo '______end make $@_____'
+
+#
+$(LIB) : $(OBJECTS_LIB)
+ ar rcs $(LIB) $(OBJECTS_LIB)
+
+-include 7_makefile_deps
+
+# recipe for making object files:
+#
+%.o : %.c
+ $(CC) $(CFLAGS) -c $<
+
+
diff --git a/src/common.lib.c b/src/common.lib.c
deleted file mode 100644
index b949af4..0000000
--- a/src/common.lib.c
+++ /dev/null
@@ -1,20 +0,0 @@
-
-#include "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 *Config_File = "/etc/subu.db";
-char Config_File[] = "subu.db";
-uint Subuhome_Perms = 0700;
-uint First_Max_Subu_number = 114;
-char Subuland_Extension[] = "/subuland/";
diff --git a/src/makefile b/src/makefile
deleted file mode 100755
index 0fbf5a1..0000000
--- a/src/makefile
+++ /dev/null
@@ -1,147 +0,0 @@
-# Copyright 2011 (C) Reasoning Technology Ltd. All Rights Reserved
-#
-# 2010 11 20 TWL Created
-# 2019 02 24 TWL modified for subu project and placed under MIT license
-
-# 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)
-
-# 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
-
-SHELL=/bin/bash
-SCRATCHDIR= 5_scratch # clean and others put things here
-CC=gcc
-CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB
-#CFLAGS=-std=gnu11 -fPIC -I. -Werror
-LIB="libsubu.a"
-LINKFLAGS=-L. -lsubu -lsqlite3
-
-#these are the source files that exist
-SOURCES_LIB= $(wildcard *.lib.c)
-SOURCES_CLI= $(wildcard *.cli.c)
-SOURCES= $(SOURCES_LIB) $(SOURCES_CLI)
-
-#these are the object files to be made
-OBJECTS_LIB= $(patsubst %.c, %.o, $(SOURCES_LIB))
-OBJECTS_CLI= $(patsubst %.c, %.o, $(SOURCES_CLI))
-OBJECTS= $(OBJECTS_LIB) $(OBJECTS_CLI)
-
-#these are the header files that exist, makeheaders will want to see them
-HFILES = $(wildcard *.lib.h) $(wildcard *.cli.h)
-
-# sort causes compiles to go in lexical order by file name, this is used to order the tests e.g.
-EXECS= $(sort $(patsubst %.cli.c, %, $(wildcard *.cli.c)))
-
-all: version deps libs execs
-
-version:
- @echo '---- make $@:------------------------------------------------------------'
- @echo `pwd`'>'
- @echo makefile version 2.0
- @echo "CC: " $(CC)
- @echo "CFLAGS: " $(CFLAGS)
- @echo "LIB: " $(LIB)
- @echo "LINKFLAGS: " $(LINKFLAGS)
- @echo '______end make $@_____'
-
-# safe to run this in an already setup or partially setup directory
-setup:
- @echo '---- make $@:------------------------------------------------------------'
- @echo `pwd`'>'
- if [ ! -e $(SCRATCHDIR) ]; then mkdir $(SCRATCHDIR); fi
- if [ ! -e 1_tests ]; then mkdir 1_tests; fi
- if [ ! -e 1_try ]; then mkdir 1_try; fi
- if [ ! -e 2_bin ]; then mkdir 2_bin; fi
- if [ ! -e 3_documents ]; then mkdir 3_documents; fi
- if [ ! -e 3_to_do.txt ]; then touch 3_to_do.txt; fi
- if [ ! -e 5_deprecated ]; then mkdir 5_deprecated; fi
- @echo '______end make $@_____'
-
-
-deps:
- @echo '---- make $@:------------------------------------------------------------'
- @echo `pwd`'>'
- makeheaders $(SOURCES) $(HFILES)
- sed -i '/^ *int *main *(.*)/d' *.h
- $(CC) $(CFLAGS) -MM $(SOURCES) 1> 2_makefile_deps
- for i in $(EXECS) ; do\
- $(ECHO) >> 2_makefile_deps;\
- $(ECHO) "$$i : $$i.cli.o $(LIB)" >> 2_makefile_deps;\
- $(ECHO) " $(CXX) -o $$i $$i.cli.o $(LINKFLAGS)" >> 2_makefile_deps;\
- done
- @echo '______end make $@_____'
-
-libs:
- @echo '---- make $@:------------------------------------------------------------'
- @echo `pwd`'>'
- if [ ! -e 2_makefile_deps ]; then make deps; fi
- make sub_libs
- @echo '______end make $@_____'
-
-sub_libs: $(LIB)
-
-
-execs: $(LIB)
- @echo '---- make $@:------------------------------------------------------------'
- @echo `pwd`'>'
- if [ ! -e 2_makefile_deps ]; then make deps; fi
- make sub_execs
- @echo '______end make $@_____'
-
-sub_execs: $(EXECS)
-
-#not ready yet
-install: all
- @echo '---- make $@:------------------------------------------------------------'
- @echo `pwd`'>'
- @if[ ! -e 1_tests_passed ]; then echo "can't install as tests have not passed"; fi
- @test -e test_passed
- for i in $(BIN); do cp $$i $(RT_BASE)/bin; done
- cp $(LIB) $(RT_BASE)/lib
- cp $(APPLICATION).h $(RT_BASE)/include
- if [ -d $(APPLICATION) ]; then cp $(APPLICATION)/*.h $(RT_BASE)/include/$(APPLICATION); fi
- @echo '______end make $@_____'
-
-clean:
- @echo '---- make $@:------------------------------------------------------------'
- @echo `pwd`'>'
- if [ -f subu.db ]; then rm subu.db; fi
- for i in $(wildcard *~); do mv $$i $(SCRATCHDIR); done
- for i in $(wildcard *.lib.o) $(wildcard *.cli.o); do rm $$i; done
- for i in $(HFILES); do mv $$i 5_scratch; done # just in case someone wrote a header file
- if [ -f 2_makefile_deps ]; then rm 2_makefile_deps; fi
- if [ -f $(LIB) ]; then rm $(LIB); fi
- for i in $(EXECS); do if [ -e $$i ]; then rm $$i; fi; done
- @echo '______end make $@_____'
-
-
-# not ready ...
-# dist_clean is used to clean thing up before doing a checkin, hg add should be safe after a dist_clean
-# dist_clean will recurse into the include directory = $(APPLICATION), tests, and try if they are present
-#
-dist_clean:
- @echo '---- make $@:------------------------------------------------------------'
- @echo `pwd`'>'
- make clean
- if [ -d $(APPLICATION) ]; then cd $(APPLICATION); make clean; fi
- if [ -d 1_tests ]; then cd 1_tests; make clean; fi
- if [ -d 1_try ] ; then cd 1_try; make clean; fi
- @echo '______end make $@_____'
-
-
--include 2_makefile_deps
-
-# recipe for making object files:
-#
-%.o : %.c
- $(CC) $(CFLAGS) -c $<
-
-
-#
-$(LIB) : $(OBJECTS_LIB)
- ar rcs $(LIB) $(OBJECTS_LIB)
diff --git a/src/subu-common.lib.c b/src/subu-common.lib.c
new file mode 100644
index 0000000..9ce5a27
--- /dev/null
+++ b/src/subu-common.lib.c
@@ -0,0 +1,20 @@
+
+#include "subu-common.lib.h"
+
+#if INTERFACE
+typedef unsigned int uint;
+/*
+ Fedora 29's sss_cache is checking the inherited uid instead of the effective
+ uid, so setuid root scripts will fail when calling sss_cache.
+
+ Fedora 29's 'useradd' calls sss_cache, and useradd is called by our setuid root
+ program subu-mk-0.
+*/
+#define BUG_SSS_CACHE_RUID 1
+#endif
+
+// char *DB_File = "/etc/subudb";
+char DB_File[] = "subudb";
+uint Subuhome_Perms = 0700;
+uint First_Max_Subunumber = 114;
+char Subuland_Extension[] = "/subuland/";
diff --git a/src/subu-config.lib.c b/src/subu-config.lib.c
deleted file mode 100644
index de7bcbb..0000000
--- a/src/subu-config.lib.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
-The config file is maintained in SQLite
-
-Because user names of are of limited length, subu user names are always named _s.
-A separate table translates the numbers into the subu names.
-
-The first argument is the biggest subu number in the system, or one minus an
-starting point for subu numbering.
-
-currently a unit converted to base 10 will always fit in a 21 bit buffer.
-
-*/
-#include "subu-config.lib.h"
-
-#if INTERFACE
-#include
-#define ERR_CONFIG_FILE 1
-#endif
-
-#include
-#include
-#include
-
-//--------------------------------------------------------------------------------
-int subudb_schema(sqlite3 *db, uint max_subu_number){
- char max_subu_number_string[32];
- uint max_subu_number_string_len = snprintf(max_subu_number_string, 32, "%u", max_subu_number);
- if( max_subu_number_string_len >= 32 ){
- fprintf(stderr, "error exit, max_subu_number too big to deal with\n");
- return ERR_CONFIG_FILE;
- }
- char sql1[] = "CREATE TABLE Masteru_Subu(masteru_name TEXT, subuname TEXT, subu_username TEXT); ";
- char sql2[] = "CREATE TABLE Key_Int(key TEXT, value INT); ";
-
- char sql3_1[] = "INSERT INTO Key_Int VALUES( 'max_subu_number', ";
- char sql3_2[] = " ); ";
- char sql3_len = strlen(sql3_1) + max_subu_number_string_len + strlen(sql3_2) + 1;
- char sql3[sql3_len];
- strcpy(sql3, sql3_1);
- strcpy(sql3 + strlen(sql3_1), max_subu_number_string);
- strcpy(sql3 + strlen(sql3_1) + max_subu_number_string_len, sql3_2);
-
- char sql[strlen(sql1) + strlen(sql2) + strlen(sql3) + 1];
- strcpy(sql, sql1);
- strcpy(sql + strlen(sql1), sql2);
- strcpy(sql + strlen(sql1) + strlen(sql2), sql3);
-
- return sqlite3_exec(db, sql, NULL, NULL, NULL);
-}
-
-//--------------------------------------------------------------------------------
-
-// the call back for subu_number_next, note also 3_doc/sqlite3.txt
-static int subu_number_extract(void *nsp, int colcnt, char **colvals, char **colnames){
- if(colcnt >= 1){
- *(char **)nsp = strdup( colvals[0] );
- return 0;
- }
- return -1;
-}
-int subu_number_next(sqlite3 *db, char **nsp, char **mess){
- 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;";
- int rc = sqlite3_exec(db, sql, subu_number_extract, (void *)nsp, mess);
- return rc;
-}
-int subu_number_get(sqlite3 *db, int *n){
- char *sql = "SELECT value FROM Key_Int WHERE key = 'max_subu_number';";
- 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);
- }else{
- sqlite3_finalize(stmt);
- return rc; // woops this needs to return an error!, be sure it is not SQLITE_DONE
- }
- rc = sqlite3_step(stmt);
- sqlite3_finalize(stmt);
- return rc;
-}
-int subu_number_set(sqlite3 *db, int n){
- char *sql = "UPDATE Key_Int SET value = ?1 WHERE key = 'max_subu_number';";
- sqlite3_stmt *stmt;
- sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
- sqlite3_bind_int(stmt, 1, n);
- int rc = sqlite3_step(stmt);
- sqlite3_finalize(stmt);
- return rc;
-}
-
-
-//--------------------------------------------------------------------------------
-// put relation into Masteru_Subu table
-int subu_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);
- return rc;
-}
-
-//--------------------------------------------------------------------------------
-int subu_Masteru_Subu_get(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);
- sqlite3_finalize(stmt);
- return rc;
-}
-
-//--------------------------------------------------------------------------------
-int subu_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);
- return rc;
-}
diff --git a/src/subu-init.cli.c b/src/subu-init.cli.c
deleted file mode 100644
index 1dcd7b1..0000000
--- a/src/subu-init.cli.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-This command initializes the configuration file.
-
-*/
-#include "subu-init.cli.h"
-#include
-
-int main(){
- sqlite3 *db;
- if( sqlite3_open(Config_File, &db) != SQLITE_OK ){
- fprintf(stderr, "error exit, could not open configuration file \"%s\"\n", Config_File);
- return ERR_CONFIG_FILE;
- }
- if( subudb_schema(db, First_Max_Subu_number) != SQLITE_OK ){
- fprintf(stderr, "error exit, opened config file but could not build scheme\n");
- return ERR_CONFIG_FILE;
- }
- if( sqlite3_close(db) != SQLITE_OK ){
- fprintf(stderr, "error exit, could not close the db\n");
- return ERR_CONFIG_FILE;
- }
- return 0;
-}
diff --git a/src/subu-mk-0.cli.c b/src/subu-mk-0.cli.c
index 93c79d3..d9f9c74 100644
--- a/src/subu-mk-0.cli.c
+++ b/src/subu-mk-0.cli.c
@@ -14,19 +14,29 @@ int main(int argc, char **argv){
}
char *subuname = argv[1];
+ int rc;
sqlite3 *db;
- {
- int ret = sqlite3_open_v2(Config_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( ret != SQLITE_OK ){
- fprintf(stderr, "error exit, could not open configuration file \"%s\"\n", Config_File);
- return SUBU_ERR_CONFIG_FILE;
- }}
+ 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;
+ }
- {
- char *mess;
- int ret = subu_mk_0(&mess, db, subuname);
- subu_err("subu_mk_0", ret, mess);
+ char *mess;
+ rc = subu_mk_0(&mess, db, subuname);
+ if( rc ){
+ subu_err("subu_mk_0", rc, mess);
free(mess);
- return ret;
+ sqlite3_close(db);
+ return rc;
}
+
+ rc = sqlite3_close(db);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+
}
diff --git a/src/subu-number.cli.c b/src/subu-number.cli.c
deleted file mode 100644
index c4690e4..0000000
--- a/src/subu-number.cli.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-Set or get a new maximum subu number. Currently doesn't do the setting part.
-
-*/
-#include "subu-number.cli.h"
-#include
-#include
-#include
-
-int main(int argc, char **argv){
-
- if( argc > 2 ){
- fprintf(stderr, "usage: %s [n]\n",argv[0]);
- return SUBU_ERR_ARG_CNT;
- }
-
- int rc;
- sqlite3 *db;
- rc = sqlite3_open_v2(Config_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( rc != SQLITE_OK ){
- sqlite3_close(db);
- fprintf(stderr, "error exit, could not open configuration file\n");
- return SUBU_ERR_CONFIG_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;
- }
- int n = i;
- subu_number_set(db, n);
- }
-
- // read and print the current max
- int n;
- rc = subu_number_get(db, &n);
- if( rc == SQLITE_DONE ){
- printf("%d\n", n);
- }else{
- sqlite3_close(db);
- return SUBU_ERR_CONFIG_FILE;
- }
- rc = sqlite3_close(db);
- if( rc != SQLITE_OK ){
- fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db));
- return SUBU_ERR_CONFIG_FILE;
- }
- return 0;
-
-}
diff --git a/src/subu-rel-get.cli.c b/src/subu-rel-get.cli.c
deleted file mode 100644
index 737080c..0000000
--- a/src/subu-rel-get.cli.c
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-get the username from the config file
-for testing subu_Masteru_Subu_get_user
-
-*/
-#include "subu-rel-get.cli.h"
-#include
-#include
-
-int main(int argc, char **argv){
-
- if(argc != 3){
- fprintf(stderr, "usage: %s masteru_name subuname\n", argv[0]);
- return SUBU_ERR_ARG_CNT;
- }
-
- int rc;
- sqlite3 *db;
- rc = sqlite3_open_v2(Config_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( rc != SQLITE_OK ){
- fprintf(stderr, "could not open configuration file \"%s\"\n", Config_File);
- return SUBU_ERR_CONFIG_FILE;
- }
-
- char *masteru_name = argv[1];
- char *subuname = argv[2];
- char *subu_username;
-
- int ret = subu_Masteru_Subu_get(db, masteru_name, subuname, &subu_username);
- if( ret != SQLITE_DONE ){
- fprintf(stderr, "subu_Masteru_Subu_get indicates failure by returning %d\n",ret);
- fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db));
- return SUBU_ERR_CONFIG_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_CONFIG_FILE;
- }
- return 0;
-}
diff --git a/src/subu-rel-put.cli.c b/src/subu-rel-put.cli.c
deleted file mode 100644
index c930465..0000000
--- a/src/subu-rel-put.cli.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-puts a relation in the masteru/subu table
-
-*/
-#include "subu-rel-put.cli.h"
-#include
-#include
-
-int main(int argc, char **argv){
-
- if(argc != 4){
- fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
- return 1;
- }
- char *masteru_name = argv[1];
- char *subuname = argv[2];
- char *subu_username = argv[3];
-
- sqlite3 *db;
- {
- int ret = sqlite3_open_v2(Config_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( ret != SQLITE_OK ){
- fprintf(stderr, "could not open configuration file \"%s\"\n", Config_File);
- return SUBU_ERR_CONFIG_FILE;
- }}
-
- int ret = subu_Masteru_Subu_put(db, masteru_name, subuname, subu_username);
- if( ret != SQLITE_DONE ){
- fprintf(stderr, "subu_Masteru_Subu_put 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_CONFIG_FILE;
- }
- return 0;
-}
diff --git a/src/subu-rel-rm.cli.c b/src/subu-rel-rm.cli.c
deleted file mode 100644
index 06eb4e5..0000000
--- a/src/subu-rel-rm.cli.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-puts a relation in the masteru/subu table
-
-*/
-#include "subu-rel-rm.cli.h"
-#include
-#include
-
-int main(int argc, char **argv){
-
- if(argc != 4){
- fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
- return 1;
- }
- char *masteru_name = argv[1];
- char *subuname = argv[2];
- char *subu_username = argv[3];
-
- sqlite3 *db;
- {
- int ret = sqlite3_open_v2(Config_File, &db, SQLITE_OPEN_READWRITE, NULL);
- if( ret != SQLITE_OK ){
- fprintf(stderr, "could not open configuration file \"%s\"\n", Config_File);
- return SUBU_ERR_CONFIG_FILE;
- }}
-
- int ret = subu_Masteru_Subu_rm(db, masteru_name, subuname, subu_username);
- if( ret != SQLITE_DONE ){
- fprintf(stderr, "subu_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_CONFIG_FILE;
- }
- return 0;
-}
diff --git a/src/subu-rm-0.cli.c b/src/subu-rm-0.cli.c
index 93c79d3..a7e5926 100644
--- a/src/subu-rm-0.cli.c
+++ b/src/subu-rm-0.cli.c
@@ -2,7 +2,7 @@
subu-mk-0 command
*/
-#include "subu-mk-0.cli.h"
+#include "subu-rm-0.cli.h"
#include
#include
@@ -16,16 +16,16 @@ int main(int argc, char **argv){
sqlite3 *db;
{
- int ret = sqlite3_open_v2(Config_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
if( ret != SQLITE_OK ){
- fprintf(stderr, "error exit, could not open configuration file \"%s\"\n", Config_File);
- return SUBU_ERR_CONFIG_FILE;
+ fprintf(stderr, "error exit, could not open db file \"%s\"\n", DB_File);
+ return SUBU_ERR_DB_FILE;
}}
{
- char *mess;
- int ret = subu_mk_0(&mess, db, subuname);
- subu_err("subu_mk_0", ret, mess);
+ char *mess=0;
+ int ret = subu_rm_0(&mess, db, subuname);
+ subu_err("subu_rm_0", ret, mess);
free(mess);
return ret;
}
diff --git a/src/subu.lib.c b/src/subu.lib.c
index e20fac3..d456c53 100644
--- a/src/subu.lib.c
+++ b/src/subu.lib.c
@@ -1,5 +1,5 @@
/*
- sqllite3 is used to maintain the configuration file, which is currently compiled
+ 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
@@ -9,11 +9,11 @@
subu-mk-0 synthesizes a new user user name s, calls useradd to creat
the new uswer account, and enters the relationship between masteru, subu, and
- s in the config file. It is this relation in the config file that
+ s in the db file. It is this relation in the db file that
associates the subuname with the s user.
subu-rm-0 uses userdel to delete the related s user account. It
- then removes the relaton from the config file.
+ then removes the relaton from the db file.
subu-mk-0 and subu-rm-0 are setuid root scripts.
@@ -86,7 +86,6 @@ char *userdel_mess(int err){
//--------------------------------------------------------------------------------
-// an instance is subu_mk_0_ctx is returned by subu_mk_0
//
#if INTERFACE
#define SUBU_ERR_ARG_CNT 1
@@ -96,7 +95,7 @@ char *userdel_mess(int err){
#define SUBU_ERR_RMDIR_SUBUHOME 5
#define SUBU_ERR_SUBUNAME_MALFORMED 6
#define SUBU_ERR_MASTERU_HOMELESS 7
-#define SUBU_ERR_CONFIG_FILE 8
+#define SUBU_ERR_DB_FILE 8
#define SUBU_ERR_SUBUHOME_EXISTS 9
#define SUBU_ERR_BUG_SSS 10
#define SUBU_ERR_FAILED_USERADD 11
@@ -121,8 +120,8 @@ void subu_err(char *fname, int err, char *mess){
case SUBU_ERR_MALLOC:
perror(fname);
break;
- case SUBU_ERR_CONFIG_FILE:
- fprintf(stderr, "config file error: %s", Config_File); // Config_File is in common
+ case SUBU_ERR_DB_FILE:
+ fprintf(stderr, "db file error: %s", DB_File); // DB_File is in common
break;
case SUBU_ERR_MASTERU_HOMELESS:
fprintf(stderr,"Masteru, \"%s\", has no home directory", mess);
@@ -209,14 +208,11 @@ static int masteru_rmdir_subuhome(void *arg){
//--------------------------------------------------------------------------------
// build strings
//
-static int mk_subu_user(sqlite3 *db, char **mess, char **subu_username){
- char *ns=0; // 'ns' Number as String
- if( subu_number_next( db, &ns, mess ) != SQLITE_OK ) return SUBU_ERR_CONFIG_FILE;
- size_t ns_len = strlen(ns);
- *subu_username = malloc(1 + ns_len + 1); // the first 1 is for the "s" prefix
- if( !*subu_username ) SUBU_ERR_MALLOC;
- strcpy(*subu_username, "s");
- strcpy(*subu_username + 1, ns);
+static int mk_subu_user(char **mess, sqlite3 *db, char *masteru_name, int n, char **subu_username){
+ size_t len = 0;
+ FILE* name_stream = open_memstream(subu_username, &len);
+ fprintf(name_stream, "s%x", n);
+ fclose(name_stream);
return 0;
}
@@ -264,21 +260,18 @@ static int mk_subuhome(char *subuland, char *subuname, char **subuhome){
//===============================================================================
int subu_mk_0(char **mess, sqlite3 *db, char *subuname){
- int ret;
+ int rc;
if(mess)*mess = 0;
//--------------------------------------------------------------------------------
+ size_t subuname_len;
+ rc = allowed_subuname(mess, subuname, &subuname_len);
+ if(rc) return rc;
#ifdef DEBUG
- dbprintf("Check that subuname is well formed and find its length\n");
+ dbprintf("subuname is well formed\n");
#endif
- size_t subuname_len;
- ret = allowed_subuname(mess, subuname, &subuname_len);
- if(ret) return ret;
//--------------------------------------------------------------------------------
- #ifdef DEBUG
- dbprintf("Check 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;
@@ -295,30 +288,59 @@ int subu_mk_0(char **mess, sqlite3 *db, char *subuname){
}
//--------------------------------------------------------------------------------
- // various strings that we will need
- char *subu_username = 0;
+ // lookup the masteru name
char *masteru_name = 0;
char *masteru_home = 0;
+ rc = mk_masteru_name(masteru_uid, &masteru_name, &masteru_home);
+ if(rc) return rc;
+ #ifdef DEBUG
+ dbprintf("masteru_name: \"%s\"\n", masteru_name);
+ #endif
+
+ db_begin(db);
+ int n;
+ rc = subudb_number_get(db, masteru_name, &n);
+ if( rc == SQLITE_OK ){
+ n++;
+ rc = subudb_number_set(db, masteru_name, n);
+ if( rc != SQLITE_OK ){
+ db_rollback(db);
+ return SUBU_ERR_DB_FILE;
+ }
+ }else{ // perhaps this masteru's first subu, so we will try init
+ n = First_Max_Subunumber;
+ rc = subudb_number_init(db, masteru_name, n);
+ if( rc != SQLITE_OK ){
+ db_rollback(db);
+ return SUBU_ERR_DB_FILE;
+ }
+ }
+ db_commit(db);
+ #ifdef DEBUG
+ dbprintf("masteru max subunumber: %d\n", n);
+ #endif
+
+ //--------------------------------------------------------------------------------
+ // subu details
+ char *subu_username = 0;
char *subuland = 0;
char *subuhome = 0; // the name of the directory to put in subuland, not subu_user home dir
- ret =
- mk_subu_user(db, mess, &subu_username)
- ||
- mk_masteru_name(masteru_uid, &masteru_name, &masteru_home)
+ rc =
+ mk_subu_user(mess, db, masteru_name, n, &subu_username)
||
mk_subuland(masteru_home, &subuland)
||
mk_subuhome(subuland, subuname, &subuhome)
;
- if(ret) RETURN(ret);
+ if(rc) RETURN(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
{
- #ifdef DEBUG
- dbprintf("as masteru, making the directory \"%s\"\n", subuhome);
- #endif
struct stat st;
if( stat(subuhome, &st) != -1 ){
if(mess)*mess = strdup(subuhome);
@@ -341,7 +363,7 @@ int subu_mk_0(char **mess, sqlite3 *db, char *subuname){
}
}
#ifdef DEBUG
- dbprintf("masteru made directory \"%s\"\n", subuhome);
+ dbprintf("made directory \"%s\"\n", subuhome);
#endif
//--------------------------------------------------------------------------------
@@ -393,10 +415,10 @@ int subu_mk_0(char **mess, sqlite3 *db, char *subuname){
dbprintf("setting the masteru_name, subuname, subu_username relation\n");
#endif
{
- int ret = subu_Masteru_Subu_put(db, masteru_name, subuname, subu_username);
- if( ret != SQLITE_DONE ){
+ int rc = subudb_Masteru_Subu_put(db, masteru_name, subuname, subu_username);
+ if( rc != SQLITE_DONE ){
if(mess)*mess = strdup("insert of masteru subu relation failed");
- RETURN(SUBU_ERR_CONFIG_FILE);
+ RETURN(SUBU_ERR_DB_FILE);
}
}
#ifdef DEBUG
@@ -408,7 +430,7 @@ int subu_mk_0(char **mess, sqlite3 *db, char *subuname){
//================================================================================
int subu_rm_0(char **mess, sqlite3 *db, char *subuname){
- int ret;
+ int rc;
if(mess)*mess = 0;
//--------------------------------------------------------------------------------
@@ -416,8 +438,8 @@ int subu_rm_0(char **mess, sqlite3 *db, char *subuname){
dbprintf("Check that subuname is well formed and find its length\n");
#endif
size_t subuname_len;
- ret = allowed_subuname(mess, subuname, &subuname_len);
- if(ret) return ret;
+ rc = allowed_subuname(mess, subuname, &subuname_len);
+ if(rc) return rc;
//--------------------------------------------------------------------------------
#ifdef DEBUG
@@ -440,44 +462,47 @@ int subu_rm_0(char **mess, sqlite3 *db, char *subuname){
//--------------------------------------------------------------------------------
// various strings that we will need
+ #ifdef DEBUG
+ dbprintf("building strings.\n");
+ #endif
char *subu_username = 0;
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
- ret =
+ rc =
mk_masteru_name(masteru_uid, &masteru_name, &masteru_home)
||
mk_subuland(masteru_home, &subuland)
||
mk_subuhome(subuland, subuname, &subuhome)
;
- if(ret) RETURN(ret);
+ if(rc) RETURN(rc);
#ifdef DEBUG
dbprintf("looking up subu_username given masteru_name/subuname\n");
#endif
{
- int sgret = subu_Masteru_Subu_get(db, masteru_name, subuname, &subu_username);
+ int sgret = subudb_Masteru_Subu_get(db, masteru_name, subuname, &subu_username);
if( sgret != SQLITE_DONE ){
- if(mess) *mess = strdup("subu requested for removal not found under this masteru in config file");
- ret = SUBU_ERR_CONFIG_SUBU_NOT_FOUND;
- RETURN(ret);
+ if(mess) *mess = strdup("subu requested for removal not found under this masteru in db file");
+ rc = SUBU_ERR_CONFIG_SUBU_NOT_FOUND;
+ RETURN(rc);
}
- #ifdef DEBUG
- printf("subu_username: %s\n", subu_username);
- #endif
}
+ #ifdef DEBUG
+ printf("subu_username: %s\n", subu_username);
+ #endif
//--------------------------------------------------------------------------------
#ifdef DEBUG
dbprintf("remove the masteru_name, subuname, subu_username relation\n");
#endif
{
- int ret = subu_Masteru_Subu_rm(db, masteru_name, subuname, subu_username);
- if( ret != SQLITE_DONE ){
+ int rc = subudb_Masteru_Subu_rm(db, masteru_name, subuname, subu_username);
+ if( rc != SQLITE_DONE ){
if(mess)*mess = strdup("removal of masteru subu relation failed");
- RETURN(SUBU_ERR_CONFIG_FILE);
+ RETURN(SUBU_ERR_DB_FILE);
}
}
@@ -517,8 +542,8 @@ int subu_rm_0(char **mess, sqlite3 *db, char *subuname){
dbprintf("setting inherited real uid to 0 to accomodate SSS_CACHE UID BUG\n");
#endif
if( setuid(0) == -1 ){
- ret = SUBU_ERR_BUG_SSS;
- RETURN(ret);
+ rc = SUBU_ERR_BUG_SSS;
+ RETURN(rc);
}
#endif
char *command = "/usr/sbin/userdel";
diff --git a/src/subudb-init.cli.c b/src/subudb-init.cli.c
new file mode 100644
index 0000000..429c98e
--- /dev/null
+++ b/src/subudb-init.cli.c
@@ -0,0 +1,23 @@
+/*
+This command initializes the db file.
+
+*/
+#include "subudb-init.cli.h"
+#include
+
+int main(){
+ sqlite3 *db;
+ if( sqlite3_open(DB_File, &db) != SQLITE_OK ){
+ fprintf(stderr, "error exit, could not open db file \"%s\"\n", DB_File);
+ return SUBU_ERR_DB_FILE;
+ }
+ if( subudb_schema(db) != SQLITE_OK ){
+ fprintf(stderr, "error exit, opened db file but could not build schema\n");
+ return SUBU_ERR_DB_FILE;
+ }
+ if( sqlite3_close(db) != SQLITE_OK ){
+ fprintf(stderr, "error exit, could not close the db\n");
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+}
diff --git a/src/subudb-number.cli.c b/src/subudb-number.cli.c
new file mode 100644
index 0000000..265e7e9
--- /dev/null
+++ b/src/subudb-number.cli.c
@@ -0,0 +1,61 @@
+/*
+Set or get a new maximum subu number. Currently doesn't do the setting part.
+
+*/
+#include "subudb-number.cli.h"
+#include
+#include
+#include
+
+int main(int argc, char **argv){
+
+ if( argc < 2 || argc > 3){
+ fprintf(stderr, "usage: %s masteru_name [n]\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;
+ }
+
+ // then arg[2] holds a number to set the max to
+ if(argc == 3){
+ long int i = strtol(argv[2], 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;
+ }
+ int n = i;
+ subudb_number_set(db, masteru_name, n);
+ }
+
+ // read and print the current max
+ int n;
+ rc = subudb_number_get(db, masteru_name, &n);
+ if( rc == SQLITE_DONE ){
+ printf("%d\n", n);
+ }else{
+ fprintf(stderr, "lookup failed %s\n", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ return SUBU_ERR_DB_FILE;
+ }
+ rc = sqlite3_close(db);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+
+}
diff --git a/src/subudb-rel-get.cli.c b/src/subudb-rel-get.cli.c
new file mode 100644
index 0000000..442d61e
--- /dev/null
+++ b/src/subudb-rel-get.cli.c
@@ -0,0 +1,42 @@
+/*
+get the username from the db file
+for testing subudb_Masteru_Subu_get_user
+
+*/
+#include "subudb-rel-get.cli.h"
+#include
+#include
+
+int main(int argc, char **argv){
+
+ if(argc != 3){
+ fprintf(stderr, "usage: %s masteru_name subuname\n", argv[0]);
+ return SUBU_ERR_ARG_CNT;
+ }
+
+ int rc;
+ sqlite3 *db;
+ rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( rc != SQLITE_OK ){
+ fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
+ return SUBU_ERR_DB_FILE;
+ }
+
+ char *masteru_name = argv[1];
+ char *subuname = argv[2];
+ char *subu_username;
+
+ int ret = subudb_Masteru_Subu_get(db, masteru_name, subuname, &subu_username);
+ if( ret != SQLITE_DONE ){
+ fprintf(stderr, "subudb_Masteru_Subu_get indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ ret = sqlite3_close(db);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+}
diff --git a/src/subudb-rel-put.cli.c b/src/subudb-rel-put.cli.c
new file mode 100644
index 0000000..f679d24
--- /dev/null
+++ b/src/subudb-rel-put.cli.c
@@ -0,0 +1,41 @@
+/*
+puts a relation in the masteru/subu table
+
+*/
+#include "subudb-rel-put.cli.h"
+#include
+#include
+
+int main(int argc, char **argv){
+
+ if(argc != 4){
+ fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
+ return 1;
+ }
+ char *masteru_name = argv[1];
+ char *subuname = argv[2];
+ char *subu_username = argv[3];
+
+ sqlite3 *db;
+ {
+ int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
+ return SUBU_ERR_DB_FILE;
+ }}
+
+ int ret = subudb_Masteru_Subu_put(db, masteru_name, subuname, subu_username);
+ if( ret != SQLITE_DONE ){
+ fprintf(stderr, "subudb_Masteru_Subu_put indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db));
+ printf("put failed\n");
+ return SUBU_ERR_DB_FILE;
+ }
+ ret = sqlite3_close(db);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+}
diff --git a/src/subudb-rel-rm.cli.c b/src/subudb-rel-rm.cli.c
new file mode 100644
index 0000000..3d15ca9
--- /dev/null
+++ b/src/subudb-rel-rm.cli.c
@@ -0,0 +1,41 @@
+/*
+puts a relation in the masteru/subu table
+
+*/
+#include "subudb-rel-rm.cli.h"
+#include
+#include
+
+int main(int argc, char **argv){
+
+ if(argc != 4){
+ fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
+ return 1;
+ }
+ char *masteru_name = argv[1];
+ char *subuname = argv[2];
+ char *subu_username = argv[3];
+
+ sqlite3 *db;
+ {
+ int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
+ return SUBU_ERR_DB_FILE;
+ }}
+
+ int ret = subudb_Masteru_Subu_rm(db, masteru_name, subuname, subu_username);
+ if( ret != SQLITE_DONE ){
+ fprintf(stderr, "subudb_Masteru_Subu_rm indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db));
+ printf("put failed\n");
+ return 2;
+ }
+ ret = sqlite3_close(db);
+ if( ret != SQLITE_OK ){
+ fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret);
+ fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db));
+ return SUBU_ERR_DB_FILE;
+ }
+ return 0;
+}
diff --git a/src/subudb.lib.c b/src/subudb.lib.c
new file mode 100644
index 0000000..99ec7f0
--- /dev/null
+++ b/src/subudb.lib.c
@@ -0,0 +1,157 @@
+/*
+The db file is maintained in SQLite
+
+Because user names of are of limited length, subu user names are always named _s.
+A separate table translates the numbers into the subu names.
+
+The first argument is the biggest subu number in the system, or one minus an
+starting point for subu numbering.
+
+currently a unit converted to base 10 will always fit in a 21 bit buffer.
+
+Each of these returns SQLITE_OK upon success
+*/
+#include "subudb.lib.h"
+
+#if INTERFACE
+#include
+#endif
+
+#include
+#include
+#include
+
+//--------------------------------------------------------------------------------
+// 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){
+ char sql[] =
+ "CREATE TABLE Masteru_Subu(masteru_name TEXT, subuname TEXT, subu_username TEXT);"
+ "CREATE TABLE Masteru_Max(masteru_name TEXT, max_subu_number INT);"
+ ;
+ return sqlite3_exec(db, sql, NULL, NULL, NULL);
+}
+
+//--------------------------------------------------------------------------------
+int subudb_number_init(sqlite3 *db, char *masteru_name, int n){
+ int rc;
+ char *sql = "INSERT INTO Masteru_Max (masteru_name, max_subu_number) VALUES (?1, ?2);";
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+ sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC);
+ sqlite3_bind_int(stmt, 2, n);
+ rc = sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+ if( rc == SQLITE_DONE ) return SQLITE_OK;
+ return rc;
+}
+
+int subudb_number_get(sqlite3 *db, char *masteru_name, int *n){
+ char *sql = "SELECT max_subu_number FROM Masteru_Max WHERE masteru_name = ?1;";
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+ sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC);
+ 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 SQLITE_OK;
+ return rc;
+ }
+ // should have a message return, suppose
+ sqlite3_finalize(stmt);
+ return SQLITE_NOTFOUND;
+}
+
+// on success returns SQLITE_DONE
+int subudb_number_set(sqlite3 *db, char *masteru_name, int n){
+ int rc;
+ char *sql = "UPDATE Masteru_Max SET max_subu_number = ?1 WHERE masteru_name = ?2;";
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+ sqlite3_bind_int(stmt, 1, n);
+ sqlite3_bind_text(stmt, 2, masteru_name, -1, SQLITE_STATIC);
+ rc = sqlite3_step(stmt);
+ sqlite3_finalize(stmt);
+ if( rc == SQLITE_DONE ) return SQLITE_OK;
+ return rc;
+}
+
+// returns SQLITE_DONE or an error code
+// removes masteru/max_number relation from table
+int subudb_number_rm(sqlite3 *db, char *masteru_name){
+ char *sql = "DELETE FROM Masteru_Max WHERE masteru_name = ?1;";
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+ sqlite3_bind_text(stmt, 1, masteru_name, -1, SQLITE_STATIC);
+ int 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);
+ return rc;
+}
+
+//--------------------------------------------------------------------------------
+int subudb_Masteru_Subu_get(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);
+ sqlite3_finalize(stmt);
+ return rc;
+}
+
+//--------------------------------------------------------------------------------
+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);
+ return rc;
+}
diff --git a/tools/bin/gitadd b/tools/bin/gitadd
new file mode 100755
index 0000000..c2f2e02
--- /dev/null
+++ b/tools/bin/gitadd
@@ -0,0 +1,5 @@
+#!/bin/bash
+set -x
+make dist_clean
+git add "$@"
+
diff --git a/tools/bin/make b/tools/bin/make
new file mode 100755
index 0000000..3312575
--- /dev/null
+++ b/tools/bin/make
@@ -0,0 +1,2 @@
+#!/bin/bash
+/usr/bin/make -f 7_makefile "$@"
diff --git a/tools/bin/makeheaders b/tools/bin/makeheaders
new file mode 100755
index 0000000..a50d9a0
Binary files /dev/null and b/tools/bin/makeheaders differ
diff --git a/tools/bin/setuid_root.sh b/tools/bin/setuid_root.sh
new file mode 100755
index 0000000..aedd564
--- /dev/null
+++ b/tools/bin/setuid_root.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+chown root "$@" && \
+chmod u+rsx,u-w,go+rx-s-w "$@"
+
diff --git a/tools/doc/makeheaders.html b/tools/doc/makeheaders.html
new file mode 100644
index 0000000..289da58
--- /dev/null
+++ b/tools/doc/makeheaders.html
@@ -0,0 +1,1150 @@
+
+The Makeheaders Program
+
+
The Makeheaders Program
+
+
+
+This document describes makeheaders,
+a tool that automatically generates “.h”
+files for a C or C++ programming project.
+
+A piece of C source code can be one of two things:
+a declaration or a definition.
+A declaration is source text that gives information to the
+compiler but doesn't directly result in any code being generated.
+A definition is source text that results in executable machine
+instructions or initialization data.
+(These two terms are sometimes used inconsistently by other authors.
+In particular, many people reverse the meanings of these words when
+discussing Pascal or Ada code.
+The meanings described here are the same as used in the ANSI-C
+standards document.)
+
+
+
+Declarations in C include things such as the following:
+
+
Typedefs.
+
Structure, union and enumeration declarations.
+
Function and procedure prototypes.
+
Preprocessor macros and #defines.
+
“extern” variable declarations.
+
+
+
+
+Definitions in C, on the other hand, include these kinds of things:
+
+
Variable definitions.
+
The bodies of functions and procedures.
+
Initialization data.
+
+
+
+
+The distinction between a declaration and a definition is common in
+modern software engineering.
+Another way of looking at the difference is that the declaration
+is the interface and the definition is the implementation.
+
+
+
+In C programs, it has always been the tradition that declarations are
+put in files with the “.h” suffix and definitions are
+placed in “.c” files.
+The .c files contain “#include” preprocessor statements
+that cause the contents of .h files to be included as part of the
+source code when the .c file is compiled.
+In this way, the .h files define the interface to a subsystem and
+the .c files define how the subsystem is implemented.
+
+
+
+
1.1 Problems With The Traditional Approach
+
+
+As the art of computer programming continues to advance, and the size
+and complexity of programs continues to swell, the traditional C
+approach of placing declarations and definitions in separate files begins
+to present the programmer with logistics and
+maintenance problems.
+To wit:
+
+
+
+
+
+In large codes with many source files, it becomes difficult to determine
+which .h files should be included in which .c files.
+
+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.
+
+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.
+
+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.
+
+The C programming language requires that declarations depending upon
+each other must occur in a particular order.
+In a program with complex, interwoven data structures, the correct
+declaration order can become very difficult to determine manually,
+especially when the declarations involved are spread out over several
+files.
+
+
+
+
+
1.2 The Makeheaders Solution
+
+
+The makeheaders program is designed to ameliorate the problems associated
+with the traditional C programming model by automatically generating
+the interface information in the .h files from
+interface information contained in other .h files and
+from implementation information in the .c files.
+When the makeheaders program is run, it scans the source
+files for a project,
+then generates a series of new .h files, one for each .c file.
+The generated .h files contain exactly those declarations required by the
+corresponding .c files, no more and no less.
+
+
+
+The makeheaders programming model overcomes all of the objections to the
+traditional C programming model.
+
+
+Because all declarations needed by a .c file are contained in a
+single .h file, there is never any question about what .h files
+a .c will need to include. If the .c file is named
+alpha.c then it must include only the single .h file
+named alpha.h.
+(The .c file might also use some include files from the standard
+library, such as <stdio.h>, but that is another matter.)
+
+The generated .h files do not include other .h files, and so there
+are no include chains to worry about.
+The file alpha.c depends on alpha.h and
+nothing more.
+
+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.
+
+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.
+
+The makeheaders program automatically sorts declarations into the
+correct order, completely eliminating the wearisome and error-prone
+task of sorting declarations by hand.
+
+
+
+
+In addition, the makeheaders program is fast and unintrusive.
+It is a simple matter to incorporate makeheaders into a Makefile
+so that makeheaders will be run automatically whenever the project
+is rebuilt.
+And the burden of running makeheaders is light.
+It will easily process tens of thousands of lines of source
+code per second.
+
+
+
+
2.0 Running The Makeheaders Program
+
+
+The makeheaders program is very easy to run.
+If you have a collection of C source code and include files in the working
+directory, then you can run makeheaders to generate appropriate .h
+files using the following command:
+
+ makeheaders *.[ch]
+
+That's really all there is to it!
+This command will generate one .h file for every .c file.
+Any .h files that were generated by a prior run of makeheaders
+are ignored,
+but manually entered .h files
+that contain structure declarations and so forth will be scanned and
+the declarations will be copied into the generated .h files as
+appropriate.
+But if makeheaders sees that the .h file that it has generated is no
+different from the .h file it generated last time, it doesn't update
+the file.
+This prevents the corresponding .c files from having to
+be needlessly recompiled.
+
+
+
+There are several options to the makeheaders program that can
+be used to alter its behavior.
+The default behavior is to write a single .h file for each .c file and
+to give the .h file the same base name as the .c file.
+Instead of generating a whole mess of .h files, you can, if you choose,
+generate a single big .h file that contains all declarations needed
+by all the .c files. Do this using the -h option to makeheaders.
+As follows:
+
+ makeheaders -h *.[ch] >common.h
+
+With the -h option, the .h file is not actually written to a disk file but
+instead appears on standard output, where you are free to redirect it
+into the file of your choice.
+
+
+
+A similar option is -H. Like the lower-case -h option, big -H
+generates a single include file on standard output. But unlike
+small -h, the big -H only emits prototypes and declarations that
+have been designated as “exportable”.
+The idea is that -H will generate an include file that defines
+the interface to a library.
+More will be said about this in section 3.4.
+
+
+
+Sometimes you want the base name of the .c file and the .h file to
+be different.
+For example, suppose you want the include file for alpha.c
+to be called beta.h.
+In this case, you would invoke makeheaders as follows:
+
+ makeheaders alpha.c:beta.h
+
+Any time a filename argument contains a colon, the name before the
+colon is taken to be the name of the .c file and the name after the
+colon is taken to be the name of the .h file.
+You can't use the shell's wildcard mechanism with this approach, but that
+normally isn't a problem in Makefiles, which is where this stuff
+comes in handy.
+
+
+
+If you want a particular file to be scanned by makeheaders but you
+don't want makeheaders to generate a header file for that file,
+then you can supply an empty header filename, like this:
+
+ makeheaders alpha.c beta.c gamma.c:
+
+In this example, makeheaders will scan the three files named
+“alpha.c”,
+“beta.c” and
+“gamma.c”
+but because of the colon on the end of third filename
+it will only generate headers for the first two files.
+Unfortunately,
+it is not possible to get makeheaders to process any file whose
+name contains a colon.
+
+
+
+In a large project, the length of the command line for makeheaders
+can become very long.
+If the operating system doesn't support long command lines
+(example: DOS and Win32) you may not be able to list all of the
+input files in the space available.
+In that case, you can use the “-f” option followed
+by the name of a file to cause makeheaders to read command line
+options and filename from the file instead of from the command line.
+For example, you might prepare a file named “mkhdr.dat”
+that contains text like this:
+
+The “-local” option causes makeheaders to
+generate of prototypes for “static” functions and
+procedures.
+Such prototypes are normally omitted.
+
+
+
+Finally, makeheaders also includes a “-doc” option.
+This command line option prevents makeheaders from generating any
+headers at all.
+Instead, makeheaders will write to standard output
+information about every definition and declaration that it encounters
+in its scan of source files.
+The information output includes the type of the definition or
+declaration and any comment that preceeds the definition or
+declaration.
+The output is in a format that can be easily parsed, and is
+intended to be read by another program that will generate
+documentation about the program.
+We'll talk more about this feature later.
+
+
+
+If you forget what command line options are available, or forget
+their exact name, you can invoke makeheaders using an unknown
+command line option (like “--help” or
+“-?”)
+and it will print a summary of the available options on standard
+error.
+If you need to process a file whose name begins with
+“-”,
+you can prepend a “./” to its name in order to get it
+accepted by the command line parser.
+Or, you can insert the special option “--” on the
+command line to cause all subsequent command line arguments to be treated as
+filenames even if their names begin with “-”.
+
+
+
+
3.0 Preparing Source Files For Use With Makeheaders
+
+
+Very little has to be done to prepare source files for use with
+makeheaders since makeheaders will read and understand ordinary
+C code.
+But it is important that you structure your files in a way that
+makes sense in the makeheaders context.
+This section will describe several typical uses of makeheaders.
+
+
+
+
3.1 The Basic Setup
+
+
+The simplest way to use makeheaders is to put all definitions in
+one or more .c files and all structure and type declarations in
+separate .h files.
+The only restriction is that you should take care to chose basenames
+for your .h files that are different from the basenames for your
+.c files.
+Recall that if your .c file is named (for example)
+“alpha.c”
+makeheaders will attempt to generate a corresponding header file
+named “alpha.h”.
+For that reason, you don't want to use that name for
+any of the .h files you write since that will prevent makeheaders
+from generating the .h file automatically.
+
+
+
+The structure of a .c file intented for use with makeheaders is very
+simple.
+All you have to do is add a single “#include” to the
+top of the file that sources the header file that makeheaders will generate.
+Hence, the beginning of a source file named “alpha.c”
+might look something like this:
+
+
+
+ /*
+ * Introductory comment...
+ */
+ #include "alpha.h"
+
+ /* The rest of your code... */
+
+
+
+Your manually generated header files require no special attention at all.
+Code them as you normally would.
+However, makeheaders will work better if you omit the
+“#if” statements people often put around the outside of
+header files that prevent the files from being included more than once.
+For example, to create a header file named “beta.h”,
+many people will habitually write the following:
+
+
+ #ifndef BETA_H
+ #define BETA_H
+
+ /* declarations for beta.h go here */
+
+ #endif
+
+
+You can forego this cleverness with makeheaders.
+Remember that the header files you write will never really be
+included by any C code.
+Instead, makeheaders will scan your header files to extract only
+those declarations that are needed by individual .c files and then
+copy those declarations to the .h files corresponding to the .c files.
+Hence, the “#if” wrapper serves no useful purpose.
+But it does make makeheaders work harder, forcing it to put
+the statements
+
+
+ #if !defined(BETA_H)
+ #endif
+
+
+around every declaration that it copies out of your header file.
+No ill effect should come of this, but neither is there any benefit.
+
+
+
+Having prepared your .c and .h files as described above, you can
+cause makeheaders to generate its .h files using the following simple
+command:
+
+
+ makeheaders *.[ch]
+
+
+The makeheaders program will scan all of the .c files and all of the
+manually written .h files and then automatically generate .h files
+corresponding to all .c files.
+
+
+
+Note that
+the wildcard expression used in the above example,
+“*.[ch]”,
+will expand to include all .h files in the current directory, both
+those entered manually be the programmer and others generated automatically
+by a prior run of makeheaders.
+But that is not a problem.
+The makeheaders program will recognize and ignore any files it
+has previously generated that show up on its input list.
+
+
+
+
3.2 What Declarations Get Copied
+
+
+The following list details all of the code constructs that makeheaders
+will extract and place in
+the automatically generated .h files:
+
+
+
+
+When a function is defined in any .c file, a prototype of that function
+is placed in the generated .h file of every .c file that
+calls the function.
+
+
If the “static” keyword of C appears at the
+beginning of the function definition, the prototype is suppressed.
+If you use the “LOCAL” keyword where you would normally
+say “static”, then a prototype is generated, but it
+will only appear in the single header file that corresponds to the
+source file containing the function. For example, if the file
+alpha.c contains the following:
+
+ LOCAL int testFunc(void){
+ return 0;
+ }
+
+Then the header file alpha.h will contain
+
+ #define LOCAL static
+ LOCAL int testFunc(void);
+
+However, no other generated header files will contain a prototype for
+testFunc() since the function has only file scope.
+
+
When the “LOCAL” keyword is used, makeheaders will
+also generate a #define for LOCAL, like this:
+
+ #define LOCAL static
+
+so that the C compiler will know what it means.
+
+
If you invoke makeheaders with a “-local”
+command-line option, then it treats the “static”
+keyword like “LOCAL” and generates prototypes in the
+header file that corresponds to the source file containing the function
+definition.
+
+
+When a global variable is defined in a .c file, an
+“extern”
+declaration of that variable is placed in the header of every
+.c file that uses the variable.
+
+
+
+When a structure, union or enumeration declaration or a
+function prototype or a C++ class declaration appears in a
+manually produced .h file, that declaration is copied into the
+automatically generated
+.h files of all .c files that use the structure, union, enumeration,
+function or class.
+But declarations that appear in a
+.c file are considered private to that .c file and are not copied into
+any automatically generated files.
+
+
+
+All #defines and typedefs that appear in manually produced .h files
+are copied into automatically generated .h files as needed.
+Similar constructs that appear in .c files are considered private to
+those files and are not copied.
+
+
+
+When a structure, union or enumeration declaration appears in a .h
+file, makeheaders will automatically
+generate a typedef that allows the declaration to be referenced without
+the “struct”, “union” or
+“enum” qualifier.
+In other words, if makeheaders sees the code:
+
+ struct Examp { /* ... */ };
+
+it will automatically generate a corresponding typedef like this:
+
+ typedef struct Examp Examp;
+
+
+
+
+Makeheaders generates an error message if it encounters a function or
+variable definition within a .h file.
+The .h files are suppose to contain only interface, not implementation.
+C compilers will not enforce this convention, but makeheaders does.
+
+
+
+As a final note, we observe that automatically generated declarations
+are ordered as required by the ANSI-C programming language.
+If the declaration of some structure “X” requires a
+prior declaration of another structure “Y”, then Y will
+appear first in the generated headers.
+
+
+
+
3.3 How To Avoid Having To Write Any Header Files
+
+
+In my experience, large projects work better if all of the manually
+written code is placed in .c files and all .h files are generated
+automatically.
+This is slightly different for the traditional C method of placing
+the interface in .h files and the implementation in .c files, but
+it is a refreshing change that brings a noticable improvement to the
+coding experience.
+Others, I believe, share this view since I've
+noticed recent languages (ex: java, tcl, perl, awk) tend to
+support the one-file approach to coding as the only option.
+
+
+
+The makeheaders program supports putting both
+interface and implementation into the same source file.
+But you do have to tell makeheaders which part of the source file is the
+interface and which part is the implementation.
+Makeheaders has to know this in order to be able to figure out whether or
+not structures declarations, typedefs, #defines and so forth should
+be copied into the generated headers of other source files.
+
+
+
+You can instruct makeheaders to treat any part of a .c file as if
+it were a .h file by enclosing that part of the .c file within:
+
+ #if INTERFACE
+ #endif
+
+Thus any structure definitions that appear after the
+“#if INTERFACE” but before the corresponding
+“#endif” are eligable to be copied into the
+automatically generated
+.h files of other .c files.
+
+
+
+If you use the “#if INTERFACE” mechanism in a .c file,
+then the generated header for that .c file will contain a line
+like this:
+
+ #define INTERFACE 0
+
+In other words, the C compiler will never see any of the text that
+defines the interface.
+But makeheaders will copy all necessary definitions and declarations
+into the .h file it generates, so .c files will compile as if the
+declarations were really there.
+This approach has the advantage that you don't have to worry with
+putting the declarations in the correct ANSI-C order -- makeheaders
+will do that for you automatically.
+
+
+
+Note that you don't have to use this approach exclusively.
+You can put some declarations in .h files and others within the
+“#if INTERFACE” regions of .c files.
+Makeheaders treats all declarations alike, no matter where they
+come from.
+You should also note that a single .c file can contain as many
+“#if INTERFACE” regions as desired.
+
+
+
+
3.4 Designating Declarations For Export
+
+
+In a large project, one will often construct a hierarchy of
+interfaces.
+For example, you may have a group of 20 or so files that form
+a library used in several other parts of the system.
+Each file in this library will present two interfaces.
+One interface will be the routines and data structures it is
+willing to share with other files in the same library, and the
+second interface is those routines and data structures it wishes
+to make available to other subsystems.
+(The second interface is normally a subset of the first.)
+Ordinary C does not provide support for a tiered interface
+like this, but makeheaders does.
+
+
+
+Using makeheaders, it is possible to designate routines and data
+structures as being for “export”.
+Exported objects are visible not only to other files within the
+same library or subassembly but also to other
+libraries and subassemblies in the larger program.
+By default, makeheaders only makes objects visible to other members
+of the same library.
+
+
+
+That isn't the complete truth, actually.
+The semantics of C are such that once an object becomes visible
+outside of a single source file, it is also visible to any user
+of the library that is made from the source file.
+Makeheaders can not prevent outsiders for using non-exported resources,
+but it can discourage the practice by refusing to provide prototypes
+and declarations for the services it does not want to export.
+Thus the only real effect of the making an object exportable is
+to include it in the output makeheaders generates when it is run
+using the -H command line option.
+This is not a perfect solution, but it works well in practice.
+
+
+
+But trouble quickly arises when we attempt to devise a mechanism for
+telling makeheaders which prototypes it should export and which it should
+keep local.
+The built-in “static” keyword of C works well for
+prohibiting prototypes from leaving a single source file, but because C doesn't
+support a linkage hierarchy, there is nothing in the C language to help us.
+We'll have to invite our own keyword: “EXPORT”
+
+
+
+Makeheaders allows the EXPORT keyword to precede any function or
+procedure definition.
+The routine following the EXPORT keyword is then eligable to appear
+in the header file generated using the -H command line option.
+Note that if a .c file contains the EXPORT keyword, makeheaders will
+put the macro
+
+ #define EXPORT
+
+in the header file it generates for the .c file so that the EXPORT keyword
+will never be seen by the C compiler.
+
+
+
+But the EXPORT keyword only works for function and procedure definitions.
+For structure, union and enum definitions, typedefs, #defines and
+class declarations, a second mechanism is used.
+Just as any declarations or definition contained within
+
+ #if INTERFACE
+ #endif
+
+are visible to all files within the library, any declarations
+or definitions within
+
+ #if EXPORT_INTERFACE
+ #endif
+
+will become part of the exported interface.
+The “#if EXPORT_INTERFACE” mechanism can be used in
+either .c or .h files.
+(The “#if INTERFACE” can also be used in both .h and
+.c files, but since it's use in a .h file would be redundant, we haven't
+mentioned it before.)
+
+
+
+
3.5 Local declarations processed by makeheaders
+
+
+Structure declarations and typedefs that appear in .c files are normally
+ignored by makeheaders.
+Such declarations are only intended for use by the source file in which
+they appear and so makeheaders doesn't need to copy them into any
+generated header files.
+We call such declarations “private”.
+
+
+
+Sometimes it is convenient to have makeheaders sort a sequence
+of private declarations into the correct order for us automatically.
+Or, we could have static functions and procedures for which we would like
+makeheaders to generate prototypes, but the arguments to these
+functions and procedures uses private declarations.
+In both of these cases, we want makeheaders to be aware of the
+private declarations and copy them into the local header file,
+but we don't want makeheaders to propagate the
+declarations outside of the file in which they are declared.
+
+
+
+When this situation arises, enclose the private declarations
+within
+
+ #if LOCAL_INTERFACE
+ #endif
+
+A “LOCAL_INTERFACE” block works very much like the
+“INTERFACE” and
+“EXPORT_INTERFACE”
+blocks described above, except that makeheaders insures that the
+objects declared in a LOCAL_INTERFACE are only visible to the
+file containing the LOCAL_INTERFACE.
+
+
+
+
3.6 Using Makeheaders With C++ Code
+
+
+You can use makeheaders to generate header files for C++ code, in
+addition to C.
+Makeheaders will recognize and copy both “class”
+declarations
+and inline function definitions, and it knows not to try to generate
+prototypes for methods.
+
+
+
+In fact, makeheaders is smart enough to be used in projects that employ
+a mixture of C and C++.
+For example, if a C function is called from within a C++ code module,
+makeheaders will know to prepend the text
+
+ extern "C"
+
+to the prototype for that function in the C++ header file.
+Going the other way,
+if you try to call a C++ function from within C, an
+appropriate error message is issued, since C++ routines can not
+normally be called by C code (due to fact that most C++ compilers
+use name mangling to facilitate type-safe linkage.)
+
+
+
+No special command-line options are required to use makeheaders with
+C++ input. Makeheaders will recognize that its source code is C++
+by the suffix on the source code filename. Simple ".c" or ".h" suffixes
+are assumed to be ANSI-C. Anything else, including ".cc", ".C" and
+".cpp" is assumed to be C++.
+The name of the header file generated by makeheaders is derived from
+the name of the source file by converting every "c" to "h" and
+every "C" to "H" in the suffix of the filename.
+Thus the C++ source
+file “alpha.cpp” will induce makeheaders to
+generate a header file named “alpha.hpp”.
+
+
+
+Makeheaders augments class definitions by inserting prototypes to
+methods where appropriate. If a method definition begins with one
+of the special keywords PUBLIC, PROTECTED, or
+PRIVATE (in upper-case to distinguish them from the regular
+C++ keywords with the same meaning) then a prototype for that
+method will be inserted into the class definition. If none of
+these keywords appear, then the prototype is not inserted. For
+example, in the following code, the constructor is not explicitly
+declared in the class definition but makeheaders will add it there
+because of the PUBLIC keyword that appears before the constructor
+definition.
+
+The first form is preferred because only a single declaration of
+the constructor is required. The second form requires two declarations,
+one in the class definition and one on the defintion of the constructor.
+
+
+
3.6.1 C++ Limitations
+
+
+Makeheaders does not understand more recent
+C++ syntax such as templates and namespaces.
+Perhaps these issues will be addressed in future revisions.
+
+
+
+
3.7 Conditional Compilation
+
+
+The makeheaders program understands and tracks the conditional
+compilation constructs in the source code files it scans.
+Hence, if the following code appears in a source file
+
+The conditional compilation constructs can be nested to any depth.
+Makeheaders also recognizes the special case of
+
+ #if 0
+ #endif
+
+and treats the enclosed text as a comment.
+
+
+
+
3.8 Caveats
+
+
+The makeheaders system is designed to be robust
+but it is possible for a devious programmer to fool the system,
+usually with unhelpful consequences.
+This subsection is a guide to helping you avoid trouble.
+
+
+
+Makeheaders does not understand the old K&R style of function
+and procedure definitions.
+It only understands the modern ANSI-C style, and will probably
+become very confused if it encounters an old K&R function.
+Therefore you should take care to avoid putting K&R function definitions
+in your code.
+
+
+
+Makeheaders does not understand when you define more than one
+global variable with the same type separated by a comma.
+In other words, makeheaders does not understand this:
+
+ int a = 4, b = 5;
+
+The makeheaders program wants every variable to have its own
+definition. Like this:
+
+ int a = 4;
+ int b = 5;
+
+Notice that this applies to global variables only, not to variables
+you declare inside your functions.
+Since global variables ought to be exceedingly rare, and since it is
+good style to declare them separately anyhow, this restriction is
+not seen as a terrible hardship.
+
+
+
+Makeheaders does not support defining an enumerated or aggregate type in
+the same statement as a variable declaration. None of the following
+statements work completely:
+
+struct {int field;} a;
+struct Tag {int field;} b;
+struct Tag c;
+
+Instead, define types separately from variables:
+
+#if INTERFACE
+struct Tag {int field;};
+#endif
+Tag a;
+Tag b; /* No more than one variable per declaration. */
+Tag c; /* So must put each on its own line. */
+
+The makeheaders program processes its source file prior to sending
+those files through the C preprocessor.
+Hence, if you hide important structure information in preprocessor defines,
+makeheaders might not be able to successfully extract the information
+it needs from variables, functions and procedure definitions.
+For example, if you write this:
+
+ #define BEGIN {
+ #define END }
+
+at the beginning of your source file, and then try to create a function
+definition like this:
+
+ char *StrDup(const char *zSrc)
+ BEGIN
+ /* Code here */
+ END
+
+then makeheaders won't be able to find the end of the function definition
+and bad things are likely to happen.
+
+
+
+For most projects the code constructs that makeheaders cannot
+handle are very rare.
+As long as you avoid excessive cleverness, makeheaders will
+probably be able to figure out what you want and will do the right
+thing.
+
+
+
+Makeheaders has limited understanding of enums. In particular, it does
+not realize the significance of enumerated values, so the enum is not
+emitted in the header files when its enumerated values are used unless
+the name associated with the enum is also used. Moreover, enums can be
+completely anonymous, e.g. “enum {X, Y, Z};”.
+Makeheaders ignores such enums so they can at least be used within a
+single source file. Makeheaders expects you to use #define constants
+instead. If you want enum features that #define lacks, and you need the
+enum in the interface, bypass makeheaders and write a header file by
+hand, or teach makeheaders to emit the enum definition when any of the
+enumerated values are used, rather than only when the top-level name (if
+any) is used.
+
+
+
+
4.0 Using Makeheaders To Generate Documentation
+
+
+Many people have observed the advantages of generating program
+documentation directly from the source code:
+
+
Less effort is involved. It is easier to write a program than
+ it is to write a program and a document.
+
The documentation is more likely to agree with the code.
+ When documentation is derived directly from the code, or is
+ contained in comments immediately adjacent to the code, it is much
+ more likely to be correct than if it is contained in a separate
+ unrelated file in a different part of the source tree.
+
Information is kept in only one place. When a change occurs
+ in the code, it is not necessary to make a corresponding change
+ in a separate document. Just rerun the documentation generator.
+
+The makeheaders program does not generate program documentation itself.
+But you can use makeheaders to parse the program source code, extract
+the information that is relevant to the documentation and to pass this
+information to another tool to do the actual documentation preparation.
+
+
+
+When makeheaders is run with the “-doc” option, it
+emits no header files at all.
+Instead, it does a complete dump of its internal tables to standard
+output in a form that is easily parsed.
+This output can then be used by another program (the implementation
+of which is left as an exercise to the reader) that will use the
+information to prepare suitable documentation.
+
+
+
+The “-doc” option causes makeheaders to print
+information to standard output about all of the following objects:
+
+
C++ class declarations
+
Structure and union declarations
+
Enumerations
+
Typedefs
+
Procedure and function definitions
+
Global variables
+
Preprocessor macros (ex: “#define”)
+
+For each of these objects, the following information is output:
+
+
The name of the object.
+
The type of the object. (Structure, typedef, macro, etc.)
+
Flags to indicate if the declaration is exported (contained within
+ an EXPORT_INTERFACE block) or local (contained with LOCAL_INTERFACE).
+
A flag to indicate if the object is declared in a C++ file.
+
The name of the file in which the object was declared.
+
The complete text of any block comment that preceeds the declarations.
+
If the declaration occurred inside a preprocessor conditional
+ (“#if”) then the text of that conditional is
+ provided.
+
The complete text of a declaration for the object.
+
+The exact output format will not be described here.
+It is simple to understand and parse and should be obvious to
+anyone who inspects some sample output.
+
+
+
+
5.0 Compiling The Makeheaders Program
+
+
+The source code for makeheaders is a single file of ANSI-C code,
+approximately 3000 lines in length.
+The program makes only modest demands of the system and C library
+and should compile without alteration on most ANSI C compilers
+and on most operating systems.
+It is known to compile using several variations of GCC for Unix
+as well as Cygwin32 and MSVC 5.0 for Win32.
+
+
+
+
6.0 History
+
+
+The makeheaders program was first written by D. Richard Hipp
+(also the original author of
+SQLite and
+Fossil) in 1993.
+Hipp open-sourced the project immediately, but it never caught
+on with any other developers and it continued to be used mostly
+by Hipp himself for over a decade. When Hipp was first writing
+the Fossil version control system in 2006 and 2007, he used
+makeheaders on that project to help simplify the source code.
+As the popularity of Fossil increased, the makeheaders
+that was incorporated into the Fossil source tree became the
+"official" makeheaders implementation.
+
+
+
+As this paragraph is being composed (2016-11-05), Fossil is the
+only project known to Hipp that is still using makeheaders. On
+the other hand, makeheaders has served the Fossil project well and
+there are no plans remove it.
+
+
+
+
7.0 Summary And Conclusion
+
+
+The makeheaders program will automatically generate a minimal header file
+for each of a set of C source and header files, and will
+generate a composite header file for the entire source file suite,
+for either internal or external use.
+It can also be used as the parser in an automated program
+documentation system.
+
+
+
+The makeheaders program has been in use since 1994,
+in a wide variety of projects under both UNIX and Win32.
+In every project where it has been used, makeheaders has proven
+to be a very helpful aid
+in the construction and maintenance of large C codes.
+In at least two cases, makeheaders has facilitated development
+of programs that would have otherwise been all but impossible
+due to their size and complexity.
+
+
+
diff --git a/tools/lib/7_makefile b/tools/lib/7_makefile
new file mode 100755
index 0000000..ae06d60
--- /dev/null
+++ b/tools/lib/7_makefile
@@ -0,0 +1,156 @@
+# Copyright 2011 (C) Reasoning Technology Ltd. All Rights Reserved
+#
+# 2010 11 20 TWL Created
+# 2019 02 24 TWL modified for subu project and placed under MIT license
+
+# 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)
+
+# 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
+
+SHELL=/bin/bash
+SCRATCHDIR= 5_scratch # clean and others put things here
+CC=gcc
+CFLAGS=-std=gnu11 -fPIC -I. -ggdb -Werror -DDEBUG -DDEBUGDB
+#CFLAGS=-std=gnu11 -fPIC -I. -Werror
+LIB="2_lib/libsubu.a"
+LINKFLAGS=-L2_lib -lsubu -lsqlite3
+
+#these are the source files that exist
+SOURCES_LIB= $(wildcard *.lib.c)
+SOURCES_CLI= $(wildcard *.cli.c)
+SOURCES= $(SOURCES_LIB) $(SOURCES_CLI)
+
+#these are the object files to be made
+OBJECTS_LIB= $(patsubst %.c, %.o, $(SOURCES_LIB))
+OBJECTS_CLI= $(patsubst %.c, %.o, $(SOURCES_CLI))
+OBJECTS= $(OBJECTS_LIB) $(OBJECTS_CLI)
+
+#these are the header files that exist, makeheaders will want to see them
+HFILES = $(wildcard *.lib.h) $(wildcard *.cli.h)
+
+# sort causes compiles to go in lexical order by file name, this is used to order the tests e.g.
+EXECS= $(sort $(patsubst %.cli.c, %, $(wildcard *.cli.c)))
+
+all: version deps lib execs
+
+version:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ @echo makefile version 2.0
+ @echo "CC: " $(CC)
+ @echo "CFLAGS: " $(CFLAGS)
+ @echo "LIB: " $(LIB)
+ @echo "LINKFLAGS: " $(LINKFLAGS)
+ @echo '______end make $@_____'
+
+# safe to run this in an already setup or partially setup directory
+setup:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ if [ ! -e $(SCRATCHDIR) ]; then mkdir $(SCRATCHDIR); fi
+ if [ ! -e 1_tests ]; then mkdir 1_tests; fi
+ if [ ! -e 1_try ]; then mkdir 1_try; fi
+ if [ ! -e 2_bin ]; then mkdir 2_bin; fi
+ if [ ! -e 2_lib ]; then mkdir 2_lib; fi
+ if [ ! -e 2_doc ]; then mkdir 2_doc; fi
+ if [ ! -e 2_include ]; then mkdir 2_include; fi
+ if [ ! -e 5_deprecated ]; then mkdir 5_deprecated; fi
+ if [ ! -e 5_scratch ]; then mkdir 5_scratch; fi
+ @echo '______end make $@_____'
+
+
+deps:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ makeheaders $(SOURCES) $(HFILES)
+ sed -i '/^ *int *main *(.*)/d' *.h
+ $(CC) $(CFLAGS) -MM $(SOURCES) 1> 7_makefile_deps
+ for i in $(EXECS) ; do\
+ $(ECHO) >> 7_makefile_deps;\
+ $(ECHO) "2_bin/$$i : $$i.cli.o $(LIB)" >> 7_makefile_deps;\
+ $(ECHO) " $(CXX) -o 2_bin/$$i $$i.cli.o $(LINKFLAGS)" >> 7_makefile_deps;\
+ done
+ @echo '______end make $@_____'
+
+lib:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ if [ ! -e 7_makefile_deps ]; then make deps; fi
+ make sub_lib
+ @echo '______end make $@_____'
+
+sub_lib: $(LIB)
+
+
+execs: $(LIB)
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ if [ ! -e 7_makefile_deps ]; then make deps; fi
+ make sub_execs
+ @echo '-> sudo 2_bin/setuid_root.sh subu-mk-0 subu-rm-0'
+ cat 2_bin/setuid_root.sh
+ @echo -n "Are you sure? [y/N] " && read ans && [ $${ans:-N} == y ]
+ sudo 2_bin/setuid_root.sh subu-mk-0 subu-rm-0
+ @echo '______end make $@_____'
+
+sub_execs: $(patsubst %, 2_bin/%, $(EXECS))
+
+#not ready yet
+install: all
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ @if[ ! -e 1_tests_passed ]; then echo "can't install as tests have not passed"; fi
+ @test -e test_passed
+ for i in $(BIN); do cp $$i $(RT_BASE)/bin; done
+ cp $(LIB) $(RT_BASE)/lib
+ cp $(APPLICATION).h $(RT_BASE)/include
+ if [ -d $(APPLICATION) ]; then cp $(APPLICATION)/*.h $(RT_BASE)/include/$(APPLICATION); fi
+ @echo '______end make $@_____'
+
+clean:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ if [ -f subudb ]; then rm subudb; fi
+ for i in $(wildcard *~); do mv $$i $(SCRATCHDIR); done
+ for i in $(wildcard *.lib.o) $(wildcard *.cli.o); do rm $$i; done
+ for i in $(HFILES); do mv $$i 5_scratch; done # just in case someone wrote a header file
+ if [ -f 7_makefile_deps ]; then rm 7_makefile_deps; fi
+ @echo '______end make $@_____'
+
+
+# not ready ...
+# dist_clean is used to clean thing up before doing a checkin, hg add should be safe after a dist_clean
+# dist_clean will recurse into the include directory = $(APPLICATION), tests, and try if they are present
+#
+dist_clean:
+ @echo '---- make $@:------------------------------------------------------------'
+ @echo `pwd`'>'
+ make clean
+ if [ -d $(APPLICATION) ]; then cd $(APPLICATION); make clean; fi
+ if [ -f $(LIB) ]; then rm $(LIB); fi
+ for i in $(EXECS); do if [ -e 2_bin/$$i ]; then rm 2_bin/$$i; fi; done
+ if [ -d 1_tests ]; then cd 1_tests; make dist_clean; fi
+ if [ -d 1_try ] ; then cd 1_try; make dist_clean; fi
+ @echo '______end make $@_____'
+
+# not written yet
+# copies stuff from the src dir to the stage dirs
+# stage:
+
+
+-include 7_makefile_deps
+
+# recipe for making object files:
+#
+%.o : %.c
+ $(CC) $(CFLAGS) -c $<
+
+#
+$(LIB) : $(OBJECTS_LIB)
+ ar rcs $(LIB) $(OBJECTS_LIB)
diff --git a/tools/lib/bashrc b/tools/lib/bashrc
new file mode 100755
index 0000000..5cb8f77
--- /dev/null
+++ b/tools/lib/bashrc
@@ -0,0 +1,19 @@
+#
+
+umask 0077
+
+if [ $EMACS ]; then
+ echo Hello Emacs
+fi
+export PS1='\n$(/usr/local/bin/Z)\n\u@\h§\w§\n> '
+export PS2='>'
+
+PATH=~/tools/bin:$PATH
+
+PS_FORMAT=user:15,pid,%cpu,%mem,vsz,rss,tty,stat,start,time,command
+export PS_FORMAT
+
+EDITOR=emacs
+export EDITOR
+
+
diff --git a/tools/src/makeheaders.c b/tools/src/makeheaders.c
new file mode 100644
index 0000000..b58e787
--- /dev/null
+++ b/tools/src/makeheaders.c
@@ -0,0 +1,3739 @@
+
+
+
+
+
+
+Fossil: File Content
+
+
+
+
+
File
+src/makeheaders.c
+— part of check-in
+[8cecc544]
+at
+2018-11-02 15:21:54
+on branch trunk
+— Enhance makeheaders so that it is able to deal with static_assert() statements.
+(These do not come up in Fossil itself. This check-in is in response to use
+of Makeheaders by external projects.)
+ (user:
+drh
+size: 100011)
+[more...]
+
+
+
+
+/*
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** Copyright 1993 D. Richard Hipp. All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or
+** without modification, are permitted provided that the following
+** conditions are met:
+**
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+**
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+**
+** This software is provided "as is" and any express or implied warranties,
+** including, but not limited to, the implied warranties of merchantability
+** and fitness for a particular purpose are disclaimed. In no event shall
+** the author or contributors be liable for any direct, indirect, incidental,
+** special, exemplary, or consequential damages (including, but not limited
+** to, procurement of substitute goods or services; loss of use, data or
+** profits; or business interruption) however caused and on any theory of
+** liability, whether in contract, strict liability, or tort (including
+** negligence or otherwise) arising in any way out of the use of this
+** software, even if advised of the possibility of such damage.
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+** appropriate header files.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <string.h>
+
+#if defined( __MINGW32__) || defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__)
+# ifndef WIN32
+# define WIN32
+# endif
+#else
+# include <unistd.h>
+#endif
+
+/*
+** Macros for debugging.
+*/
+#ifdef DEBUG
+static int debugMask = 0;
+# define debug0(F,M) if( (F)&debugMask ){ fprintf(stderr,M); }
+# define debug1(F,M,A) if( (F)&debugMask ){ fprintf(stderr,M,A); }
+# define debug2(F,M,A,B) if( (F)&debugMask ){ fprintf(stderr,M,A,B); }
+# define debug3(F,M,A,B,C) if( (F)&debugMask ){ fprintf(stderr,M,A,B,C); }
+# define PARSER 0x00000001
+# define DECL_DUMP 0x00000002
+# define TOKENIZER 0x00000004
+#else
+# define debug0(Flags, Format)
+# define debug1(Flags, Format, A)
+# define debug2(Flags, Format, A, B)
+# define debug3(Flags, Format, A, B, C)
+#endif
+
+/*
+** The following macros are purely for the purpose of testing this
+** program on itself. They don't really contribute to the code.
+*/
+#define INTERFACE 1
+#define EXPORT_INTERFACE 1
+#define EXPORT
+
+/*
+** Each token in a source file is represented by an instance of
+** the following structure. Tokens are collected onto a list.
+*/
+typedef struct Token Token;
+struct Token {
+ const char *zText; /* The text of the token */
+ int nText; /* Number of characters in the token's text */
+ int eType; /* The type of this token */
+ int nLine; /* The line number on which the token starts */
+ Token *pComment; /* Most recent block comment before this token */
+ Token *pNext; /* Next token on the list */
+ Token *pPrev; /* Previous token on the list */
+};
+
+/*
+** During tokenization, information about the state of the input
+** stream is held in an instance of the following structure
+*/
+typedef struct InStream InStream;
+struct InStream {
+ const char *z; /* Complete text of the input */
+ int i; /* Next character to read from the input */
+ int nLine; /* The line number for character z[i] */
+};
+
+/*
+** Each declaration in the C or C++ source files is parsed out and stored as
+** an instance of the following structure.
+**
+** A "forward declaration" is a declaration that an object exists that
+** doesn't tell about the objects structure. A typical forward declaration
+** is:
+**
+** struct Xyzzy;
+**
+** Not every object has a forward declaration. If it does, thought, the
+** forward declaration will be contained in the zFwd field for C and
+** the zFwdCpp for C++. The zDecl field contains the complete
+** declaration text.
+*/
+typedef struct Decl Decl;
+struct Decl {
+ char *zName; /* Name of the object being declared. The appearance
+ ** of this name is a source file triggers the declaration
+ ** to be added to the header for that file. */
+ const char *zFile; /* File from which extracted. */
+ char *zIf; /* Surround the declaration with this #if */
+ char *zFwd; /* A forward declaration. NULL if there is none. */
+ char *zFwdCpp; /* Use this forward declaration for C++. */
+ char *zDecl; /* A full declaration of this object */
+ char *zExtra; /* Extra declaration text inserted into class objects */
+ int extraType; /* Last public:, protected: or private: in zExtraDecl */
+ struct Include *pInclude; /* #includes that come before this declaration */
+ int flags; /* See the "Properties" below */
+ Token *pComment; /* A block comment associated with this declaration */
+ Token tokenCode; /* Implementation of functions and procedures */
+ Decl *pSameName; /* Next declaration with the same "zName" */
+ Decl *pSameHash; /* Next declaration with same hash but different zName */
+ Decl *pNext; /* Next declaration with a different name */
+};
+
+/*
+** Properties associated with declarations.
+**
+** DP_Forward and DP_Declared are used during the generation of a single
+** header file in order to prevent duplicate declarations and definitions.
+** DP_Forward is set after the object has been given a forward declaration
+** and DP_Declared is set after the object gets a full declarations.
+** (Example: A forward declaration is "typedef struct Abc Abc;" and the
+** full declaration is "struct Abc { int a; float b; };".)
+**
+** The DP_Export and DP_Local flags are more permanent. They mark objects
+** that have EXPORT scope and LOCAL scope respectively. If both of these
+** marks are missing, then the object has library scope. The meanings of
+** the scopes are as follows:
+**
+** LOCAL scope The object is only usable within the file in
+** which it is declared.
+**
+** library scope The object is visible and usable within other
+** files in the same project. By if the project is
+** a library, then the object is not visible to users
+** of the library. (i.e. the object does not appear
+** in the output when using the -H option.)
+**
+** EXPORT scope The object is visible and usable everywhere.
+**
+** The DP_Flag is a temporary use flag that is used during processing to
+** prevent an infinite loop. It's use is localized.
+**
+** The DP_Cplusplus, DP_ExternCReqd and DP_ExternReqd flags are permanent
+** and are used to specify what type of declaration the object requires.
+*/
+#define DP_Forward 0x001 /* Has a forward declaration in this file */
+#define DP_Declared 0x002 /* Has a full declaration in this file */
+#define DP_Export 0x004 /* Export this declaration */
+#define DP_Local 0x008 /* Declare in its home file only */
+#define DP_Flag 0x010 /* Use to mark a subset of a Decl list
+ ** for special processing */
+#define DP_Cplusplus 0x020 /* Has C++ linkage and cannot appear in a
+ ** C header file */
+#define DP_ExternCReqd 0x040 /* Prepend 'extern "C"' in a C++ header.
+ ** Prepend nothing in a C header */
+#define DP_ExternReqd 0x080 /* Prepend 'extern "C"' in a C++ header if
+ ** DP_Cplusplus is not also set. If DP_Cplusplus
+ ** is set or this is a C header then
+ ** prepend 'extern' */
+
+/*
+** Convenience macros for dealing with declaration properties
+*/
+#define DeclHasProperty(D,P) (((D)->flags&(P))==(P))
+#define DeclHasAnyProperty(D,P) (((D)->flags&(P))!=0)
+#define DeclSetProperty(D,P) (D)->flags |= (P)
+#define DeclClearProperty(D,P) (D)->flags &= ~(P)
+
+/*
+** These are state properties of the parser. Each of the values is
+** distinct from the DP_ values above so that both can be used in
+** the same "flags" field.
+**
+** Be careful not to confuse PS_Export with DP_Export or
+** PS_Local with DP_Local. Their names are similar, but the meanings
+** of these flags are very different.
+*/
+#define PS_Extern 0x000800 /* "extern" has been seen */
+#define PS_Export 0x001000 /* If between "#if EXPORT_INTERFACE"
+ ** and "#endif" */
+#define PS_Export2 0x002000 /* If "EXPORT" seen */
+#define PS_Typedef 0x004000 /* If "typedef" has been seen */
+#define PS_Static 0x008000 /* If "static" has been seen */
+#define PS_Interface 0x010000 /* If within #if INTERFACE..#endif */
+#define PS_Method 0x020000 /* If "::" token has been seen */
+#define PS_Local 0x040000 /* If within #if LOCAL_INTERFACE..#endif */
+#define PS_Local2 0x080000 /* If "LOCAL" seen. */
+#define PS_Public 0x100000 /* If "PUBLIC" seen. */
+#define PS_Protected 0x200000 /* If "PROTECTED" seen. */
+#define PS_Private 0x400000 /* If "PRIVATE" seen. */
+#define PS_PPP 0x700000 /* If any of PUBLIC, PRIVATE, PROTECTED */
+
+/*
+** The following set of flags are ORed into the "flags" field of
+** a Decl in order to identify what type of object is being
+** declared.
+*/
+#define TY_Class 0x00100000
+#define TY_Subroutine 0x00200000
+#define TY_Macro 0x00400000
+#define TY_Typedef 0x00800000
+#define TY_Variable 0x01000000
+#define TY_Structure 0x02000000
+#define TY_Union 0x04000000
+#define TY_Enumeration 0x08000000
+#define TY_Defunct 0x10000000 /* Used to erase a declaration */
+
+/*
+** Each nested #if (or #ifdef or #ifndef) is stored in a stack of
+** instances of the following structure.
+*/
+typedef struct Ifmacro Ifmacro;
+struct Ifmacro {
+ int nLine; /* Line number where this macro occurs */
+ char *zCondition; /* Text of the condition for this macro */
+ Ifmacro *pNext; /* Next down in the stack */
+ int flags; /* Can hold PS_Export, PS_Interface or PS_Local flags */
+};
+
+/*
+** When parsing a file, we need to keep track of what other files have
+** be #include-ed. For each #include found, we create an instance of
+** the following structure.
+*/
+typedef struct Include Include;
+struct Include {
+ char *zFile; /* The name of file include. Includes "" or <> */
+ char *zIf; /* If not NULL, #include should be enclosed in #if */
+ char *zLabel; /* A unique label used to test if this #include has
+ * appeared already in a file or not */
+ Include *pNext; /* Previous include file, or NULL if this is the first */
+};
+
+/*
+** Identifiers found in a source file that might be used later to provoke
+** the copying of a declaration into the corresponding header file are
+** stored in a hash table as instances of the following structure.
+*/
+typedef struct Ident Ident;
+struct Ident {
+ char *zName; /* The text of this identifier */
+ Ident *pCollide; /* Next identifier with the same hash */
+ Ident *pNext; /* Next identifier in a list of them all */
+};
+
+/*
+** A complete table of identifiers is stored in an instance of
+** the next structure.
+*/
+#define IDENT_HASH_SIZE 2237
+typedef struct IdentTable IdentTable;
+struct IdentTable {
+ Ident *pList; /* List of all identifiers in this table */
+ Ident *apTable[IDENT_HASH_SIZE]; /* The hash table */
+};
+
+/*
+** The following structure holds all information for a single
+** source file named on the command line of this program.
+*/
+typedef struct InFile InFile;
+struct InFile {
+ char *zSrc; /* Name of input file */
+ char *zHdr; /* Name of the generated .h file for this input.
+ ** Will be NULL if input is to be scanned only */
+ int flags; /* One or more DP_, PS_ and/or TY_ flags */
+ InFile *pNext; /* Next input file in the list of them all */
+ IdentTable idTable; /* All identifiers in this input file */
+};
+
+/*
+** An unbounded string is able to grow without limit. We use these
+** to construct large in-memory strings from lots of smaller components.
+*/
+typedef struct String String;
+struct String {
+ int nAlloc; /* Number of bytes allocated */
+ int nUsed; /* Number of bytes used (not counting null terminator) */
+ char *zText; /* Text of the string */
+};
+
+/*
+** The following structure contains a lot of state information used
+** while generating a .h file. We put the information in this structure
+** and pass around a pointer to this structure, rather than pass around
+** all of the information separately. This helps reduce the number of
+** arguments to generator functions.
+*/
+typedef struct GenState GenState;
+struct GenState {
+ String *pStr; /* Write output to this string */
+ IdentTable *pTable; /* A table holding the zLabel of every #include that
+ * has already been generated. Used to avoid
+ * generating duplicate #includes. */
+ const char *zIf; /* If not NULL, then we are within a #if with
+ * this argument. */
+ int nErr; /* Number of errors */
+ const char *zFilename; /* Name of the source file being scanned */
+ int flags; /* Various flags (DP_ and PS_ flags above) */
+};
+
+/*
+** The following text line appears at the top of every file generated
+** by this program. By recognizing this line, the program can be sure
+** never to read a file that it generated itself.
+**
+** The "#undef INTERFACE" part is a hack to work around a name collision
+** in MSVC 2008.
+*/
+const char zTopLine[] =
+ "/* \aThis file was automatically generated. Do not edit! */\n"
+ "#undef INTERFACE\n";
+#define nTopLine (sizeof(zTopLine)-1)
+
+/*
+** The name of the file currently being parsed.
+*/
+static const char *zFilename;
+
+/*
+** The stack of #if macros for the file currently being parsed.
+*/
+static Ifmacro *ifStack = 0;
+
+/*
+** A list of all files that have been #included so far in a file being
+** parsed.
+*/
+static Include *includeList = 0;
+
+/*
+** The last block comment seen.
+*/
+static Token *blockComment = 0;
+
+/*
+** The following flag is set if the -doc flag appears on the
+** command line.
+*/
+static int doc_flag = 0;
+
+/*
+** If the following flag is set, then makeheaders will attempt to
+** generate prototypes for static functions and procedures.
+*/
+static int proto_static = 0;
+
+/*
+** A list of all declarations. The list is held together using the
+** pNext field of the Decl structure.
+*/
+static Decl *pDeclFirst; /* First on the list */
+static Decl *pDeclLast; /* Last on the list */
+
+/*
+** A hash table of all declarations
+*/
+#define DECL_HASH_SIZE 3371
+static Decl *apTable[DECL_HASH_SIZE];
+
+/*
+** The TEST macro must be defined to something. Make sure this is the
+** case.
+*/
+#ifndef TEST
+# define TEST 0
+#endif
+
+#ifdef NOT_USED
+/*
+** We do our own assertion macro so that we can have more control
+** over debugging.
+*/
+#define Assert(X) if(!(X)){ CantHappen(__LINE__); }
+#define CANT_HAPPEN CantHappen(__LINE__)
+static void CantHappen(int iLine){
+ fprintf(stderr,"Assertion failed on line %d\n",iLine);
+ *(char*)1 = 0; /* Force a core-dump */
+}
+#endif
+
+/*
+** Memory allocation functions that are guaranteed never to return NULL.
+*/
+static void *SafeMalloc(int nByte){
+ void *p = malloc( nByte );
+ if( p==0 ){
+ fprintf(stderr,"Out of memory. Can't allocate %d bytes.\n",nByte);
+ exit(1);
+ }
+ return p;
+}
+static void SafeFree(void *pOld){
+ if( pOld ){
+ free(pOld);
+ }
+}
+static void *SafeRealloc(void *pOld, int nByte){
+ void *p;
+ if( pOld==0 ){
+ p = SafeMalloc(nByte);
+ }else{
+ p = realloc(pOld, nByte);
+ if( p==0 ){
+ fprintf(stderr,
+ "Out of memory. Can't enlarge an allocation to %d bytes\n",nByte);
+ exit(1);
+ }
+ }
+ return p;
+}
+static char *StrDup(const char *zSrc, int nByte){
+ char *zDest;
+ if( nByte<=0 ){
+ nByte = strlen(zSrc);
+ }
+ zDest = SafeMalloc( nByte + 1 );
+ strncpy(zDest,zSrc,nByte);
+ zDest[nByte] = 0;
+ return zDest;
+}
+
+/*
+** Return TRUE if the character X can be part of an identifier
+*/
+#define ISALNUM(X) ((X)=='_' || isalnum(X))
+
+/*
+** Routines for dealing with unbounded strings.
+*/
+static void StringInit(String *pStr){
+ pStr->nAlloc = 0;
+ pStr->nUsed = 0;
+ pStr->zText = 0;
+}
+static void StringReset(String *pStr){
+ SafeFree(pStr->zText);
+ StringInit(pStr);
+}
+static void StringAppend(String *pStr, const char *zText, int nByte){
+ if( nByte<=0 ){
+ nByte = strlen(zText);
+ }
+ if( pStr->nUsed + nByte >= pStr->nAlloc ){
+ if( pStr->nAlloc==0 ){
+ pStr->nAlloc = nByte + 100;
+ pStr->zText = SafeMalloc( pStr->nAlloc );
+ }else{
+ pStr->nAlloc = pStr->nAlloc*2 + nByte;
+ pStr->zText = SafeRealloc(pStr->zText, pStr->nAlloc);
+ }
+ }
+ strncpy(&pStr->zText[pStr->nUsed],zText,nByte);
+ pStr->nUsed += nByte;
+ pStr->zText[pStr->nUsed] = 0;
+}
+#define StringGet(S) ((S)->zText?(S)->zText:"")
+
+/*
+** Compute a hash on a string. The number returned is a non-negative
+** value between 0 and 2**31 - 1
+*/
+static int Hash(const char *z, int n){
+ int h = 0;
+ if( n<=0 ){
+ n = strlen(z);
+ }
+ while( n-- ){
+ h = h ^ (h<<5) ^ *z++;
+ }
+ return h & 0x7fffffff;
+}
+
+/*
+** Given an identifier name, try to find a declaration for that
+** identifier in the hash table. If found, return a pointer to
+** the Decl structure. If not found, return 0.
+*/
+static Decl *FindDecl(const char *zName, int len){
+ int h;
+ Decl *p;
+
+ if( len<=0 ){
+ len = strlen(zName);
+ }
+ h = Hash(zName,len) % DECL_HASH_SIZE;
+ p = apTable[h];
+ while( p && (strncmp(p->zName,zName,len)!=0 || p->zName[len]!=0) ){
+ p = p->pSameHash;
+ }
+ return p;
+}
+
+/*
+** Install the given declaration both in the hash table and on
+** the list of all declarations.
+*/
+static void InstallDecl(Decl *pDecl){
+ int h;
+ Decl *pOther;
+
+ h = Hash(pDecl->zName,0) % DECL_HASH_SIZE;
+ pOther = apTable[h];
+ while( pOther && strcmp(pDecl->zName,pOther->zName)!=0 ){
+ pOther = pOther->pSameHash;
+ }
+ if( pOther ){
+ pDecl->pSameName = pOther->pSameName;
+ pOther->pSameName = pDecl;
+ }else{
+ pDecl->pSameName = 0;
+ pDecl->pSameHash = apTable[h];
+ apTable[h] = pDecl;
+ }
+ pDecl->pNext = 0;
+ if( pDeclFirst==0 ){
+ pDeclFirst = pDeclLast = pDecl;
+ }else{
+ pDeclLast->pNext = pDecl;
+ pDeclLast = pDecl;
+ }
+}
+
+/*
+** Look at the current ifStack. If anything declared at the current
+** position must be surrounded with
+**
+** #if STUFF
+** #endif
+**
+** Then this routine computes STUFF and returns a pointer to it. Memory
+** to hold the value returned is obtained from malloc().
+*/
+static char *GetIfString(void){
+ Ifmacro *pIf;
+ char *zResult = 0;
+ int hasIf = 0;
+ String str;
+
+ for(pIf = ifStack; pIf; pIf=pIf->pNext){
+ if( pIf->zCondition==0 || *pIf->zCondition==0 ) continue;
+ if( !hasIf ){
+ hasIf = 1;
+ StringInit(&str);
+ }else{
+ StringAppend(&str," && ",4);
+ }
+ StringAppend(&str,pIf->zCondition,0);
+ }
+ if( hasIf ){
+ zResult = StrDup(StringGet(&str),0);
+ StringReset(&str);
+ }else{
+ zResult = 0;
+ }
+ return zResult;
+}
+
+/*
+** Create a new declaration and put it in the hash table. Also
+** return a pointer to it so that we can fill in the zFwd and zDecl
+** fields, and so forth.
+*/
+static Decl *CreateDecl(
+ const char *zName, /* Name of the object being declared. */
+ int nName /* Length of the name */
+){
+ Decl *pDecl;
+
+ pDecl = SafeMalloc( sizeof(Decl) + nName + 1);
+ memset(pDecl,0,sizeof(Decl));
+ pDecl->zName = (char*)&pDecl[1];
+ sprintf(pDecl->zName,"%.*s",nName,zName);
+ pDecl->zFile = zFilename;
+ pDecl->pInclude = includeList;
+ pDecl->zIf = GetIfString();
+ InstallDecl(pDecl);
+ return pDecl;
+}
+
+/*
+** Insert a new identifier into an table of identifiers. Return TRUE if
+** a new identifier was inserted and return FALSE if the identifier was
+** already in the table.
+*/
+static int IdentTableInsert(
+ IdentTable *pTable, /* The table into which we will insert */
+ const char *zId, /* Name of the identifiers */
+ int nId /* Length of the identifier name */
+){
+ int h;
+ Ident *pId;
+
+ if( nId<=0 ){
+ nId = strlen(zId);
+ }
+ h = Hash(zId,nId) % IDENT_HASH_SIZE;
+ for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){
+ if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){
+ /* printf("Already in table: %.*s\n",nId,zId); */
+ return 0;
+ }
+ }
+ pId = SafeMalloc( sizeof(Ident) + nId + 1 );
+ pId->zName = (char*)&pId[1];
+ sprintf(pId->zName,"%.*s",nId,zId);
+ pId->pNext = pTable->pList;
+ pTable->pList = pId;
+ pId->pCollide = pTable->apTable[h];
+ pTable->apTable[h] = pId;
+ /* printf("Add to table: %.*s\n",nId,zId); */
+ return 1;
+}
+
+/*
+** Check to see if the given value is in the given IdentTable. Return
+** true if it is and false if it is not.
+*/
+static int IdentTableTest(
+ IdentTable *pTable, /* The table in which to search */
+ const char *zId, /* Name of the identifiers */
+ int nId /* Length of the identifier name */
+){
+ int h;
+ Ident *pId;
+
+ if( nId<=0 ){
+ nId = strlen(zId);
+ }
+ h = Hash(zId,nId) % IDENT_HASH_SIZE;
+ for(pId = pTable->apTable[h]; pId; pId=pId->pCollide){
+ if( strncmp(zId,pId->zName,nId)==0 && pId->zName[nId]==0 ){
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** Remove every identifier from the given table. Reset the table to
+** its initial state.
+*/
+static void IdentTableReset(IdentTable *pTable){
+ Ident *pId, *pNext;
+
+ for(pId = pTable->pList; pId; pId = pNext){
+ pNext = pId->pNext;
+ SafeFree(pId);
+ }
+ memset(pTable,0,sizeof(IdentTable));
+}
+
+#ifdef DEBUG
+/*
+** Print the name of every identifier in the given table, one per line
+*/
+static void IdentTablePrint(IdentTable *pTable, FILE *pOut){
+ Ident *pId;
+
+ for(pId = pTable->pList; pId; pId = pId->pNext){
+ fprintf(pOut,"%s\n",pId->zName);
+ }
+}
+#endif
+
+/*
+** Read an entire file into memory. Return a pointer to the memory.
+**
+** The memory is obtained from SafeMalloc and must be freed by the
+** calling function.
+**
+** If the read fails for any reason, 0 is returned.
+*/
+static char *ReadFile(const char *zFilename){
+ struct stat sStat;
+ FILE *pIn;
+ char *zBuf;
+ int n;
+
+ if( stat(zFilename,&sStat)!=0
+#ifndef WIN32
+ || !S_ISREG(sStat.st_mode)
+#endif
+ ){
+ return 0;
+ }
+ pIn = fopen(zFilename,"r");
+ if( pIn==0 ){
+ return 0;
+ }
+ zBuf = SafeMalloc( sStat.st_size + 1 );
+ n = fread(zBuf,1,sStat.st_size,pIn);
+ zBuf[n] = 0;
+ fclose(pIn);
+ return zBuf;
+}
+
+/*
+** Write the contents of a string into a file. Return the number of
+** errors
+*/
+static int WriteFile(const char *zFilename, const char *zOutput){
+ FILE *pOut;
+ pOut = fopen(zFilename,"w");
+ if( pOut==0 ){
+ return 1;
+ }
+ fwrite(zOutput,1,strlen(zOutput),pOut);
+ fclose(pOut);
+ return 0;
+}
+
+/*
+** Major token types
+*/
+#define TT_Space 1 /* Contiguous white space */
+#define TT_Id 2 /* An identifier */
+#define TT_Preprocessor 3 /* Any C preprocessor directive */
+#define TT_Comment 4 /* Either C or C++ style comment */
+#define TT_Number 5 /* Any numeric constant */
+#define TT_String 6 /* String or character constants. ".." or '.' */
+#define TT_Braces 7 /* All text between { and a matching } */
+#define TT_EOF 8 /* End of file */
+#define TT_Error 9 /* An error condition */
+#define TT_BlockComment 10 /* A C-Style comment at the left margin that
+ * spans multiple lines */
+#define TT_Other 0 /* None of the above */
+
+/*
+** Get a single low-level token from the input file. Update the
+** file pointer so that it points to the first character beyond the
+** token.
+**
+** A "low-level token" is any token except TT_Braces. A TT_Braces token
+** consists of many smaller tokens and is assembled by a routine that
+** calls this one.
+**
+** The function returns the number of errors. An error is an
+** unterminated string or character literal or an unterminated
+** comment.
+**
+** Profiling shows that this routine consumes about half the
+** CPU time on a typical run of makeheaders.
+*/
+static int GetToken(InStream *pIn, Token *pToken){
+ int i;
+ const char *z;
+ int cStart;
+ int c;
+ int startLine; /* Line on which a structure begins */
+ int nlisc = 0; /* True if there is a new-line in a ".." or '..' */
+ int nErr = 0; /* Number of errors seen */
+
+ z = pIn->z;
+ i = pIn->i;
+ pToken->nLine = pIn->nLine;
+ pToken->zText = &z[i];
+ switch( z[i] ){
+ case 0:
+ pToken->eType = TT_EOF;
+ pToken->nText = 0;
+ break;
+
+ case '#':
+ if( i==0 || z[i-1]=='\n' || (i>1 && z[i-1]=='\r' && z[i-2]=='\n')){
+ /* We found a preprocessor statement */
+ pToken->eType = TT_Preprocessor;
+ i++;
+ while( z[i]!=0 && z[i]!='\n' ){
+ if( z[i]=='\\' ){
+ i++;
+ if( z[i]=='\n' ) pIn->nLine++;
+ }
+ i++;
+ }
+ pToken->nText = i - pIn->i;
+ }else{
+ /* Just an operator */
+ pToken->eType = TT_Other;
+ pToken->nText = 1;
+ }
+ break;
+
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\f':
+ case '\n':
+ while( isspace(z[i]) ){
+ if( z[i]=='\n' ) pIn->nLine++;
+ i++;
+ }
+ pToken->eType = TT_Space;
+ pToken->nText = i - pIn->i;
+ break;
+
+ case '\\':
+ pToken->nText = 2;
+ pToken->eType = TT_Other;
+ if( z[i+1]=='\n' ){
+ pIn->nLine++;
+ pToken->eType = TT_Space;
+ }else if( z[i+1]==0 ){
+ pToken->nText = 1;
+ }
+ break;
+
+ case '\'':
+ case '\"':
+ cStart = z[i];
+ startLine = pIn->nLine;
+ do{
+ i++;
+ c = z[i];
+ if( c=='\n' ){
+ if( !nlisc ){
+ fprintf(stderr,
+ "%s:%d: (warning) Newline in string or character literal.\n",
+ zFilename, pIn->nLine);
+ nlisc = 1;
+ }
+ pIn->nLine++;
+ }
+ if( c=='\\' ){
+ i++;
+ c = z[i];
+ if( c=='\n' ){
+ pIn->nLine++;
+ }
+ }else if( c==cStart ){
+ i++;
+ c = 0;
+ }else if( c==0 ){
+ fprintf(stderr, "%s:%d: Unterminated string or character literal.\n",
+ zFilename, startLine);
+ nErr++;
+ }
+ }while( c );
+ pToken->eType = TT_String;
+ pToken->nText = i - pIn->i;
+ break;
+
+ case '/':
+ if( z[i+1]=='/' ){
+ /* C++ style comment */
+ while( z[i] && z[i]!='\n' ){ i++; }
+ pToken->eType = TT_Comment;
+ pToken->nText = i - pIn->i;
+ }else if( z[i+1]=='*' ){
+ /* C style comment */
+ int isBlockComment = i==0 || z[i-1]=='\n';
+ i += 2;
+ startLine = pIn->nLine;
+ while( z[i] && (z[i]!='*' || z[i+1]!='/') ){
+ if( z[i]=='\n' ){
+ pIn->nLine++;
+ if( isBlockComment ){
+ if( z[i+1]=='*' || z[i+2]=='*' ){
+ isBlockComment = 2;
+ }else{
+ isBlockComment = 0;
+ }
+ }
+ }
+ i++;
+ }
+ if( z[i] ){
+ i += 2;
+ }else{
+ isBlockComment = 0;
+ fprintf(stderr,"%s:%d: Unterminated comment\n",
+ zFilename, startLine);
+ nErr++;
+ }
+ pToken->eType = isBlockComment==2 ? TT_BlockComment : TT_Comment;
+ pToken->nText = i - pIn->i;
+ }else{
+ /* A divide operator */
+ pToken->eType = TT_Other;
+ pToken->nText = 1 + (z[i+1]=='+');
+ }
+ break;
+
+ case '0':
+ if( z[i+1]=='x' || z[i+1]=='X' ){
+ /* A hex constant */
+ i += 2;
+ while( isxdigit(z[i]) ){ i++; }
+ }else{
+ /* An octal constant */
+ while( isdigit(z[i]) ){ i++; }
+ }
+ pToken->eType = TT_Number;
+ pToken->nText = i - pIn->i;
+ break;
+
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ while( isdigit(z[i]) ){ i++; }
+ if( (c=z[i])=='.' ){
+ i++;
+ while( isdigit(z[i]) ){ i++; }
+ c = z[i];
+ if( c=='e' || c=='E' ){
+ i++;
+ if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; }
+ while( isdigit(z[i]) ){ i++; }
+ c = z[i];
+ }
+ if( c=='f' || c=='F' || c=='l' || c=='L' ){ i++; }
+ }else if( c=='e' || c=='E' ){
+ i++;
+ if( ((c=z[i])=='+' || c=='-') && isdigit(z[i+1]) ){ i++; }
+ while( isdigit(z[i]) ){ i++; }
+ }else if( c=='L' || c=='l' ){
+ i++;
+ c = z[i];
+ if( c=='u' || c=='U' ){ i++; }
+ }else if( c=='u' || c=='U' ){
+ i++;
+ c = z[i];
+ if( c=='l' || c=='L' ){ i++; }
+ }
+ pToken->eType = TT_Number;
+ pToken->nText = i - pIn->i;
+ break;
+
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B':
+ case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I':
+ case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P':
+ case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W':
+ case 'X': case 'Y': case 'Z': case '_':
+ while( isalnum(z[i]) || z[i]=='_' ){ i++; };
+ pToken->eType = TT_Id;
+ pToken->nText = i - pIn->i;
+ break;
+
+ case ':':
+ pToken->eType = TT_Other;
+ pToken->nText = 1 + (z[i+1]==':');
+ break;
+
+ case '=':
+ case '<':
+ case '>':
+ case '+':
+ case '-':
+ case '*':
+ case '%':
+ case '^':
+ case '&':
+ case '|':
+ pToken->eType = TT_Other;
+ pToken->nText = 1 + (z[i+1]=='=');
+ break;
+
+ default:
+ pToken->eType = TT_Other;
+ pToken->nText = 1;
+ break;
+ }
+ pIn->i += pToken->nText;
+ return nErr;
+}
+
+/*
+** This routine recovers the next token from the input file which is
+** not a space or a comment or any text between an "#if 0" and "#endif".
+**
+** This routine returns the number of errors encountered. An error
+** is an unterminated token or unmatched "#if 0".
+**
+** Profiling shows that this routine uses about a quarter of the
+** CPU time in a typical run.
+*/
+static int GetNonspaceToken(InStream *pIn, Token *pToken){
+ int nIf = 0;
+ int inZero = 0;
+ const char *z;
+ int value;
+ int startLine;
+ int nErr = 0;
+
+ startLine = pIn->nLine;
+ while( 1 ){
+ nErr += GetToken(pIn,pToken);
+ /* printf("%04d: Type=%d nIf=%d [%.*s]\n",
+ pToken->nLine,pToken->eType,nIf,pToken->nText,
+ pToken->eType!=TT_Space ? pToken->zText : "<space>"); */
+ pToken->pComment = blockComment;
+ switch( pToken->eType ){
+ case TT_Comment: /*0123456789 12345678 */
+ if( strncmp(pToken->zText, "/*MAKEHEADERS-STOP", 18)==0 ) return nErr;
+ break;
+
+ case TT_Space:
+ break;
+
+ case TT_BlockComment:
+ if( doc_flag ){
+ blockComment = SafeMalloc( sizeof(Token) );
+ *blockComment = *pToken;
+ }
+ break;
+
+ case TT_EOF:
+ if( nIf ){
+ fprintf(stderr,"%s:%d: Unterminated \"#if\"\n",
+ zFilename, startLine);
+ nErr++;
+ }
+ return nErr;
+
+ case TT_Preprocessor:
+ z = &pToken->zText[1];
+ while( *z==' ' || *z=='\t' ) z++;
+ if( sscanf(z,"if %d",&value)==1 && value==0 ){
+ nIf++;
+ inZero = 1;
+ }else if( inZero ){
+ if( strncmp(z,"if",2)==0 ){
+ nIf++;
+ }else if( strncmp(z,"endif",5)==0 ){
+ nIf--;
+ if( nIf==0 ) inZero = 0;
+ }
+ }else{
+ return nErr;
+ }
+ break;
+
+ default:
+ if( !inZero ){
+ return nErr;
+ }
+ break;
+ }
+ }
+ /* NOT REACHED */
+}
+
+/*
+** This routine looks for identifiers (strings of contiguous alphanumeric
+** characters) within a preprocessor directive and adds every such string
+** found to the given identifier table
+*/
+static void FindIdentifiersInMacro(Token *pToken, IdentTable *pTable){
+ Token sToken;
+ InStream sIn;
+ int go = 1;
+
+ sIn.z = pToken->zText;
+ sIn.i = 1;
+ sIn.nLine = 1;
+ while( go && sIn.i < pToken->nText ){
+ GetToken(&sIn,&sToken);
+ switch( sToken.eType ){
+ case TT_Id:
+ IdentTableInsert(pTable,sToken.zText,sToken.nText);
+ break;
+
+ case TT_EOF:
+ go = 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/*
+** This routine gets the next token. Everything contained within
+** {...} is collapsed into a single TT_Braces token. Whitespace is
+** omitted.
+**
+** If pTable is not NULL, then insert every identifier seen into the
+** IdentTable. This includes any identifiers seen inside of {...}.
+**
+** The number of errors encountered is returned. An error is an
+** unterminated token.
+*/
+static int GetBigToken(InStream *pIn, Token *pToken, IdentTable *pTable){
+ const char *zStart;
+ int iStart;
+ int nBrace;
+ int c;
+ int nLine;
+ int nErr;
+
+ nErr = GetNonspaceToken(pIn,pToken);
+ switch( pToken->eType ){
+ case TT_Id:
+ if( pTable!=0 ){
+ IdentTableInsert(pTable,pToken->zText,pToken->nText);
+ }
+ return nErr;
+
+ case TT_Preprocessor:
+ if( pTable!=0 ){
+ FindIdentifiersInMacro(pToken,pTable);
+ }
+ return nErr;
+
+ case TT_Other:
+ if( pToken->zText[0]=='{' ) break;
+ return nErr;
+
+ default:
+ return nErr;
+ }
+
+ iStart = pIn->i;
+ zStart = pToken->zText;
+ nLine = pToken->nLine;
+ nBrace = 1;
+ while( nBrace ){
+ nErr += GetNonspaceToken(pIn,pToken);
+ /* printf("%04d: nBrace=%d [%.*s]\n",pToken->nLine,nBrace,
+ pToken->nText,pToken->zText); */
+ switch( pToken->eType ){
+ case TT_EOF:
+ fprintf(stderr,"%s:%d: Unterminated \"{\"\n",
+ zFilename, nLine);
+ nErr++;
+ pToken->eType = TT_Error;
+ return nErr;
+
+ case TT_Id:
+ if( pTable ){
+ IdentTableInsert(pTable,pToken->zText,pToken->nText);
+ }
+ break;
+
+ case TT_Preprocessor:
+ if( pTable!=0 ){
+ FindIdentifiersInMacro(pToken,pTable);
+ }
+ break;
+
+ case TT_Other:
+ if( (c = pToken->zText[0])=='{' ){
+ nBrace++;
+ }else if( c=='}' ){
+ nBrace--;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ pToken->eType = TT_Braces;
+ pToken->nText = 1 + pIn->i - iStart;
+ pToken->zText = zStart;
+ pToken->nLine = nLine;
+ return nErr;
+}
+
+/*
+** This routine frees up a list of Tokens. The pComment tokens are
+** not cleared by this. So we leak a little memory when using the -doc
+** option. So what.
+*/
+static void FreeTokenList(Token *pList){
+ Token *pNext;
+ while( pList ){
+ pNext = pList->pNext;
+ SafeFree(pList);
+ pList = pNext;
+ }
+}
+
+/*
+** Tokenize an entire file. Return a pointer to the list of tokens.
+**
+** Space for each token is obtained from a separate malloc() call. The
+** calling function is responsible for freeing this space.
+**
+** If pTable is not NULL, then fill the table with all identifiers seen in
+** the input file.
+*/
+static Token *TokenizeFile(const char *zFile, IdentTable *pTable){
+ InStream sIn;
+ Token *pFirst = 0, *pLast = 0, *pNew;
+ int nErr = 0;
+
+ sIn.z = zFile;
+ sIn.i = 0;
+ sIn.nLine = 1;
+ blockComment = 0;
+
+ while( sIn.z[sIn.i]!=0 ){
+ pNew = SafeMalloc( sizeof(Token) );
+ nErr += GetBigToken(&sIn,pNew,pTable);
+ debug3(TOKENIZER, "Token on line %d: [%.*s]\n",
+ pNew->nLine, pNew->nText<50 ? pNew->nText : 50, pNew->zText);
+ if( pFirst==0 ){
+ pFirst = pLast = pNew;
+ pNew->pPrev = 0;
+ }else{
+ pLast->pNext = pNew;
+ pNew->pPrev = pLast;
+ pLast = pNew;
+ }
+ if( pNew->eType==TT_EOF ) break;
+ }
+ if( pLast ) pLast->pNext = 0;
+ blockComment = 0;
+ if( nErr ){
+ FreeTokenList(pFirst);
+ pFirst = 0;
+ }
+
+ return pFirst;
+}
+
+#if TEST==1
+/*
+** Use the following routine to test or debug the tokenizer.
+*/
+void main(int argc, char **argv){
+ char *zFile;
+ Token *pList, *p;
+ IdentTable sTable;
+
+ if( argc!=2 ){
+ fprintf(stderr,"Usage: %s filename\n",*argv);
+ exit(1);
+ }
+ memset(&sTable,0,sizeof(sTable));
+ zFile = ReadFile(argv[1]);
+ if( zFile==0 ){
+ fprintf(stderr,"Can't read file \"%s\"\n",argv[1]);
+ exit(1);
+ }
+ pList = TokenizeFile(zFile,&sTable);
+ for(p=pList; p; p=p->pNext){
+ int j;
+ switch( p->eType ){
+ case TT_Space:
+ printf("%4d: Space\n",p->nLine);
+ break;
+ case TT_Id:
+ printf("%4d: Id %.*s\n",p->nLine,p->nText,p->zText);
+ break;
+ case TT_Preprocessor:
+ printf("%4d: Preprocessor %.*s\n",p->nLine,p->nText,p->zText);
+ break;
+ case TT_Comment:
+ printf("%4d: Comment\n",p->nLine);
+ break;
+ case TT_BlockComment:
+ printf("%4d: Block Comment\n",p->nLine);
+ break;
+ case TT_Number:
+ printf("%4d: Number %.*s\n",p->nLine,p->nText,p->zText);
+ break;
+ case TT_String:
+ printf("%4d: String %.*s\n",p->nLine,p->nText,p->zText);
+ break;
+ case TT_Other:
+ printf("%4d: Other %.*s\n",p->nLine,p->nText,p->zText);
+ break;
+ case TT_Braces:
+ for(j=0; j<p->nText && j<30 && p->zText[j]!='\n'; j++){}
+ printf("%4d: Braces %.*s...}\n",p->nLine,j,p->zText);
+ break;
+ case TT_EOF:
+ printf("%4d: End of file\n",p->nLine);
+ break;
+ default:
+ printf("%4d: type %d\n",p->nLine,p->eType);
+ break;
+ }
+ }
+ FreeTokenList(pList);
+ SafeFree(zFile);
+ IdentTablePrint(&sTable,stdout);
+}
+#endif
+
+#ifdef DEBUG
+/*
+** For debugging purposes, write out a list of tokens.
+*/
+static void PrintTokens(Token *pFirst, Token *pLast){
+ int needSpace = 0;
+ int c;
+
+ pLast = pLast->pNext;
+ while( pFirst!=pLast ){
+ switch( pFirst->eType ){
+ case TT_Preprocessor:
+ printf("\n%.*s\n",pFirst->nText,pFirst->zText);
+ needSpace = 0;
+ break;
+
+ case TT_Id:
+ case TT_Number:
+ printf("%s%.*s", needSpace ? " " : "", pFirst->nText, pFirst->zText);
+ needSpace = 1;
+ break;
+
+ default:
+ c = pFirst->zText[0];
+ printf("%s%.*s",
+ (needSpace && (c=='*' || c=='{')) ? " " : "",
+ pFirst->nText, pFirst->zText);
+ needSpace = pFirst->zText[0]==',';
+ break;
+ }
+ pFirst = pFirst->pNext;
+ }
+}
+#endif
+
+/*
+** Convert a sequence of tokens into a string and return a pointer
+** to that string. Space to hold the string is obtained from malloc()
+** and must be freed by the calling function.
+**
+** Certain keywords (EXPORT, PRIVATE, PUBLIC, PROTECTED) are always
+** skipped.
+**
+** If pSkip!=0 then skip over nSkip tokens beginning with pSkip.
+**
+** If zTerm!=0 then append the text to the end.
+*/
+static char *TokensToString(
+ Token *pFirst, /* First token in the string */
+ Token *pLast, /* Last token in the string */
+ char *zTerm, /* Terminate the string with this text if not NULL */
+ Token *pSkip, /* Skip this token if not NULL */
+ int nSkip /* Skip a total of this many tokens */
+){
+ char *zReturn;
+ String str;
+ int needSpace = 0;
+ int c;
+ int iSkip = 0;
+ int skipOne = 0;
+
+ StringInit(&str);
+ pLast = pLast->pNext;
+ while( pFirst!=pLast ){
+ if( pFirst==pSkip ){ iSkip = nSkip; }
+ if( iSkip>0 ){
+ iSkip--;
+ pFirst=pFirst->pNext;
+ continue;
+ }
+ switch( pFirst->eType ){
+ case TT_Preprocessor:
+ StringAppend(&str,"\n",1);
+ StringAppend(&str,pFirst->zText,pFirst->nText);
+ StringAppend(&str,"\n",1);
+ needSpace = 0;
+ break;
+
+ case TT_Id:
+ switch( pFirst->zText[0] ){
+ case 'E':
+ if( pFirst->nText==6 && strncmp(pFirst->zText,"EXPORT",6)==0 ){
+ skipOne = 1;
+ }
+ break;
+ case 'P':
+ switch( pFirst->nText ){
+ case 6: skipOne = !strncmp(pFirst->zText,"PUBLIC", 6); break;
+ case 7: skipOne = !strncmp(pFirst->zText,"PRIVATE",7); break;
+ case 9: skipOne = !strncmp(pFirst->zText,"PROTECTED",9); break;
+ default: break;
+ }
+ break;
+ default:
+ break;
+ }
+ if( skipOne ){
+ pFirst = pFirst->pNext;
+ continue;
+ }
+ /* Fall thru to the next case */
+ case TT_Number:
+ if( needSpace ){
+ StringAppend(&str," ",1);
+ }
+ StringAppend(&str,pFirst->zText,pFirst->nText);
+ needSpace = 1;
+ break;
+
+ default:
+ c = pFirst->zText[0];
+ if( needSpace && (c=='*' || c=='{') ){
+ StringAppend(&str," ",1);
+ }
+ StringAppend(&str,pFirst->zText,pFirst->nText);
+ /* needSpace = pFirst->zText[0]==','; */
+ needSpace = 0;
+ break;
+ }
+ pFirst = pFirst->pNext;
+ }
+ if( zTerm && *zTerm ){
+ StringAppend(&str,zTerm,strlen(zTerm));
+ }
+ zReturn = StrDup(StringGet(&str),0);
+ StringReset(&str);
+ return zReturn;
+}
+
+/*
+** This routine is called when we see one of the keywords "struct",
+** "enum", "union" or "class". This might be the beginning of a
+** type declaration. This routine will process the declaration and
+** remove the declaration tokens from the input stream.
+**
+** If this is a type declaration that is immediately followed by a
+** semicolon (in other words it isn't also a variable definition)
+** then set *pReset to ';'. Otherwise leave *pReset at 0. The
+** *pReset flag causes the parser to skip ahead to the next token
+** that begins with the value placed in the *pReset flag, if that
+** value is different from 0.
+*/
+static int ProcessTypeDecl(Token *pList, int flags, int *pReset){
+ Token *pName, *pEnd;
+ Decl *pDecl;
+ String str;
+ int need_to_collapse = 1;
+ int type = 0;
+
+ *pReset = 0;
+ if( pList==0 || pList->pNext==0 || pList->pNext->eType!=TT_Id ){
+ return 0;
+ }
+ pName = pList->pNext;
+
+ /* Catch the case of "struct Foo;" and skip it. */
+ if( pName->pNext && pName->pNext->zText[0]==';' ){
+ *pReset = ';';
+ return 0;
+ }
+
+ for(pEnd=pName->pNext; pEnd && pEnd->eType!=TT_Braces; pEnd=pEnd->pNext){
+ switch( pEnd->zText[0] ){
+ case '(':
+ case ')':
+ case '*':
+ case '[':
+ case '=':
+ case ';':
+ return 0;
+ }
+ }
+ if( pEnd==0 ){
+ return 0;
+ }
+
+ /*
+ ** At this point, we know we have a type declaration that is bounded
+ ** by pList and pEnd and has the name pName.
+ */
+
+ /*
+ ** If the braces are followed immediately by a semicolon, then we are
+ ** dealing a type declaration only. There is not variable definition
+ ** following the type declaration. So reset...
+ */
+ if( pEnd->pNext==0 || pEnd->pNext->zText[0]==';' ){
+ *pReset = ';';
+ need_to_collapse = 0;
+ }else{
+ need_to_collapse = 1;
+ }
+
+ if( proto_static==0 && (flags & (PS_Local|PS_Export|PS_Interface))==0 ){
+ /* Ignore these objects unless they are explicitly declared as interface,
+ ** or unless the "-local" command line option was specified. */
+ *pReset = ';';
+ return 0;
+ }
+
+#ifdef DEBUG
+ if( debugMask & PARSER ){
+ printf("**** Found type: %.*s %.*s...\n",
+ pList->nText, pList->zText, pName->nText, pName->zText);
+ PrintTokens(pList,pEnd);
+ printf(";\n");
+ }
+#endif
+
+ /*
+ ** Create a new Decl object for this definition. Actually, if this
+ ** is a C++ class definition, then the Decl object might already exist,
+ ** so check first for that case before creating a new one.
+ */
+ switch( *pList->zText ){
+ case 'c': type = TY_Class; break;
+ case 's': type = TY_Structure; break;
+ case 'e': type = TY_Enumeration; break;
+ case 'u': type = TY_Union; break;
+ default: /* Can't Happen */ break;
+ }
+ if( type!=TY_Class ){
+ pDecl = 0;
+ }else{
+ pDecl = FindDecl(pName->zText, pName->nText);
+ if( pDecl && (pDecl->flags & type)!=type ) pDecl = 0;
+ }
+ if( pDecl==0 ){
+ pDecl = CreateDecl(pName->zText,pName->nText);
+ }
+ if( (flags & PS_Static) || !(flags & (PS_Interface|PS_Export)) ){
+ DeclSetProperty(pDecl,DP_Local);
+ }
+ DeclSetProperty(pDecl,type);
+
+ /* The object has a full declaration only if it is contained within
+ ** "#if INTERFACE...#endif" or "#if EXPORT_INTERFACE...#endif" or
+ ** "#if LOCAL_INTERFACE...#endif". Otherwise, we only give it a
+ ** forward declaration.
+ */
+ if( flags & (PS_Local | PS_Export | PS_Interface) ){
+ pDecl->zDecl = TokensToString(pList,pEnd,";\n",0,0);
+ }else{
+ pDecl->zDecl = 0;
+ }
+ pDecl->pComment = pList->pComment;
+ StringInit(&str);
+ StringAppend(&str,"typedef ",0);
+ StringAppend(&str,pList->zText,pList->nText);
+ StringAppend(&str," ",0);
+ StringAppend(&str,pName->zText,pName->nText);
+ StringAppend(&str," ",0);
+ StringAppend(&str,pName->zText,pName->nText);
+ StringAppend(&str,";\n",2);
+ pDecl->zFwd = StrDup(StringGet(&str),0);
+ StringReset(&str);
+ StringInit(&str);
+ StringAppend(&str,pList->zText,pList->nText);
+ StringAppend(&str," ",0);
+ StringAppend(&str,pName->zText,pName->nText);
+ StringAppend(&str,";\n",2);
+ pDecl->zFwdCpp = StrDup(StringGet(&str),0);
+ StringReset(&str);
+ if( flags & PS_Export ){
+ DeclSetProperty(pDecl,DP_Export);
+ }else if( flags & PS_Local ){
+ DeclSetProperty(pDecl,DP_Local);
+ }
+
+ /* Here's something weird. ANSI-C doesn't allow a forward declaration
+ ** of an enumeration. So we have to build the typedef into the
+ ** definition.
+ */
+ if( pDecl->zDecl && DeclHasProperty(pDecl, TY_Enumeration) ){
+ StringInit(&str);
+ StringAppend(&str,pDecl->zDecl,0);
+ StringAppend(&str,pDecl->zFwd,0);
+ SafeFree(pDecl->zDecl);
+ SafeFree(pDecl->zFwd);
+ pDecl->zFwd = 0;
+ pDecl->zDecl = StrDup(StringGet(&str),0);
+ StringReset(&str);
+ }
+
+ if( pName->pNext->zText[0]==':' ){
+ DeclSetProperty(pDecl,DP_Cplusplus);
+ }
+ if( pName->nText==5 && strncmp(pName->zText,"class",5)==0 ){
+ DeclSetProperty(pDecl,DP_Cplusplus);
+ }
+
+ /*
+ ** Remove all but pList and pName from the input stream.
+ */
+ if( need_to_collapse ){
+ while( pEnd!=pName ){
+ Token *pPrev = pEnd->pPrev;
+ pPrev->pNext = pEnd->pNext;
+ pEnd->pNext->pPrev = pPrev;
+ SafeFree(pEnd);
+ pEnd = pPrev;
+ }
+ }
+ return 0;
+}
+
+/*
+** Given a list of tokens that declare something (a function, procedure,
+** variable or typedef) find the token which contains the name of the
+** thing being declared.
+**
+** Algorithm:
+**
+** The name is:
+**
+** 1. The first identifier that is followed by a "[", or
+**
+** 2. The first identifier that is followed by a "(" where the
+** "(" is followed by another identifier, or
+**
+** 3. The first identifier followed by "::", or
+**
+** 4. If none of the above, then the last identifier.
+**
+** In all of the above, certain reserved words (like "char") are
+** not considered identifiers.
+*/
+static Token *FindDeclName(Token *pFirst, Token *pLast){
+ Token *pName = 0;
+ Token *p;
+ int c;
+
+ if( pFirst==0 || pLast==0 ){
+ return 0;
+ }
+ pLast = pLast->pNext;
+ for(p=pFirst; p && p!=pLast; p=p->pNext){
+ if( p->eType==TT_Id ){
+ static IdentTable sReserved;
+ static int isInit = 0;
+ static const char *aWords[] = { "char", "class",
+ "const", "double", "enum", "extern", "EXPORT", "ET_PROC",
+ "float", "int", "long",
+ "PRIVATE", "PROTECTED", "PUBLIC",
+ "register", "static", "struct", "sizeof", "signed", "typedef",
+ "union", "volatile", "virtual", "void", };
+
+ if( !isInit ){
+ int i;
+ for(i=0; i<sizeof(aWords)/sizeof(aWords[0]); i++){
+ IdentTableInsert(&sReserved,aWords[i],0);
+ }
+ isInit = 1;
+ }
+ if( !IdentTableTest(&sReserved,p->zText,p->nText) ){
+ pName = p;
+ }
+ }else if( p==pFirst ){
+ continue;
+ }else if( (c=p->zText[0])=='[' && pName ){
+ break;
+ }else if( c=='(' && p->pNext && p->pNext->eType==TT_Id && pName ){
+ break;
+ }else if( c==':' && p->zText[1]==':' && pName ){
+ break;
+ }
+ }
+ return pName;
+}
+
+/*
+** This routine is called when we see a method for a class that begins
+** with the PUBLIC, PRIVATE, or PROTECTED keywords. Such methods are
+** added to their class definitions.
+*/
+static int ProcessMethodDef(Token *pFirst, Token *pLast, int flags){
+ Token *pClass;
+ char *zDecl;
+ Decl *pDecl;
+ String str;
+ int type;
+
+ pLast = pLast->pPrev;
+ while( pFirst->zText[0]=='P' ){
+ int rc = 1;
+ switch( pFirst->nText ){
+ case 6: rc = strncmp(pFirst->zText,"PUBLIC",6); break;
+ case 7: rc = strncmp(pFirst->zText,"PRIVATE",7); break;
+ case 9: rc = strncmp(pFirst->zText,"PROTECTED",9); break;
+ default: break;
+ }
+ if( rc ) break;
+ pFirst = pFirst->pNext;
+ }
+ pClass = FindDeclName(pFirst,pLast);
+ if( pClass==0 ){
+ fprintf(stderr,"%s:%d: Unable to find the class name for this method\n",
+ zFilename, pFirst->nLine);
+ return 1;
+ }
+ pDecl = FindDecl(pClass->zText, pClass->nText);
+ if( pDecl==0 || (pDecl->flags & TY_Class)!=TY_Class ){
+ pDecl = CreateDecl(pClass->zText, pClass->nText);
+ DeclSetProperty(pDecl, TY_Class);
+ }
+ StringInit(&str);
+ if( pDecl->zExtra ){
+ StringAppend(&str, pDecl->zExtra, 0);
+ SafeFree(pDecl->zExtra);
+ pDecl->zExtra = 0;
+ }
+ type = flags & PS_PPP;
+ if( pDecl->extraType!=type ){
+ if( type & PS_Public ){
+ StringAppend(&str, "public:\n", 0);
+ pDecl->extraType = PS_Public;
+ }else if( type & PS_Protected ){
+ StringAppend(&str, "protected:\n", 0);
+ pDecl->extraType = PS_Protected;
+ }else if( type & PS_Private ){
+ StringAppend(&str, "private:\n", 0);
+ pDecl->extraType = PS_Private;
+ }
+ }
+ StringAppend(&str, " ", 0);
+ zDecl = TokensToString(pFirst, pLast, ";\n", pClass, 2);
+ StringAppend(&str, zDecl, 0);
+ SafeFree(zDecl);
+ pDecl->zExtra = StrDup(StringGet(&str), 0);
+ StringReset(&str);
+ return 0;
+}
+
+/*
+** This routine is called when we see a function or procedure definition.
+** We make an entry in the declaration table that is a prototype for this
+** function or procedure.
+*/
+static int ProcessProcedureDef(Token *pFirst, Token *pLast, int flags){
+ Token *pName;
+ Decl *pDecl;
+ Token *pCode;
+
+ if( pFirst==0 || pLast==0 ){
+ return 0;
+ }
+ if( flags & PS_Method ){
+ if( flags & PS_PPP ){
+ return ProcessMethodDef(pFirst, pLast, flags);
+ }else{
+ return 0;
+ }
+ }
+ if( (flags & PS_Static)!=0 && !proto_static ){
+ return 0;
+ }
+ pCode = pLast;
+ while( pLast && pLast!=pFirst && pLast->zText[0]!=')' ){
+ pLast = pLast->pPrev;
+ }
+ if( pLast==0 || pLast==pFirst || pFirst->pNext==pLast ){
+ fprintf(stderr,"%s:%d: Unrecognized syntax.\n",
+ zFilename, pFirst->nLine);
+ return 1;
+ }
+ if( flags & (PS_Interface|PS_Export|PS_Local) ){
+ fprintf(stderr,"%s:%d: Missing \"inline\" on function or procedure.\n",
+ zFilename, pFirst->nLine);
+ return 1;
+ }
+ pName = FindDeclName(pFirst,pLast);
+ if( pName==0 ){
+ fprintf(stderr,"%s:%d: Malformed function or procedure definition.\n",
+ zFilename, pFirst->nLine);
+ return 1;
+ }
+
+ /*
+ ** At this point we've isolated a procedure declaration between pFirst
+ ** and pLast with the name pName.
+ */
+#ifdef DEBUG
+ if( debugMask & PARSER ){
+ printf("**** Found routine: %.*s on line %d...\n", pName->nText,
+ pName->zText, pFirst->nLine);
+ PrintTokens(pFirst,pLast);
+ printf(";\n");
+ }
+#endif
+ pDecl = CreateDecl(pName->zText,pName->nText);
+ pDecl->pComment = pFirst->pComment;
+ if( pCode && pCode->eType==TT_Braces ){
+ pDecl->tokenCode = *pCode;
+ }
+ DeclSetProperty(pDecl,TY_Subroutine);
+ pDecl->zDecl = TokensToString(pFirst,pLast,";\n",0,0);
+ if( (flags & (PS_Static|PS_Local2))!=0 ){
+ DeclSetProperty(pDecl,DP_Local);
+ }else if( (flags & (PS_Export2))!=0 ){
+ DeclSetProperty(pDecl,DP_Export);
+ }
+
+ if( flags & DP_Cplusplus ){
+ DeclSetProperty(pDecl,DP_Cplusplus);
+ }else{
+ DeclSetProperty(pDecl,DP_ExternCReqd);
+ }
+
+ return 0;
+}
+
+/*
+** This routine is called whenever we see the "inline" keyword. We
+** need to seek-out the inline function or procedure and make a
+** declaration out of the entire definition.
+*/
+static int ProcessInlineProc(Token *pFirst, int flags, int *pReset){
+ Token *pName;
+ Token *pEnd;
+ Decl *pDecl;
+
+ for(pEnd=pFirst; pEnd; pEnd = pEnd->pNext){
+ if( pEnd->zText[0]=='{' || pEnd->zText[0]==';' ){
+ *pReset = pEnd->zText[0];
+ break;
+ }
+ }
+ if( pEnd==0 ){
+ *pReset = ';';
+ fprintf(stderr,"%s:%d: incomplete inline procedure definition\n",
+ zFilename, pFirst->nLine);
+ return 1;
+ }
+ pName = FindDeclName(pFirst,pEnd);
+ if( pName==0 ){
+ fprintf(stderr,"%s:%d: malformed inline procedure definition\n",
+ zFilename, pFirst->nLine);
+ return 1;
+ }
+
+#ifdef DEBUG
+ if( debugMask & PARSER ){
+ printf("**** Found inline routine: %.*s on line %d...\n",
+ pName->nText, pName->zText, pFirst->nLine);
+ PrintTokens(pFirst,pEnd);
+ printf("\n");
+ }
+#endif
+ pDecl = CreateDecl(pName->zText,pName->nText);
+ pDecl->pComment = pFirst->pComment;
+ DeclSetProperty(pDecl,TY_Subroutine);
+ pDecl->zDecl = TokensToString(pFirst,pEnd,";\n",0,0);
+ if( (flags & (PS_Static|PS_Local|PS_Local2)) ){
+ DeclSetProperty(pDecl,DP_Local);
+ }else if( flags & (PS_Export|PS_Export2) ){
+ DeclSetProperty(pDecl,DP_Export);
+ }
+
+ if( flags & DP_Cplusplus ){
+ DeclSetProperty(pDecl,DP_Cplusplus);
+ }else{
+ DeclSetProperty(pDecl,DP_ExternCReqd);
+ }
+
+ return 0;
+}
+
+/*
+** Determine if the tokens between pFirst and pEnd form a variable
+** definition or a function prototype. Return TRUE if we are dealing
+** with a variable defintion and FALSE for a prototype.
+**
+** pEnd is the token that ends the object. It can be either a ';' or
+** a '='. If it is '=', then assume we have a variable definition.
+**
+** If pEnd is ';', then the determination is more difficult. We have
+** to search for an occurrence of an ID followed immediately by '('.
+** If found, we have a prototype. Otherwise we are dealing with a
+** variable definition.
+*/
+static int isVariableDef(Token *pFirst, Token *pEnd){
+ if( pEnd && pEnd->zText[0]=='=' &&
+ (pEnd->pPrev->nText!=8 || strncmp(pEnd->pPrev->zText,"operator",8)!=0)
+ ){
+ return 1;
+ }
+ while( pFirst && pFirst!=pEnd && pFirst->pNext && pFirst->pNext!=pEnd ){
+ if( pFirst->eType==TT_Id && pFirst->pNext->zText[0]=='(' ){
+ return 0;
+ }
+ pFirst = pFirst->pNext;
+ }
+ return 1;
+}
+
+/*
+** Return TRUE if pFirst is the first token of a static assert.
+*/
+static int isStaticAssert(Token *pFirst){
+ if( (pFirst->nText==13 && strncmp(pFirst->zText, "static_assert", 13)==0)
+ || (pFirst->nText==14 && strncmp(pFirst->zText, "_Static_assert", 14)==0)
+ ){
+ return 1;
+ }else{
+ return 0;
+ }
+}
+
+/*
+** This routine is called whenever we encounter a ";" or "=". The stuff
+** between pFirst and pLast constitutes either a typedef or a global
+** variable definition. Do the right thing.
+*/
+static int ProcessDecl(Token *pFirst, Token *pEnd, int flags){
+ Token *pName;
+ Decl *pDecl;
+ int isLocal = 0;
+ int isVar;
+ int nErr = 0;
+
+ if( pFirst==0 || pEnd==0 ){
+ return 0;
+ }
+ if( flags & PS_Typedef ){
+ if( (flags & (PS_Export2|PS_Local2))!=0 ){
+ fprintf(stderr,"%s:%d: \"EXPORT\" or \"LOCAL\" ignored before typedef.\n",
+ zFilename, pFirst->nLine);
+ nErr++;
+ }
+ if( (flags & (PS_Interface|PS_Export|PS_Local|DP_Cplusplus))==0 ){
+ /* It is illegal to duplicate a typedef in C (but OK in C++).
+ ** So don't record typedefs that aren't within a C++ file or
+ ** within #if INTERFACE..#endif */
+ return nErr;
+ }
+ if( (flags & (PS_Interface|PS_Export|PS_Local))==0 && proto_static==0 ){
+ /* Ignore typedefs that are not with "#if INTERFACE..#endif" unless
+ ** the "-local" command line option is used. */
+ return nErr;
+ }
+ if( (flags & (PS_Interface|PS_Export))==0 ){
+ /* typedefs are always local, unless within #if INTERFACE..#endif */
+ isLocal = 1;
+ }
+ }else if( flags & (PS_Static|PS_Local2) ){
+ if( proto_static==0 && (flags & PS_Local2)==0 ){
+ /* Don't record static variables unless the "-local" command line
+ ** option was specified or the "LOCAL" keyword is used. */
+ return nErr;
+ }
+ while( pFirst!=0 && pFirst->pNext!=pEnd &&
+ ((pFirst->nText==6 && strncmp(pFirst->zText,"static",6)==0)
+ || (pFirst->nText==5 && strncmp(pFirst->zText,"LOCAL",6)==0))
+ ){
+ /* Lose the initial "static" or local from local variables.
+ ** We'll prepend "extern" later. */
+ pFirst = pFirst->pNext;
+ isLocal = 1;
+ }
+ if( pFirst==0 || !isLocal ){
+ return nErr;
+ }
+ }else if( flags & PS_Method ){
+ /* Methods are declared by their class. Don't declare separately. */
+ return nErr;
+ }else if( isStaticAssert(pFirst) ){
+ return 0;
+ }
+ isVar = (flags & (PS_Typedef|PS_Method))==0 && isVariableDef(pFirst,pEnd);
+ if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0
+ && (flags & PS_Extern)==0 ){
+ fprintf(stderr,"%s:%d: Can't define a variable in this context\n",
+ zFilename, pFirst->nLine);
+ nErr++;
+ }
+ pName = FindDeclName(pFirst,pEnd->pPrev);
+ if( pName==0 ){
+ if( pFirst->nText==4 && strncmp(pFirst->zText,"enum",4)==0 ){
+ /* Ignore completely anonymous enums. See documentation section 3.8.1. */
+ return nErr;
+ }else{
+ fprintf(stderr,"%s:%d: Can't find a name for the object declared here.\n",
+ zFilename, pFirst->nLine);
+ return nErr+1;
+ }
+ }
+
+#ifdef DEBUG
+ if( debugMask & PARSER ){
+ if( flags & PS_Typedef ){
+ printf("**** Found typedef %.*s at line %d...\n",
+ pName->nText, pName->zText, pName->nLine);
+ }else if( isVar ){
+ printf("**** Found variable %.*s at line %d...\n",
+ pName->nText, pName->zText, pName->nLine);
+ }else{
+ printf("**** Found prototype %.*s at line %d...\n",
+ pName->nText, pName->zText, pName->nLine);
+ }
+ PrintTokens(pFirst,pEnd->pPrev);
+ printf(";\n");
+ }
+#endif
+
+ pDecl = CreateDecl(pName->zText,pName->nText);
+ if( (flags & PS_Typedef) ){
+ DeclSetProperty(pDecl, TY_Typedef);
+ }else if( isVar ){
+ DeclSetProperty(pDecl,DP_ExternReqd | TY_Variable);
+ if( !(flags & DP_Cplusplus) ){
+ DeclSetProperty(pDecl,DP_ExternCReqd);
+ }
+ }else{
+ DeclSetProperty(pDecl, TY_Subroutine);
+ if( !(flags & DP_Cplusplus) ){
+ DeclSetProperty(pDecl,DP_ExternCReqd);
+ }
+ }
+ pDecl->pComment = pFirst->pComment;
+ pDecl->zDecl = TokensToString(pFirst,pEnd->pPrev,";\n",0,0);
+ if( isLocal || (flags & (PS_Local|PS_Local2))!=0 ){
+ DeclSetProperty(pDecl,DP_Local);
+ }else if( flags & (PS_Export|PS_Export2) ){
+ DeclSetProperty(pDecl,DP_Export);
+ }
+ if( flags & DP_Cplusplus ){
+ DeclSetProperty(pDecl,DP_Cplusplus);
+ }
+ return nErr;
+}
+
+/*
+** Push an if condition onto the if stack
+*/
+static void PushIfMacro(
+ const char *zPrefix, /* A prefix, like "define" or "!" */
+ const char *zText, /* The condition */
+ int nText, /* Number of characters in zText */
+ int nLine, /* Line number where this macro occurs */
+ int flags /* Either 0, PS_Interface, PS_Export or PS_Local */
+){
+ Ifmacro *pIf;
+ int nByte;
+
+ nByte = sizeof(Ifmacro);
+ if( zText ){
+ if( zPrefix ){
+ nByte += strlen(zPrefix) + 2;
+ }
+ nByte += nText + 1;
+ }
+ pIf = SafeMalloc( nByte );
+ if( zText ){
+ pIf->zCondition = (char*)&pIf[1];
+ if( zPrefix ){
+ sprintf(pIf->zCondition,"%s(%.*s)",zPrefix,nText,zText);
+ }else{
+ sprintf(pIf->zCondition,"%.*s",nText,zText);
+ }
+ }else{
+ pIf->zCondition = 0;
+ }
+ pIf->nLine = nLine;
+ pIf->flags = flags;
+ pIf->pNext = ifStack;
+ ifStack = pIf;
+}
+
+/*
+** This routine is called to handle all preprocessor directives.
+**
+** This routine will recompute the value of *pPresetFlags to be the
+** logical or of all flags on all nested #ifs. The #ifs that set flags
+** are as follows:
+**
+** conditional flag set
+** ------------------------ --------------------
+** #if INTERFACE PS_Interface
+** #if EXPORT_INTERFACE PS_Export
+** #if LOCAL_INTERFACE PS_Local
+**
+** For example, if after processing the preprocessor token given
+** by pToken there is an "#if INTERFACE" on the preprocessor
+** stack, then *pPresetFlags will be set to PS_Interface.
+*/
+static int ParsePreprocessor(Token *pToken, int flags, int *pPresetFlags){
+ const char *zCmd;
+ int nCmd;
+ const char *zArg;
+ int nArg;
+ int nErr = 0;
+ Ifmacro *pIf;
+
+ zCmd = &pToken->zText[1];
+ while( isspace(*zCmd) && *zCmd!='\n' ){
+ zCmd++;
+ }
+ if( !isalpha(*zCmd) ){
+ return 0;
+ }
+ nCmd = 1;
+ while( isalpha(zCmd[nCmd]) ){
+ nCmd++;
+ }
+
+ if( nCmd==5 && strncmp(zCmd,"endif",5)==0 ){
+ /*
+ ** Pop the if stack
+ */
+ pIf = ifStack;
+ if( pIf==0 ){
+ fprintf(stderr,"%s:%d: extra '#endif'.\n",zFilename,pToken->nLine);
+ return 1;
+ }
+ ifStack = pIf->pNext;
+ SafeFree(pIf);
+ }else if( nCmd==6 && strncmp(zCmd,"define",6)==0 ){
+ /*
+ ** Record a #define if we are in PS_Interface or PS_Export
+ */
+ Decl *pDecl;
+ if( !(flags & (PS_Local|PS_Interface|PS_Export)) ){ return 0; }
+ zArg = &zCmd[6];
+ while( *zArg && isspace(*zArg) && *zArg!='\n' ){
+ zArg++;
+ }
+ if( *zArg==0 || *zArg=='\n' ){ return 0; }
+ for(nArg=0; ISALNUM(zArg[nArg]); nArg++){}
+ if( nArg==0 ){ return 0; }
+ pDecl = CreateDecl(zArg,nArg);
+ pDecl->pComment = pToken->pComment;
+ DeclSetProperty(pDecl,TY_Macro);
+ pDecl->zDecl = SafeMalloc( pToken->nText + 2 );
+ sprintf(pDecl->zDecl,"%.*s\n",pToken->nText,pToken->zText);
+ if( flags & PS_Export ){
+ DeclSetProperty(pDecl,DP_Export);
+ }else if( flags & PS_Local ){
+ DeclSetProperty(pDecl,DP_Local);
+ }
+ }else if( nCmd==7 && strncmp(zCmd,"include",7)==0 ){
+ /*
+ ** Record an #include if we are in PS_Interface or PS_Export
+ */
+ Include *pInclude;
+ char *zIf;
+
+ if( !(flags & (PS_Interface|PS_Export)) ){ return 0; }
+ zArg = &zCmd[7];
+ while( *zArg && isspace(*zArg) ){ zArg++; }
+ for(nArg=0; !isspace(zArg[nArg]); nArg++){}
+ if( (zArg[0]=='"' && zArg[nArg-1]!='"')
+ ||(zArg[0]=='<' && zArg[nArg-1]!='>')
+ ){
+ fprintf(stderr,"%s:%d: malformed #include statement.\n",
+ zFilename,pToken->nLine);
+ return 1;
+ }
+ zIf = GetIfString();
+ if( zIf ){
+ pInclude = SafeMalloc( sizeof(Include) + nArg*2 + strlen(zIf) + 10 );
+ pInclude->zFile = (char*)&pInclude[1];
+ pInclude->zLabel = &pInclude->zFile[nArg+1];
+ sprintf(pInclude->zFile,"%.*s",nArg,zArg);
+ sprintf(pInclude->zLabel,"%.*s:%s",nArg,zArg,zIf);
+ pInclude->zIf = &pInclude->zLabel[nArg+1];
+ SafeFree(zIf);
+ }else{
+ pInclude = SafeMalloc( sizeof(Include) + nArg + 1 );
+ pInclude->zFile = (char*)&pInclude[1];
+ sprintf(pInclude->zFile,"%.*s",nArg,zArg);
+ pInclude->zIf = 0;
+ pInclude->zLabel = pInclude->zFile;
+ }
+ pInclude->pNext = includeList;
+ includeList = pInclude;
+ }else if( nCmd==2 && strncmp(zCmd,"if",2)==0 ){
+ /*
+ ** Push an #if. Watch for the special cases of INTERFACE
+ ** and EXPORT_INTERFACE and LOCAL_INTERFACE
+ */
+ zArg = &zCmd[2];
+ while( *zArg && isspace(*zArg) && *zArg!='\n' ){
+ zArg++;
+ }
+ if( *zArg==0 || *zArg=='\n' ){ return 0; }
+ nArg = pToken->nText + (int)(pToken->zText - zArg);
+ if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
+ PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
+ }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
+ PushIfMacro(0,0,0,pToken->nLine,PS_Export);
+ }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
+ PushIfMacro(0,0,0,pToken->nLine,PS_Local);
+ }else if( nArg==15 && strncmp(zArg,"MAKEHEADERS_STOPLOCAL_INTERFACE",15)==0 ){
+ PushIfMacro(0,0,0,pToken->nLine,PS_Local);
+ }else{
+ PushIfMacro(0,zArg,nArg,pToken->nLine,0);
+ }
+ }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){
+ /*
+ ** Push an #ifdef.
+ */
+ zArg = &zCmd[5];
+ while( *zArg && isspace(*zArg) && *zArg!='\n' ){
+ zArg++;
+ }
+ if( *zArg==0 || *zArg=='\n' ){ return 0; }
+ nArg = pToken->nText + (int)(pToken->zText - zArg);
+ PushIfMacro("defined",zArg,nArg,pToken->nLine,0);
+ }else if( nCmd==6 && strncmp(zCmd,"ifndef",6)==0 ){
+ /*
+ ** Push an #ifndef.
+ */
+ zArg = &zCmd[6];
+ while( *zArg && isspace(*zArg) && *zArg!='\n' ){
+ zArg++;
+ }
+ if( *zArg==0 || *zArg=='\n' ){ return 0; }
+ nArg = pToken->nText + (int)(pToken->zText - zArg);
+ PushIfMacro("!defined",zArg,nArg,pToken->nLine,0);
+ }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){
+ /*
+ ** Invert the #if on the top of the stack
+ */
+ if( ifStack==0 ){
+ fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename,
+ pToken->nLine);
+ return 1;
+ }
+ pIf = ifStack;
+ if( pIf->zCondition ){
+ ifStack = ifStack->pNext;
+ PushIfMacro("!",pIf->zCondition,strlen(pIf->zCondition),pIf->nLine,0);
+ SafeFree(pIf);
+ }else{
+ pIf->flags = 0;
+ }
+ }else{
+ /*
+ ** This directive can be safely ignored
+ */
+ return 0;
+ }
+
+ /*
+ ** Recompute the preset flags
+ */
+ *pPresetFlags = 0;
+ for(pIf = ifStack; pIf; pIf=pIf->pNext){
+ *pPresetFlags |= pIf->flags;
+ }
+
+ return nErr;
+}
+
+/*
+** Parse an entire file. Return the number of errors.
+**
+** pList is a list of tokens in the file. Whitespace tokens have been
+** eliminated, and text with {...} has been collapsed into a
+** single TT_Brace token.
+**
+** initFlags are a set of parse flags that should always be set for this
+** file. For .c files this is normally 0. For .h files it is PS_Interface.
+*/
+static int ParseFile(Token *pList, int initFlags){
+ int nErr = 0;
+ Token *pStart = 0;
+ int flags = initFlags;
+ int presetFlags = initFlags;
+ int resetFlag = 0;
+
+ includeList = 0;
+ while( pList ){
+ switch( pList->eType ){
+ case TT_EOF:
+ goto end_of_loop;
+
+ case TT_Preprocessor:
+ nErr += ParsePreprocessor(pList,flags,&presetFlags);
+ pStart = 0;
+ presetFlags |= initFlags;
+ flags = presetFlags;
+ break;
+
+ case TT_Other:
+ switch( pList->zText[0] ){
+ case ';':
+ nErr += ProcessDecl(pStart,pList,flags);
+ pStart = 0;
+ flags = presetFlags;
+ break;
+
+ case '=':
+ if( pList->pPrev->nText==8
+ && strncmp(pList->pPrev->zText,"operator",8)==0 ){
+ break;
+ }
+ nErr += ProcessDecl(pStart,pList,flags);
+ pStart = 0;
+ while( pList && pList->zText[0]!=';' ){
+ pList = pList->pNext;
+ }
+ if( pList==0 ) goto end_of_loop;
+ flags = presetFlags;
+ break;
+
+ case ':':
+ if( pList->zText[1]==':' ){
+ flags |= PS_Method;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case TT_Braces:
+ nErr += ProcessProcedureDef(pStart,pList,flags);
+ pStart = 0;
+ flags = presetFlags;
+ break;
+
+ case TT_Id:
+ if( pStart==0 ){
+ pStart = pList;
+ flags = presetFlags;
+ }
+ resetFlag = 0;
+ switch( pList->zText[0] ){
+ case 'c':
+ if( pList->nText==5 && strncmp(pList->zText,"class",5)==0 ){
+ nErr += ProcessTypeDecl(pList,flags,&resetFlag);
+ }
+ break;
+
+ case 'E':
+ if( pList->nText==6 && strncmp(pList->zText,"EXPORT",6)==0 ){
+ flags |= PS_Export2;
+ /* pStart = 0; */
+ }
+ break;
+
+ case 'e':
+ if( pList->nText==4 && strncmp(pList->zText,"enum",4)==0 ){
+ if( pList->pNext && pList->pNext->eType==TT_Braces ){
+ pList = pList->pNext;
+ }else{
+ nErr += ProcessTypeDecl(pList,flags,&resetFlag);
+ }
+ }else if( pList->nText==6 && strncmp(pList->zText,"extern",6)==0 ){
+ pList = pList->pNext;
+ if( pList && pList->nText==3 && strncmp(pList->zText,"\"C\"",3)==0 ){
+ pList = pList->pNext;
+ flags &= ~DP_Cplusplus;
+ }else{
+ flags |= PS_Extern;
+ }
+ pStart = pList;
+ }
+ break;
+
+ case 'i':
+ if( pList->nText==6 && strncmp(pList->zText,"inline",6)==0
+ && (flags & PS_Static)==0
+ ){
+ nErr += ProcessInlineProc(pList,flags,&resetFlag);
+ }
+ break;
+
+ case 'L':
+ if( pList->nText==5 && strncmp(pList->zText,"LOCAL",5)==0 ){
+ flags |= PS_Local2;
+ pStart = pList;
+ }
+ break;
+
+ case 'P':
+ if( pList->nText==6 && strncmp(pList->zText, "PUBLIC",6)==0 ){
+ flags |= PS_Public;
+ pStart = pList;
+ }else if( pList->nText==7 && strncmp(pList->zText, "PRIVATE",7)==0 ){
+ flags |= PS_Private;
+ pStart = pList;
+ }else if( pList->nText==9 && strncmp(pList->zText,"PROTECTED",9)==0 ){
+ flags |= PS_Protected;
+ pStart = pList;
+ }
+ break;
+
+ case 's':
+ if( pList->nText==6 && strncmp(pList->zText,"struct",6)==0 ){
+ if( pList->pNext && pList->pNext->eType==TT_Braces ){
+ pList = pList->pNext;
+ }else{
+ nErr += ProcessTypeDecl(pList,flags,&resetFlag);
+ }
+ }else if( pList->nText==6 && strncmp(pList->zText,"static",6)==0 ){
+ flags |= PS_Static;
+ }
+ break;
+
+ case 't':
+ if( pList->nText==7 && strncmp(pList->zText,"typedef",7)==0 ){
+ flags |= PS_Typedef;
+ }
+ break;
+
+ case 'u':
+ if( pList->nText==5 && strncmp(pList->zText,"union",5)==0 ){
+ if( pList->pNext && pList->pNext->eType==TT_Braces ){
+ pList = pList->pNext;
+ }else{
+ nErr += ProcessTypeDecl(pList,flags,&resetFlag);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ if( resetFlag!=0 ){
+ while( pList && pList->zText[0]!=resetFlag ){
+ pList = pList->pNext;
+ }
+ if( pList==0 ) goto end_of_loop;
+ pStart = 0;
+ flags = presetFlags;
+ }
+ break;
+
+ case TT_String:
+ case TT_Number:
+ break;
+
+ default:
+ pStart = pList;
+ flags = presetFlags;
+ break;
+ }
+ pList = pList->pNext;
+ }
+ end_of_loop:
+
+ /* Verify that all #ifs have a matching "#endif" */
+ while( ifStack ){
+ Ifmacro *pIf = ifStack;
+ ifStack = pIf->pNext;
+ fprintf(stderr,"%s:%d: This '#if' has no '#endif'\n",zFilename,
+ pIf->nLine);
+ SafeFree(pIf);
+ }
+
+ return nErr;
+}
+
+/*
+** If the given Decl object has a non-null zExtra field, then the text
+** of that zExtra field needs to be inserted in the middle of the
+** zDecl field before the last "}" in the zDecl. This routine does that.
+** If the zExtra is NULL, this routine is a no-op.
+**
+** zExtra holds extra method declarations for classes. The declarations
+** have to be inserted into the class definition.
+*/
+static void InsertExtraDecl(Decl *pDecl){
+ int i;
+ String str;
+
+ if( pDecl==0 || pDecl->zExtra==0 || pDecl->zDecl==0 ) return;
+ i = strlen(pDecl->zDecl) - 1;
+ while( i>0 && pDecl->zDecl[i]!='}' ){ i--; }
+ StringInit(&str);
+ StringAppend(&str, pDecl->zDecl, i);
+ StringAppend(&str, pDecl->zExtra, 0);
+ StringAppend(&str, &pDecl->zDecl[i], 0);
+ SafeFree(pDecl->zDecl);
+ SafeFree(pDecl->zExtra);
+ pDecl->zDecl = StrDup(StringGet(&str), 0);
+ StringReset(&str);
+ pDecl->zExtra = 0;
+}
+
+/*
+** Reset the DP_Forward and DP_Declared flags on all Decl structures.
+** Set both flags for anything that is tagged as local and isn't
+** in the file zFilename so that it won't be printing in other files.
+*/
+static void ResetDeclFlags(char *zFilename){
+ Decl *pDecl;
+
+ for(pDecl = pDeclFirst; pDecl; pDecl = pDecl->pNext){
+ DeclClearProperty(pDecl,DP_Forward|DP_Declared);
+ if( DeclHasProperty(pDecl,DP_Local) && pDecl->zFile!=zFilename ){
+ DeclSetProperty(pDecl,DP_Forward|DP_Declared);
+ }
+ }
+}
+
+/*
+** Forward declaration of the ScanText() function.
+*/
+static void ScanText(const char*, GenState *pState);
+
+/*
+** The output in pStr is currently within an #if CONTEXT where context
+** is equal to *pzIf. (*pzIf might be NULL to indicate that we are
+** not within any #if at the moment.) We are getting ready to output
+** some text that needs to be within the context of "#if NEW" where
+** NEW is zIf. Make an appropriate change to the context.
+*/
+static void ChangeIfContext(
+ const char *zIf, /* The desired #if context */
+ GenState *pState /* Current state of the code generator */
+){
+ if( zIf==0 ){
+ if( pState->zIf==0 ) return;
+ StringAppend(pState->pStr,"#endif\n",0);
+ pState->zIf = 0;
+ }else{
+ if( pState->zIf ){
+ if( strcmp(zIf,pState->zIf)==0 ) return;
+ StringAppend(pState->pStr,"#endif\n",0);
+ pState->zIf = 0;
+ }
+ ScanText(zIf, pState);
+ if( pState->zIf!=0 ){
+ StringAppend(pState->pStr,"#endif\n",0);
+ }
+ StringAppend(pState->pStr,"#if ",0);
+ StringAppend(pState->pStr,zIf,0);
+ StringAppend(pState->pStr,"\n",0);
+ pState->zIf = zIf;
+ }
+}
+
+/*
+** Add to the string pStr a #include of every file on the list of
+** include files pInclude. The table pTable contains all files that
+** have already been #included at least once. Don't add any
+** duplicates. Update pTable with every new #include that is added.
+*/
+static void AddIncludes(
+ Include *pInclude, /* Write every #include on this list */
+ GenState *pState /* Current state of the code generator */
+){
+ if( pInclude ){
+ if( pInclude->pNext ){
+ AddIncludes(pInclude->pNext,pState);
+ }
+ if( IdentTableInsert(pState->pTable,pInclude->zLabel,0) ){
+ ChangeIfContext(pInclude->zIf,pState);
+ StringAppend(pState->pStr,"#include ",0);
+ StringAppend(pState->pStr,pInclude->zFile,0);
+ StringAppend(pState->pStr,"\n",1);
+ }
+ }
+}
+
+/*
+** Add to the string pStr a declaration for the object described
+** in pDecl.
+**
+** If pDecl has already been declared in this file, detect that
+** fact and abort early. Do not duplicate a declaration.
+**
+** If the needFullDecl flag is false and this object has a forward
+** declaration, then supply the forward declaration only. A later
+** call to CompleteForwardDeclarations() will finish the declaration
+** for us. But if needFullDecl is true, we must supply the full
+** declaration now. Some objects do not have a forward declaration.
+** For those objects, we must print the full declaration now.
+**
+** Because it is illegal to duplicate a typedef in C, care is taken
+** to insure that typedefs for the same identifier are only issued once.
+*/
+static void DeclareObject(
+ Decl *pDecl, /* The thing to be declared */
+ GenState *pState, /* Current state of the code generator */
+ int needFullDecl /* Must have the full declaration. A forward
+ * declaration isn't enough */
+){
+ Decl *p; /* The object to be declared */
+ int flag;
+ int isCpp; /* True if generating C++ */
+ int doneTypedef = 0; /* True if a typedef has been done for this object */
+
+ /* printf("BEGIN %s of %s\n",needFullDecl?"FULL":"PROTOTYPE",pDecl->zName);*/
+ /*
+ ** For any object that has a forward declaration, go ahead and do the
+ ** forward declaration first.
+ */
+ isCpp = (pState->flags & DP_Cplusplus) != 0;
+ for(p=pDecl; p; p=p->pSameName){
+ if( p->zFwd ){
+ if( !DeclHasProperty(p,DP_Forward) ){
+ DeclSetProperty(p,DP_Forward);
+ if( strncmp(p->zFwd,"typedef",7)==0 ){
+ if( doneTypedef ) continue;
+ doneTypedef = 1;
+ }
+ ChangeIfContext(p->zIf,pState);
+ StringAppend(pState->pStr,isCpp ? p->zFwdCpp : p->zFwd,0);
+ }
+ }
+ }
+
+ /*
+ ** Early out if everything is already suitably declared.
+ **
+ ** This is a very important step because it prevents us from
+ ** executing the code the follows in a recursive call to this
+ ** function with the same value for pDecl.
+ */
+ flag = needFullDecl ? DP_Declared|DP_Forward : DP_Forward;
+ for(p=pDecl; p; p=p->pSameName){
+ if( !DeclHasProperty(p,flag) ) break;
+ }
+ if( p==0 ){
+ return;
+ }
+
+ /*
+ ** Make sure we have all necessary #includes
+ */
+ for(p=pDecl; p; p=p->pSameName){
+ AddIncludes(p->pInclude,pState);
+ }
+
+ /*
+ ** Go ahead an mark everything as being declared, to prevent an
+ ** infinite loop thru the ScanText() function. At the same time,
+ ** we decide which objects need a full declaration and mark them
+ ** with the DP_Flag bit. We are only able to use DP_Flag in this
+ ** way because we know we'll never execute this far into this
+ ** function on a recursive call with the same pDecl. Hence, recursive
+ ** calls to this function (through ScanText()) can never change the
+ ** value of DP_Flag out from under us.
+ */
+ for(p=pDecl; p; p=p->pSameName){
+ if( !DeclHasProperty(p,DP_Declared)
+ && (p->zFwd==0 || needFullDecl)
+ && p->zDecl!=0
+ ){
+ DeclSetProperty(p,DP_Forward|DP_Declared|DP_Flag);
+ }else{
+ DeclClearProperty(p,DP_Flag);
+ }
+ }
+
+ /*
+ ** Call ScanText() recursively (this routine is called from ScanText())
+ ** to include declarations required to come before these declarations.
+ */
+ for(p=pDecl; p; p=p->pSameName){
+ if( DeclHasProperty(p,DP_Flag) ){
+ if( p->zDecl[0]=='#' ){
+ ScanText(&p->zDecl[1],pState);
+ }else{
+ InsertExtraDecl(p);
+ ScanText(p->zDecl,pState);
+ }
+ }
+ }
+
+ /*
+ ** Output the declarations. Do this in two passes. First
+ ** output everything that isn't a typedef. Then go back and
+ ** get the typedefs by the same name.
+ */
+ for(p=pDecl; p; p=p->pSameName){
+ if( DeclHasProperty(p,DP_Flag) && !DeclHasProperty(p,TY_Typedef) ){
+ if( DeclHasAnyProperty(p,TY_Enumeration) ){
+ if( doneTypedef ) continue;
+ doneTypedef = 1;
+ }
+ ChangeIfContext(p->zIf,pState);
+ if( !isCpp && DeclHasAnyProperty(p,DP_ExternReqd) ){
+ StringAppend(pState->pStr,"extern ",0);
+ }else if( isCpp && DeclHasProperty(p,DP_Cplusplus|DP_ExternReqd) ){
+ StringAppend(pState->pStr,"extern ",0);
+ }else if( isCpp && DeclHasAnyProperty(p,DP_ExternCReqd|DP_ExternReqd) ){
+ StringAppend(pState->pStr,"extern \"C\" ",0);
+ }
+ InsertExtraDecl(p);
+ StringAppend(pState->pStr,p->zDecl,0);
+ if( !isCpp && DeclHasProperty(p,DP_Cplusplus) ){
+ fprintf(stderr,
+ "%s: C code ought not reference the C++ object \"%s\"\n",
+ pState->zFilename, p->zName);
+ pState->nErr++;
+ }
+ DeclClearProperty(p,DP_Flag);
+ }
+ }
+ for(p=pDecl; p && !doneTypedef; p=p->pSameName){
+ if( DeclHasProperty(p,DP_Flag) ){
+ /* This has to be a typedef */
+ doneTypedef = 1;
+ ChangeIfContext(p->zIf,pState);
+ InsertExtraDecl(p);
+ StringAppend(pState->pStr,p->zDecl,0);
+ }
+ }
+}
+
+/*
+** This routine scans the input text given, and appends to the
+** string in pState->pStr the text of any declarations that must
+** occur before the text in zText.
+**
+** If an identifier in zText is immediately followed by '*', then
+** only forward declarations are needed for that identifier. If the
+** identifier name is not followed immediately by '*', we must supply
+** a full declaration.
+*/
+static void ScanText(
+ const char *zText, /* The input text to be scanned */
+ GenState *pState /* Current state of the code generator */
+){
+ int nextValid = 0; /* True is sNext contains valid data */
+ InStream sIn; /* The input text */
+ Token sToken; /* The current token being examined */
+ Token sNext; /* The next non-space token */
+
+ /* printf("BEGIN SCAN TEXT on %s\n", zText); */
+
+ sIn.z = zText;
+ sIn.i = 0;
+ sIn.nLine = 1;
+ while( sIn.z[sIn.i]!=0 ){
+ if( nextValid ){
+ sToken = sNext;
+ nextValid = 0;
+ }else{
+ GetNonspaceToken(&sIn,&sToken);
+ }
+ if( sToken.eType==TT_Id ){
+ int needFullDecl; /* True if we need to provide the full declaration,
+ ** not just the forward declaration */
+ Decl *pDecl; /* The declaration having the name in sToken */
+
+ /*
+ ** See if there is a declaration in the database with the name given
+ ** by sToken.
+ */
+ pDecl = FindDecl(sToken.zText,sToken.nText);
+ if( pDecl==0 ) continue;
+
+ /*
+ ** If we get this far, we've found an identifier that has a
+ ** declaration in the database. Now see if we the full declaration
+ ** or just a forward declaration.
+ */
+ GetNonspaceToken(&sIn,&sNext);
+ if( sNext.zText[0]=='*' ){
+ needFullDecl = 0;
+ }else{
+ needFullDecl = 1;
+ nextValid = sNext.eType==TT_Id;
+ }
+
+ /*
+ ** Generate the needed declaration.
+ */
+ DeclareObject(pDecl,pState,needFullDecl);
+ }else if( sToken.eType==TT_Preprocessor ){
+ sIn.i -= sToken.nText - 1;
+ }
+ }
+ /* printf("END SCANTEXT\n"); */
+}
+
+/*
+** Provide a full declaration to any object which so far has had only
+** a forward declaration.
+*/
+static void CompleteForwardDeclarations(GenState *pState){
+ Decl *pDecl;
+ int progress;
+
+ do{
+ progress = 0;
+ for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){
+ if( DeclHasProperty(pDecl,DP_Forward)
+ && !DeclHasProperty(pDecl,DP_Declared)
+ ){
+ DeclareObject(pDecl,pState,1);
+ progress = 1;
+ assert( DeclHasProperty(pDecl,DP_Declared) );
+ }
+ }
+ }while( progress );
+}
+
+/*
+** Generate an include file for the given source file. Return the number
+** of errors encountered.
+**
+** if nolocal_flag is true, then we do not generate declarations for
+** objected marked DP_Local.
+*/
+static int MakeHeader(InFile *pFile, FILE *report, int nolocal_flag){
+ int nErr = 0;
+ GenState sState;
+ String outStr;
+ IdentTable includeTable;
+ Ident *pId;
+ char *zNewVersion;
+ char *zOldVersion;
+
+ if( pFile->zHdr==0 || *pFile->zHdr==0 ) return 0;
+ sState.pStr = &outStr;
+ StringInit(&outStr);
+ StringAppend(&outStr,zTopLine,nTopLine);
+ sState.pTable = &includeTable;
+ memset(&includeTable,0,sizeof(includeTable));
+ sState.zIf = 0;
+ sState.nErr = 0;
+ sState.zFilename = pFile->zSrc;
+ sState.flags = pFile->flags & DP_Cplusplus;
+ ResetDeclFlags(nolocal_flag ? "no" : pFile->zSrc);
+ for(pId = pFile->idTable.pList; pId; pId=pId->pNext){
+ Decl *pDecl = FindDecl(pId->zName,0);
+ if( pDecl ){
+ DeclareObject(pDecl,&sState,1);
+ }
+ }
+ CompleteForwardDeclarations(&sState);
+ ChangeIfContext(0,&sState);
+ nErr += sState.nErr;
+ zOldVersion = ReadFile(pFile->zHdr);
+ zNewVersion = StringGet(&outStr);
+ if( report ) fprintf(report,"%s: ",pFile->zHdr);
+ if( zOldVersion==0 ){
+ if( report ) fprintf(report,"updated\n");
+ if( WriteFile(pFile->zHdr,zNewVersion) ){
+ fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
+ nErr++;
+ }
+ }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){
+ if( report ) fprintf(report,"error!\n");
+ fprintf(stderr,
+ "%s: Can't overwrite this file because it wasn't previously\n"
+ "%*s generated by 'makeheaders'.\n",
+ pFile->zHdr, (int)strlen(pFile->zHdr), "");
+ nErr++;
+ }else if( strcmp(zOldVersion,zNewVersion)!=0 ){
+ if( report ) fprintf(report,"updated\n");
+ if( WriteFile(pFile->zHdr,zNewVersion) ){
+ fprintf(stderr,"%s: Can't write to file\n",pFile->zHdr);
+ nErr++;
+ }
+ }else if( report ){
+ fprintf(report,"unchanged\n");
+ }
+ SafeFree(zOldVersion);
+ IdentTableReset(&includeTable);
+ StringReset(&outStr);
+ return nErr;
+}
+
+/*
+** Generate a global header file -- a header file that contains all
+** declarations. If the forExport flag is true, then only those
+** objects that are exported are included in the header file.
+*/
+static int MakeGlobalHeader(int forExport){
+ GenState sState;
+ String outStr;
+ IdentTable includeTable;
+ Decl *pDecl;
+
+ sState.pStr = &outStr;
+ StringInit(&outStr);
+ /* StringAppend(&outStr,zTopLine,nTopLine); */
+ sState.pTable = &includeTable;
+ memset(&includeTable,0,sizeof(includeTable));
+ sState.zIf = 0;
+ sState.nErr = 0;
+ sState.zFilename = "(all)";
+ sState.flags = 0;
+ ResetDeclFlags(0);
+ for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){
+ if( forExport==0 || DeclHasProperty(pDecl,DP_Export) ){
+ DeclareObject(pDecl,&sState,1);
+ }
+ }
+ ChangeIfContext(0,&sState);
+ printf("%s",StringGet(&outStr));
+ IdentTableReset(&includeTable);
+ StringReset(&outStr);
+ return 0;
+}
+
+#ifdef DEBUG
+/*
+** Return the number of characters in the given string prior to the
+** first newline.
+*/
+static int ClipTrailingNewline(char *z){
+ int n = strlen(z);
+ while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ){ n--; }
+ return n;
+}
+
+/*
+** Dump the entire declaration list for debugging purposes
+*/
+static void DumpDeclList(void){
+ Decl *pDecl;
+
+ for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){
+ printf("**** %s from file %s ****\n",pDecl->zName,pDecl->zFile);
+ if( pDecl->zIf ){
+ printf("If: [%.*s]\n",ClipTrailingNewline(pDecl->zIf),pDecl->zIf);
+ }
+ if( pDecl->zFwd ){
+ printf("Decl: [%.*s]\n",ClipTrailingNewline(pDecl->zFwd),pDecl->zFwd);
+ }
+ if( pDecl->zDecl ){
+ InsertExtraDecl(pDecl);
+ printf("Def: [%.*s]\n",ClipTrailingNewline(pDecl->zDecl),pDecl->zDecl);
+ }
+ if( pDecl->flags ){
+ static struct {
+ int mask;
+ char *desc;
+ } flagSet[] = {
+ { TY_Class, "class" },
+ { TY_Enumeration, "enum" },
+ { TY_Structure, "struct" },
+ { TY_Union, "union" },
+ { TY_Variable, "variable" },
+ { TY_Subroutine, "function" },
+ { TY_Typedef, "typedef" },
+ { TY_Macro, "macro" },
+ { DP_Export, "export" },
+ { DP_Local, "local" },
+ { DP_Cplusplus, "C++" },
+ };
+ int i;
+ printf("flags:");
+ for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){
+ if( flagSet[i].mask & pDecl->flags ){
+ printf(" %s", flagSet[i].desc);
+ }
+ }
+ printf("\n");
+ }
+ if( pDecl->pInclude ){
+ Include *p;
+ printf("includes:");
+ for(p=pDecl->pInclude; p; p=p->pNext){
+ printf(" %s",p->zFile);
+ }
+ printf("\n");
+ }
+ }
+}
+#endif
+
+/*
+** When the "-doc" command-line option is used, this routine is called
+** to print all of the database information to standard output.
+*/
+static void DocumentationDump(void){
+ Decl *pDecl;
+ static struct {
+ int mask;
+ char flag;
+ } flagSet[] = {
+ { TY_Class, 'c' },
+ { TY_Enumeration, 'e' },
+ { TY_Structure, 's' },
+ { TY_Union, 'u' },
+ { TY_Variable, 'v' },
+ { TY_Subroutine, 'f' },
+ { TY_Typedef, 't' },
+ { TY_Macro, 'm' },
+ { DP_Export, 'x' },
+ { DP_Local, 'l' },
+ { DP_Cplusplus, '+' },
+ };
+
+ for(pDecl = pDeclFirst; pDecl; pDecl=pDecl->pNext){
+ int i;
+ int nLabel = 0;
+ char *zDecl;
+ char zLabel[50];
+ for(i=0; i<sizeof(flagSet)/sizeof(flagSet[0]); i++){
+ if( DeclHasProperty(pDecl,flagSet[i].mask) ){
+ zLabel[nLabel++] = flagSet[i].flag;
+ }
+ }
+ if( nLabel==0 ) continue;
+ zLabel[nLabel] = 0;
+ InsertExtraDecl(pDecl);
+ zDecl = pDecl->zDecl;
+ if( zDecl==0 ) zDecl = pDecl->zFwd;
+ printf("%s %s %s %p %d %d %d %d %d\n",
+ pDecl->zName,
+ zLabel,
+ pDecl->zFile,
+ pDecl->pComment,
+ pDecl->pComment ? pDecl->pComment->nText+1 : 0,
+ pDecl->zIf ? (int)strlen(pDecl->zIf)+1 : 0,
+ zDecl ? (int)strlen(zDecl) : 0,
+ pDecl->pComment ? pDecl->pComment->nLine : 0,
+ pDecl->tokenCode.nText ? pDecl->tokenCode.nText+1 : 0
+ );
+ if( pDecl->pComment ){
+ printf("%.*s\n",pDecl->pComment->nText, pDecl->pComment->zText);
+ }
+ if( pDecl->zIf ){
+ printf("%s\n",pDecl->zIf);
+ }
+ if( zDecl ){
+ printf("%s",zDecl);
+ }
+ if( pDecl->tokenCode.nText ){
+ printf("%.*s\n",pDecl->tokenCode.nText, pDecl->tokenCode.zText);
+ }
+ }
+}
+
+/*
+** Given the complete text of an input file, this routine prints a
+** documentation record for the header comment at the beginning of the
+** file (if the file has a header comment.)
+*/
+void PrintModuleRecord(const char *zFile, const char *zFilename){
+ int i;
+ static int addr = 5;
+ while( isspace(*zFile) ){ zFile++; }
+ if( *zFile!='/' || zFile[1]!='*' ) return;
+ for(i=2; zFile[i] && (zFile[i-1]!='/' || zFile[i-2]!='*'); i++){}
+ if( zFile[i]==0 ) return;
+ printf("%s M %s %d %d 0 0 0 0\n%.*s\n",
+ zFilename, zFilename, addr, i+1, i, zFile);
+ addr += 4;
+}
+
+
+/*
+** Given an input argument to the program, construct a new InFile
+** object.
+*/
+static InFile *CreateInFile(char *zArg, int *pnErr){
+ int nSrc;
+ char *zSrc;
+ InFile *pFile;
+ int i;
+
+ /*
+ ** Get the name of the input file to be scanned. The input file is
+ ** everything before the first ':' or the whole file if no ':' is seen.
+ **
+ ** Except, on windows, ignore any ':' that occurs as the second character
+ ** since it might be part of the drive specifier. So really, the ":' has
+ ** to be the 3rd or later character in the name. This precludes 1-character
+ ** file names, which really should not be a problem.
+ */
+ zSrc = zArg;
+ for(nSrc=2; zSrc[nSrc] && zArg[nSrc]!=':'; nSrc++){}
+ pFile = SafeMalloc( sizeof(InFile) );
+ memset(pFile,0,sizeof(InFile));
+ pFile->zSrc = StrDup(zSrc,nSrc);
+
+ /* Figure out if we are dealing with C or C++ code. Assume any
+ ** file with ".c" or ".h" is C code and all else is C++.
+ */
+ if( nSrc>2 && zSrc[nSrc-2]=='.' && (zSrc[nSrc-1]=='c' || zSrc[nSrc-1]=='h')){
+ pFile->flags &= ~DP_Cplusplus;
+ }else{
+ pFile->flags |= DP_Cplusplus;
+ }
+
+ /*
+ ** If a separate header file is specified, use it
+ */
+ if( zSrc[nSrc]==':' ){
+ int nHdr;
+ char *zHdr;
+ zHdr = &zSrc[nSrc+1];
+ for(nHdr=0; zHdr[nHdr]; nHdr++){}
+ pFile->zHdr = StrDup(zHdr,nHdr);
+ }
+
+ /* Look for any 'c' or 'C' in the suffix of the file name and change
+ ** that character to 'h' or 'H' respectively. If no 'c' or 'C' is found,
+ ** then assume we are dealing with a header.
+ */
+ else{
+ int foundC = 0;
+ pFile->zHdr = StrDup(zSrc,nSrc);
+ for(i = nSrc-1; i>0 && pFile->zHdr[i]!='.'; i--){
+ if( pFile->zHdr[i]=='c' ){
+ foundC = 1;
+ pFile->zHdr[i] = 'h';
+ }else if( pFile->zHdr[i]=='C' ){
+ foundC = 1;
+ pFile->zHdr[i] = 'H';
+ }
+ }
+ if( !foundC ){
+ SafeFree(pFile->zHdr);
+ pFile->zHdr = 0;
+ }
+ }
+
+ /*
+ ** If pFile->zSrc contains no 'c' or 'C' in its extension, it
+ ** must be a header file. In that case, we need to set the
+ ** PS_Interface flag.
+ */
+ pFile->flags |= PS_Interface;
+ for(i=nSrc-1; i>0 && zSrc[i]!='.'; i--){
+ if( zSrc[i]=='c' || zSrc[i]=='C' ){
+ pFile->flags &= ~PS_Interface;
+ break;
+ }
+ }
+
+ /* Done!
+ */
+ return pFile;
+}
+
+/* MS-Windows and MS-DOS both have the following serious OS bug: the
+** length of a command line is severely restricted. But this program
+** occasionally requires long command lines. Hence the following
+** work around.
+**
+** If the parameters "-f FILENAME" appear anywhere on the command line,
+** then the named file is scanned for additional command line arguments.
+** These arguments are substituted in place of the "FILENAME" argument
+** in the original argument list.
+**
+** This first parameter to this routine is the index of the "-f"
+** parameter in the argv[] array. The argc and argv are passed by
+** pointer so that they can be changed.
+**
+** Parsing of the parameters in the file is very simple. Parameters
+** can be separated by any amount of white-space (including newlines
+** and carriage returns.) There are now quoting characters of any
+** kind. The length of a token is limited to about 1000 characters.
+*/
+static void AddParameters(int index, int *pArgc, char ***pArgv){
+ int argc = *pArgc; /* The original argc value */
+ char **argv = *pArgv; /* The original argv value */
+ int newArgc; /* Value for argc after inserting new arguments */
+ char **zNew = 0; /* The new argv after this routine is done */
+ char *zFile; /* Name of the input file */
+ int nNew = 0; /* Number of new entries in the argv[] file */
+ int nAlloc = 0; /* Space allocated for zNew[] */
+ int i; /* Loop counter */
+ int n; /* Number of characters in a new argument */
+ int c; /* Next character of input */
+ int startOfLine = 1; /* True if we are where '#' can start a comment */
+ FILE *in; /* The input file */
+ char zBuf[1000]; /* A single argument is accumulated here */
+
+ if( index+1==argc ) return;
+ zFile = argv[index+1];
+ in = fopen(zFile,"r");
+ if( in==0 ){
+ fprintf(stderr,"Can't open input file \"%s\"\n",zFile);
+ exit(1);
+ }
+ c = ' ';
+ while( c!=EOF ){
+ while( c!=EOF && isspace(c) ){
+ if( c=='\n' ){
+ startOfLine = 1;
+ }
+ c = getc(in);
+ if( startOfLine && c=='#' ){
+ while( c!=EOF && c!='\n' ){
+ c = getc(in);
+ }
+ }
+ }
+ n = 0;
+ while( c!=EOF && !isspace(c) ){
+ if( n<sizeof(zBuf)-1 ){ zBuf[n++] = c; }
+ startOfLine = 0;
+ c = getc(in);
+ }
+ zBuf[n] = 0;
+ if( n>0 ){
+ nNew++;
+ if( nNew + argc > nAlloc ){
+ if( nAlloc==0 ){
+ nAlloc = 100 + argc;
+ zNew = malloc( sizeof(char*) * nAlloc );
+ }else{
+ nAlloc *= 2;
+ zNew = realloc( zNew, sizeof(char*) * nAlloc );
+ }
+ }
+ if( zNew ){
+ int j = nNew + index;
+ zNew[j] = malloc( n + 1 );
+ if( zNew[j] ){
+ strcpy( zNew[j], zBuf );
+ }
+ }
+ }
+ }
+ newArgc = argc + nNew - 1;
+ for(i=0; i<=index; i++){
+ zNew[i] = argv[i];
+ }
+ for(i=nNew + index + 1; i<newArgc; i++){
+ zNew[i] = argv[i + 1 - nNew];
+ }
+ zNew[newArgc] = 0;
+ *pArgc = newArgc;
+ *pArgv = zNew;
+}
+
+#ifdef NOT_USED
+/*
+** Return the time that the given file was last modified. If we can't
+** locate the file (because, for example, it doesn't exist), then
+** return 0.
+*/
+static unsigned int ModTime(const char *zFilename){
+ unsigned int mTime = 0;
+ struct stat sStat;
+ if( stat(zFilename,&sStat)==0 ){
+ mTime = sStat.st_mtime;
+ }
+ return mTime;
+}
+#endif
+
+/*
+** Print a usage comment for this program.
+*/
+static void Usage(const char *argv0, const char *argvN){
+ fprintf(stderr,"%s: Illegal argument \"%s\"\n",argv0,argvN);
+ fprintf(stderr,"Usage: %s [options] filename...\n"
+ "Options:\n"
+ " -h Generate a single .h to standard output.\n"
+ " -H Like -h, but only output EXPORT declarations.\n"
+ " -v (verbose) Write status information to the screen.\n"
+ " -doc Generate no header files. Instead, output information\n"
+ " that can be used by an automatic program documentation\n"
+ " and cross-reference generator.\n"
+ " -local Generate prototypes for \"static\" functions and\n"
+ " procedures.\n"
+ " -f FILE Read additional command-line arguments from the file named\n"
+ " \"FILE\".\n"
+#ifdef DEBUG
+ " -! MASK Set the debugging mask to the number \"MASK\".\n"
+#endif
+ " -- Treat all subsequent comment-line parameters as filenames,\n"
+ " even if they begin with \"-\".\n",
+ argv0
+ );
+}
+
+/*
+** The following text contains a few simple #defines that we want
+** to be available to every file.
+*/
+static const char zInit[] =
+ "#define INTERFACE 0\n"
+ "#define EXPORT_INTERFACE 0\n"
+ "#define LOCAL_INTERFACE 0\n"
+ "#define EXPORT\n"
+ "#define LOCAL static\n"
+ "#define PUBLIC\n"
+ "#define PRIVATE\n"
+ "#define PROTECTED\n"
+;
+
+#if TEST==0
+int main(int argc, char **argv){
+ int i; /* Loop counter */
+ int nErr = 0; /* Number of errors encountered */
+ Token *pList; /* List of input tokens for one file */
+ InFile *pFileList = 0;/* List of all input files */
+ InFile *pTail = 0; /* Last file on the list */
+ InFile *pFile; /* for looping over the file list */
+ int h_flag = 0; /* True if -h is present. Output unified header */
+ int H_flag = 0; /* True if -H is present. Output EXPORT header */
+ int v_flag = 0; /* Verbose */
+ int noMoreFlags; /* True if -- has been seen. */
+ FILE *report; /* Send progress reports to this, if not NULL */
+
+ noMoreFlags = 0;
+ for(i=1; i<argc; i++){
+ if( argv[i][0]=='-' && !noMoreFlags ){
+ switch( argv[i][1] ){
+ case 'h': h_flag = 1; break;
+ case 'H': H_flag = 1; break;
+ case 'v': v_flag = 1; break;
+ case 'd': doc_flag = 1; proto_static = 1; break;
+ case 'l': proto_static = 1; break;
+ case 'f': AddParameters(i, &argc, &argv); break;
+ case '-': noMoreFlags = 1; break;
+#ifdef DEBUG
+ case '!': i++; debugMask = strtol(argv[i],0,0); break;
+#endif
+ default: Usage(argv[0],argv[i]); return 1;
+ }
+ }else{
+ pFile = CreateInFile(argv[i],&nErr);
+ if( pFile ){
+ if( pFileList ){
+ pTail->pNext = pFile;
+ pTail = pFile;
+ }else{
+ pFileList = pTail = pFile;
+ }
+ }
+ }
+ }
+ if( h_flag && H_flag ){
+ h_flag = 0;
+ }
+ if( v_flag ){
+ report = (h_flag || H_flag) ? stderr : stdout;
+ }else{
+ report = 0;
+ }
+ if( nErr>0 ){
+ return nErr;
+ }
+ for(pFile=pFileList; pFile; pFile=pFile->pNext){
+ char *zFile;
+
+ zFilename = pFile->zSrc;
+ if( zFilename==0 ) continue;
+ zFile = ReadFile(zFilename);
+ if( zFile==0 ){
+ fprintf(stderr,"Can't read input file \"%s\"\n",zFilename);
+ nErr++;
+ continue;
+ }
+ if( strncmp(zFile,zTopLine,nTopLine)==0 ){
+ pFile->zSrc = 0;
+ }else{
+ if( report ) fprintf(report,"Reading %s...\n",zFilename);
+ pList = TokenizeFile(zFile,&pFile->idTable);
+ if( pList ){
+ nErr += ParseFile(pList,pFile->flags);
+ FreeTokenList(pList);
+ }else if( zFile[0]==0 ){
+ fprintf(stderr,"Input file \"%s\" is empty.\n", zFilename);
+ nErr++;
+ }else{
+ fprintf(stderr,"Errors while processing \"%s\"\n", zFilename);
+ nErr++;
+ }
+ }
+ if( !doc_flag ) SafeFree(zFile);
+ if( doc_flag ) PrintModuleRecord(zFile,zFilename);
+ }
+ if( nErr>0 ){
+ return nErr;
+ }
+#ifdef DEBUG
+ if( debugMask & DECL_DUMP ){
+ DumpDeclList();
+ return nErr;
+ }
+#endif
+ if( doc_flag ){
+ DocumentationDump();
+ return nErr;
+ }
+ zFilename = "--internal--";
+ pList = TokenizeFile(zInit,0);
+ if( pList==0 ){
+ return nErr+1;
+ }
+ ParseFile(pList,PS_Interface);
+ FreeTokenList(pList);
+ if( h_flag || H_flag ){
+ nErr += MakeGlobalHeader(H_flag);
+ }else{
+ for(pFile=pFileList; pFile; pFile=pFile->pNext){
+ if( pFile->zSrc==0 ) continue;
+ nErr += MakeHeader(pFile,report,0);
+ }
+ }
+ return nErr;
+}
+#endif
+
+
+
+
+
+
+
+
+
+
diff --git a/try/mh_main_prob/command1.c b/try/mh_main_prob/command1.c
new file mode 100644
index 0000000..637df55
--- /dev/null
+++ b/try/mh_main_prob/command1.c
@@ -0,0 +1,6 @@
+#include "command1.h"
+#include
+int main(int argc, char **argv){
+ printf("command1 %d\n", f());
+ return 0;
+}
diff --git a/try/mh_main_prob/command2.c b/try/mh_main_prob/command2.c
new file mode 100644
index 0000000..5d8c612
--- /dev/null
+++ b/try/mh_main_prob/command2.c
@@ -0,0 +1,6 @@
+#include "command2.h"
+#include
+int main(int argc, char **argv){
+ printf("command2 %d\n", f() + argc);
+ return 0;
+}
diff --git a/try/mh_main_prob/just_fun.c b/try/mh_main_prob/just_fun.c
new file mode 100644
index 0000000..67625f4
--- /dev/null
+++ b/try/mh_main_prob/just_fun.c
@@ -0,0 +1,4 @@
+#include "just_fun.h"
+int f(){
+ return 5;
+}
diff --git a/try/mh_main_prob/transcript1.txt b/try/mh_main_prob/transcript1.txt
new file mode 100644
index 0000000..c5511fe
--- /dev/null
+++ b/try/mh_main_prob/transcript1.txt
@@ -0,0 +1,36 @@
+Various commmand files each with its own main for testing a library. makeheaders gets confused and puts all the
+declarations in the headers, leading to a failure.
+
+
+> ls
+command1.c command2.c just_fun.c
+> cat just_fun.c
+#include "just_fun.h"
+int f(){
+ return 5;
+}
+> cat command1.c
+#include "command1.h"
+#include
+int main(){
+ printf("command1 %d\n", f());
+ return 0;
+}
+> cat command2.c
+#include "command2.h"
+#include
+int main(int argc, char **argv){
+ printf("command2 %d\n", f() + argc);
+ return 0;
+}
+> makeheaders *.c
+> gcc -o command1 command1.c
+command1.c: In function âmainâ:
+command1.c:3:1: error: number of arguments doesnât match prototype
+ int main(){
+ ^~~
+In file included from command1.c:1:
+command1.h:5:5: error: prototype declaration
+ int main(int argc,char **argv);
+ ^~~~
+>
diff --git a/try/mh_main_prob/transcript2.txt b/try/mh_main_prob/transcript2.txt
new file mode 100644
index 0000000..77ec819
--- /dev/null
+++ b/try/mh_main_prob/transcript2.txt
@@ -0,0 +1,20 @@
+Making each main call static so it won't be in the header. gcc can't find main.
+
+> cat command1.c
+#include "command1.h"
+#include
+static int main(){
+ printf("command1 %d\n", f());
+ return 0;
+}
+> cat command2.c
+#include "command2.h"
+#include
+static int main(int argc, char **argv){
+ printf("command2 %d\n", f() + argc);
+ return 0;
+}
+> gcc -o command1 command1.c just_fun.c
+/usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/8/../../../../lib64/crt1.o: in function `_start':
+(.text+0x24): undefined reference to `main'
+collect2: error: ld returned 1 exit status
diff --git a/try/mh_main_prob/transcript3.txt b/try/mh_main_prob/transcript3.txt
new file mode 100644
index 0000000..b3a00b7
--- /dev/null
+++ b/try/mh_main_prob/transcript3.txt
@@ -0,0 +1,27 @@
+This time making each main definition have the same prototype. Still end up with multiple main declarations,
+it is just that they agree.
+
+> rm *.h
+> makeheaders *.c
+> cat command1.c
+#include "command1.h"
+#include
+int main(int argc, char **argv){
+ printf("command1 %d\n", f());
+ return 0;
+}
+> cat command1.h
+/* This file was automatically generated. Do not edit! */
+#undef INTERFACE
+int f();
+int main(int argc,char **argv);
+int main(int argc,char **argv);
+> cat command2.c
+#include "command2.h"
+#include
+int main(int argc, char **argv){
+ printf("command2 %d\n", f() + argc);
+ return 0;
+}
+> gcc -o command1 command1.c just_fun.c
+> .. worked