checkpoint
authorThomas Walker Lynch <thomas.lynch@reasoningtechnology.com>
Tue, 12 Mar 2019 17:47:50 +0000 (18:47 +0100)
committerThomas Walker Lynch <thomas.lynch@reasoningtechnology.com>
Tue, 12 Mar 2019 17:47:50 +0000 (18:47 +0100)
69 files changed:
.gitignore [new file with mode: 0644]
7_makefile [new file with mode: 0755]
bin/gitadd [deleted file]
doc/dir-structure.txt [new file with mode: 0644]
makefile [deleted file]
src/1_try/mh_main_prob/command1.c [deleted file]
src/1_try/mh_main_prob/command2.c [deleted file]
src/1_try/mh_main_prob/just_fun.c [deleted file]
src/1_try/mh_main_prob/transcript1.txt [deleted file]
src/1_try/mh_main_prob/transcript2.txt [deleted file]
src/1_try/mh_main_prob/transcript3.txt [deleted file]
src/2_bin/setuid_root.sh [deleted file]
src/2_doc/README.txt [new file with mode: 0644]
src/2_doc/makefile.txt [new file with mode: 0644]
src/2_doc/makeheaders.txt [new file with mode: 0644]
src/2_doc/sqlite.txt [new file with mode: 0644]
src/2_doc/to_do.txt [new file with mode: 0644]
src/3_documents/README.txt [deleted file]
src/3_documents/makefile.txt [deleted file]
src/3_documents/makeheaders.txt [deleted file]
src/3_documents/sqlite.txt [deleted file]
src/3_to_do.txt [deleted file]
src/5_deprecated/subudb-number-next.cli.c [new file with mode: 0644]
src/5_scratch/common.lib.h
src/5_scratch/subu-common.lib.h [new file with mode: 0644]
src/5_scratch/subu-config.lib.c.~ceea6e7d697546c47f7736b72e7fb60b15c104de~ [new file with mode: 0644]
src/5_scratch/subu-mk-0.cli.h
src/5_scratch/subu-rm-0.cli.h
src/5_scratch/subu.lib.h
src/5_scratch/subudb-init.cli.h [new file with mode: 0644]
src/5_scratch/subudb-number-next.cli.h [new file with mode: 0644]
src/5_scratch/subudb-number.cli.h [new file with mode: 0644]
src/5_scratch/subudb-rel-get.cli.h [new file with mode: 0644]
src/5_scratch/subudb-rel-put.cli.h [new file with mode: 0644]
src/5_scratch/subudb-rel-rm.cli.h [new file with mode: 0644]
src/5_scratch/subudb.lib.h [new file with mode: 0644]
src/7_makefile [new file with mode: 0755]
src/common.lib.c [deleted file]
src/makefile [deleted file]
src/subu-common.lib.c [new file with mode: 0644]
src/subu-config.lib.c [deleted file]
src/subu-init.cli.c [deleted file]
src/subu-mk-0.cli.c
src/subu-number.cli.c [deleted file]
src/subu-rel-get.cli.c [deleted file]
src/subu-rel-put.cli.c [deleted file]
src/subu-rel-rm.cli.c [deleted file]
src/subu-rm-0.cli.c
src/subu.lib.c
src/subudb-init.cli.c [new file with mode: 0644]
src/subudb-number.cli.c [new file with mode: 0644]
src/subudb-rel-get.cli.c [new file with mode: 0644]
src/subudb-rel-put.cli.c [new file with mode: 0644]
src/subudb-rel-rm.cli.c [new file with mode: 0644]
src/subudb.lib.c [new file with mode: 0644]
tools/bin/gitadd [new file with mode: 0755]
tools/bin/make [new file with mode: 0755]
tools/bin/makeheaders [new file with mode: 0755]
tools/bin/setuid_root.sh [new file with mode: 0755]
tools/doc/makeheaders.html [new file with mode: 0644]
tools/lib/7_makefile [new file with mode: 0755]
tools/lib/bashrc [new file with mode: 0755]
tools/src/makeheaders.c [new file with mode: 0644]
try/mh_main_prob/command1.c [new file with mode: 0644]
try/mh_main_prob/command2.c [new file with mode: 0644]
try/mh_main_prob/just_fun.c [new file with mode: 0644]
try/mh_main_prob/transcript1.txt [new file with mode: 0644]
try/mh_main_prob/transcript2.txt [new file with mode: 0644]
try/mh_main_prob/transcript3.txt [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..de56908
--- /dev/null
@@ -0,0 +1,7 @@
+
+__pycache__/
+tmp/
+.*
+!.gitignore
+
+
diff --git a/7_makefile b/7_makefile
new file mode 100755 (executable)
index 0000000..24dfafb
--- /dev/null
@@ -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 (executable)
index 7097d77..0000000
+++ /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 (file)
index 0000000..668b909
--- /dev/null
@@ -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 (executable)
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 (file)
index 637df55..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "command1.h"
-#include <stdio.h>
-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 (file)
index 5d8c612..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "command2.h"
-#include <stdio.h>
-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 (file)
index 67625f4..0000000
+++ /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 (file)
index c5511fe..0000000
+++ /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 <stdio.h>
-int main(){
-  printf("command1 %d\n", f());
-  return 0;
-}
-> cat command2.c
-#include "command2.h"
-#include <stdio.h>
-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 (file)
index 77ec819..0000000
+++ /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 <stdio.h>
-static int main(){
-  printf("command1 %d\n", f());
-  return 0;
-}
-> cat command2.c
-#include "command2.h"
-#include <stdio.h>
-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 (file)
index b3a00b7..0000000
+++ /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 <stdio.h>
-int main(int argc, char **argv){
-  printf("command1 %d\n", f());
-  return 0;
-}
-> cat command1.h
-/* \aThis 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 <stdio.h>
-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 (executable)
index bea2d00..0000000
+++ /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 (file)
index 0000000..a9a2b21
--- /dev/null
@@ -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 (file)
index 0000000..722de84
--- /dev/null
@@ -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 (file)
index 0000000..5aebda9
--- /dev/null
@@ -0,0 +1,16 @@
+
+This worked to force the include to be part of the interface:
+
+#if INTERFACE
+#include <sqlite3.h>
+#endif
+
+But this did not:
+
+#if INTERFACE
+  #include <sqlite3.h>
+#endif
+
+makeheaders looks to be sensitive to indentation
+
+
diff --git a/src/2_doc/sqlite.txt b/src/2_doc/sqlite.txt
new file mode 100644 (file)
index 0000000..b7f8cbb
--- /dev/null
@@ -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 (file)
index 0000000..eebf52a
--- /dev/null
@@ -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 (file)
index a9a2b21..0000000
+++ /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 (file)
index 722de84..0000000
+++ /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 (file)
index 5aebda9..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-
-This worked to force the include to be part of the interface:
-
-#if INTERFACE
-#include <sqlite3.h>
-#endif
-
-But this did not:
-
-#if INTERFACE
-  #include <sqlite3.h>
-#endif
-
-makeheaders looks to be sensitive to indentation
-
-
diff --git a/src/3_documents/sqlite.txt b/src/3_documents/sqlite.txt
deleted file mode 100644 (file)
index b7f8cbb..0000000
+++ /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 (file)
index eebf52a..0000000
+++ /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 (file)
index 0000000..3373173
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+int main(int argc, char **argv){
+
+  if( argc != 2 ){
+    fprintf(stderr, "usage: %s masteru_name \n",argv[0]);
+    return SUBU_ERR_ARG_CNT;
+  }
+  char *masteru_name = argv[1];
+  
+  int rc;
+  sqlite3 *db;
+  rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+  if( rc != SQLITE_OK ){
+    sqlite3_close(db);
+    fprintf(stderr, "error exit, could not open db file\n");
+    return SUBU_ERR_DB_FILE;
+  }
+
+  // read and print the current max
+  char *mess;
+  int n;
+  rc = subudb_number_next(db, masteru_name, &n, &mess);
+  if( rc == SQLITE_DONE ){
+    printf("%d\n", n);
+  }else{
+    fprintf(stderr, "subudb_number_next indicates failure by returning %d\n",rc);
+    fprintf(stderr, "and issues message, %s\n", sqlite3_errmsg(db));
+    sqlite3_close(db);
+    return SUBU_ERR_DB_FILE;
+  }
+  rc = sqlite3_close(db);
+  if( rc != SQLITE_OK ){
+    fprintf(stderr, "when closing db, %s\n", sqlite3_errmsg(db));
+    return SUBU_ERR_DB_FILE;
+  }    
+  return 0;
+}
index 2454943..cfdc520 100644 (file)
@@ -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 (file)
index 0000000..cfdc520
--- /dev/null
@@ -0,0 +1,9 @@
+/* \aThis file was automatically generated.  Do not edit! */
+#undef INTERFACE
+extern char Subuland_Extension[];
+typedef unsigned int uint;
+extern uint First_Max_Subunumber;
+extern uint Subuhome_Perms;
+extern char DB_File[];
+#define BUG_SSS_CACHE_RUID 1
+#define INTERFACE 0
diff --git a/src/5_scratch/subu-config.lib.c.~ceea6e7d697546c47f7736b72e7fb60b15c104de~ b/src/5_scratch/subu-config.lib.c.~ceea6e7d697546c47f7736b72e7fb60b15c104de~
new file mode 100644 (file)
index 0000000..de7bcbb
--- /dev/null
@@ -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<number>.
+A separate table translates the numbers into the subu names.
+
+The first argument is the biggest subu number in the system, or one minus an 
+starting point for subu numbering.
+
+currently a unit converted to base 10 will always fit in a 21 bit buffer.
+
+*/
+#include "subu-config.lib.h"
+
+#if INTERFACE
+#include <sqlite3.h>
+#define ERR_CONFIG_FILE 1
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+//--------------------------------------------------------------------------------
+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;
+}
index 56f6cf6..487b509 100644 (file)
@@ -5,6 +5,6 @@
 #include <sqlite3.h>
 void subu_err(char *fname,int err,char *mess);
 int subu_mk_0(char **mess,sqlite3 *db,char *subuname);
-#define SUBU_ERR_CONFIG_FILE 8
-extern char Config_File[];
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
 #define SUBU_ERR_ARG_CNT 1
index 56f6cf6..070bfe8 100644 (file)
@@ -4,7 +4,7 @@
 #include <errno.h>
 #include <sqlite3.h>
 void subu_err(char *fname,int err,char *mess);
-int subu_mk_0(char **mess,sqlite3 *db,char *subuname);
-#define SUBU_ERR_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
index 199daab..45c0b81 100644 (file)
@@ -1,12 +1,12 @@
 /* \aThis file was automatically generated.  Do not edit! */
 #undef INTERFACE
 #include <sqlite3.h>
-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 <stdbool.h>
 #include <errno.h>
 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 <sys/types.h>
 #include <unistd.h>
 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 (file)
index 0000000..36fa2c9
--- /dev/null
@@ -0,0 +1,8 @@
+/* \aThis file was automatically generated.  Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_schema(sqlite3 *db);
+#include <stdbool.h>
+#include <errno.h>
+#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 (file)
index 0000000..2c295dc
--- /dev/null
@@ -0,0 +1,9 @@
+/* \aThis file was automatically generated.  Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_number_next(sqlite3 *db,char *masteru_name,int *n,char **mess);
+#include <stdbool.h>
+#include <errno.h>
+#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 (file)
index 0000000..3e4e351
--- /dev/null
@@ -0,0 +1,11 @@
+/* \aThis file was automatically generated.  Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_number_get(sqlite3 *db,char *masteru_name,int *n);
+int subudb_number_set(sqlite3 *db,char *masteru_name,int n);
+#include <stdbool.h>
+#include <errno.h>
+#define SUBU_ERR_N 14
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
+#define SUBU_ERR_ARG_CNT 1
diff --git a/src/5_scratch/subudb-rel-get.cli.h b/src/5_scratch/subudb-rel-get.cli.h
new file mode 100644 (file)
index 0000000..23c41b1
--- /dev/null
@@ -0,0 +1,9 @@
+/* \aThis file was automatically generated.  Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_Masteru_Subu_get(sqlite3 *db,char *masteru_name,char *subuname,char **subu_username);
+#include <stdbool.h>
+#include <errno.h>
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
+#define SUBU_ERR_ARG_CNT 1
diff --git a/src/5_scratch/subudb-rel-put.cli.h b/src/5_scratch/subudb-rel-put.cli.h
new file mode 100644 (file)
index 0000000..243b3a9
--- /dev/null
@@ -0,0 +1,8 @@
+/* \aThis file was automatically generated.  Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_Masteru_Subu_put(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
+#include <stdbool.h>
+#include <errno.h>
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
diff --git a/src/5_scratch/subudb-rel-rm.cli.h b/src/5_scratch/subudb-rel-rm.cli.h
new file mode 100644 (file)
index 0000000..595427f
--- /dev/null
@@ -0,0 +1,8 @@
+/* \aThis file was automatically generated.  Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
+#include <stdbool.h>
+#include <errno.h>
+#define SUBU_ERR_DB_FILE 8
+extern char DB_File[];
diff --git a/src/5_scratch/subudb.lib.h b/src/5_scratch/subudb.lib.h
new file mode 100644 (file)
index 0000000..1c854b3
--- /dev/null
@@ -0,0 +1,15 @@
+/* \aThis file was automatically generated.  Do not edit! */
+#undef INTERFACE
+#include <sqlite3.h>
+int subudb_Masteru_Subu_rm(sqlite3 *db,char *masteru_name,char *subuname,char *subu_username);
+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 (executable)
index 0000000..c16155b
--- /dev/null
@@ -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 (file)
index b949af4..0000000
+++ /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 (executable)
index 0fbf5a1..0000000
+++ /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 (file)
index 0000000..9ce5a27
--- /dev/null
@@ -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 (file)
index de7bcbb..0000000
+++ /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<number>.
-A separate table translates the numbers into the subu names.
-
-The first argument is the biggest subu number in the system, or one minus an 
-starting point for subu numbering.
-
-currently a unit converted to base 10 will always fit in a 21 bit buffer.
-
-*/
-#include "subu-config.lib.h"
-
-#if INTERFACE
-#include <sqlite3.h>
-#define ERR_CONFIG_FILE 1
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-//--------------------------------------------------------------------------------
-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 (file)
index 1dcd7b1..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-This command initializes the configuration file.
-
-*/
-#include "subu-init.cli.h"
-#include <stdio.h>
-
-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;
-}
index 93c79d3..d9f9c74 100644 (file)
@@ -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 (file)
index c4690e4..0000000
+++ /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 <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-
-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 (file)
index 737080c..0000000
+++ /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 <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char **argv){
-
-  if(argc != 3){
-    fprintf(stderr, "usage: %s masteru_name subuname\n", argv[0]);
-    return SUBU_ERR_ARG_CNT;
-  }
-
-  int rc;
-  sqlite3 *db;
-  rc = sqlite3_open_v2(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 (file)
index c930465..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-puts a relation in the masteru/subu table
-
-*/
-#include "subu-rel-put.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char **argv){
-
-  if(argc != 4){
-    fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
-    return 1;
-  }
-  char *masteru_name = argv[1];
-  char *subuname = argv[2];
-  char *subu_username = argv[3];
-
-  sqlite3 *db;
-  {
-    int ret = sqlite3_open_v2(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 (file)
index 06eb4e5..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-puts a relation in the masteru/subu table
-
-*/
-#include "subu-rel-rm.cli.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char **argv){
-
-  if(argc != 4){
-    fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
-    return 1;
-  }
-  char *masteru_name = argv[1];
-  char *subuname = argv[2];
-  char *subu_username = argv[3];
-
-  sqlite3 *db;
-  {
-    int ret = sqlite3_open_v2(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;
-}
index 93c79d3..a7e5926 100644 (file)
@@ -2,7 +2,7 @@
   subu-mk-0 command
 
 */
-#include "subu-mk-0.cli.h"
+#include "subu-rm-0.cli.h"
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -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;
   }
index e20fac3..d456c53 100644 (file)
@@ -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<number>, calls useradd to creat
   the new uswer account, and enters the relationship between masteru, subu, and
-  s<number> in the config file.  It is this relation in the config file that
+  s<number> in the db file.  It is this relation in the db file that
   associates the subuname with the s<number> user.
 
   subu-rm-0 uses userdel to delete the related s<number> user account.  It
-  then removes the relaton from the 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 (file)
index 0000000..429c98e
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+This command initializes the db file.
+
+*/
+#include "subudb-init.cli.h"
+#include <stdio.h>
+
+int main(){
+  sqlite3 *db;
+  if( sqlite3_open(DB_File, &db) != SQLITE_OK ){
+    fprintf(stderr, "error exit, could not open db file \"%s\"\n", DB_File);
+    return SUBU_ERR_DB_FILE;
+  }
+  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 (file)
index 0000000..265e7e9
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+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 (file)
index 0000000..442d61e
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv){
+
+  if(argc != 3){
+    fprintf(stderr, "usage: %s masteru_name subuname\n", argv[0]);
+    return SUBU_ERR_ARG_CNT;
+  }
+
+  int rc;
+  sqlite3 *db;
+  rc = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+  if( rc != SQLITE_OK ){
+    fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
+    return SUBU_ERR_DB_FILE;
+  }
+
+  char *masteru_name = argv[1];
+  char *subuname = argv[2];
+  char *subu_username;
+
+  int ret = subudb_Masteru_Subu_get(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 (file)
index 0000000..f679d24
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+puts a relation in the masteru/subu table
+
+*/
+#include "subudb-rel-put.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv){
+
+  if(argc != 4){
+    fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
+    return 1;
+  }
+  char *masteru_name = argv[1];
+  char *subuname = argv[2];
+  char *subu_username = argv[3];
+
+  sqlite3 *db;
+  {
+    int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+    if( ret != SQLITE_OK ){
+      fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
+      return SUBU_ERR_DB_FILE;
+    }}
+
+  int ret = subudb_Masteru_Subu_put(db, masteru_name, subuname, subu_username);
+  if( ret != SQLITE_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 (file)
index 0000000..3d15ca9
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+puts a relation in the masteru/subu table
+
+*/
+#include "subudb-rel-rm.cli.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv){
+
+  if(argc != 4){
+    fprintf(stderr, "expected: %s masteru_name subuname subu_username\n", argv[0]);
+    return 1;
+  }
+  char *masteru_name = argv[1];
+  char *subuname = argv[2];
+  char *subu_username = argv[3];
+
+  sqlite3 *db;
+  {
+    int ret = sqlite3_open_v2(DB_File, &db, SQLITE_OPEN_READWRITE, NULL);
+    if( ret != SQLITE_OK ){
+      fprintf(stderr, "could not open db file \"%s\"\n", DB_File);
+      return SUBU_ERR_DB_FILE;
+    }}
+
+  int ret = subudb_Masteru_Subu_rm(db, masteru_name, subuname, subu_username);
+  if( ret != SQLITE_DONE ){
+    fprintf(stderr, "subudb_Masteru_Subu_rm indicates failure by returning %d\n",ret);
+    fprintf(stderr, "sqlite3 issues message, %s\n", sqlite3_errmsg(db));
+    printf("put failed\n");
+    return 2;
+  }
+  ret = sqlite3_close(db);
+  if( ret != SQLITE_OK ){
+    fprintf(stderr, "sqlite3_close(db) indicates failure by returning %d\n",ret);
+    fprintf(stderr, "sqlite3 issues message: %s\n", sqlite3_errmsg(db));
+    return SUBU_ERR_DB_FILE;
+  }    
+  return 0;
+}
diff --git a/src/subudb.lib.c b/src/subudb.lib.c
new file mode 100644 (file)
index 0000000..99ec7f0
--- /dev/null
@@ -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<number>.
+A separate table translates the numbers into the subu names.
+
+The first argument is the biggest subu number in the system, or one minus an 
+starting point for subu numbering.
+
+currently a unit converted to base 10 will always fit in a 21 bit buffer.
+
+Each of these returns SQLITE_OK upon success
+*/
+#include "subudb.lib.h"
+
+#if INTERFACE
+#include <sqlite3.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+//--------------------------------------------------------------------------------
+// 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 (executable)
index 0000000..c2f2e02
--- /dev/null
@@ -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 (executable)
index 0000000..3312575
--- /dev/null
@@ -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 (executable)
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 (executable)
index 0000000..aedd564
--- /dev/null
@@ -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 (file)
index 0000000..289da58
--- /dev/null
@@ -0,0 +1,1150 @@
+<html>
+<head><title>The Makeheaders Program</title></head>
+<body bgcolor=white>
+<h1 align=center>The Makeheaders Program</h1>
+
+
+<p>
+This document describes <em>makeheaders</em>,
+a tool that automatically generates &#8220;<code>.h</code>&#8221;
+files for a C or C++ programming project.
+</p>
+
+
+<h2>Table Of Contents</h2>
+
+<ul>
+<li><a href="#H0002">1,0 Background</a>
+<ul>
+<li><a href="#H0003">1.1 Problems With The Traditional Approach</a>
+
+<li><a href="#H0004">1.2 The Makeheaders Solution</a>
+</ul>
+<li><a href="#H0005">2.0 Running The Makeheaders Program</a>
+
+<li><a href="#H0006">3.0 Preparing Source Files For Use With Makeheaders</a>
+<ul>
+<li><a href="#H0007">3.1 The Basic Setup</a>
+
+<li><a href="#H0008">3.2 What Declarations Get Copied</a>
+
+<li><a href="#H0009">3.3 How To Avoid Having To Write Any Header Files</a>
+
+<li><a href="#H0010">3.4 Designating Declarations For Export</a>
+
+<li><a href="#H0011">3.5 Local declarations processed by makeheaders</a>
+
+<li><a href="#H0012">3.6 Using Makeheaders With C++ Code</a>
+
+<li><a href="#H0013">3.7 Conditional Compilation</a>
+
+<li><a href="#H0014">3.8 Caveats</a>
+</ul>
+<li><a href="#H0015">4.0 Using Makeheaders To Generate Documentation</a>
+
+<li><a href="#H0016">5.0 Compiling The Makeheaders Program</a>
+
+<li><a href="#H0017">6.0 History</a>
+
+<li><a href="#H0018">7.0 Summary And Conclusion</a>
+</ul><a name="H0002"></a>
+<h2>1.0 Background</h2>
+
+<p>
+A piece of C source code can be one of two things:
+a <em>declaration</em> or a <em>definition</em>.
+A declaration is source text that gives information to the
+compiler but doesn't directly result in any code being generated.
+A definition is source text that results in executable machine
+instructions or initialization data.
+(These two terms are sometimes used inconsistently by other authors.
+In particular, many people reverse the meanings of these words when
+discussing Pascal or Ada code.
+The meanings described here are the same as used in the ANSI-C
+standards document.)
+</p>
+
+<p>
+Declarations in C include things such as the following:
+<ul>
+<li> Typedefs.
+<li> Structure, union and enumeration declarations.
+<li> Function and procedure prototypes.
+<li> Preprocessor macros and #defines.
+<li> &#8220;<code>extern</code>&#8221; variable declarations.
+</ul>
+</p>
+
+<p>
+Definitions in C, on the other hand, include these kinds of things:
+<ul>
+<li> Variable definitions.
+<li> The bodies of functions and procedures.
+<li> Initialization data.
+</ul>
+</p>
+
+<p>
+The distinction between a declaration and a definition is common in
+modern software engineering.
+Another way of looking at the difference is that the declaration
+is the <em>interface</em> and the definition is the <em>implementation</em>.
+</p>
+
+<p>
+In C programs, it has always been the tradition that declarations are
+put in files with the &#8220;<code>.h</code>&#8221; suffix and definitions are
+placed in &#8220;<code>.c</code>&#8221; files.
+The .c files contain &#8220;<code>#include</code>&#8221; preprocessor statements
+that cause the contents of .h files to be included as part of the
+source code when the .c file is compiled.
+In this way, the .h files define the interface to a subsystem and
+the .c files define how the subsystem is implemented.
+</p>
+
+<a name="H0003"></a>
+<h3>1.1 Problems With The Traditional Approach</h3>
+
+<p>
+As the art of computer programming continues to advance, and the size
+and complexity of programs continues to swell, the traditional C
+approach of placing declarations and definitions in separate files begins
+to present the programmer with logistics and
+maintenance problems.
+To wit:
+</p>
+
+<p>
+<ol>
+<p><li>
+In large codes with many source files, it becomes difficult to determine
+which .h files should be included in which .c files.
+<p><li>
+It is typically the case that a .h file will be forced to include
+another .h files, which in turn might include other .h files,
+and so forth.
+The .c file must be recompiled when any of the .h files in this chain
+are altered, but it can be difficult to determine what .h files are found
+in the include chain.
+A frequent Makefile error is to omit some .h files from a dependency
+list even though those files are on the include file chain.
+<p><li>
+Some information is common to both the declaration and the definition of
+an object in C, and so must be repeated in both the .h and the .c files
+for that object.
+In a large project, it can become increasingly difficult to keep the two
+files in sync.
+<p><li>
+When a .c file includes a .h file and the .h files changes, the .c file
+must be recompiled, even if the part of the .h file that changed is not
+actually used by the .c file.
+In a large program, it is generally the case that almost every .c file ends up
+depending on one or two of the more important .h files, and so when those .h
+files change, the entire program must be recompiled.
+It also happens that those important .h files tend to be the ones that
+change most frequently.
+This means that the entire program must be recompiled frequently,
+leading to a lengthy modify-compile-test cycle and a corresponding
+decrease in programmer productivity.
+<p><li>
+The C programming language requires that declarations depending upon
+each other must occur in a particular order.
+In a program with complex, interwoven data structures, the correct
+declaration order can become very difficult to determine manually,
+especially when the declarations involved are spread out over several
+files.
+</ol>
+</p>
+
+<a name="H0004"></a>
+<h3>1.2 The Makeheaders Solution</h3>
+
+<p>
+The makeheaders program is designed to ameliorate the problems associated
+with the traditional C programming model by automatically generating
+the interface information in the .h files from
+interface information contained in other .h files and
+from implementation information in the .c files.
+When the makeheaders program is run, it scans the source
+files for a project,
+then generates a series of new .h files, one for each .c file.
+The generated .h files contain exactly those declarations required by the
+corresponding .c files, no more and no less.
+</p>
+
+<p>
+The makeheaders programming model overcomes all of the objections to the
+traditional C programming model.
+<ol>
+<p><li>
+Because all declarations needed by a .c file are contained in a
+single .h file, there is never any question about what .h files
+a .c will need to include.  If the .c file is named
+<code>alpha.c</code> then it must include only the single .h file
+named <code>alpha.h</code>.
+(The .c file might also use some include files from the standard
+library, such as <code>&lt;stdio.h&gt</code>, but that is another matter.)
+<p><li>
+The generated .h files do not include other .h files, and so there
+are no include chains to worry about.
+The file <code>alpha.c</code> depends on <code>alpha.h</code> and
+nothing more.
+<p><li>
+There is still duplication in the .h and the .c file, but because
+the duplicate information is automatically generated, it is no longer
+a problem.
+Simply rerun makeheaders to resynchronize everything.
+<p><li>
+The generated .h file contains the minimal set of declarations needed
+by the .c file.
+This means that when something changes, a minimal amount of recompilation
+is required to produce an updated executable.
+Experience has shown that this gives a dramatic improvement
+in programmer productivity by facilitating a rapid modify-compile-test
+cycle during development.
+<p><li>
+The makeheaders program automatically sorts declarations into the
+correct order, completely eliminating the wearisome and error-prone
+task of sorting declarations by hand.
+</ol>
+<p>
+
+<p>
+In addition, the makeheaders program is fast and unintrusive.
+It is a simple matter to incorporate makeheaders into a Makefile
+so that makeheaders will be run automatically whenever the project
+is rebuilt.
+And the burden of running makeheaders is light.
+It will easily process tens of thousands of lines of source
+code per second.
+</p>
+
+<a name="H0005"></a>
+<h2>2.0 Running The Makeheaders Program</h2>
+
+<p>
+The makeheaders program is very easy to run.
+If you have a collection of C source code and include files in the working
+directory, then you can run makeheaders to generate appropriate .h
+files using the following command:
+<pre>
+   makeheaders *.[ch]
+</pre>
+That's really all there is to it!
+This command will generate one .h file for every .c file.
+Any .h files that were generated by a prior run of makeheaders
+are ignored,
+but manually entered .h files
+that contain structure declarations and so forth will be scanned and
+the declarations will be copied into the generated .h files as
+appropriate.
+But if makeheaders sees that the .h file that it has generated is no
+different from the .h file it generated last time, it doesn't update
+the file.
+This prevents the corresponding .c files from having to
+be needlessly recompiled.
+</p>
+
+<p>
+There are several options to the makeheaders program that can
+be used to alter its behavior.
+The default behavior is to write a single .h file for each .c file and
+to give the .h file the same base name as the .c file.
+Instead of generating a whole mess of .h files, you can, if you choose,
+generate a single big .h file that contains all declarations needed
+by all the .c files.  Do this using the -h option to makeheaders.
+As follows:
+<pre>
+   makeheaders -h *.[ch] >common.h
+</pre>
+With the -h option, the .h file is not actually written to a disk file but
+instead appears on standard output, where you are free to redirect it
+into the file of your choice.
+</p>
+
+<p>
+A similar option is -H.  Like the lower-case -h option, big -H
+generates a single include file on standard output.  But unlike
+small -h, the big -H only emits prototypes and declarations that
+have been designated as &#8220;exportable&#8221;.
+The idea is that -H will generate an include file that defines
+the interface to a library.
+More will be said about this in section 3.4.
+</p>
+
+<p>
+Sometimes you want the base name of the .c file and the .h file to
+be different.
+For example, suppose you want the include file for <code>alpha.c</code>
+to be called <code>beta.h</code>.
+In this case, you would invoke makeheaders as follows:
+<pre>
+   makeheaders alpha.c:beta.h
+</pre>
+Any time a filename argument contains a colon, the name before the
+colon is taken to be the name of the .c file and the name after the
+colon is taken to be the name of the .h file.
+You can't use the shell's wildcard mechanism with this approach, but that
+normally isn't a problem in Makefiles, which is where this stuff
+comes in handy.
+</p>
+
+<p>
+If you want a particular file to be scanned by makeheaders but you
+don't want makeheaders to generate a header file for that file,
+then you can supply an empty header filename, like this:
+<pre>
+   makeheaders alpha.c beta.c gamma.c:
+</pre>
+In this example, makeheaders will scan the three files named
+&#8220;<code>alpha.c</code>&#8221;,
+&#8220;<code>beta.c</code>&#8221; and
+&#8220;<code>gamma.c</code>&#8221;
+but because of the colon on the end of third filename
+it will only generate headers for the first two files.
+Unfortunately,
+it is not possible to get makeheaders to process any file whose
+name contains a colon.
+</p>
+
+<p>
+In a large project, the length of the command line for makeheaders
+can become very long.
+If the operating system doesn't support long command lines
+(example: DOS and Win32) you may not be able to list all of the
+input files in the space available.
+In that case, you can use the &#8220;<code>-f</code>&#8221; 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 &#8220;<code>mkhdr.dat</code>&#8221;
+that contains text like this:
+<pre>
+  src/alpha.c:hdr/alpha.h
+  src/beta.c:hdr/beta.h
+  src/gamma.c:hdr/gamma.h
+  ...
+</pre>
+Then invoke makeheaders as follows:
+<pre>
+  makeheaders -f mkhdr.dat
+</pre>
+</p>
+
+<p>
+The &#8220;<code>-local</code>&#8221; option causes makeheaders to
+generate of prototypes for &#8220;<code>static</code>&#8221; functions and
+procedures.
+Such prototypes are normally omitted.
+</p>
+
+<p>
+Finally, makeheaders also includes a &#8220;<code>-doc</code>&#8221; option.
+This command line option prevents makeheaders from generating any
+headers at all.
+Instead, makeheaders will write to standard output
+information about every definition and declaration that it encounters
+in its scan of source files.
+The information output includes the type of the definition or
+declaration and any comment that preceeds the definition or
+declaration.
+The output is in a format that can be easily parsed, and is
+intended to be read by another program that will generate
+documentation about the program.
+We'll talk more about this feature later.
+</p>
+
+<p>
+If you forget what command line options are available, or forget
+their exact name, you can invoke makeheaders using an unknown
+command line option (like &#8220;<code>--help</code>&#8221; or
+&#8220;<code>-?</code>&#8221;)
+and it will print a summary of the available options on standard
+error.
+If you need to process a file whose name begins with
+&#8220;<code>-</code>&#8221;,
+you can prepend a &#8220;<code>./</code>&#8221; to its name in order to get it
+accepted by the command line parser.
+Or, you can insert the special option &#8220;<code>--</code>&#8221; on the
+command line to cause all subsequent command line arguments to be treated as
+filenames even if their names begin with &#8220;<code>-</code>&#8221;.
+</p>
+
+<a name="H0006"></a>
+<h2>3.0 Preparing Source Files For Use With Makeheaders</h2>
+
+<p>
+Very little has to be done to prepare source files for use with
+makeheaders since makeheaders will read and understand ordinary
+C code.
+But it is important that you structure your files in a way that
+makes sense in the makeheaders context.
+This section will describe several typical uses of makeheaders.
+</p>
+
+<a name="H0007"></a>
+<h3>3.1 The Basic Setup</h3>
+
+<p>
+The simplest way to use makeheaders is to put all definitions in
+one or more .c files and all structure and type declarations in
+separate .h files.
+The only restriction is that you should take care to chose basenames
+for your .h files that are different from the basenames for your
+.c files.
+Recall that if your .c file is named (for example)
+&#8220;<code>alpha.c</code>&#8221;
+makeheaders will attempt to generate a corresponding header file
+named &#8220;<code>alpha.h</code>&#8221;.
+For that reason, you don't want to use that name for
+any of the .h files you write since that will prevent makeheaders
+from generating the .h file automatically.
+</p>
+
+<p>
+The structure of a .c file intented for use with makeheaders is very
+simple.
+All you have to do is add a single &#8220;<code>#include</code>&#8221; to the
+top of the file that sources the header file that makeheaders will generate.
+Hence, the beginning of a source file named &#8220;<code>alpha.c</code>&#8221;
+might look something like this:
+</p>
+
+<pre>
+   /*
+    * Introductory comment...
+    */
+   #include "alpha.h"
+
+   /* The rest of your code... */
+</pre>
+
+<p>
+Your manually generated header files require no special attention at all.
+Code them as you normally would.
+However, makeheaders will work better if you omit the
+&#8220;<code>#if</code>&#8221; 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 &#8220;<code>beta.h</code>&#8221;,
+many people will habitually write the following:
+
+<pre>
+   #ifndef BETA_H
+   #define BETA_H
+
+   /* declarations for beta.h go here */
+
+   #endif
+</pre>
+
+You can forego this cleverness with makeheaders.
+Remember that the header files you write will never really be
+included by any C code.
+Instead, makeheaders will scan your header files to extract only
+those declarations that are needed by individual .c files and then
+copy those declarations to the .h files corresponding to the .c files.
+Hence, the &#8220;<code>#if</code>&#8221; wrapper serves no useful purpose.
+But it does make makeheaders work harder, forcing it to put
+the statements
+
+<pre>
+   #if !defined(BETA_H)
+   #endif
+</pre>
+
+around every declaration that it copies out of your header file.
+No ill effect should come of this, but neither is there any benefit.
+</p>
+
+<p>
+Having prepared your .c and .h files as described above, you can
+cause makeheaders to generate its .h files using the following simple
+command:
+
+<pre>
+   makeheaders *.[ch]
+</pre>
+
+The makeheaders program will scan all of the .c files and all of the
+manually written .h files and then automatically generate .h files
+corresponding to all .c files.
+</p>
+
+<p>
+Note that
+the wildcard expression used in the above example,
+&#8220;<code>*.[ch]</code>&#8221;,
+will expand to include all .h files in the current directory, both
+those entered manually be the programmer and others generated automatically
+by a prior run of makeheaders.
+But that is not a problem.
+The makeheaders program will recognize and ignore any files it
+has previously generated that show up on its input list.
+</p>
+
+<a name="H0008"></a>
+<h3>3.2 What Declarations Get Copied</h3>
+
+<p>
+The following list details all of the code constructs that makeheaders
+will extract and place in
+the automatically generated .h files:
+</p>
+
+<ul>
+<p><li>
+When a function is defined in any .c file, a prototype of that function
+is placed in the generated .h file of every .c file that
+calls the function.</p>
+
+<P>If the &#8220;<code>static</code>&#8221; keyword of C appears at the
+beginning of the function definition, the prototype is suppressed.
+If you use the &#8220;<code>LOCAL</code>&#8221; keyword where you would normally
+say &#8220;<code>static</code>&#8221;, then a prototype is generated, but it
+will only appear in the single header file that corresponds to the
+source file containing the function.  For example, if the file
+<code>alpha.c</code> contains the following:
+<pre>
+  LOCAL int testFunc(void){
+    return 0;
+  }
+</pre>
+Then the header file <code>alpha.h</code> will contain
+<pre>
+  #define LOCAL static
+  LOCAL int testFunc(void);
+</pre>
+However, no other generated header files will contain a prototype for
+<code>testFunc()</code> since the function has only file scope.</p>
+
+<p>When the &#8220;<code>LOCAL</code>&#8221; keyword is used, makeheaders will
+also generate a #define for LOCAL, like this:
+<pre>
+   #define LOCAL static
+</pre>
+so that the C compiler will know what it means.</p>
+
+<p>If you invoke makeheaders with a &#8220;<code>-local</code>&#8221;
+command-line option, then it treats the &#8220;<code>static</code>&#8221;
+keyword like &#8220;<code>LOCAL</code>&#8221; and generates prototypes in the
+header file that corresponds to the source file containing the function
+definition.</p>
+
+<p><li>
+When a global variable is defined in a .c file, an
+&#8220;<code>extern</code>&#8221;
+declaration of that variable is placed in the header of every
+.c file that uses the variable.
+</p>
+
+<p><li>
+When a structure, union or enumeration declaration or a
+function prototype or a C++ class declaration appears in a
+manually produced .h file, that declaration is copied into the
+automatically generated
+.h files of all .c files that use the structure, union, enumeration,
+function or class.
+But declarations that appear in a
+.c file are considered private to that .c file and are not copied into
+any automatically generated files.
+</p>
+
+<p><li>
+All #defines and typedefs that appear in manually produced .h files
+are copied into automatically generated .h files as needed.
+Similar constructs that appear in .c files are considered private to
+those files and are not copied.
+</p>
+
+<p><li>
+When a structure, union or enumeration declaration appears in a .h
+file, makeheaders will automatically
+generate a typedef that allows the declaration to be referenced without
+the &#8220;<code>struct</code>&#8221;, &#8220;<code>union</code>&#8221; or
+&#8220;<code>enum</code>&#8221; qualifier.
+In other words, if makeheaders sees the code:
+<pre>
+  struct Examp { /* ... */ };
+</pre>
+it will automatically generate a corresponding typedef like this:
+<pre>
+  typedef struct Examp Examp;
+</pre>
+</p>
+
+<p><li>
+Makeheaders generates an error message if it encounters a function or
+variable definition within a .h file.
+The .h files are suppose to contain only interface, not implementation.
+C compilers will not enforce this convention, but makeheaders does.
+</ul>
+
+<p>
+As a final note, we observe that automatically generated declarations
+are ordered as required by the ANSI-C programming language.
+If the declaration of some structure &#8220;<code>X</code>&#8221; requires a
+prior declaration of another structure &#8220;<code>Y</code>&#8221;, then Y will
+appear first in the generated headers.
+</p>
+
+<a name="H0009"></a>
+<h3>3.3 How To Avoid Having To Write Any Header Files</h3>
+
+<p>
+In my experience, large projects work better if all of the manually
+written code is placed in .c files and all .h files are generated
+automatically.
+This is slightly different for the traditional C method of placing
+the interface in .h files and the implementation in .c files, but
+it is a refreshing change that brings a noticable improvement to the
+coding experience.
+Others, I believe, share this view since I've
+noticed recent languages (ex: java, tcl, perl, awk) tend to
+support the one-file approach to coding as the only option.
+</p>
+
+<p>
+The makeheaders program supports putting both
+interface and implementation into the same source file.
+But you do have to tell makeheaders which part of the source file is the
+interface and which part is the implementation.
+Makeheaders has to know this in order to be able to figure out whether or
+not structures declarations, typedefs, #defines and so forth should
+be copied into the generated headers of other source files.
+</p>
+
+<p>
+You can instruct makeheaders to treat any part of a .c file as if
+it were a .h file by enclosing that part of the .c file within:
+<pre>
+   #if INTERFACE
+   #endif
+</pre>
+Thus any structure definitions that appear after the
+&#8220;<code>#if INTERFACE</code>&#8221; but before the corresponding
+&#8220;<code>#endif</code>&#8221; are eligable to be copied into the
+automatically generated
+.h files of other .c files.
+</p>
+
+<p>
+If you use the &#8220;<code>#if INTERFACE</code>&#8221; mechanism in a .c file,
+then the generated header for that .c file will contain a line
+like this:
+<pre>
+   #define INTERFACE 0
+</pre>
+In other words, the C compiler will never see any of the text that
+defines the interface.
+But makeheaders will copy all necessary definitions and declarations
+into the .h file it generates, so .c files will compile as if the
+declarations were really there.
+This approach has the advantage that you don't have to worry with
+putting the declarations in the correct ANSI-C order -- makeheaders
+will do that for you automatically.
+</p>
+
+<p>
+Note that you don't have to use this approach exclusively.
+You can put some declarations in .h files and others within the
+&#8220;<code>#if INTERFACE</code>&#8221; 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
+&#8220;<code>#if INTERFACE</code>&#8221; regions as desired.
+</p>
+
+<a name="H0010"></a>
+<h3>3.4 Designating Declarations For Export</h3>
+
+<p>
+In a large project, one will often construct a hierarchy of
+interfaces.
+For example, you may have a group of 20 or so files that form
+a library used in several other parts of the system.
+Each file in this library will present two interfaces.
+One interface will be the routines and data structures it is
+willing to share with other files in the same library, and the
+second interface is those routines and data structures it wishes
+to make available to other subsystems.
+(The second interface is normally a subset of the first.)
+Ordinary C does not provide support for a tiered interface
+like this, but makeheaders does.
+</p>
+
+<p>
+Using makeheaders, it is possible to designate routines and data
+structures as being for &#8220;<code>export</code>&#8221;.
+Exported objects are visible not only to other files within the
+same library or subassembly but also to other
+libraries and subassemblies in the larger program.
+By default, makeheaders only makes objects visible to other members
+of the same library.
+</p>
+
+<p>
+That isn't the complete truth, actually.
+The semantics of C are such that once an object becomes visible
+outside of a single source file, it is also visible to any user
+of the library that is made from the source file.
+Makeheaders can not prevent outsiders for using non-exported resources,
+but it can discourage the practice by refusing to provide prototypes
+and declarations for the services it does not want to export.
+Thus the only real effect of the making an object exportable is
+to include it in the output makeheaders generates when it is run
+using the -H command line option.
+This is not a perfect solution, but it works well in practice.
+</p>
+
+<p>
+But trouble quickly arises when we attempt to devise a mechanism for
+telling makeheaders which prototypes it should export and which it should
+keep local.
+The built-in &#8220;<code>static</code>&#8221; 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: &#8220;<code>EXPORT</code>&#8221;
+</p>
+
+<p>
+Makeheaders allows the EXPORT keyword to precede any function or
+procedure definition.
+The routine following the EXPORT keyword is then eligable to appear
+in the header file generated using the -H command line option.
+Note that if a .c file contains the EXPORT keyword, makeheaders will
+put the macro
+<pre>
+   #define EXPORT
+</pre>
+in the header file it generates for the .c file so that the EXPORT keyword
+will never be seen by the C compiler.
+</p>
+
+<p>
+But the EXPORT keyword only works for function and procedure definitions.
+For structure, union and enum definitions, typedefs, #defines and
+class declarations, a second mechanism is used.
+Just as any declarations or definition contained within
+<pre>
+   #if INTERFACE
+   #endif
+</pre>
+are visible to all files within the library, any declarations
+or definitions within
+<pre>
+   #if EXPORT_INTERFACE
+   #endif
+</pre>
+will become part of the exported interface.
+The &#8220;<code>#if EXPORT_INTERFACE</code>&#8221; mechanism can be used in
+either .c or .h files.
+(The &#8220;<code>#if INTERFACE</code>&#8221; can also be used in both .h and
+.c files, but since it's use in a .h file would be redundant, we haven't
+mentioned it before.)
+</p>
+
+<a name="H0011"></a>
+<h3>3.5 Local declarations processed by makeheaders</h3>
+
+<p>
+Structure declarations and typedefs that appear in .c files are normally
+ignored by makeheaders.
+Such declarations are only intended for use by the source file in which
+they appear and so makeheaders doesn't need to copy them into any
+generated header files.
+We call such declarations &#8220;<code>private</code>&#8221;.
+</p>
+
+<p>
+Sometimes it is convenient to have makeheaders sort a sequence
+of private declarations into the correct order for us automatically.
+Or, we could have static functions and procedures for which we would like
+makeheaders to generate prototypes, but the arguments to these
+functions and procedures uses private declarations.
+In both of these cases, we want makeheaders to be aware of the
+private declarations and copy them into the local header file,
+but we don't want makeheaders to propagate the
+declarations outside of the file in which they are declared.
+</p>
+
+<p>
+When this situation arises, enclose the private declarations
+within
+<pre>
+  #if LOCAL_INTERFACE
+  #endif
+</pre>
+A &#8220;<code>LOCAL_INTERFACE</code>&#8221; block works very much like the
+&#8220;<code>INTERFACE</code>&#8221; and
+&#8220;<code>EXPORT_INTERFACE</code>&#8221;
+blocks described above, except that makeheaders insures that the
+objects declared in a LOCAL_INTERFACE are only visible to the
+file containing the LOCAL_INTERFACE.
+</p>
+
+<a name="H0012"></a>
+<h3>3.6 Using Makeheaders With C++ Code</h3>
+
+<p>
+You can use makeheaders to generate header files for C++ code, in
+addition to C.
+Makeheaders will recognize and copy both &#8220;<code>class</code>&#8221;
+declarations
+and inline function definitions, and it knows not to try to generate
+prototypes for methods.
+</p>
+
+<p>
+In fact, makeheaders is smart enough to be used in projects that employ
+a mixture of C and C++.
+For example, if a C function is called from within a C++ code module,
+makeheaders will know to prepend the text
+<pre>
+   extern "C"
+</pre>
+to the prototype for that function in the C++ header file.
+Going the other way,
+if you try to call a C++ function from within C, an
+appropriate error message is issued, since C++ routines can not
+normally be called by C code (due to fact that most C++ compilers
+use name mangling to facilitate type-safe linkage.)
+</p>
+
+<p>
+No special command-line options are required to use makeheaders with
+C++ input.  Makeheaders will recognize that its source code is C++
+by the suffix on the source code filename.  Simple ".c" or ".h" suffixes
+are assumed to be ANSI-C.  Anything else, including ".cc", ".C" and
+".cpp" is assumed to be C++.
+The name of the header file generated by makeheaders is derived from
+the name of the source file by converting every "c" to "h" and
+every "C" to "H" in the suffix of the filename.
+Thus the C++ source
+file &#8220;<code>alpha.cpp</code>&#8221; will induce makeheaders to
+generate a header file named &#8220;<code>alpha.hpp</code>&#8221;.
+</p>
+
+<p>
+Makeheaders augments class definitions by inserting prototypes to
+methods where appropriate.  If a method definition begins with one
+of the special keywords <b>PUBLIC</b>, <b>PROTECTED</b>, or
+<b>PRIVATE</b> (in upper-case to distinguish them from the regular
+C++ keywords with the same meaning) then a prototype for that
+method will be inserted into the class definition.  If none of
+these keywords appear, then the prototype is not inserted.  For
+example, in the following code, the constructor is not explicitly
+declared in the class definition but makeheaders will add it there
+because of the PUBLIC keyword that appears before the constructor
+definition.
+</p>
+
+<blockquote><pre>
+#if INTERFACE
+class Example1 {
+private:
+  int v1;
+};
+#endif
+PUBLIC Example1::Example1(){
+  v1 = 0;
+}
+</pre></blockquote>
+
+<p>
+The code above is equivalent to the following:
+</p>
+
+<blockquote><pre>
+#if INTERFACE
+class Example1 {
+private:
+  int v1;
+public:
+  Example1();
+};
+#endif
+Example1::Example1(){
+  v1 = 0;
+}
+</pre></blockquote>
+
+<p>
+The first form is preferred because only a single declaration of
+the constructor is required.  The second form requires two declarations,
+one in the class definition and one on the defintion of the constructor.
+</p>
+
+<h4>3.6.1 C++ Limitations</h4>
+
+<p>
+Makeheaders does not understand more recent
+C++ syntax such as templates and namespaces.
+Perhaps these issues will be addressed in future revisions.
+</p>
+
+<a name="H0013"></a>
+<h3>3.7 Conditional Compilation</h3>
+
+<p>
+The makeheaders program understands and tracks the conditional
+compilation constructs in the source code files it scans.
+Hence, if the following code appears in a source file
+<pre>
+  #ifdef UNIX
+  #  define WORKS_WELL 1
+  #else
+  #  define WORKS_WELL 0
+  #endif
+</pre>
+then the next patch of code will appear in the generated header for
+every .c file that uses the WORKS_WELL constant:
+<pre>
+  #if defined(UNIX)
+  #  define WORKS_WELL 1
+  #endif
+  #if !defined(UNIX)
+  #  define WORKS_WELL 0
+  #endif
+</pre>
+The conditional compilation constructs can be nested to any depth.
+Makeheaders also recognizes the special case of
+<pre>
+  #if 0
+  #endif
+</pre>
+and treats the enclosed text as a comment.
+</p>
+
+<a name="H0014"></a>
+<h3>3.8 Caveats</h3>
+
+<p>
+The makeheaders system is designed to be robust
+but it is possible for a devious programmer to fool the system,
+usually with unhelpful consequences.
+This subsection is a guide to helping you avoid trouble.
+</p>
+
+<p>
+Makeheaders does not understand the old K&amp;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&amp;R function.
+Therefore you should take care to avoid putting K&amp;R function definitions
+in your code.
+</p>
+
+<p>
+Makeheaders does not understand when you define more than one
+global variable with the same type separated by a comma.
+In other words, makeheaders does not understand this:
+<pre>
+   int a = 4, b = 5;
+</pre>
+The makeheaders program wants every variable to have its own
+definition.  Like this:
+<pre>
+   int a = 4;
+   int b = 5;
+</pre>
+Notice that this applies to global variables only, not to variables
+you declare inside your functions.
+Since global variables ought to be exceedingly rare, and since it is
+good style to declare them separately anyhow, this restriction is
+not seen as a terrible hardship.
+</p>
+
+<p>
+Makeheaders does not support defining an enumerated or aggregate type in
+the same statement as a variable declaration.  None of the following
+statements work completely:
+<pre>
+struct {int field;} a;
+struct Tag {int field;} b;
+struct Tag c;
+</pre>
+Instead, define types separately from variables:
+<pre>
+#if INTERFACE
+struct Tag {int field;};
+#endif
+Tag a;
+Tag b; /* No more than one variable per declaration. */
+Tag c; /* So must put each on its own line. */
+</pre>
+See <a href="#H0008">3.2 What Declarations Get Copied</a> for details,
+including on the automatic typedef.
+</p>
+
+<p>
+The makeheaders program processes its source file prior to sending
+those files through the C preprocessor.
+Hence, if you hide important structure information in preprocessor defines,
+makeheaders might not be able to successfully extract the information
+it needs from variables, functions and procedure definitions.
+For example, if you write this:
+<pre>
+  #define BEGIN {
+  #define END }
+</pre>
+at the beginning of your source file, and then try to create a function
+definition like this:
+<pre>
+  char *StrDup(const char *zSrc)
+    BEGIN
+      /* Code here */
+    END
+</pre>
+then makeheaders won't be able to find the end of the function definition
+and bad things are likely to happen.
+</p>
+
+<p>
+For most projects the code constructs that makeheaders cannot
+handle are very rare.
+As long as you avoid excessive cleverness, makeheaders will
+probably be able to figure out what you want and will do the right
+thing.
+</p>
+
+<p>
+Makeheaders has limited understanding of enums.  In particular, it does
+not realize the significance of enumerated values, so the enum is not
+emitted in the header files when its enumerated values are used unless
+the name associated with the enum is also used.  Moreover, enums can be
+completely anonymous, e.g. &#8220;<code>enum {X, Y, Z};</code>&#8221;.
+Makeheaders ignores such enums so they can at least be used within a
+single source file.  Makeheaders expects you to use #define constants
+instead.  If you want enum features that #define lacks, and you need the
+enum in the interface, bypass makeheaders and write a header file by
+hand, or teach makeheaders to emit the enum definition when any of the
+enumerated values are used, rather than only when the top-level name (if
+any) is used.
+</p>
+
+<a name="H0015"></a>
+<h2>4.0 Using Makeheaders To Generate Documentation</h2>
+
+<p>
+Many people have observed the advantages of generating program
+documentation directly from the source code:
+<ul>
+<li> Less effort is involved.  It is easier to write a program than
+     it is to write a program and a document.
+<li> The documentation is more likely to agree with the code.
+     When documentation is derived directly from the code, or is
+     contained in comments immediately adjacent to the code, it is much
+     more likely to be correct than if it is contained in a separate
+     unrelated file in a different part of the source tree.
+<li> Information is kept in only one place.  When a change occurs
+     in the code, it is not necessary to make a corresponding change
+     in a separate document.  Just rerun the documentation generator.
+</ul>
+The makeheaders program does not generate program documentation itself.
+But you can use makeheaders to parse the program source code, extract
+the information that is relevant to the documentation and to pass this
+information to another tool to do the actual documentation preparation.
+</p>
+
+<p>
+When makeheaders is run with the &#8220;<code>-doc</code>&#8221; option, it
+emits no header files at all.
+Instead, it does a complete dump of its internal tables to standard
+output in a form that is easily parsed.
+This output can then be used by another program (the implementation
+of which is left as an exercise to the reader) that will use the
+information to prepare suitable documentation.
+</p>
+
+<p>
+The &#8220;<code>-doc</code>&#8221; option causes makeheaders to print
+information to standard output about all of the following objects:
+<ul>
+<li> C++ class declarations
+<li> Structure and union declarations
+<li> Enumerations
+<li> Typedefs
+<li> Procedure and function definitions
+<li> Global variables
+<li> Preprocessor macros (ex: &#8220;<code>#define</code>&#8221;)
+</ul>
+For each of these objects, the following information is output:
+<ul>
+<li> The name of the object.
+<li> The type of the object.  (Structure, typedef, macro, etc.)
+<li> Flags to indicate if the declaration is exported (contained within
+     an EXPORT_INTERFACE block) or local (contained with LOCAL_INTERFACE).
+<li> A flag to indicate if the object is declared in a C++ file.
+<li> The name of the file in which the object was declared.
+<li> The complete text of any block comment that preceeds the declarations.
+<li> If the declaration occurred inside a preprocessor conditional
+     (&#8220;<code>#if</code>&#8221;) then the text of that conditional is
+     provided.
+<li> The complete text of a declaration for the object.
+</ul>
+The exact output format will not be described here.
+It is simple to understand and parse and should be obvious to
+anyone who inspects some sample output.
+</p>
+
+<a name="H0016"></a>
+<h2>5.0 Compiling The Makeheaders Program</h2>
+
+<p>
+The source code for makeheaders is a single file of ANSI-C code,
+approximately 3000 lines in length.
+The program makes only modest demands of the system and C library
+and should compile without alteration on most ANSI C compilers
+and on most operating systems.
+It is known to compile using several variations of GCC for Unix
+as well as Cygwin32 and MSVC 5.0 for Win32.
+</p>
+
+<a name="H0017"></a>
+<h2>6.0 History</h2>
+
+<p>
+The makeheaders program was first written by D. Richard Hipp
+(also the original author of
+<a href="https://sqlite.org/">SQLite</a> and
+<a href="https://www.fossil-scm.org/">Fossil</a>) in 1993.
+Hipp open-sourced the project immediately, but it never caught
+on with any other developers and it continued to be used mostly
+by Hipp himself for over a decade.  When Hipp was first writing
+the Fossil version control system in 2006 and 2007, he used
+makeheaders on that project to help simplify the source code.
+As the popularity of Fossil increased, the makeheaders
+that was incorporated into the Fossil source tree became the
+"official" makeheaders implementation.
+</p>
+
+<p>
+As this paragraph is being composed (2016-11-05), Fossil is the
+only project known to Hipp that is still using makeheaders.  On
+the other hand, makeheaders has served the Fossil project well and
+there are no plans remove it.
+</p>
+
+<a name="H0018"></a>
+<h2>7.0 Summary And Conclusion</h2>
+
+<p>
+The makeheaders program will automatically generate a minimal header file
+for each of a set of C source and header files, and will
+generate a composite header file for the entire source file suite,
+for either internal or external use.
+It can also be used as the parser in an automated program
+documentation system.
+</p>
+
+<p>
+The makeheaders program has been in use since 1994,
+in a wide variety of projects under both UNIX and Win32.
+In every project where it has been used, makeheaders has proven
+to be a very helpful aid
+in the construction and maintenance of large C codes.
+In at least two cases, makeheaders has facilitated development
+of programs that would have otherwise been all but impossible
+due to their size and complexity.
+</p>
+</body>
+</html>
diff --git a/tools/lib/7_makefile b/tools/lib/7_makefile
new file mode 100755 (executable)
index 0000000..ae06d60
--- /dev/null
@@ -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 (executable)
index 0000000..5cb8f77
--- /dev/null
@@ -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 (file)
index 0000000..b58e787
--- /dev/null
@@ -0,0 +1,3739 @@
+<!DOCTYPE html>
+<html>
+<head>
+<base href="https://fossil-scm.org/fossil/doc/8cecc544/src/makeheaders.c" />
+<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: ; script-src 'self' 'nonce-e2f4709a3deeafd8212401c84e2a082e1fbd3068470984f2' ; style-src 'self' 'unsafe-inline'" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Fossil: File Content</title>
+<link rel="alternate" type="application/rss+xml" title="RSS Feed"  href="/fossil/timeline.rss" />
+<link rel="stylesheet" href="/fossil/style.css?id=9eb7de39" type="text/css"  media="screen" />
+</head>
+<body>
+<div class="header">\r
+  <div class="title"><h1>Fossil</h1>File Content</div>\r
+    <div class="status"><a href='/fossil/login'>Login</a>
+</div>\r
+</div>\r
+<div class="mainmenu">\r
+<a id='hbbtn' href='#'>&#9776;</a><a href='/fossil/doc/trunk/www/index.wiki' class=''>Home</a>
+<a href='/fossil/timeline' class=''>Timeline</a>
+<a href='/fossil/doc/trunk/www/permutedindex.html' class=''>Docs</a>
+<a href='https://fossil-scm.org/forum'>Forum</a><a href='/fossil/uv/download.html' class='desktoponly'>Download</a>
+</div>\r
+<div id='hbdrop'></div>\r
+<form id='f01' method='GET' action='/fossil/file'>
+<input type='hidden' name='udc' value='1'>
+<div class="submenu">
+<a class="label" href="/fossil/artifact/49c76a69">Artifact</a>
+<a class="label" href="/fossil/timeline?n=200&amp;uf=49c76a6973d579ff0b346e5f73182fa72dd797cbb07e8b20612849dc2adef85d">Check-ins Using</a>
+<a class="label" href="/fossil/raw/src/makeheaders.c?name=49c76a6973d579ff0b346e5f73182fa72dd797cbb07e8b20612849dc2adef85d">Download</a>
+<a class="label" href="/fossil/hexdump?name=49c76a6973d579ff0b346e5f73182fa72dd797cbb07e8b20612849dc2adef85d">Hex</a>
+<label class='submenuctrl submenuckbox'><input type='checkbox' name='ln' id='submenuctrl-0' >Line Numbers</label>
+</div>
+<input type="hidden" name="name" value="src/makeheaders.c">
+</form>
+<script src='/fossil/builtin/menu.js?id=6f60cb38'></script>
+<div class="content">
+<h2>Latest version of file 'src/makeheaders.c':</h2>
+<ul>
+<li>File
+<a data-href='/fossil/finfo?name=src/makeheaders.c&m=49c76a6973d579ff' href='/fossil/honeypot'>src/makeheaders.c</a>
+&mdash; part of check-in
+<span class="timelineHistDsp">[8cecc544]</span>
+at
+2018-11-02 15:21:54
+on branch <a data-href='/fossil/timeline?r=trunk' href='/fossil/honeypot'>trunk</a>
+&mdash; Enhance makeheaders so that it is able to deal with static_assert() statements.
+(These do not come up in Fossil itself.  This check-in is in response to use
+of Makeheaders by external projects.)
+ (user:
+drh
+size: 100011)
+<a data-href='/fossil/whatis/49c76a6973d579ff' href='/fossil/honeypot'>[more...]</a>
+</ul>
+<hr />
+<blockquote>
+<pre>
+/*
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the &quot;2-Clause License&quot; or &quot;FreeBSD License&quot;.)
+**
+** 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 &quot;as is&quot; 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 &lt;stdio.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;ctype.h&gt;
+#include &lt;memory.h&gt;
+#include &lt;sys/stat.h&gt;
+#include &lt;assert.h&gt;
+#include &lt;string.h&gt;
+
+#if defined( __MINGW32__) ||  defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__)
+#  ifndef WIN32
+#    define WIN32
+#  endif
+#else
+# include &lt;unistd.h&gt;
+#endif
+
+/*
+** Macros for debugging.
+*/
+#ifdef DEBUG
+static int debugMask = 0;
+# define debug0(F,M)       if( (F)&amp;debugMask ){ fprintf(stderr,M); }
+# define debug1(F,M,A)     if( (F)&amp;debugMask ){ fprintf(stderr,M,A); }
+# define debug2(F,M,A,B)   if( (F)&amp;debugMask ){ fprintf(stderr,M,A,B); }
+# define debug3(F,M,A,B,C) if( (F)&amp;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&#39;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&#39;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 &quot;forward declaration&quot; is a declaration that an object exists that
+** doesn&#39;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 &quot;Properties&quot; below */
+  Token *pComment;   /* A block comment associated with this declaration */
+  Token tokenCode;   /* Implementation of functions and procedures */
+  Decl *pSameName;   /* Next declaration with the same &quot;zName&quot; */
+  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 &quot;typedef struct Abc Abc;&quot; and the
+** full declaration is &quot;struct Abc { int a; float b; };&quot;.)
+**
+** 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&#39;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 &#39;extern &quot;C&quot;&#39; in a C++ header.
+                                ** Prepend nothing in a C header */
+#define DP_ExternReqd   0x080   /* Prepend &#39;extern &quot;C&quot;&#39; in a C++ header if
+                                ** DP_Cplusplus is not also set. If DP_Cplusplus
+                                ** is set or this is a C header then
+                                ** prepend &#39;extern&#39; */
+
+/*
+** Convenience macros for dealing with declaration properties
+*/
+#define DeclHasProperty(D,P)    (((D)-&gt;flags&amp;(P))==(P))
+#define DeclHasAnyProperty(D,P) (((D)-&gt;flags&amp;(P))!=0)
+#define DeclSetProperty(D,P)    (D)-&gt;flags |= (P)
+#define DeclClearProperty(D,P)  (D)-&gt;flags &amp;= ~(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 &quot;flags&quot; 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    /* &quot;extern&quot; has been seen */
+#define PS_Export        0x001000    /* If between &quot;#if EXPORT_INTERFACE&quot;
+                                     ** and &quot;#endif&quot; */
+#define PS_Export2       0x002000    /* If &quot;EXPORT&quot; seen */
+#define PS_Typedef       0x004000    /* If &quot;typedef&quot; has been seen */
+#define PS_Static        0x008000    /* If &quot;static&quot; has been seen */
+#define PS_Interface     0x010000    /* If within #if INTERFACE..#endif */
+#define PS_Method        0x020000    /* If &quot;::&quot; token has been seen */
+#define PS_Local         0x040000    /* If within #if LOCAL_INTERFACE..#endif */
+#define PS_Local2        0x080000    /* If &quot;LOCAL&quot; seen. */
+#define PS_Public        0x100000    /* If &quot;PUBLIC&quot; seen. */
+#define PS_Protected     0x200000    /* If &quot;PROTECTED&quot; seen. */
+#define PS_Private       0x400000    /* If &quot;PRIVATE&quot; seen. */
+#define PS_PPP           0x700000    /* If any of PUBLIC, PRIVATE, PROTECTED */
+
+/*
+** The following set of flags are ORed into the &quot;flags&quot; 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 &quot;&quot; or &lt;&gt; */
+  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 &quot;#undef INTERFACE&quot; part is a hack to work around a name collision
+** in MSVC 2008.
+*/
+const char zTopLine[] =
+  &quot;/* \aThis file was automatically generated.  Do not edit! */\n&quot;
+  &quot;#undef INTERFACE\n&quot;;
+#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,&quot;Assertion failed on line %d\n&quot;,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,&quot;Out of memory.  Can&#39;t allocate %d bytes.\n&quot;,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,
+        &quot;Out of memory.  Can&#39;t enlarge an allocation to %d bytes\n&quot;,nByte);
+      exit(1);
+    }
+  }
+  return p;
+}
+static char *StrDup(const char *zSrc, int nByte){
+  char *zDest;
+  if( nByte&lt;=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)==&#39;_&#39; || isalnum(X))
+
+/*
+** Routines for dealing with unbounded strings.
+*/
+static void StringInit(String *pStr){
+  pStr-&gt;nAlloc = 0;
+  pStr-&gt;nUsed = 0;
+  pStr-&gt;zText = 0;
+}
+static void StringReset(String *pStr){
+  SafeFree(pStr-&gt;zText);
+  StringInit(pStr);
+}
+static void StringAppend(String *pStr, const char *zText, int nByte){
+  if( nByte&lt;=0 ){
+    nByte = strlen(zText);
+  }
+  if( pStr-&gt;nUsed + nByte &gt;= pStr-&gt;nAlloc ){
+    if( pStr-&gt;nAlloc==0 ){
+      pStr-&gt;nAlloc = nByte + 100;
+      pStr-&gt;zText = SafeMalloc( pStr-&gt;nAlloc );
+    }else{
+      pStr-&gt;nAlloc = pStr-&gt;nAlloc*2 + nByte;
+      pStr-&gt;zText = SafeRealloc(pStr-&gt;zText, pStr-&gt;nAlloc);
+    }
+  }
+  strncpy(&amp;pStr-&gt;zText[pStr-&gt;nUsed],zText,nByte);
+  pStr-&gt;nUsed += nByte;
+  pStr-&gt;zText[pStr-&gt;nUsed] = 0;
+}
+#define StringGet(S) ((S)-&gt;zText?(S)-&gt;zText:&quot;&quot;)
+
+/*
+** 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&lt;=0 ){
+    n = strlen(z);
+  }
+  while( n-- ){
+    h = h ^ (h&lt;&lt;5) ^ *z++;
+  }
+  return h &amp; 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&lt;=0 ){
+    len = strlen(zName);
+  }
+  h = Hash(zName,len) % DECL_HASH_SIZE;
+  p = apTable[h];
+  while( p &amp;&amp; (strncmp(p-&gt;zName,zName,len)!=0 || p-&gt;zName[len]!=0) ){
+    p = p-&gt;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-&gt;zName,0) % DECL_HASH_SIZE;
+  pOther = apTable[h];
+  while( pOther &amp;&amp; strcmp(pDecl-&gt;zName,pOther-&gt;zName)!=0 ){
+    pOther = pOther-&gt;pSameHash;
+  }
+  if( pOther ){
+    pDecl-&gt;pSameName = pOther-&gt;pSameName;
+    pOther-&gt;pSameName = pDecl;
+  }else{
+    pDecl-&gt;pSameName = 0;
+    pDecl-&gt;pSameHash = apTable[h];
+    apTable[h] = pDecl;
+  }
+  pDecl-&gt;pNext = 0;
+  if( pDeclFirst==0 ){
+    pDeclFirst = pDeclLast = pDecl;
+  }else{
+    pDeclLast-&gt;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-&gt;pNext){
+    if( pIf-&gt;zCondition==0 || *pIf-&gt;zCondition==0 ) continue;
+    if( !hasIf ){
+      hasIf = 1;
+      StringInit(&amp;str);
+    }else{
+      StringAppend(&amp;str,&quot; &amp;&amp; &quot;,4);
+    }
+    StringAppend(&amp;str,pIf-&gt;zCondition,0);
+  }
+  if( hasIf ){
+    zResult = StrDup(StringGet(&amp;str),0);
+    StringReset(&amp;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-&gt;zName = (char*)&amp;pDecl[1];
+  sprintf(pDecl-&gt;zName,&quot;%.*s&quot;,nName,zName);
+  pDecl-&gt;zFile = zFilename;
+  pDecl-&gt;pInclude = includeList;
+  pDecl-&gt;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&lt;=0 ){
+    nId = strlen(zId);
+  }
+  h = Hash(zId,nId) % IDENT_HASH_SIZE;
+  for(pId = pTable-&gt;apTable[h]; pId; pId=pId-&gt;pCollide){
+    if( strncmp(zId,pId-&gt;zName,nId)==0 &amp;&amp; pId-&gt;zName[nId]==0 ){
+      /* printf(&quot;Already in table: %.*s\n&quot;,nId,zId); */
+      return 0;
+    }
+  }
+  pId = SafeMalloc( sizeof(Ident) + nId + 1 );
+  pId-&gt;zName = (char*)&amp;pId[1];
+  sprintf(pId-&gt;zName,&quot;%.*s&quot;,nId,zId);
+  pId-&gt;pNext = pTable-&gt;pList;
+  pTable-&gt;pList = pId;
+  pId-&gt;pCollide = pTable-&gt;apTable[h];
+  pTable-&gt;apTable[h] = pId;
+  /* printf(&quot;Add to table: %.*s\n&quot;,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&lt;=0 ){
+    nId = strlen(zId);
+  }
+  h = Hash(zId,nId) % IDENT_HASH_SIZE;
+  for(pId = pTable-&gt;apTable[h]; pId; pId=pId-&gt;pCollide){
+    if( strncmp(zId,pId-&gt;zName,nId)==0 &amp;&amp; pId-&gt;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-&gt;pList; pId; pId = pNext){
+    pNext = pId-&gt;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-&gt;pList; pId; pId = pId-&gt;pNext){
+    fprintf(pOut,&quot;%s\n&quot;,pId-&gt;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,&amp;sStat)!=0
+#ifndef WIN32
+    || !S_ISREG(sStat.st_mode)
+#endif
+  ){
+    return 0;
+  }
+  pIn = fopen(zFilename,&quot;r&quot;);
+  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,&quot;w&quot;);
+  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. &quot;..&quot; or &#39;.&#39; */
+#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 &quot;low-level token&quot; 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 &quot;..&quot; or &#39;..&#39; */
+  int nErr = 0;    /* Number of errors seen */
+
+  z = pIn-&gt;z;
+  i = pIn-&gt;i;
+  pToken-&gt;nLine = pIn-&gt;nLine;
+  pToken-&gt;zText = &amp;z[i];
+  switch( z[i] ){
+    case 0:
+      pToken-&gt;eType = TT_EOF;
+      pToken-&gt;nText = 0;
+      break;
+
+    case &#39;#&#39;:
+      if( i==0 || z[i-1]==&#39;\n&#39; || (i&gt;1 &amp;&amp; z[i-1]==&#39;\r&#39; &amp;&amp; z[i-2]==&#39;\n&#39;)){
+        /* We found a preprocessor statement */
+        pToken-&gt;eType = TT_Preprocessor;
+        i++;
+        while( z[i]!=0 &amp;&amp; z[i]!=&#39;\n&#39; ){
+          if( z[i]==&#39;\\&#39; ){
+            i++;
+            if( z[i]==&#39;\n&#39; ) pIn-&gt;nLine++;
+          }
+          i++;
+        }
+        pToken-&gt;nText = i - pIn-&gt;i;
+      }else{
+        /* Just an operator */
+        pToken-&gt;eType = TT_Other;
+        pToken-&gt;nText = 1;
+      }
+      break;
+
+    case &#39; &#39;:
+    case &#39;\t&#39;:
+    case &#39;\r&#39;:
+    case &#39;\f&#39;:
+    case &#39;\n&#39;:
+      while( isspace(z[i]) ){
+        if( z[i]==&#39;\n&#39; ) pIn-&gt;nLine++;
+        i++;
+      }
+      pToken-&gt;eType = TT_Space;
+      pToken-&gt;nText = i - pIn-&gt;i;
+      break;
+
+    case &#39;\\&#39;:
+      pToken-&gt;nText = 2;
+      pToken-&gt;eType = TT_Other;
+      if( z[i+1]==&#39;\n&#39; ){
+        pIn-&gt;nLine++;
+        pToken-&gt;eType = TT_Space;
+      }else if( z[i+1]==0 ){
+        pToken-&gt;nText = 1;
+      }
+      break;
+
+    case &#39;\&#39;&#39;:
+    case &#39;\&quot;&#39;:
+      cStart = z[i];
+      startLine = pIn-&gt;nLine;
+      do{
+        i++;
+        c = z[i];
+        if( c==&#39;\n&#39; ){
+          if( !nlisc ){
+            fprintf(stderr,
+              &quot;%s:%d: (warning) Newline in string or character literal.\n&quot;,
+              zFilename, pIn-&gt;nLine);
+            nlisc = 1;
+          }
+          pIn-&gt;nLine++;
+        }
+        if( c==&#39;\\&#39; ){
+          i++;
+          c = z[i];
+          if( c==&#39;\n&#39; ){
+            pIn-&gt;nLine++;
+          }
+        }else if( c==cStart ){
+          i++;
+          c = 0;
+        }else if( c==0 ){
+          fprintf(stderr, &quot;%s:%d: Unterminated string or character literal.\n&quot;,
+             zFilename, startLine);
+          nErr++;
+        }
+      }while( c );
+      pToken-&gt;eType = TT_String;
+      pToken-&gt;nText = i - pIn-&gt;i;
+      break;
+
+    case &#39;/&#39;:
+      if( z[i+1]==&#39;/&#39; ){
+        /* C++ style comment */
+        while( z[i] &amp;&amp; z[i]!=&#39;\n&#39; ){ i++; }
+        pToken-&gt;eType = TT_Comment;
+        pToken-&gt;nText = i - pIn-&gt;i;
+      }else if( z[i+1]==&#39;*&#39; ){
+        /* C style comment */
+        int isBlockComment = i==0 || z[i-1]==&#39;\n&#39;;
+        i += 2;
+        startLine = pIn-&gt;nLine;
+        while( z[i] &amp;&amp; (z[i]!=&#39;*&#39; || z[i+1]!=&#39;/&#39;) ){
+          if( z[i]==&#39;\n&#39; ){
+            pIn-&gt;nLine++;
+            if( isBlockComment ){
+              if( z[i+1]==&#39;*&#39; || z[i+2]==&#39;*&#39; ){
+                 isBlockComment = 2;
+              }else{
+                 isBlockComment = 0;
+              }
+            }
+          }
+          i++;
+        }
+        if( z[i] ){
+          i += 2;
+        }else{
+          isBlockComment = 0;
+          fprintf(stderr,&quot;%s:%d: Unterminated comment\n&quot;,
+            zFilename, startLine);
+          nErr++;
+        }
+        pToken-&gt;eType = isBlockComment==2 ? TT_BlockComment : TT_Comment;
+        pToken-&gt;nText = i - pIn-&gt;i;
+      }else{
+        /* A divide operator */
+        pToken-&gt;eType = TT_Other;
+        pToken-&gt;nText = 1 + (z[i+1]==&#39;+&#39;);
+      }
+      break;
+
+    case &#39;0&#39;:
+      if( z[i+1]==&#39;x&#39; || z[i+1]==&#39;X&#39; ){
+        /* A hex constant */
+        i += 2;
+        while( isxdigit(z[i]) ){ i++; }
+      }else{
+        /* An octal constant */
+        while( isdigit(z[i]) ){ i++; }
+      }
+      pToken-&gt;eType = TT_Number;
+      pToken-&gt;nText = i - pIn-&gt;i;
+      break;
+
+    case &#39;1&#39;: case &#39;2&#39;: case &#39;3&#39;: case &#39;4&#39;:
+    case &#39;5&#39;: case &#39;6&#39;: case &#39;7&#39;: case &#39;8&#39;: case &#39;9&#39;:
+      while( isdigit(z[i]) ){ i++; }
+      if( (c=z[i])==&#39;.&#39; ){
+         i++;
+         while( isdigit(z[i]) ){ i++; }
+         c = z[i];
+         if( c==&#39;e&#39; || c==&#39;E&#39; ){
+           i++;
+           if( ((c=z[i])==&#39;+&#39; || c==&#39;-&#39;) &amp;&amp; isdigit(z[i+1]) ){ i++; }
+           while( isdigit(z[i]) ){ i++; }
+           c = z[i];
+         }
+         if( c==&#39;f&#39; || c==&#39;F&#39; || c==&#39;l&#39; || c==&#39;L&#39; ){ i++; }
+      }else if( c==&#39;e&#39; || c==&#39;E&#39; ){
+         i++;
+         if( ((c=z[i])==&#39;+&#39; || c==&#39;-&#39;) &amp;&amp; isdigit(z[i+1]) ){ i++; }
+         while( isdigit(z[i]) ){ i++; }
+      }else if( c==&#39;L&#39; || c==&#39;l&#39; ){
+         i++;
+         c = z[i];
+         if( c==&#39;u&#39; || c==&#39;U&#39; ){ i++; }
+      }else if( c==&#39;u&#39; || c==&#39;U&#39; ){
+         i++;
+         c = z[i];
+         if( c==&#39;l&#39; || c==&#39;L&#39; ){ i++; }
+      }
+      pToken-&gt;eType = TT_Number;
+      pToken-&gt;nText = i - pIn-&gt;i;
+      break;
+
+    case &#39;a&#39;: case &#39;b&#39;: case &#39;c&#39;: case &#39;d&#39;: case &#39;e&#39;: case &#39;f&#39;: case &#39;g&#39;:
+    case &#39;h&#39;: case &#39;i&#39;: case &#39;j&#39;: case &#39;k&#39;: case &#39;l&#39;: case &#39;m&#39;: case &#39;n&#39;:
+    case &#39;o&#39;: case &#39;p&#39;: case &#39;q&#39;: case &#39;r&#39;: case &#39;s&#39;: case &#39;t&#39;: case &#39;u&#39;:
+    case &#39;v&#39;: case &#39;w&#39;: case &#39;x&#39;: case &#39;y&#39;: case &#39;z&#39;: case &#39;A&#39;: case &#39;B&#39;:
+    case &#39;C&#39;: case &#39;D&#39;: case &#39;E&#39;: case &#39;F&#39;: case &#39;G&#39;: case &#39;H&#39;: case &#39;I&#39;:
+    case &#39;J&#39;: case &#39;K&#39;: case &#39;L&#39;: case &#39;M&#39;: case &#39;N&#39;: case &#39;O&#39;: case &#39;P&#39;:
+    case &#39;Q&#39;: case &#39;R&#39;: case &#39;S&#39;: case &#39;T&#39;: case &#39;U&#39;: case &#39;V&#39;: case &#39;W&#39;:
+    case &#39;X&#39;: case &#39;Y&#39;: case &#39;Z&#39;: case &#39;_&#39;:
+      while( isalnum(z[i]) || z[i]==&#39;_&#39; ){ i++; };
+      pToken-&gt;eType = TT_Id;
+      pToken-&gt;nText = i - pIn-&gt;i;
+      break;
+
+    case &#39;:&#39;:
+      pToken-&gt;eType = TT_Other;
+      pToken-&gt;nText = 1 + (z[i+1]==&#39;:&#39;);
+      break;
+
+    case &#39;=&#39;:
+    case &#39;&lt;&#39;:
+    case &#39;&gt;&#39;:
+    case &#39;+&#39;:
+    case &#39;-&#39;:
+    case &#39;*&#39;:
+    case &#39;%&#39;:
+    case &#39;^&#39;:
+    case &#39;&amp;&#39;:
+    case &#39;|&#39;:
+      pToken-&gt;eType = TT_Other;
+      pToken-&gt;nText = 1 + (z[i+1]==&#39;=&#39;);
+      break;
+
+    default:
+      pToken-&gt;eType = TT_Other;
+      pToken-&gt;nText = 1;
+      break;
+  }
+  pIn-&gt;i += pToken-&gt;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 &quot;#if 0&quot; and &quot;#endif&quot;.
+**
+** This routine returns the number of errors encountered.  An error
+** is an unterminated token or unmatched &quot;#if 0&quot;.
+**
+** 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-&gt;nLine;
+  while( 1 ){
+    nErr += GetToken(pIn,pToken);
+    /* printf(&quot;%04d: Type=%d nIf=%d [%.*s]\n&quot;,
+       pToken-&gt;nLine,pToken-&gt;eType,nIf,pToken-&gt;nText,
+       pToken-&gt;eType!=TT_Space ? pToken-&gt;zText : &quot;&lt;space&gt;&quot;); */
+    pToken-&gt;pComment = blockComment;
+    switch( pToken-&gt;eType ){
+      case TT_Comment:          /*0123456789 12345678 */
+       if( strncmp(pToken-&gt;zText, &quot;/*MAKEHEADERS-STOP&quot;, 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,&quot;%s:%d: Unterminated \&quot;#if\&quot;\n&quot;,
+             zFilename, startLine);
+          nErr++;
+        }
+        return nErr;
+
+      case TT_Preprocessor:
+        z = &amp;pToken-&gt;zText[1];
+        while( *z==&#39; &#39; || *z==&#39;\t&#39; ) z++;
+        if( sscanf(z,&quot;if %d&quot;,&amp;value)==1 &amp;&amp; value==0 ){
+          nIf++;
+          inZero = 1;
+        }else if( inZero ){
+          if( strncmp(z,&quot;if&quot;,2)==0 ){
+            nIf++;
+          }else if( strncmp(z,&quot;endif&quot;,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-&gt;zText;
+  sIn.i = 1;
+  sIn.nLine = 1;
+  while( go &amp;&amp; sIn.i &lt; pToken-&gt;nText ){
+    GetToken(&amp;sIn,&amp;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-&gt;eType ){
+    case TT_Id:
+      if( pTable!=0 ){
+        IdentTableInsert(pTable,pToken-&gt;zText,pToken-&gt;nText);
+      }
+      return nErr;
+
+    case TT_Preprocessor:
+      if( pTable!=0 ){
+        FindIdentifiersInMacro(pToken,pTable);
+      }
+      return nErr;
+
+    case TT_Other:
+      if( pToken-&gt;zText[0]==&#39;{&#39; ) break;
+      return nErr;
+
+    default:
+      return nErr;
+  }
+
+  iStart = pIn-&gt;i;
+  zStart = pToken-&gt;zText;
+  nLine = pToken-&gt;nLine;
+  nBrace = 1;
+  while( nBrace ){
+    nErr += GetNonspaceToken(pIn,pToken);
+    /* printf(&quot;%04d: nBrace=%d [%.*s]\n&quot;,pToken-&gt;nLine,nBrace,
+       pToken-&gt;nText,pToken-&gt;zText); */
+    switch( pToken-&gt;eType ){
+      case TT_EOF:
+        fprintf(stderr,&quot;%s:%d: Unterminated \&quot;{\&quot;\n&quot;,
+           zFilename, nLine);
+        nErr++;
+        pToken-&gt;eType = TT_Error;
+        return nErr;
+
+      case TT_Id:
+        if( pTable ){
+          IdentTableInsert(pTable,pToken-&gt;zText,pToken-&gt;nText);
+        }
+        break;
+
+      case TT_Preprocessor:
+        if( pTable!=0 ){
+          FindIdentifiersInMacro(pToken,pTable);
+        }
+        break;
+
+      case TT_Other:
+        if( (c = pToken-&gt;zText[0])==&#39;{&#39; ){
+          nBrace++;
+        }else if( c==&#39;}&#39; ){
+          nBrace--;
+        }
+        break;
+
+      default:
+        break;
+    }
+  }
+  pToken-&gt;eType = TT_Braces;
+  pToken-&gt;nText = 1 + pIn-&gt;i - iStart;
+  pToken-&gt;zText = zStart;
+  pToken-&gt;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-&gt;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(&amp;sIn,pNew,pTable);
+    debug3(TOKENIZER, &quot;Token on line %d: [%.*s]\n&quot;,
+       pNew-&gt;nLine, pNew-&gt;nText&lt;50 ? pNew-&gt;nText : 50, pNew-&gt;zText);
+    if( pFirst==0 ){
+      pFirst = pLast = pNew;
+      pNew-&gt;pPrev = 0;
+    }else{
+      pLast-&gt;pNext = pNew;
+      pNew-&gt;pPrev = pLast;
+      pLast = pNew;
+    }
+    if( pNew-&gt;eType==TT_EOF ) break;
+  }
+  if( pLast ) pLast-&gt;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,&quot;Usage: %s filename\n&quot;,*argv);
+    exit(1);
+  }
+  memset(&amp;sTable,0,sizeof(sTable));
+  zFile = ReadFile(argv[1]);
+  if( zFile==0 ){
+    fprintf(stderr,&quot;Can&#39;t read file \&quot;%s\&quot;\n&quot;,argv[1]);
+    exit(1);
+  }
+  pList = TokenizeFile(zFile,&amp;sTable);
+  for(p=pList; p; p=p-&gt;pNext){
+    int j;
+    switch( p-&gt;eType ){
+      case TT_Space:
+        printf(&quot;%4d: Space\n&quot;,p-&gt;nLine);
+        break;
+      case TT_Id:
+        printf(&quot;%4d: Id           %.*s\n&quot;,p-&gt;nLine,p-&gt;nText,p-&gt;zText);
+        break;
+      case TT_Preprocessor:
+        printf(&quot;%4d: Preprocessor %.*s\n&quot;,p-&gt;nLine,p-&gt;nText,p-&gt;zText);
+        break;
+      case TT_Comment:
+        printf(&quot;%4d: Comment\n&quot;,p-&gt;nLine);
+        break;
+      case TT_BlockComment:
+        printf(&quot;%4d: Block Comment\n&quot;,p-&gt;nLine);
+        break;
+      case TT_Number:
+        printf(&quot;%4d: Number       %.*s\n&quot;,p-&gt;nLine,p-&gt;nText,p-&gt;zText);
+        break;
+      case TT_String:
+        printf(&quot;%4d: String       %.*s\n&quot;,p-&gt;nLine,p-&gt;nText,p-&gt;zText);
+        break;
+      case TT_Other:
+        printf(&quot;%4d: Other        %.*s\n&quot;,p-&gt;nLine,p-&gt;nText,p-&gt;zText);
+        break;
+      case TT_Braces:
+        for(j=0; j&lt;p-&gt;nText &amp;&amp; j&lt;30 &amp;&amp; p-&gt;zText[j]!=&#39;\n&#39;; j++){}
+        printf(&quot;%4d: Braces       %.*s...}\n&quot;,p-&gt;nLine,j,p-&gt;zText);
+        break;
+      case TT_EOF:
+        printf(&quot;%4d: End of file\n&quot;,p-&gt;nLine);
+        break;
+      default:
+        printf(&quot;%4d: type %d\n&quot;,p-&gt;nLine,p-&gt;eType);
+        break;
+    }
+  }
+  FreeTokenList(pList);
+  SafeFree(zFile);
+  IdentTablePrint(&amp;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-&gt;pNext;
+  while( pFirst!=pLast ){
+    switch( pFirst-&gt;eType ){
+      case TT_Preprocessor:
+        printf(&quot;\n%.*s\n&quot;,pFirst-&gt;nText,pFirst-&gt;zText);
+        needSpace = 0;
+        break;
+
+      case TT_Id:
+      case TT_Number:
+        printf(&quot;%s%.*s&quot;, needSpace ? &quot; &quot; : &quot;&quot;, pFirst-&gt;nText, pFirst-&gt;zText);
+        needSpace = 1;
+        break;
+
+      default:
+        c = pFirst-&gt;zText[0];
+        printf(&quot;%s%.*s&quot;,
+          (needSpace &amp;&amp; (c==&#39;*&#39; || c==&#39;{&#39;)) ? &quot; &quot; : &quot;&quot;,
+          pFirst-&gt;nText, pFirst-&gt;zText);
+        needSpace = pFirst-&gt;zText[0]==&#39;,&#39;;
+        break;
+    }
+    pFirst = pFirst-&gt;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(&amp;str);
+  pLast = pLast-&gt;pNext;
+  while( pFirst!=pLast ){
+    if( pFirst==pSkip ){ iSkip = nSkip; }
+    if( iSkip&gt;0 ){
+      iSkip--;
+      pFirst=pFirst-&gt;pNext;
+      continue;
+    }
+    switch( pFirst-&gt;eType ){
+      case TT_Preprocessor:
+        StringAppend(&amp;str,&quot;\n&quot;,1);
+        StringAppend(&amp;str,pFirst-&gt;zText,pFirst-&gt;nText);
+        StringAppend(&amp;str,&quot;\n&quot;,1);
+        needSpace = 0;
+        break;
+
+      case TT_Id:
+        switch( pFirst-&gt;zText[0] ){
+          case &#39;E&#39;:
+            if( pFirst-&gt;nText==6 &amp;&amp; strncmp(pFirst-&gt;zText,&quot;EXPORT&quot;,6)==0 ){
+              skipOne = 1;
+            }
+            break;
+          case &#39;P&#39;:
+            switch( pFirst-&gt;nText ){
+              case 6:  skipOne = !strncmp(pFirst-&gt;zText,&quot;PUBLIC&quot;, 6);    break;
+              case 7:  skipOne = !strncmp(pFirst-&gt;zText,&quot;PRIVATE&quot;,7);    break;
+              case 9:  skipOne = !strncmp(pFirst-&gt;zText,&quot;PROTECTED&quot;,9);  break;
+              default: break;
+            }
+            break;
+          default:
+            break;
+        }
+        if( skipOne ){
+          pFirst = pFirst-&gt;pNext;
+          continue;
+        }
+        /* Fall thru to the next case */
+      case TT_Number:
+        if( needSpace ){
+          StringAppend(&amp;str,&quot; &quot;,1);
+        }
+        StringAppend(&amp;str,pFirst-&gt;zText,pFirst-&gt;nText);
+        needSpace = 1;
+        break;
+
+      default:
+        c = pFirst-&gt;zText[0];
+        if( needSpace &amp;&amp; (c==&#39;*&#39; || c==&#39;{&#39;) ){
+          StringAppend(&amp;str,&quot; &quot;,1);
+        }
+        StringAppend(&amp;str,pFirst-&gt;zText,pFirst-&gt;nText);
+        /* needSpace = pFirst-&gt;zText[0]==&#39;,&#39;; */
+        needSpace = 0;
+        break;
+    }
+    pFirst = pFirst-&gt;pNext;
+  }
+  if( zTerm &amp;&amp; *zTerm ){
+    StringAppend(&amp;str,zTerm,strlen(zTerm));
+  }
+  zReturn = StrDup(StringGet(&amp;str),0);
+  StringReset(&amp;str);
+  return zReturn;
+}
+
+/*
+** This routine is called when we see one of the keywords &quot;struct&quot;,
+** &quot;enum&quot;, &quot;union&quot; or &quot;class&quot;.  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&#39;t also a variable definition)
+** then set *pReset to &#39;;&#39;.  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-&gt;pNext==0 || pList-&gt;pNext-&gt;eType!=TT_Id ){
+    return 0;
+  }
+  pName = pList-&gt;pNext;
+
+  /* Catch the case of &quot;struct Foo;&quot; and skip it. */
+  if( pName-&gt;pNext &amp;&amp; pName-&gt;pNext-&gt;zText[0]==&#39;;&#39; ){
+    *pReset = &#39;;&#39;;
+    return 0;
+  }
+
+  for(pEnd=pName-&gt;pNext; pEnd &amp;&amp; pEnd-&gt;eType!=TT_Braces; pEnd=pEnd-&gt;pNext){
+    switch( pEnd-&gt;zText[0] ){
+      case &#39;(&#39;:
+      case &#39;)&#39;:
+      case &#39;*&#39;:
+      case &#39;[&#39;:
+      case &#39;=&#39;:
+      case &#39;;&#39;:
+        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-&gt;pNext==0 || pEnd-&gt;pNext-&gt;zText[0]==&#39;;&#39; ){
+    *pReset = &#39;;&#39;;
+    need_to_collapse = 0;
+  }else{
+    need_to_collapse = 1;
+  }
+
+  if( proto_static==0 &amp;&amp; (flags &amp; (PS_Local|PS_Export|PS_Interface))==0 ){
+    /* Ignore these objects unless they are explicitly declared as interface,
+    ** or unless the &quot;-local&quot; command line option was specified. */
+    *pReset = &#39;;&#39;;
+    return 0;
+  }
+
+#ifdef DEBUG
+  if( debugMask &amp; PARSER ){
+    printf(&quot;**** Found type: %.*s %.*s...\n&quot;,
+      pList-&gt;nText, pList-&gt;zText, pName-&gt;nText, pName-&gt;zText);
+    PrintTokens(pList,pEnd);
+    printf(&quot;;\n&quot;);
+  }
+#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-&gt;zText ){
+    case &#39;c&#39;:  type = TY_Class;        break;
+    case &#39;s&#39;:  type = TY_Structure;    break;
+    case &#39;e&#39;:  type = TY_Enumeration;  break;
+    case &#39;u&#39;:  type = TY_Union;        break;
+    default:   /* Can&#39;t Happen */      break;
+  }
+  if( type!=TY_Class ){
+    pDecl = 0;
+  }else{
+    pDecl = FindDecl(pName-&gt;zText, pName-&gt;nText);
+    if( pDecl &amp;&amp; (pDecl-&gt;flags &amp; type)!=type ) pDecl = 0;
+  }
+  if( pDecl==0 ){
+    pDecl = CreateDecl(pName-&gt;zText,pName-&gt;nText);
+  }
+  if( (flags &amp; PS_Static) || !(flags &amp; (PS_Interface|PS_Export)) ){
+    DeclSetProperty(pDecl,DP_Local);
+  }
+  DeclSetProperty(pDecl,type);
+
+  /* The object has a full declaration only if it is contained within
+  ** &quot;#if INTERFACE...#endif&quot; or &quot;#if EXPORT_INTERFACE...#endif&quot; or
+  ** &quot;#if LOCAL_INTERFACE...#endif&quot;.  Otherwise, we only give it a
+  ** forward declaration.
+  */
+  if( flags &amp; (PS_Local | PS_Export | PS_Interface)  ){
+    pDecl-&gt;zDecl = TokensToString(pList,pEnd,&quot;;\n&quot;,0,0);
+  }else{
+    pDecl-&gt;zDecl = 0;
+  }
+  pDecl-&gt;pComment = pList-&gt;pComment;
+  StringInit(&amp;str);
+  StringAppend(&amp;str,&quot;typedef &quot;,0);
+  StringAppend(&amp;str,pList-&gt;zText,pList-&gt;nText);
+  StringAppend(&amp;str,&quot; &quot;,0);
+  StringAppend(&amp;str,pName-&gt;zText,pName-&gt;nText);
+  StringAppend(&amp;str,&quot; &quot;,0);
+  StringAppend(&amp;str,pName-&gt;zText,pName-&gt;nText);
+  StringAppend(&amp;str,&quot;;\n&quot;,2);
+  pDecl-&gt;zFwd = StrDup(StringGet(&amp;str),0);
+  StringReset(&amp;str);
+  StringInit(&amp;str);
+  StringAppend(&amp;str,pList-&gt;zText,pList-&gt;nText);
+  StringAppend(&amp;str,&quot; &quot;,0);
+  StringAppend(&amp;str,pName-&gt;zText,pName-&gt;nText);
+  StringAppend(&amp;str,&quot;;\n&quot;,2);
+  pDecl-&gt;zFwdCpp = StrDup(StringGet(&amp;str),0);
+  StringReset(&amp;str);
+  if( flags &amp; PS_Export ){
+    DeclSetProperty(pDecl,DP_Export);
+  }else if( flags &amp; PS_Local ){
+    DeclSetProperty(pDecl,DP_Local);
+  }
+
+  /* Here&#39;s something weird.  ANSI-C doesn&#39;t allow a forward declaration
+  ** of an enumeration.  So we have to build the typedef into the
+  ** definition.
+  */
+  if( pDecl-&gt;zDecl &amp;&amp; DeclHasProperty(pDecl, TY_Enumeration) ){
+    StringInit(&amp;str);
+    StringAppend(&amp;str,pDecl-&gt;zDecl,0);
+    StringAppend(&amp;str,pDecl-&gt;zFwd,0);
+    SafeFree(pDecl-&gt;zDecl);
+    SafeFree(pDecl-&gt;zFwd);
+    pDecl-&gt;zFwd = 0;
+    pDecl-&gt;zDecl = StrDup(StringGet(&amp;str),0);
+    StringReset(&amp;str);
+  }
+
+  if( pName-&gt;pNext-&gt;zText[0]==&#39;:&#39; ){
+    DeclSetProperty(pDecl,DP_Cplusplus);
+  }
+  if( pName-&gt;nText==5 &amp;&amp; strncmp(pName-&gt;zText,&quot;class&quot;,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-&gt;pPrev;
+      pPrev-&gt;pNext = pEnd-&gt;pNext;
+      pEnd-&gt;pNext-&gt;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 &quot;[&quot;, or
+**
+**     2.  The first identifier that is followed by a &quot;(&quot; where the
+**         &quot;(&quot; is followed by another identifier, or
+**
+**     3.  The first identifier followed by &quot;::&quot;, or
+**
+**     4.  If none of the above, then the last identifier.
+**
+**   In all of the above, certain reserved words (like &quot;char&quot;) 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-&gt;pNext;
+  for(p=pFirst; p &amp;&amp; p!=pLast; p=p-&gt;pNext){
+    if( p-&gt;eType==TT_Id ){
+      static IdentTable sReserved;
+      static int isInit = 0;
+      static const char *aWords[] = { &quot;char&quot;, &quot;class&quot;,
+       &quot;const&quot;, &quot;double&quot;, &quot;enum&quot;, &quot;extern&quot;, &quot;EXPORT&quot;, &quot;ET_PROC&quot;,
+       &quot;float&quot;, &quot;int&quot;, &quot;long&quot;,
+       &quot;PRIVATE&quot;, &quot;PROTECTED&quot;, &quot;PUBLIC&quot;,
+       &quot;register&quot;, &quot;static&quot;, &quot;struct&quot;, &quot;sizeof&quot;, &quot;signed&quot;, &quot;typedef&quot;,
+       &quot;union&quot;, &quot;volatile&quot;, &quot;virtual&quot;, &quot;void&quot;, };
+
+      if( !isInit ){
+        int i;
+        for(i=0; i&lt;sizeof(aWords)/sizeof(aWords[0]); i++){
+          IdentTableInsert(&amp;sReserved,aWords[i],0);
+        }
+        isInit = 1;
+      }
+      if( !IdentTableTest(&amp;sReserved,p-&gt;zText,p-&gt;nText) ){
+        pName = p;
+      }
+    }else if( p==pFirst ){
+      continue;
+    }else if( (c=p-&gt;zText[0])==&#39;[&#39; &amp;&amp; pName ){
+      break;
+    }else if( c==&#39;(&#39; &amp;&amp; p-&gt;pNext &amp;&amp; p-&gt;pNext-&gt;eType==TT_Id &amp;&amp; pName ){
+      break;
+    }else if( c==&#39;:&#39; &amp;&amp; p-&gt;zText[1]==&#39;:&#39; &amp;&amp; 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-&gt;pPrev;
+  while( pFirst-&gt;zText[0]==&#39;P&#39; ){
+    int rc = 1;
+    switch( pFirst-&gt;nText ){
+      case 6:  rc = strncmp(pFirst-&gt;zText,&quot;PUBLIC&quot;,6); break;
+      case 7:  rc = strncmp(pFirst-&gt;zText,&quot;PRIVATE&quot;,7); break;
+      case 9:  rc = strncmp(pFirst-&gt;zText,&quot;PROTECTED&quot;,9); break;
+      default:  break;
+    }
+    if( rc ) break;
+    pFirst = pFirst-&gt;pNext;
+  }
+  pClass = FindDeclName(pFirst,pLast);
+  if( pClass==0 ){
+    fprintf(stderr,&quot;%s:%d: Unable to find the class name for this method\n&quot;,
+       zFilename, pFirst-&gt;nLine);
+    return 1;
+  }
+  pDecl = FindDecl(pClass-&gt;zText, pClass-&gt;nText);
+  if( pDecl==0 || (pDecl-&gt;flags &amp; TY_Class)!=TY_Class ){
+    pDecl = CreateDecl(pClass-&gt;zText, pClass-&gt;nText);
+    DeclSetProperty(pDecl, TY_Class);
+  }
+  StringInit(&amp;str);
+  if( pDecl-&gt;zExtra ){
+    StringAppend(&amp;str, pDecl-&gt;zExtra, 0);
+    SafeFree(pDecl-&gt;zExtra);
+    pDecl-&gt;zExtra = 0;
+  }
+  type = flags &amp; PS_PPP;
+  if( pDecl-&gt;extraType!=type ){
+    if( type &amp; PS_Public ){
+      StringAppend(&amp;str, &quot;public:\n&quot;, 0);
+      pDecl-&gt;extraType = PS_Public;
+    }else if( type &amp; PS_Protected ){
+      StringAppend(&amp;str, &quot;protected:\n&quot;, 0);
+      pDecl-&gt;extraType = PS_Protected;
+    }else if( type &amp; PS_Private ){
+      StringAppend(&amp;str, &quot;private:\n&quot;, 0);
+      pDecl-&gt;extraType = PS_Private;
+    }
+  }
+  StringAppend(&amp;str, &quot;  &quot;, 0);
+  zDecl = TokensToString(pFirst, pLast, &quot;;\n&quot;, pClass, 2);
+  StringAppend(&amp;str, zDecl, 0);
+  SafeFree(zDecl);
+  pDecl-&gt;zExtra = StrDup(StringGet(&amp;str), 0);
+  StringReset(&amp;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 &amp; PS_Method ){
+    if( flags &amp; PS_PPP ){
+      return ProcessMethodDef(pFirst, pLast, flags);
+    }else{
+      return 0;
+    }
+  }
+  if( (flags &amp; PS_Static)!=0 &amp;&amp; !proto_static ){
+    return 0;
+  }
+  pCode = pLast;
+  while( pLast &amp;&amp; pLast!=pFirst &amp;&amp; pLast-&gt;zText[0]!=&#39;)&#39; ){
+    pLast = pLast-&gt;pPrev;
+  }
+  if( pLast==0 || pLast==pFirst || pFirst-&gt;pNext==pLast ){
+    fprintf(stderr,&quot;%s:%d: Unrecognized syntax.\n&quot;,
+      zFilename, pFirst-&gt;nLine);
+    return 1;
+  }
+  if( flags &amp; (PS_Interface|PS_Export|PS_Local) ){
+    fprintf(stderr,&quot;%s:%d: Missing \&quot;inline\&quot; on function or procedure.\n&quot;,
+      zFilename, pFirst-&gt;nLine);
+    return 1;
+  }
+  pName = FindDeclName(pFirst,pLast);
+  if( pName==0 ){
+    fprintf(stderr,&quot;%s:%d: Malformed function or procedure definition.\n&quot;,
+      zFilename, pFirst-&gt;nLine);
+    return 1;
+  }
+
+  /*
+  ** At this point we&#39;ve isolated a procedure declaration between pFirst
+  ** and pLast with the name pName.
+  */
+#ifdef DEBUG
+  if( debugMask &amp; PARSER ){
+    printf(&quot;**** Found routine: %.*s on line %d...\n&quot;, pName-&gt;nText,
+       pName-&gt;zText, pFirst-&gt;nLine);
+    PrintTokens(pFirst,pLast);
+    printf(&quot;;\n&quot;);
+  }
+#endif
+  pDecl = CreateDecl(pName-&gt;zText,pName-&gt;nText);
+  pDecl-&gt;pComment = pFirst-&gt;pComment;
+  if( pCode &amp;&amp; pCode-&gt;eType==TT_Braces ){
+    pDecl-&gt;tokenCode = *pCode;
+  }
+  DeclSetProperty(pDecl,TY_Subroutine);
+  pDecl-&gt;zDecl = TokensToString(pFirst,pLast,&quot;;\n&quot;,0,0);
+  if( (flags &amp; (PS_Static|PS_Local2))!=0 ){
+    DeclSetProperty(pDecl,DP_Local);
+  }else if( (flags &amp; (PS_Export2))!=0 ){
+    DeclSetProperty(pDecl,DP_Export);
+  }
+
+  if( flags &amp; DP_Cplusplus ){
+    DeclSetProperty(pDecl,DP_Cplusplus);
+  }else{
+    DeclSetProperty(pDecl,DP_ExternCReqd);
+  }
+
+  return 0;
+}
+
+/*
+** This routine is called whenever we see the &quot;inline&quot; 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-&gt;pNext){
+    if( pEnd-&gt;zText[0]==&#39;{&#39; || pEnd-&gt;zText[0]==&#39;;&#39; ){
+      *pReset = pEnd-&gt;zText[0];
+      break;
+    }
+  }
+  if( pEnd==0 ){
+    *pReset = &#39;;&#39;;
+    fprintf(stderr,&quot;%s:%d: incomplete inline procedure definition\n&quot;,
+      zFilename, pFirst-&gt;nLine);
+    return 1;
+  }
+  pName = FindDeclName(pFirst,pEnd);
+  if( pName==0 ){
+    fprintf(stderr,&quot;%s:%d: malformed inline procedure definition\n&quot;,
+      zFilename, pFirst-&gt;nLine);
+    return 1;
+  }
+
+#ifdef DEBUG
+  if( debugMask &amp; PARSER ){
+    printf(&quot;**** Found inline routine: %.*s on line %d...\n&quot;,
+       pName-&gt;nText, pName-&gt;zText, pFirst-&gt;nLine);
+    PrintTokens(pFirst,pEnd);
+    printf(&quot;\n&quot;);
+  }
+#endif
+  pDecl = CreateDecl(pName-&gt;zText,pName-&gt;nText);
+  pDecl-&gt;pComment = pFirst-&gt;pComment;
+  DeclSetProperty(pDecl,TY_Subroutine);
+  pDecl-&gt;zDecl = TokensToString(pFirst,pEnd,&quot;;\n&quot;,0,0);
+  if( (flags &amp; (PS_Static|PS_Local|PS_Local2)) ){
+    DeclSetProperty(pDecl,DP_Local);
+  }else if( flags &amp; (PS_Export|PS_Export2) ){
+    DeclSetProperty(pDecl,DP_Export);
+  }
+
+  if( flags &amp; 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 &#39;;&#39; or
+** a &#39;=&#39;.  If it is &#39;=&#39;, then assume we have a variable definition.
+**
+** If pEnd is &#39;;&#39;, then the determination is more difficult.  We have
+** to search for an occurrence of an ID followed immediately by &#39;(&#39;.
+** If found, we have a prototype.  Otherwise we are dealing with a
+** variable definition.
+*/
+static int isVariableDef(Token *pFirst, Token *pEnd){
+  if( pEnd &amp;&amp; pEnd-&gt;zText[0]==&#39;=&#39; &amp;&amp;
+    (pEnd-&gt;pPrev-&gt;nText!=8 || strncmp(pEnd-&gt;pPrev-&gt;zText,&quot;operator&quot;,8)!=0)
+  ){
+    return 1;
+  }
+  while( pFirst &amp;&amp; pFirst!=pEnd &amp;&amp; pFirst-&gt;pNext &amp;&amp; pFirst-&gt;pNext!=pEnd ){
+    if( pFirst-&gt;eType==TT_Id &amp;&amp; pFirst-&gt;pNext-&gt;zText[0]==&#39;(&#39; ){
+      return 0;
+    }
+    pFirst = pFirst-&gt;pNext;
+  }
+  return 1;
+}
+
+/*
+** Return TRUE if pFirst is the first token of a static assert.
+*/
+static int isStaticAssert(Token *pFirst){
+  if( (pFirst-&gt;nText==13 &amp;&amp; strncmp(pFirst-&gt;zText, &quot;static_assert&quot;, 13)==0)
+   || (pFirst-&gt;nText==14 &amp;&amp; strncmp(pFirst-&gt;zText, &quot;_Static_assert&quot;, 14)==0)
+  ){
+    return 1;
+  }else{
+    return 0;
+  }
+}
+
+/*
+** This routine is called whenever we encounter a &quot;;&quot; or &quot;=&quot;.  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 &amp; PS_Typedef ){
+    if( (flags &amp; (PS_Export2|PS_Local2))!=0 ){
+      fprintf(stderr,&quot;%s:%d: \&quot;EXPORT\&quot; or \&quot;LOCAL\&quot; ignored before typedef.\n&quot;,
+        zFilename, pFirst-&gt;nLine);
+      nErr++;
+    }
+    if( (flags &amp; (PS_Interface|PS_Export|PS_Local|DP_Cplusplus))==0 ){
+      /* It is illegal to duplicate a typedef in C (but OK in C++).
+      ** So don&#39;t record typedefs that aren&#39;t within a C++ file or
+      ** within #if INTERFACE..#endif */
+      return nErr;
+    }
+    if( (flags &amp; (PS_Interface|PS_Export|PS_Local))==0 &amp;&amp; proto_static==0 ){
+      /* Ignore typedefs that are not with &quot;#if INTERFACE..#endif&quot; unless
+      ** the &quot;-local&quot; command line option is used. */
+      return nErr;
+    }
+    if( (flags &amp; (PS_Interface|PS_Export))==0 ){
+      /* typedefs are always local, unless within #if INTERFACE..#endif */
+      isLocal = 1;
+    }
+  }else if( flags &amp; (PS_Static|PS_Local2) ){
+    if( proto_static==0 &amp;&amp; (flags &amp; PS_Local2)==0 ){
+      /* Don&#39;t record static variables unless the &quot;-local&quot; command line
+      ** option was specified or the &quot;LOCAL&quot; keyword is used. */
+      return nErr;
+    }
+    while( pFirst!=0 &amp;&amp; pFirst-&gt;pNext!=pEnd &amp;&amp;
+       ((pFirst-&gt;nText==6 &amp;&amp; strncmp(pFirst-&gt;zText,&quot;static&quot;,6)==0)
+        || (pFirst-&gt;nText==5 &amp;&amp; strncmp(pFirst-&gt;zText,&quot;LOCAL&quot;,6)==0))
+    ){
+      /* Lose the initial &quot;static&quot; or local from local variables.
+      ** We&#39;ll prepend &quot;extern&quot; later. */
+      pFirst = pFirst-&gt;pNext;
+      isLocal = 1;
+    }
+    if( pFirst==0 || !isLocal ){
+      return nErr;
+    }
+  }else if( flags &amp; PS_Method ){
+    /* Methods are declared by their class.  Don&#39;t declare separately. */
+    return nErr;
+  }else if( isStaticAssert(pFirst) ){
+    return 0;
+  }
+  isVar =  (flags &amp; (PS_Typedef|PS_Method))==0 &amp;&amp; isVariableDef(pFirst,pEnd);
+  if( isVar &amp;&amp; (flags &amp; (PS_Interface|PS_Export|PS_Local))!=0
+  &amp;&amp; (flags &amp; PS_Extern)==0 ){
+    fprintf(stderr,&quot;%s:%d: Can&#39;t define a variable in this context\n&quot;,
+      zFilename, pFirst-&gt;nLine);
+    nErr++;
+  }
+  pName = FindDeclName(pFirst,pEnd-&gt;pPrev);
+  if( pName==0 ){
+    if( pFirst-&gt;nText==4 &amp;&amp; strncmp(pFirst-&gt;zText,&quot;enum&quot;,4)==0 ){
+      /* Ignore completely anonymous enums.  See documentation section 3.8.1. */
+      return nErr;
+    }else{
+      fprintf(stderr,&quot;%s:%d: Can&#39;t find a name for the object declared here.\n&quot;,
+        zFilename, pFirst-&gt;nLine);
+      return nErr+1;
+    }
+  }
+
+#ifdef DEBUG
+  if( debugMask &amp; PARSER ){
+    if( flags &amp; PS_Typedef ){
+      printf(&quot;**** Found typedef %.*s at line %d...\n&quot;,
+        pName-&gt;nText, pName-&gt;zText, pName-&gt;nLine);
+    }else if( isVar ){
+      printf(&quot;**** Found variable %.*s at line %d...\n&quot;,
+        pName-&gt;nText, pName-&gt;zText, pName-&gt;nLine);
+    }else{
+      printf(&quot;**** Found prototype %.*s at line %d...\n&quot;,
+        pName-&gt;nText, pName-&gt;zText, pName-&gt;nLine);
+    }
+    PrintTokens(pFirst,pEnd-&gt;pPrev);
+    printf(&quot;;\n&quot;);
+  }
+#endif
+
+  pDecl = CreateDecl(pName-&gt;zText,pName-&gt;nText);
+  if( (flags &amp; PS_Typedef) ){
+    DeclSetProperty(pDecl, TY_Typedef);
+  }else if( isVar ){
+    DeclSetProperty(pDecl,DP_ExternReqd | TY_Variable);
+    if( !(flags &amp; DP_Cplusplus) ){
+      DeclSetProperty(pDecl,DP_ExternCReqd);
+    }
+  }else{
+    DeclSetProperty(pDecl, TY_Subroutine);
+    if( !(flags &amp; DP_Cplusplus) ){
+      DeclSetProperty(pDecl,DP_ExternCReqd);
+    }
+  }
+  pDecl-&gt;pComment = pFirst-&gt;pComment;
+  pDecl-&gt;zDecl = TokensToString(pFirst,pEnd-&gt;pPrev,&quot;;\n&quot;,0,0);
+  if( isLocal || (flags &amp; (PS_Local|PS_Local2))!=0 ){
+    DeclSetProperty(pDecl,DP_Local);
+  }else if( flags &amp; (PS_Export|PS_Export2) ){
+    DeclSetProperty(pDecl,DP_Export);
+  }
+  if( flags &amp; 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 &quot;define&quot; or &quot;!&quot; */
+  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-&gt;zCondition = (char*)&amp;pIf[1];
+    if( zPrefix ){
+      sprintf(pIf-&gt;zCondition,&quot;%s(%.*s)&quot;,zPrefix,nText,zText);
+    }else{
+      sprintf(pIf-&gt;zCondition,&quot;%.*s&quot;,nText,zText);
+    }
+  }else{
+    pIf-&gt;zCondition = 0;
+  }
+  pIf-&gt;nLine = nLine;
+  pIf-&gt;flags = flags;
+  pIf-&gt;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 &quot;#if INTERFACE&quot; 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 = &amp;pToken-&gt;zText[1];
+  while( isspace(*zCmd) &amp;&amp; *zCmd!=&#39;\n&#39; ){
+    zCmd++;
+  }
+  if( !isalpha(*zCmd) ){
+    return 0;
+  }
+  nCmd = 1;
+  while( isalpha(zCmd[nCmd]) ){
+    nCmd++;
+  }
+
+  if( nCmd==5 &amp;&amp; strncmp(zCmd,&quot;endif&quot;,5)==0 ){
+    /*
+    ** Pop the if stack
+    */
+    pIf = ifStack;
+    if( pIf==0 ){
+      fprintf(stderr,&quot;%s:%d: extra &#39;#endif&#39;.\n&quot;,zFilename,pToken-&gt;nLine);
+      return 1;
+    }
+    ifStack = pIf-&gt;pNext;
+    SafeFree(pIf);
+  }else if( nCmd==6 &amp;&amp; strncmp(zCmd,&quot;define&quot;,6)==0 ){
+    /*
+    ** Record a #define if we are in PS_Interface or PS_Export
+    */
+    Decl *pDecl;
+    if( !(flags &amp; (PS_Local|PS_Interface|PS_Export)) ){ return 0; }
+    zArg = &amp;zCmd[6];
+    while( *zArg &amp;&amp; isspace(*zArg) &amp;&amp; *zArg!=&#39;\n&#39; ){
+      zArg++;
+    }
+    if( *zArg==0 || *zArg==&#39;\n&#39; ){ return 0; }
+    for(nArg=0; ISALNUM(zArg[nArg]); nArg++){}
+    if( nArg==0 ){ return 0; }
+    pDecl = CreateDecl(zArg,nArg);
+    pDecl-&gt;pComment = pToken-&gt;pComment;
+    DeclSetProperty(pDecl,TY_Macro);
+    pDecl-&gt;zDecl = SafeMalloc( pToken-&gt;nText + 2 );
+    sprintf(pDecl-&gt;zDecl,&quot;%.*s\n&quot;,pToken-&gt;nText,pToken-&gt;zText);
+    if( flags &amp; PS_Export ){
+      DeclSetProperty(pDecl,DP_Export);
+    }else if( flags &amp; PS_Local ){
+      DeclSetProperty(pDecl,DP_Local);
+    }
+  }else if( nCmd==7 &amp;&amp; strncmp(zCmd,&quot;include&quot;,7)==0 ){
+    /*
+    ** Record an #include if we are in PS_Interface or PS_Export
+    */
+    Include *pInclude;
+    char *zIf;
+
+    if( !(flags &amp; (PS_Interface|PS_Export)) ){ return 0; }
+    zArg = &amp;zCmd[7];
+    while( *zArg &amp;&amp; isspace(*zArg) ){ zArg++; }
+    for(nArg=0; !isspace(zArg[nArg]); nArg++){}
+    if( (zArg[0]==&#39;&quot;&#39; &amp;&amp; zArg[nArg-1]!=&#39;&quot;&#39;)
+      ||(zArg[0]==&#39;&lt;&#39; &amp;&amp; zArg[nArg-1]!=&#39;&gt;&#39;)
+    ){
+      fprintf(stderr,&quot;%s:%d: malformed #include statement.\n&quot;,
+        zFilename,pToken-&gt;nLine);
+      return 1;
+    }
+    zIf = GetIfString();
+    if( zIf ){
+      pInclude = SafeMalloc( sizeof(Include) + nArg*2 + strlen(zIf) + 10 );
+      pInclude-&gt;zFile = (char*)&amp;pInclude[1];
+      pInclude-&gt;zLabel = &amp;pInclude-&gt;zFile[nArg+1];
+      sprintf(pInclude-&gt;zFile,&quot;%.*s&quot;,nArg,zArg);
+      sprintf(pInclude-&gt;zLabel,&quot;%.*s:%s&quot;,nArg,zArg,zIf);
+      pInclude-&gt;zIf = &amp;pInclude-&gt;zLabel[nArg+1];
+      SafeFree(zIf);
+    }else{
+      pInclude = SafeMalloc( sizeof(Include) + nArg + 1 );
+      pInclude-&gt;zFile = (char*)&amp;pInclude[1];
+      sprintf(pInclude-&gt;zFile,&quot;%.*s&quot;,nArg,zArg);
+      pInclude-&gt;zIf = 0;
+      pInclude-&gt;zLabel = pInclude-&gt;zFile;
+    }
+    pInclude-&gt;pNext = includeList;
+    includeList = pInclude;
+  }else if( nCmd==2 &amp;&amp; strncmp(zCmd,&quot;if&quot;,2)==0 ){
+    /*
+    ** Push an #if.  Watch for the special cases of INTERFACE
+    ** and EXPORT_INTERFACE and LOCAL_INTERFACE
+    */
+    zArg = &amp;zCmd[2];
+    while( *zArg &amp;&amp; isspace(*zArg) &amp;&amp; *zArg!=&#39;\n&#39; ){
+      zArg++;
+    }
+    if( *zArg==0 || *zArg==&#39;\n&#39; ){ return 0; }
+    nArg = pToken-&gt;nText + (int)(pToken-&gt;zText - zArg);
+    if( nArg==9 &amp;&amp; strncmp(zArg,&quot;INTERFACE&quot;,9)==0 ){
+      PushIfMacro(0,0,0,pToken-&gt;nLine,PS_Interface);
+    }else if( nArg==16 &amp;&amp; strncmp(zArg,&quot;EXPORT_INTERFACE&quot;,16)==0 ){
+      PushIfMacro(0,0,0,pToken-&gt;nLine,PS_Export);
+    }else if( nArg==15 &amp;&amp; strncmp(zArg,&quot;LOCAL_INTERFACE&quot;,15)==0 ){
+      PushIfMacro(0,0,0,pToken-&gt;nLine,PS_Local);
+    }else if( nArg==15 &amp;&amp; strncmp(zArg,&quot;MAKEHEADERS_STOPLOCAL_INTERFACE&quot;,15)==0 ){
+      PushIfMacro(0,0,0,pToken-&gt;nLine,PS_Local);
+    }else{
+      PushIfMacro(0,zArg,nArg,pToken-&gt;nLine,0);
+    }
+  }else if( nCmd==5 &amp;&amp; strncmp(zCmd,&quot;ifdef&quot;,5)==0 ){
+    /*
+    ** Push an #ifdef.
+    */
+    zArg = &amp;zCmd[5];
+    while( *zArg &amp;&amp; isspace(*zArg) &amp;&amp; *zArg!=&#39;\n&#39; ){
+      zArg++;
+    }
+    if( *zArg==0 || *zArg==&#39;\n&#39; ){ return 0; }
+    nArg = pToken-&gt;nText + (int)(pToken-&gt;zText - zArg);
+    PushIfMacro(&quot;defined&quot;,zArg,nArg,pToken-&gt;nLine,0);
+  }else if( nCmd==6 &amp;&amp; strncmp(zCmd,&quot;ifndef&quot;,6)==0 ){
+    /*
+    ** Push an #ifndef.
+    */
+    zArg = &amp;zCmd[6];
+    while( *zArg &amp;&amp; isspace(*zArg) &amp;&amp; *zArg!=&#39;\n&#39; ){
+      zArg++;
+    }
+    if( *zArg==0 || *zArg==&#39;\n&#39; ){ return 0; }
+    nArg = pToken-&gt;nText + (int)(pToken-&gt;zText - zArg);
+    PushIfMacro(&quot;!defined&quot;,zArg,nArg,pToken-&gt;nLine,0);
+  }else if( nCmd==4 &amp;&amp; strncmp(zCmd,&quot;else&quot;,4)==0 ){
+    /*
+    ** Invert the #if on the top of the stack
+    */
+    if( ifStack==0 ){
+      fprintf(stderr,&quot;%s:%d: &#39;#else&#39; without an &#39;#if&#39;\n&quot;,zFilename,
+         pToken-&gt;nLine);
+      return 1;
+    }
+    pIf = ifStack;
+    if( pIf-&gt;zCondition ){
+      ifStack = ifStack-&gt;pNext;
+      PushIfMacro(&quot;!&quot;,pIf-&gt;zCondition,strlen(pIf-&gt;zCondition),pIf-&gt;nLine,0);
+      SafeFree(pIf);
+    }else{
+      pIf-&gt;flags = 0;
+    }
+  }else{
+    /*
+    ** This directive can be safely ignored
+    */
+    return 0;
+  }
+
+  /*
+  ** Recompute the preset flags
+  */
+  *pPresetFlags = 0;
+  for(pIf = ifStack; pIf; pIf=pIf-&gt;pNext){
+    *pPresetFlags |= pIf-&gt;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-&gt;eType ){
+    case TT_EOF:
+      goto end_of_loop;
+
+    case TT_Preprocessor:
+      nErr += ParsePreprocessor(pList,flags,&amp;presetFlags);
+      pStart = 0;
+      presetFlags |= initFlags;
+      flags = presetFlags;
+      break;
+
+    case TT_Other:
+      switch( pList-&gt;zText[0] ){
+      case &#39;;&#39;:
+        nErr += ProcessDecl(pStart,pList,flags);
+        pStart = 0;
+        flags = presetFlags;
+        break;
+
+      case &#39;=&#39;:
+        if( pList-&gt;pPrev-&gt;nText==8
+            &amp;&amp; strncmp(pList-&gt;pPrev-&gt;zText,&quot;operator&quot;,8)==0 ){
+          break;
+        }
+        nErr += ProcessDecl(pStart,pList,flags);
+        pStart = 0;
+        while( pList &amp;&amp; pList-&gt;zText[0]!=&#39;;&#39; ){
+          pList = pList-&gt;pNext;
+        }
+        if( pList==0 ) goto end_of_loop;
+        flags = presetFlags;
+        break;
+
+      case &#39;:&#39;:
+        if( pList-&gt;zText[1]==&#39;:&#39; ){
+          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-&gt;zText[0] ){
+       case &#39;c&#39;:
+         if( pList-&gt;nText==5 &amp;&amp; strncmp(pList-&gt;zText,&quot;class&quot;,5)==0 ){
+           nErr += ProcessTypeDecl(pList,flags,&amp;resetFlag);
+         }
+         break;
+
+       case &#39;E&#39;:
+         if( pList-&gt;nText==6 &amp;&amp; strncmp(pList-&gt;zText,&quot;EXPORT&quot;,6)==0 ){
+           flags |= PS_Export2;
+           /* pStart = 0; */
+         }
+         break;
+
+       case &#39;e&#39;:
+         if( pList-&gt;nText==4 &amp;&amp; strncmp(pList-&gt;zText,&quot;enum&quot;,4)==0 ){
+           if( pList-&gt;pNext &amp;&amp; pList-&gt;pNext-&gt;eType==TT_Braces ){
+             pList = pList-&gt;pNext;
+           }else{
+             nErr += ProcessTypeDecl(pList,flags,&amp;resetFlag);
+           }
+         }else if( pList-&gt;nText==6 &amp;&amp; strncmp(pList-&gt;zText,&quot;extern&quot;,6)==0 ){
+           pList = pList-&gt;pNext;
+           if( pList &amp;&amp; pList-&gt;nText==3 &amp;&amp; strncmp(pList-&gt;zText,&quot;\&quot;C\&quot;&quot;,3)==0 ){
+             pList = pList-&gt;pNext;
+             flags &amp;= ~DP_Cplusplus;
+           }else{
+             flags |= PS_Extern;
+           }
+           pStart = pList;
+         }
+         break;
+
+       case &#39;i&#39;:
+         if( pList-&gt;nText==6 &amp;&amp; strncmp(pList-&gt;zText,&quot;inline&quot;,6)==0
+          &amp;&amp; (flags &amp; PS_Static)==0
+         ){
+           nErr += ProcessInlineProc(pList,flags,&amp;resetFlag);
+         }
+         break;
+
+       case &#39;L&#39;:
+         if( pList-&gt;nText==5 &amp;&amp; strncmp(pList-&gt;zText,&quot;LOCAL&quot;,5)==0 ){
+           flags |= PS_Local2;
+           pStart = pList;
+         }
+         break;
+
+       case &#39;P&#39;:
+         if( pList-&gt;nText==6 &amp;&amp; strncmp(pList-&gt;zText, &quot;PUBLIC&quot;,6)==0 ){
+           flags |= PS_Public;
+           pStart = pList;
+         }else if( pList-&gt;nText==7 &amp;&amp; strncmp(pList-&gt;zText, &quot;PRIVATE&quot;,7)==0 ){
+           flags |= PS_Private;
+           pStart = pList;
+         }else if( pList-&gt;nText==9 &amp;&amp; strncmp(pList-&gt;zText,&quot;PROTECTED&quot;,9)==0 ){
+           flags |= PS_Protected;
+           pStart = pList;
+         }
+         break;
+
+       case &#39;s&#39;:
+         if( pList-&gt;nText==6 &amp;&amp; strncmp(pList-&gt;zText,&quot;struct&quot;,6)==0 ){
+           if( pList-&gt;pNext &amp;&amp; pList-&gt;pNext-&gt;eType==TT_Braces ){
+             pList = pList-&gt;pNext;
+           }else{
+             nErr += ProcessTypeDecl(pList,flags,&amp;resetFlag);
+           }
+         }else if( pList-&gt;nText==6 &amp;&amp; strncmp(pList-&gt;zText,&quot;static&quot;,6)==0 ){
+           flags |= PS_Static;
+         }
+         break;
+
+       case &#39;t&#39;:
+         if( pList-&gt;nText==7 &amp;&amp; strncmp(pList-&gt;zText,&quot;typedef&quot;,7)==0 ){
+           flags |= PS_Typedef;
+         }
+         break;
+
+       case &#39;u&#39;:
+         if( pList-&gt;nText==5 &amp;&amp; strncmp(pList-&gt;zText,&quot;union&quot;,5)==0 ){
+           if( pList-&gt;pNext &amp;&amp; pList-&gt;pNext-&gt;eType==TT_Braces ){
+             pList = pList-&gt;pNext;
+           }else{
+             nErr += ProcessTypeDecl(pList,flags,&amp;resetFlag);
+           }
+         }
+         break;
+
+       default:
+         break;
+       }
+       if( resetFlag!=0 ){
+         while( pList &amp;&amp; pList-&gt;zText[0]!=resetFlag ){
+           pList = pList-&gt;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-&gt;pNext;
+  }
+  end_of_loop:
+
+  /* Verify that all #ifs have a matching &quot;#endif&quot; */
+  while( ifStack ){
+    Ifmacro *pIf = ifStack;
+    ifStack = pIf-&gt;pNext;
+    fprintf(stderr,&quot;%s:%d: This &#39;#if&#39; has no &#39;#endif&#39;\n&quot;,zFilename,
+      pIf-&gt;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 &quot;}&quot; 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-&gt;zExtra==0 || pDecl-&gt;zDecl==0 ) return;
+  i = strlen(pDecl-&gt;zDecl) - 1;
+  while( i&gt;0 &amp;&amp; pDecl-&gt;zDecl[i]!=&#39;}&#39; ){ i--; }
+  StringInit(&amp;str);
+  StringAppend(&amp;str, pDecl-&gt;zDecl, i);
+  StringAppend(&amp;str, pDecl-&gt;zExtra, 0);
+  StringAppend(&amp;str, &amp;pDecl-&gt;zDecl[i], 0);
+  SafeFree(pDecl-&gt;zDecl);
+  SafeFree(pDecl-&gt;zExtra);
+  pDecl-&gt;zDecl = StrDup(StringGet(&amp;str), 0);
+  StringReset(&amp;str);
+  pDecl-&gt;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&#39;t
+** in the file zFilename so that it won&#39;t be printing in other files.
+*/
+static void ResetDeclFlags(char *zFilename){
+  Decl *pDecl;
+
+  for(pDecl = pDeclFirst; pDecl; pDecl = pDecl-&gt;pNext){
+    DeclClearProperty(pDecl,DP_Forward|DP_Declared);
+    if( DeclHasProperty(pDecl,DP_Local) &amp;&amp; pDecl-&gt;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 &quot;#if NEW&quot; 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-&gt;zIf==0 ) return;
+    StringAppend(pState-&gt;pStr,&quot;#endif\n&quot;,0);
+    pState-&gt;zIf = 0;
+  }else{
+    if( pState-&gt;zIf ){
+      if( strcmp(zIf,pState-&gt;zIf)==0 ) return;
+      StringAppend(pState-&gt;pStr,&quot;#endif\n&quot;,0);
+      pState-&gt;zIf = 0;
+    }
+    ScanText(zIf, pState);
+    if( pState-&gt;zIf!=0 ){
+      StringAppend(pState-&gt;pStr,&quot;#endif\n&quot;,0);
+    }
+    StringAppend(pState-&gt;pStr,&quot;#if &quot;,0);
+    StringAppend(pState-&gt;pStr,zIf,0);
+    StringAppend(pState-&gt;pStr,&quot;\n&quot;,0);
+    pState-&gt;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&#39;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-&gt;pNext ){
+      AddIncludes(pInclude-&gt;pNext,pState);
+    }
+    if( IdentTableInsert(pState-&gt;pTable,pInclude-&gt;zLabel,0) ){
+      ChangeIfContext(pInclude-&gt;zIf,pState);
+      StringAppend(pState-&gt;pStr,&quot;#include &quot;,0);
+      StringAppend(pState-&gt;pStr,pInclude-&gt;zFile,0);
+      StringAppend(pState-&gt;pStr,&quot;\n&quot;,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&#39;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(&quot;BEGIN %s of %s\n&quot;,needFullDecl?&quot;FULL&quot;:&quot;PROTOTYPE&quot;,pDecl-&gt;zName);*/
+  /*
+  ** For any object that has a forward declaration, go ahead and do the
+  ** forward declaration first.
+  */
+  isCpp = (pState-&gt;flags &amp; DP_Cplusplus) != 0;
+  for(p=pDecl; p; p=p-&gt;pSameName){
+    if( p-&gt;zFwd ){
+      if( !DeclHasProperty(p,DP_Forward) ){
+        DeclSetProperty(p,DP_Forward);
+        if( strncmp(p-&gt;zFwd,&quot;typedef&quot;,7)==0 ){
+          if( doneTypedef ) continue;
+          doneTypedef = 1;
+        }
+        ChangeIfContext(p-&gt;zIf,pState);
+        StringAppend(pState-&gt;pStr,isCpp ? p-&gt;zFwdCpp : p-&gt;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-&gt;pSameName){
+    if( !DeclHasProperty(p,flag) ) break;
+  }
+  if( p==0 ){
+    return;
+  }
+
+  /*
+  ** Make sure we have all necessary #includes
+  */
+  for(p=pDecl; p; p=p-&gt;pSameName){
+    AddIncludes(p-&gt;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&#39;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-&gt;pSameName){
+    if( !DeclHasProperty(p,DP_Declared)
+     &amp;&amp; (p-&gt;zFwd==0 || needFullDecl)
+     &amp;&amp; p-&gt;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-&gt;pSameName){
+    if( DeclHasProperty(p,DP_Flag) ){
+      if( p-&gt;zDecl[0]==&#39;#&#39; ){
+        ScanText(&amp;p-&gt;zDecl[1],pState);
+      }else{
+        InsertExtraDecl(p);
+        ScanText(p-&gt;zDecl,pState);
+      }
+    }
+  }
+
+  /*
+  ** Output the declarations.  Do this in two passes.  First
+  ** output everything that isn&#39;t a typedef.  Then go back and
+  ** get the typedefs by the same name.
+  */
+  for(p=pDecl; p; p=p-&gt;pSameName){
+    if( DeclHasProperty(p,DP_Flag) &amp;&amp; !DeclHasProperty(p,TY_Typedef) ){
+      if( DeclHasAnyProperty(p,TY_Enumeration) ){
+        if( doneTypedef ) continue;
+        doneTypedef = 1;
+      }
+      ChangeIfContext(p-&gt;zIf,pState);
+      if( !isCpp &amp;&amp; DeclHasAnyProperty(p,DP_ExternReqd) ){
+        StringAppend(pState-&gt;pStr,&quot;extern &quot;,0);
+      }else if( isCpp &amp;&amp; DeclHasProperty(p,DP_Cplusplus|DP_ExternReqd) ){
+        StringAppend(pState-&gt;pStr,&quot;extern &quot;,0);
+      }else if( isCpp &amp;&amp; DeclHasAnyProperty(p,DP_ExternCReqd|DP_ExternReqd) ){
+        StringAppend(pState-&gt;pStr,&quot;extern \&quot;C\&quot; &quot;,0);
+      }
+      InsertExtraDecl(p);
+      StringAppend(pState-&gt;pStr,p-&gt;zDecl,0);
+      if( !isCpp &amp;&amp; DeclHasProperty(p,DP_Cplusplus) ){
+        fprintf(stderr,
+          &quot;%s: C code ought not reference the C++ object \&quot;%s\&quot;\n&quot;,
+          pState-&gt;zFilename, p-&gt;zName);
+        pState-&gt;nErr++;
+      }
+      DeclClearProperty(p,DP_Flag);
+    }
+  }
+  for(p=pDecl; p &amp;&amp; !doneTypedef; p=p-&gt;pSameName){
+    if( DeclHasProperty(p,DP_Flag) ){
+      /* This has to be a typedef */
+      doneTypedef = 1;
+      ChangeIfContext(p-&gt;zIf,pState);
+      InsertExtraDecl(p);
+      StringAppend(pState-&gt;pStr,p-&gt;zDecl,0);
+    }
+  }
+}
+
+/*
+** This routine scans the input text given, and appends to the
+** string in pState-&gt;pStr the text of any declarations that must
+** occur before the text in zText.
+**
+** If an identifier in zText is immediately followed by &#39;*&#39;, then
+** only forward declarations are needed for that identifier.  If the
+** identifier name is not followed immediately by &#39;*&#39;, 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(&quot;BEGIN SCAN TEXT on %s\n&quot;, zText); */
+
+  sIn.z = zText;
+  sIn.i = 0;
+  sIn.nLine = 1;
+  while( sIn.z[sIn.i]!=0 ){
+    if( nextValid ){
+      sToken = sNext;
+      nextValid = 0;
+    }else{
+      GetNonspaceToken(&amp;sIn,&amp;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&#39;ve found an identifier that has a
+      ** declaration in the database.  Now see if we the full declaration
+      ** or just a forward declaration.
+      */
+      GetNonspaceToken(&amp;sIn,&amp;sNext);
+      if( sNext.zText[0]==&#39;*&#39; ){
+        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(&quot;END SCANTEXT\n&quot;); */
+}
+
+/*
+** 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-&gt;pNext){
+      if( DeclHasProperty(pDecl,DP_Forward)
+       &amp;&amp; !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-&gt;zHdr==0 || *pFile-&gt;zHdr==0 ) return 0;
+  sState.pStr = &amp;outStr;
+  StringInit(&amp;outStr);
+  StringAppend(&amp;outStr,zTopLine,nTopLine);
+  sState.pTable = &amp;includeTable;
+  memset(&amp;includeTable,0,sizeof(includeTable));
+  sState.zIf = 0;
+  sState.nErr = 0;
+  sState.zFilename = pFile-&gt;zSrc;
+  sState.flags = pFile-&gt;flags &amp; DP_Cplusplus;
+  ResetDeclFlags(nolocal_flag ? &quot;no&quot; : pFile-&gt;zSrc);
+  for(pId = pFile-&gt;idTable.pList; pId; pId=pId-&gt;pNext){
+    Decl *pDecl = FindDecl(pId-&gt;zName,0);
+    if( pDecl ){
+      DeclareObject(pDecl,&amp;sState,1);
+    }
+  }
+  CompleteForwardDeclarations(&amp;sState);
+  ChangeIfContext(0,&amp;sState);
+  nErr += sState.nErr;
+  zOldVersion = ReadFile(pFile-&gt;zHdr);
+  zNewVersion = StringGet(&amp;outStr);
+  if( report ) fprintf(report,&quot;%s: &quot;,pFile-&gt;zHdr);
+  if( zOldVersion==0 ){
+    if( report ) fprintf(report,&quot;updated\n&quot;);
+    if( WriteFile(pFile-&gt;zHdr,zNewVersion) ){
+      fprintf(stderr,&quot;%s: Can&#39;t write to file\n&quot;,pFile-&gt;zHdr);
+      nErr++;
+    }
+  }else if( strncmp(zOldVersion,zTopLine,nTopLine)!=0 ){
+    if( report ) fprintf(report,&quot;error!\n&quot;);
+    fprintf(stderr,
+       &quot;%s: Can&#39;t overwrite this file because it wasn&#39;t previously\n&quot;
+       &quot;%*s  generated by &#39;makeheaders&#39;.\n&quot;,
+       pFile-&gt;zHdr, (int)strlen(pFile-&gt;zHdr), &quot;&quot;);
+    nErr++;
+  }else if( strcmp(zOldVersion,zNewVersion)!=0 ){
+    if( report ) fprintf(report,&quot;updated\n&quot;);
+    if( WriteFile(pFile-&gt;zHdr,zNewVersion) ){
+      fprintf(stderr,&quot;%s: Can&#39;t write to file\n&quot;,pFile-&gt;zHdr);
+      nErr++;
+    }
+  }else if( report ){
+    fprintf(report,&quot;unchanged\n&quot;);
+  }
+  SafeFree(zOldVersion);
+  IdentTableReset(&amp;includeTable);
+  StringReset(&amp;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 = &amp;outStr;
+  StringInit(&amp;outStr);
+  /* StringAppend(&amp;outStr,zTopLine,nTopLine); */
+  sState.pTable = &amp;includeTable;
+  memset(&amp;includeTable,0,sizeof(includeTable));
+  sState.zIf = 0;
+  sState.nErr = 0;
+  sState.zFilename = &quot;(all)&quot;;
+  sState.flags = 0;
+  ResetDeclFlags(0);
+  for(pDecl=pDeclFirst; pDecl; pDecl=pDecl-&gt;pNext){
+    if( forExport==0 || DeclHasProperty(pDecl,DP_Export) ){
+      DeclareObject(pDecl,&amp;sState,1);
+    }
+  }
+  ChangeIfContext(0,&amp;sState);
+  printf(&quot;%s&quot;,StringGet(&amp;outStr));
+  IdentTableReset(&amp;includeTable);
+  StringReset(&amp;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&gt;0 &amp;&amp; (z[n-1]==&#39;\n&#39; || z[n-1]==&#39;\r&#39;) ){ n--; }
+  return n;
+}
+
+/*
+** Dump the entire declaration list for debugging purposes
+*/
+static void DumpDeclList(void){
+  Decl *pDecl;
+
+  for(pDecl = pDeclFirst; pDecl; pDecl=pDecl-&gt;pNext){
+    printf(&quot;**** %s from file %s ****\n&quot;,pDecl-&gt;zName,pDecl-&gt;zFile);
+    if( pDecl-&gt;zIf ){
+      printf(&quot;If: [%.*s]\n&quot;,ClipTrailingNewline(pDecl-&gt;zIf),pDecl-&gt;zIf);
+    }
+    if( pDecl-&gt;zFwd ){
+      printf(&quot;Decl: [%.*s]\n&quot;,ClipTrailingNewline(pDecl-&gt;zFwd),pDecl-&gt;zFwd);
+    }
+    if( pDecl-&gt;zDecl ){
+      InsertExtraDecl(pDecl);
+      printf(&quot;Def: [%.*s]\n&quot;,ClipTrailingNewline(pDecl-&gt;zDecl),pDecl-&gt;zDecl);
+    }
+    if( pDecl-&gt;flags ){
+      static struct {
+        int mask;
+        char *desc;
+      } flagSet[] = {
+        { TY_Class,       &quot;class&quot; },
+        { TY_Enumeration, &quot;enum&quot; },
+        { TY_Structure,   &quot;struct&quot; },
+        { TY_Union,       &quot;union&quot; },
+        { TY_Variable,    &quot;variable&quot; },
+        { TY_Subroutine,  &quot;function&quot; },
+        { TY_Typedef,     &quot;typedef&quot; },
+        { TY_Macro,       &quot;macro&quot; },
+        { DP_Export,      &quot;export&quot; },
+        { DP_Local,       &quot;local&quot; },
+        { DP_Cplusplus,   &quot;C++&quot; },
+      };
+      int i;
+      printf(&quot;flags:&quot;);
+      for(i=0; i&lt;sizeof(flagSet)/sizeof(flagSet[0]); i++){
+        if( flagSet[i].mask &amp; pDecl-&gt;flags ){
+          printf(&quot; %s&quot;, flagSet[i].desc);
+        }
+      }
+      printf(&quot;\n&quot;);
+    }
+    if( pDecl-&gt;pInclude ){
+      Include *p;
+      printf(&quot;includes:&quot;);
+      for(p=pDecl-&gt;pInclude; p; p=p-&gt;pNext){
+        printf(&quot; %s&quot;,p-&gt;zFile);
+      }
+      printf(&quot;\n&quot;);
+    }
+  }
+}
+#endif
+
+/*
+** When the &quot;-doc&quot; 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,       &#39;c&#39; },
+    { TY_Enumeration, &#39;e&#39; },
+    { TY_Structure,   &#39;s&#39; },
+    { TY_Union,       &#39;u&#39; },
+    { TY_Variable,    &#39;v&#39; },
+    { TY_Subroutine,  &#39;f&#39; },
+    { TY_Typedef,     &#39;t&#39; },
+    { TY_Macro,       &#39;m&#39; },
+    { DP_Export,      &#39;x&#39; },
+    { DP_Local,       &#39;l&#39; },
+    { DP_Cplusplus,   &#39;+&#39; },
+  };
+
+  for(pDecl = pDeclFirst; pDecl; pDecl=pDecl-&gt;pNext){
+    int i;
+    int nLabel = 0;
+    char *zDecl;
+    char zLabel[50];
+    for(i=0; i&lt;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-&gt;zDecl;
+    if( zDecl==0 ) zDecl = pDecl-&gt;zFwd;
+    printf(&quot;%s %s %s %p %d %d %d %d %d\n&quot;,
+       pDecl-&gt;zName,
+       zLabel,
+       pDecl-&gt;zFile,
+       pDecl-&gt;pComment,
+       pDecl-&gt;pComment ? pDecl-&gt;pComment-&gt;nText+1 : 0,
+       pDecl-&gt;zIf ? (int)strlen(pDecl-&gt;zIf)+1 : 0,
+       zDecl ? (int)strlen(zDecl) : 0,
+       pDecl-&gt;pComment ? pDecl-&gt;pComment-&gt;nLine : 0,
+       pDecl-&gt;tokenCode.nText ? pDecl-&gt;tokenCode.nText+1 : 0
+    );
+    if( pDecl-&gt;pComment ){
+      printf(&quot;%.*s\n&quot;,pDecl-&gt;pComment-&gt;nText, pDecl-&gt;pComment-&gt;zText);
+    }
+    if( pDecl-&gt;zIf ){
+      printf(&quot;%s\n&quot;,pDecl-&gt;zIf);
+    }
+    if( zDecl ){
+      printf(&quot;%s&quot;,zDecl);
+    }
+    if( pDecl-&gt;tokenCode.nText ){
+      printf(&quot;%.*s\n&quot;,pDecl-&gt;tokenCode.nText, pDecl-&gt;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!=&#39;/&#39; || zFile[1]!=&#39;*&#39; ) return;
+  for(i=2; zFile[i] &amp;&amp; (zFile[i-1]!=&#39;/&#39; || zFile[i-2]!=&#39;*&#39;); i++){}
+  if( zFile[i]==0 ) return;
+  printf(&quot;%s M %s %d %d 0 0 0 0\n%.*s\n&quot;,
+    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 &#39;:&#39; or the whole file if no &#39;:&#39; is seen.
+  **
+  ** Except, on windows, ignore any &#39;:&#39; that occurs as the second character
+  ** since it might be part of the drive specifier.  So really, the &quot;:&#39; 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] &amp;&amp; zArg[nSrc]!=&#39;:&#39;; nSrc++){}
+  pFile = SafeMalloc( sizeof(InFile) );
+  memset(pFile,0,sizeof(InFile));
+  pFile-&gt;zSrc = StrDup(zSrc,nSrc);
+
+  /* Figure out if we are dealing with C or C++ code.  Assume any
+  ** file with &quot;.c&quot; or &quot;.h&quot; is C code and all else is C++.
+  */
+  if( nSrc&gt;2 &amp;&amp; zSrc[nSrc-2]==&#39;.&#39; &amp;&amp; (zSrc[nSrc-1]==&#39;c&#39; || zSrc[nSrc-1]==&#39;h&#39;)){
+    pFile-&gt;flags &amp;= ~DP_Cplusplus;
+  }else{
+    pFile-&gt;flags |= DP_Cplusplus;
+  }
+
+  /*
+  ** If a separate header file is specified, use it
+  */
+  if( zSrc[nSrc]==&#39;:&#39; ){
+    int nHdr;
+    char *zHdr;
+    zHdr = &amp;zSrc[nSrc+1];
+    for(nHdr=0; zHdr[nHdr]; nHdr++){}
+    pFile-&gt;zHdr = StrDup(zHdr,nHdr);
+  }
+
+  /* Look for any &#39;c&#39; or &#39;C&#39; in the suffix of the file name and change
+  ** that character to &#39;h&#39; or &#39;H&#39; respectively.  If no &#39;c&#39; or &#39;C&#39; is found,
+  ** then assume we are dealing with a header.
+  */
+  else{
+    int foundC = 0;
+    pFile-&gt;zHdr = StrDup(zSrc,nSrc);
+    for(i = nSrc-1; i&gt;0 &amp;&amp; pFile-&gt;zHdr[i]!=&#39;.&#39;; i--){
+      if( pFile-&gt;zHdr[i]==&#39;c&#39; ){
+        foundC = 1;
+        pFile-&gt;zHdr[i] = &#39;h&#39;;
+      }else if( pFile-&gt;zHdr[i]==&#39;C&#39; ){
+        foundC = 1;
+        pFile-&gt;zHdr[i] = &#39;H&#39;;
+      }
+    }
+    if( !foundC ){
+      SafeFree(pFile-&gt;zHdr);
+      pFile-&gt;zHdr = 0;
+    }
+  }
+
+  /*
+  ** If pFile-&gt;zSrc contains no &#39;c&#39; or &#39;C&#39; in its extension, it
+  ** must be a header file.   In that case, we need to set the
+  ** PS_Interface flag.
+  */
+  pFile-&gt;flags |= PS_Interface;
+  for(i=nSrc-1; i&gt;0 &amp;&amp; zSrc[i]!=&#39;.&#39;; i--){
+    if( zSrc[i]==&#39;c&#39; || zSrc[i]==&#39;C&#39; ){
+      pFile-&gt;flags &amp;= ~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 &quot;-f FILENAME&quot; 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 &quot;FILENAME&quot; argument
+** in the original argument list.
+**
+** This first parameter to this routine is the index of the &quot;-f&quot;
+** 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 &#39;#&#39; 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,&quot;r&quot;);
+  if( in==0 ){
+    fprintf(stderr,&quot;Can&#39;t open input file \&quot;%s\&quot;\n&quot;,zFile);
+    exit(1);
+  }
+  c = &#39; &#39;;
+  while( c!=EOF ){
+    while( c!=EOF &amp;&amp; isspace(c) ){
+      if( c==&#39;\n&#39; ){
+        startOfLine = 1;
+      }
+      c = getc(in);
+      if( startOfLine &amp;&amp; c==&#39;#&#39; ){
+        while( c!=EOF &amp;&amp; c!=&#39;\n&#39; ){
+          c = getc(in);
+        }
+      }
+    }
+    n = 0;
+    while( c!=EOF &amp;&amp; !isspace(c) ){
+      if( n&lt;sizeof(zBuf)-1 ){ zBuf[n++] = c; }
+      startOfLine = 0;
+      c = getc(in);
+    }
+    zBuf[n] = 0;
+    if( n&gt;0 ){
+      nNew++;
+      if( nNew + argc &gt; 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&lt;=index; i++){
+    zNew[i] = argv[i];
+  }
+  for(i=nNew + index + 1; i&lt;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&#39;t
+** locate the file (because, for example, it doesn&#39;t exist), then
+** return 0.
+*/
+static unsigned int ModTime(const char *zFilename){
+  unsigned int mTime = 0;
+  struct stat sStat;
+  if( stat(zFilename,&amp;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,&quot;%s: Illegal argument \&quot;%s\&quot;\n&quot;,argv0,argvN);
+  fprintf(stderr,&quot;Usage: %s [options] filename...\n&quot;
+    &quot;Options:\n&quot;
+    &quot;  -h          Generate a single .h to standard output.\n&quot;
+    &quot;  -H          Like -h, but only output EXPORT declarations.\n&quot;
+    &quot;  -v          (verbose) Write status information to the screen.\n&quot;
+    &quot;  -doc        Generate no header files.  Instead, output information\n&quot;
+    &quot;              that can be used by an automatic program documentation\n&quot;
+    &quot;              and cross-reference generator.\n&quot;
+    &quot;  -local      Generate prototypes for \&quot;static\&quot; functions and\n&quot;
+    &quot;              procedures.\n&quot;
+    &quot;  -f FILE     Read additional command-line arguments from the file named\n&quot;
+    &quot;              \&quot;FILE\&quot;.\n&quot;
+#ifdef DEBUG
+    &quot;  -! MASK     Set the debugging mask to the number \&quot;MASK\&quot;.\n&quot;
+#endif
+    &quot;  --          Treat all subsequent comment-line parameters as filenames,\n&quot;
+    &quot;              even if they begin with \&quot;-\&quot;.\n&quot;,
+    argv0
+  );
+}
+
+/*
+** The following text contains a few simple #defines that we want
+** to be available to every file.
+*/
+static const char zInit[] =
+  &quot;#define INTERFACE 0\n&quot;
+  &quot;#define EXPORT_INTERFACE 0\n&quot;
+  &quot;#define LOCAL_INTERFACE 0\n&quot;
+  &quot;#define EXPORT\n&quot;
+  &quot;#define LOCAL static\n&quot;
+  &quot;#define PUBLIC\n&quot;
+  &quot;#define PRIVATE\n&quot;
+  &quot;#define PROTECTED\n&quot;
+;
+
+#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&lt;argc; i++){
+    if( argv[i][0]==&#39;-&#39; &amp;&amp; !noMoreFlags ){
+      switch( argv[i][1] ){
+        case &#39;h&#39;:   h_flag = 1;   break;
+        case &#39;H&#39;:   H_flag = 1;   break;
+        case &#39;v&#39;:   v_flag = 1;   break;
+        case &#39;d&#39;:   doc_flag = 1; proto_static = 1; break;
+        case &#39;l&#39;:   proto_static = 1; break;
+        case &#39;f&#39;:   AddParameters(i, &amp;argc, &amp;argv); break;
+        case &#39;-&#39;:   noMoreFlags = 1;   break;
+#ifdef DEBUG
+        case &#39;!&#39;:   i++;  debugMask = strtol(argv[i],0,0); break;
+#endif
+        default:    Usage(argv[0],argv[i]); return 1;
+      }
+    }else{
+      pFile = CreateInFile(argv[i],&amp;nErr);
+      if( pFile ){
+        if( pFileList ){
+          pTail-&gt;pNext = pFile;
+          pTail = pFile;
+        }else{
+          pFileList = pTail = pFile;
+        }
+      }
+    }
+  }
+  if( h_flag &amp;&amp; H_flag ){
+    h_flag = 0;
+  }
+  if( v_flag ){
+    report = (h_flag || H_flag) ? stderr : stdout;
+  }else{
+    report = 0;
+  }
+  if( nErr&gt;0 ){
+    return nErr;
+  }
+  for(pFile=pFileList; pFile; pFile=pFile-&gt;pNext){
+    char *zFile;
+
+    zFilename = pFile-&gt;zSrc;
+    if( zFilename==0 ) continue;
+    zFile = ReadFile(zFilename);
+    if( zFile==0 ){
+      fprintf(stderr,&quot;Can&#39;t read input file \&quot;%s\&quot;\n&quot;,zFilename);
+      nErr++;
+      continue;
+    }
+    if( strncmp(zFile,zTopLine,nTopLine)==0 ){
+      pFile-&gt;zSrc = 0;
+    }else{
+      if( report ) fprintf(report,&quot;Reading %s...\n&quot;,zFilename);
+      pList = TokenizeFile(zFile,&amp;pFile-&gt;idTable);
+      if( pList ){
+        nErr += ParseFile(pList,pFile-&gt;flags);
+        FreeTokenList(pList);
+      }else if( zFile[0]==0 ){
+        fprintf(stderr,&quot;Input file \&quot;%s\&quot; is empty.\n&quot;, zFilename);
+        nErr++;
+      }else{
+        fprintf(stderr,&quot;Errors while processing \&quot;%s\&quot;\n&quot;, zFilename);
+        nErr++;
+      }
+    }
+    if( !doc_flag ) SafeFree(zFile);
+    if( doc_flag ) PrintModuleRecord(zFile,zFilename);
+  }
+  if( nErr&gt;0 ){
+    return nErr;
+  }
+#ifdef DEBUG
+  if( debugMask &amp; DECL_DUMP ){
+    DumpDeclList();
+    return nErr;
+  }
+#endif
+  if( doc_flag ){
+    DocumentationDump();
+    return nErr;
+  }
+  zFilename = &quot;--internal--&quot;;
+  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-&gt;pNext){
+      if( pFile-&gt;zSrc==0 ) continue;
+      nErr += MakeHeader(pFile,report,0);
+    }
+  }
+  return nErr;
+}
+#endif
+
+</pre>
+</blockquote>
+</div>
+<div class="footer">\r
+This page was generated in about\r
+0.007s by\r
+Fossil 2.9 [6f60cb3881] 2019-02-27 14:34:30\r
+</div>\r
+<script nonce="e2f4709a3deeafd8212401c84e2a082e1fbd3068470984f2">\r
+/*\r
+** Copyright © 2018 Warren Young\r
+**\r
+** This program is free software; you can redistribute it and/or\r
+** modify it under the terms of the Simplified BSD License (also\r
+** known as the "2-Clause License" or "FreeBSD License".)\r
+**\r
+** This program is distributed in the hope that it will be useful,\r
+** but without any warranty; without even the implied warranty of\r
+** merchantability or fitness for a particular purpose.\r
+**\r
+** Contact: wyoung on the Fossil forum, https://fossil-scm.org/forum/\r
+**\r
+*******************************************************************************\r
+**\r
+** This file contains the JS code specific to the Fossil default skin.\r
+** Currently, the only thing this does is handle clicks on its hamburger\r
+** menu button.\r
+*/\r
+(function() {\r
+  var hbButton = document.getElementById("hbbtn");\r
+  if (!hbButton) return;   // no hamburger button\r
+  if (!document.addEventListener) {\r
+    // Turn the button into a link to the sitemap for incompatible browsers.\r
+    hbButton.href = "/fossil/sitemap";\r
+    return;\r
+  }\r
+  var panel = document.getElementById("hbdrop");\r
+  if (!panel) return;   // site admin might've nuked it\r
+  if (!panel.style) return;  // shouldn't happen, but be sure\r
+  var panelBorder = panel.style.border;\r
+  var panelInitialized = false;   // reset if browser window is resized\r
+  var panelResetBorderTimerID = 0;   // used to cancel post-animation tasks\r
+\r
+  // Disable animation if this browser doesn't support CSS transitions.\r
+  //\r
+  // We need this ugly calling form for old browsers that don't allow\r
+  // panel.style.hasOwnProperty('transition'); catering to old browsers\r
+  // is the whole point here.\r
+  var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");\r
+\r
+  // The duration of the animation can be overridden from the default skin\r
+  // header.txt by setting the "data-anim-ms" attribute of the panel.\r
+  var animMS = panel.getAttribute("data-anim-ms");\r
+  if (animMS) {           // not null or empty string, parse it\r
+    animMS = parseInt(animMS);\r
+    if (isNaN(animMS) || animMS == 0)\r
+      animate = false;    // disable animation if non-numeric or zero\r
+    else if (animMS < 0)\r
+      animMS = 400;       // set default animation duration if negative\r
+  }\r
+  else                    // attribute is null or empty string, use default\r
+    animMS = 400;\r
+\r
+  // Calculate panel height despite its being hidden at call time.\r
+  // Based on https://stackoverflow.com/a/29047447/142454\r
+  var panelHeight;  // computed on first panel display\r
+  function calculatePanelHeight() {\r
+\r
+    // Clear the max-height CSS property in case the panel size is recalculated\r
+    // after the browser window was resized.\r
+    panel.style.maxHeight = '';\r
+\r
+    // Get initial panel styles so we can restore them below.\r
+    var es   = window.getComputedStyle(panel),\r
+        edis = es.display,\r
+        epos = es.position,\r
+        evis = es.visibility;\r
+\r
+    // Restyle the panel so we can measure its height while invisible.\r
+    panel.style.visibility = 'hidden';\r
+    panel.style.position   = 'absolute';\r
+    panel.style.display    = 'block';\r
+    panelHeight = panel.offsetHeight + 'px';\r
+\r
+    // Revert styles now that job is done.\r
+    panel.style.display    = edis;\r
+    panel.style.position   = epos;\r
+    panel.style.visibility = evis;\r
+  }\r
+\r
+  // Show the panel by changing the panel height, which kicks off the\r
+  // slide-open/closed transition set up in the XHR onload handler.\r
+  //\r
+  // Schedule the change for a near-future time in case this is the\r
+  // first call, where the div was initially invisible.  If we were\r
+  // to change the panel's visibility and height at the same time\r
+  // instead, that would prevent the browser from seeing the height\r
+  // change as a state transition, so it'd skip the CSS transition:\r
+  //\r
+  // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples\r
+  function showPanel() {\r
+    // Cancel the timer to remove the panel border after the closing animation,\r
+    // otherwise double-clicking the hamburger button with the panel opened will\r
+    // remove the borders from the (closed and immediately reopened) panel.\r
+    if (panelResetBorderTimerID) {\r
+      clearTimeout(panelResetBorderTimerID);\r
+      panelResetBorderTimerID = 0;\r
+    }\r
+    if (animate) {\r
+      if (!panelInitialized) {\r
+        panelInitialized = true;\r
+        // Set up a CSS transition to animate the panel open and\r
+        // closed.  Only needs to be done once per page load.\r
+        // Based on https://stackoverflow.com/a/29047447/142454\r
+        calculatePanelHeight();\r
+        panel.style.transition = 'max-height ' + animMS +\r
+            'ms ease-in-out';\r
+        panel.style.overflowY  = 'hidden';\r
+        panel.style.maxHeight  = '0';\r
+      }\r
+      setTimeout(function() {\r
+        panel.style.maxHeight = panelHeight;\r
+        panel.style.border    = panelBorder;\r
+      }, 40);   // 25ms is insufficient with Firefox 62\r
+    }\r
+    panel.style.display = 'block';\r
+    document.addEventListener('keydown',panelKeydown,/* useCapture == */true);\r
+    document.addEventListener('click',panelClick,false);\r
+  }\r
+\r
+  var panelKeydown = function(event) {\r
+    var key = event.which || event.keyCode;\r
+    if (key == 27) {\r
+      event.stopPropagation();   // ignore other keydown handlers\r
+      panelToggle(true);\r
+    }\r
+  };\r
+\r
+  var panelClick = function(event) {\r
+    if (!panel.contains(event.target)) {\r
+      // Call event.preventDefault() to have clicks outside the opened panel\r
+      // just close the panel, and swallow clicks on links or form elements.\r
+      //event.preventDefault();\r
+      panelToggle(true);\r
+    }\r
+  };\r
+\r
+  // Return true if the panel is showing.\r
+  function panelShowing() {\r
+    if (animate) {\r
+      return panel.style.maxHeight == panelHeight;\r
+    }\r
+    else {\r
+      return panel.style.display == 'block';\r
+    }\r
+  }\r
+\r
+  // Check if the specified HTML element has any child elements. Note that plain\r
+  // text nodes, comments, and any spaces (presentational or not) are ignored.\r
+  function hasChildren(element) {\r
+    var childElement = element.firstChild;\r
+    while (childElement) {\r
+      if (childElement.nodeType == 1) // Node.ELEMENT_NODE == 1\r
+        return true;\r
+      childElement = childElement.nextSibling;\r
+    }\r
+    return false;\r
+  }\r
+\r
+  // Reset the state of the panel to uninitialized if the browser window is\r
+  // resized, so the dimensions are recalculated the next time it's opened.\r
+  window.addEventListener('resize',function(event) {\r
+    panelInitialized = false;\r
+  },false);\r
+\r
+  // Click handler for the hamburger button.\r
+  hbButton.addEventListener('click',function(event) {\r
+    // Break the event handler chain, or the handler for document → click\r
+    // (about to be installed) may already be triggered by the current event.\r
+    event.stopPropagation();\r
+    event.preventDefault();  // prevent browser from acting on <a> click\r
+    panelToggle(false);\r
+  },false);\r
+\r
+  function panelToggle(suppressAnimation) {\r
+    if (panelShowing()) {\r
+      document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);\r
+      document.removeEventListener('click',panelClick,false);\r
+      // Transition back to hidden state.\r
+      if (animate) {\r
+        if (suppressAnimation) {\r
+          var transition = panel.style.transition;\r
+          panel.style.transition = '';\r
+          panel.style.maxHeight = '0';\r
+          panel.style.border = 'none';\r
+          setTimeout(function() {\r
+            // Make sure CSS transition won't take effect now, so restore it\r
+            // asynchronously. Outer variable 'transition' still valid here.\r
+            panel.style.transition = transition;\r
+          }, 40);   // 25ms is insufficient with Firefox 62\r
+        }\r
+        else {\r
+          panel.style.maxHeight = '0';\r
+          panelResetBorderTimerID = setTimeout(function() {\r
+            // Browsers show a 1px high border line when maxHeight == 0,\r
+            // our "hidden" state, so hide the borders in that state, too.\r
+            panel.style.border = 'none';\r
+            panelResetBorderTimerID = 0;   // clear ID of completed timer\r
+          }, animMS);\r
+        }\r
+      }\r
+      else {\r
+        panel.style.display = 'none';\r
+      }\r
+    }\r
+    else {\r
+      if (!hasChildren(panel)) {\r
+        // Only get the sitemap once per page load: it isn't likely to\r
+        // change on us.\r
+        var xhr = new XMLHttpRequest();\r
+        xhr.onload = function() {\r
+          var doc = xhr.responseXML;\r
+          if (doc) {\r
+            var sm = doc.querySelector("ul#sitemap");\r
+            if (sm && xhr.status == 200) {\r
+              // Got sitemap.  Insert it into the drop-down panel.\r
+              panel.innerHTML = sm.outerHTML;\r
+              // Display the panel\r
+              showPanel();\r
+            }\r
+          }\r
+          // else, can't parse response as HTML or XML\r
+        }\r
+        xhr.open("GET", "/fossil/sitemap?popup");   // note the TH1 substitution!\r
+        xhr.responseType = "document";\r
+        xhr.send();\r
+      }\r
+      else {\r
+        showPanel();   // just show what we built above\r
+      }\r
+    }\r
+  }\r
+})();\r
+\r
+</script>\r
+<script id='href-data' type='application/json'>{"delay":10,"mouseover":0}</script>
+<script nonce="e2f4709a3deeafd8212401c84e2a082e1fbd3068470984f2">
+function setAllHrefs(){
+var anchors = document.getElementsByTagName("a");
+for(var i=0; i<anchors.length; i++){
+var j = anchors[i];
+if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
+}
+var forms = document.getElementsByTagName("form");
+for(var i=0; i<forms.length; i++){
+var j = forms[i];
+if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
+}
+}
+function antiRobotDefense(){
+var x = document.getElementById("href-data");
+var jx = x.textContent || x.innerText;
+var g = JSON.parse(jx);
+var isOperaMini =
+Object.prototype.toString.call(window.operamini)==="[object OperaMini]";
+if(g.mouseover && !isOperaMini){
+document.getElementByTagName("body")[0].onmousemove=function(){
+setTimeout(setAllHrefs, g.delay);
+}
+}else{
+setTimeout(setAllHrefs, g.delay);
+}
+}
+antiRobotDefense()
+</script>
+</body>
+</html>
diff --git a/try/mh_main_prob/command1.c b/try/mh_main_prob/command1.c
new file mode 100644 (file)
index 0000000..637df55
--- /dev/null
@@ -0,0 +1,6 @@
+#include "command1.h"
+#include <stdio.h>
+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 (file)
index 0000000..5d8c612
--- /dev/null
@@ -0,0 +1,6 @@
+#include "command2.h"
+#include <stdio.h>
+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 (file)
index 0000000..67625f4
--- /dev/null
@@ -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 (file)
index 0000000..c5511fe
--- /dev/null
@@ -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 <stdio.h>
+int main(){
+  printf("command1 %d\n", f());
+  return 0;
+}
+> cat command2.c
+#include "command2.h"
+#include <stdio.h>
+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 (file)
index 0000000..77ec819
--- /dev/null
@@ -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 <stdio.h>
+static int main(){
+  printf("command1 %d\n", f());
+  return 0;
+}
+> cat command2.c
+#include "command2.h"
+#include <stdio.h>
+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 (file)
index 0000000..b3a00b7
--- /dev/null
@@ -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 <stdio.h>
+int main(int argc, char **argv){
+  printf("command1 %d\n", f());
+  return 0;
+}
+> cat command1.h
+/* \aThis 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 <stdio.h>
+int main(int argc, char **argv){
+  printf("command2 %d\n", f() + argc);
+  return 0;
+}
+> gcc -o command1 command1.c just_fun.c
+>  .. worked