"Q", "qu", "qUi" (case insensitive and normalized to lower case), and "quit" will all exit the interpreter, for example. Generates its own menu, super light, and highly portable. Only depends on libconfig, and even that can be removed with little effort. See the "newcmds" markdown file for info on customizing and adding new commands.
-Super simple arbitrary length, "first match", command interpeter for embedded projects in C11. I'm talking SIMPLE configuration, just try it. I'd be surprised if there's a easier to use parser than this one, unless it can be configured without re-compiling.
+Have been getting a lot of positive reviews; as I said its VERY flexible and parses a treat with very little code. Needs more testing, but so far
+I'm seeing that the first match action on this is great; say you have a command "get door". If both get and door are registered as a command and a parameter "g d" works fine, as long as they appear first in the command/parameter list. If not then again, next match. If "gummy door" is next in the list it will pick that up. Try it!
-"Q", "qu", "qUi" (case insensitive and normalized to lower case), and "quit" will all exit the interpreter, for example. Generates its own menu, super light, and highly portable. Only depends on libconfig, and even that can be removed with little effort. See the "newcmds" markdown file for info on customizing and adding new commands.
-
-Have been getting a lot of positive reviews; as I said its VERY flexible and parses a treat with very little code. Needs more testing, but so far I'm seeing that the first match action on this is great; say you have a command "get door". If both get and door are registered as a command and a parameter "g d" works fine, as long as they appear first in the command/parameter list. If not then again, next match. If "gummy door" is next in the list it will pick that up. Try it!
-
-(10/17/2024) Finally removed the dependancy on libconfig.
-(10/20/2024) Code now handles back spaces on Windows.
\ No newline at end of file
+(10/17/2024) Finally removed the dependancy on libconfig.
+(10/20/2024) Code now handles back spaces on Windows.
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..f4fca26
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,22 @@
+- [X] Fix help file reader
+- [X] Implement make install for help, ini files.
+- [ ] Debug interpreter, seems to be some issues
+- [X] Setting the prompt to the time or date output doesn't work, will need to surround command with escape characters I'm sure.
+- [X] Need to check for the existence of motd, hlp, and so on or the app falls over when it assume they are availalbe and they aren't.
+- [ ] At the point where I need to add that error reporting module.
+- [X] Port to ARM64.
+- [X] Probably getting close to time to think about changing the recursive makefile to a canonical makefile chain, or use cmake.
+- [X] Same repo builds on Windows and Linux x64.
+- [X] Fix stack corruption when windows version exits.
+- [X] Report to the user that dependancies (like libconfig) aren't on the build host.
+- [ ] Manpage? Maybe?
+- [ ] Add some code that actually uses the threadpool.
+- [ ] Check to see if the code is truly thread-safe, I'm sure its not (hello printf...)
+- [X] Fix help system.
+- [ ] Implement and USE uinfo struct in support.h.
+- [ ] Add posix style command line options.
+- [ ] Add case senitivity as an option (?).
+- [X] Make use of encryption module.
+- [X] Add a string member to the user context struct that holds the user's prompt so when user changes it it can be reflected immedaitely.
+- [ ] Rename any "motd" references to "intrpmotd" or anything else, too easy to confuse with the actual linux motd file.
+- [X] Process backspace characters.
diff --git a/autoclean.sh b/autoclean.sh
new file mode 100755
index 0000000..b52f9ef
--- /dev/null
+++ b/autoclean.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+#
+# Clean autotools config, run in top directory
+#
+make clean
+rm -rf autom4te.cache
+rm aclocal.m4 compile config.h config.h.in config.log \
+ config.status configure depcomp install-sh Makefile.in \
+ missing stamp-h1 Makefile
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..14fb397
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,1579 @@
+#!/bin/sh
+# a u t o g e n . s h
+#
+# Copyright (c) 2005-2009 United States Government as represented by
+# the U.S. Army Research Laboratory.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistribution of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistribution 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.
+#
+# 3. The name of the author may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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.
+#
+###
+#
+# Script for automatically preparing the sources for compilation by
+# performing the myriad of necessary steps. The script attempts to
+# detect proper version support, and outputs warnings about particular
+# systems that have autotool peculiarities.
+#
+# Basically, if everything is set up and installed correctly, the
+# script will validate that minimum versions of the GNU Build System
+# tools are installed, account for several common configuration
+# issues, and then simply run autoreconf for you.
+#
+# If autoreconf fails, which can happen for many valid configurations,
+# this script proceeds to run manual preparation steps effectively
+# providing a POSIX shell script (mostly complete) reimplementation of
+# autoreconf.
+#
+# The AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER
+# environment variables and corresponding _OPTIONS variables (e.g.
+# AUTORECONF_OPTIONS) may be used to override the default automatic
+# detection behaviors. Similarly the _VERSION variables will override
+# the minimum required version numbers.
+#
+# Examples:
+#
+# To obtain help on usage:
+# ./autogen.sh --help
+#
+# To obtain verbose output:
+# ./autogen.sh --verbose
+#
+# To skip autoreconf and prepare manually:
+# AUTORECONF=false ./autogen.sh
+#
+# To verbosely try running with an older (unsupported) autoconf:
+# AUTOCONF_VERSION=2.50 ./autogen.sh --verbose
+#
+# Author:
+# Christopher Sean Morrison
+#
+# Patches:
+# Sebastian Pipping
+#
+######################################################################
+
+# set to minimum acceptable version of autoconf
+if [ "x$AUTOCONF_VERSION" = "x" ] ; then
+ AUTOCONF_VERSION=2.52
+fi
+# set to minimum acceptable version of automake
+if [ "x$AUTOMAKE_VERSION" = "x" ] ; then
+ AUTOMAKE_VERSION=1.6.0
+fi
+# set to minimum acceptable version of libtool
+if [ "x$LIBTOOL_VERSION" = "x" ] ; then
+ LIBTOOL_VERSION=1.4.2
+fi
+
+
+##################
+# ident function #
+##################
+ident ( ) {
+ # extract copyright from header
+ __copyright="`grep Copyright $AUTOGEN_SH | head -${HEAD_N}1 | awk '{print $4}'`"
+ if [ "x$__copyright" = "x" ] ; then
+ __copyright="`date +%Y`"
+ fi
+
+ # extract version from CVS Id string
+ __id="$Id: autogen.sh 33925 2009-03-01 23:27:06Z brlcad $"
+ __version="`echo $__id | sed 's/.*\([0-9][0-9][0-9][0-9]\)[-\/]\([0-9][0-9]\)[-\/]\([0-9][0-9]\).*/\1\2\3/'`"
+ if [ "x$__version" = "x" ] ; then
+ __version=""
+ fi
+
+ echo "autogen.sh build preparation script by Christopher Sean Morrison"
+ echo " + config.guess download patch by Sebastian Pipping (2008-12-03)"
+ echo "revised 3-clause BSD-style license, copyright (c) $__copyright"
+ echo "script version $__version, ISO/IEC 9945 POSIX shell script"
+}
+
+
+##################
+# USAGE FUNCTION #
+##################
+usage ( ) {
+ echo "Usage: $AUTOGEN_SH [-h|--help] [-v|--verbose] [-q|--quiet] [-d|--download] [--version]"
+ echo " --help Help on $NAME_OF_AUTOGEN usage"
+ echo " --verbose Verbose progress output"
+ echo " --quiet Quiet suppressed progress output"
+ echo " --download Download the latest config.guess from gnulib"
+ echo " --version Only perform GNU Build System version checks"
+ echo
+ echo "Description: This script will validate that minimum versions of the"
+ echo "GNU Build System tools are installed and then run autoreconf for you."
+ echo "Should autoreconf fail, manual preparation steps will be run"
+ echo "potentially accounting for several common preparation issues. The"
+
+ echo "AUTORECONF, AUTOCONF, AUTOMAKE, LIBTOOLIZE, ACLOCAL, AUTOHEADER,"
+ echo "PROJECT, & CONFIGURE environment variables and corresponding _OPTIONS"
+ echo "variables (e.g. AUTORECONF_OPTIONS) may be used to override the"
+ echo "default automatic detection behavior."
+ echo
+
+ ident
+
+ return 0
+}
+
+
+##########################
+# VERSION_ERROR FUNCTION #
+##########################
+version_error ( ) {
+ if [ "x$1" = "x" ] ; then
+ echo "INTERNAL ERROR: version_error was not provided a version"
+ exit 1
+ fi
+ if [ "x$2" = "x" ] ; then
+ echo "INTERNAL ERROR: version_error was not provided an application name"
+ exit 1
+ fi
+ $ECHO
+ $ECHO "ERROR: To prepare the ${PROJECT} build system from scratch,"
+ $ECHO " at least version $1 of $2 must be installed."
+ $ECHO
+ $ECHO "$NAME_OF_AUTOGEN does not need to be run on the same machine that will"
+ $ECHO "run configure or make. Either the GNU Autotools will need to be installed"
+ $ECHO "or upgraded on this system, or $NAME_OF_AUTOGEN must be run on the source"
+ $ECHO "code on another system and then transferred to here. -- Cheers!"
+ $ECHO
+}
+
+##########################
+# VERSION_CHECK FUNCTION #
+##########################
+version_check ( ) {
+ if [ "x$1" = "x" ] ; then
+ echo "INTERNAL ERROR: version_check was not provided a minimum version"
+ exit 1
+ fi
+ _min="$1"
+ if [ "x$2" = "x" ] ; then
+ echo "INTERNAL ERROR: version check was not provided a comparison version"
+ exit 1
+ fi
+ _cur="$2"
+
+ # needed to handle versions like 1.10 and 1.4-p6
+ _min="`echo ${_min}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+ _cur="`echo ${_cur}. | sed 's/[^0-9]/./g' | sed 's/\.\././g'`"
+
+ _min_major="`echo $_min | cut -d. -f1`"
+ _min_minor="`echo $_min | cut -d. -f2`"
+ _min_patch="`echo $_min | cut -d. -f3`"
+
+ _cur_major="`echo $_cur | cut -d. -f1`"
+ _cur_minor="`echo $_cur | cut -d. -f2`"
+ _cur_patch="`echo $_cur | cut -d. -f3`"
+
+ if [ "x$_min_major" = "x" ] ; then
+ _min_major=0
+ fi
+ if [ "x$_min_minor" = "x" ] ; then
+ _min_minor=0
+ fi
+ if [ "x$_min_patch" = "x" ] ; then
+ _min_patch=0
+ fi
+ if [ "x$_cur_minor" = "x" ] ; then
+ _cur_major=0
+ fi
+ if [ "x$_cur_minor" = "x" ] ; then
+ _cur_minor=0
+ fi
+ if [ "x$_cur_patch" = "x" ] ; then
+ _cur_patch=0
+ fi
+
+ $VERBOSE_ECHO "Checking if ${_cur_major}.${_cur_minor}.${_cur_patch} is greater than ${_min_major}.${_min_minor}.${_min_patch}"
+
+ if [ $_min_major -lt $_cur_major ] ; then
+ return 0
+ elif [ $_min_major -eq $_cur_major ] ; then
+ if [ $_min_minor -lt $_cur_minor ] ; then
+ return 0
+ elif [ $_min_minor -eq $_cur_minor ] ; then
+ if [ $_min_patch -lt $_cur_patch ] ; then
+ return 0
+ elif [ $_min_patch -eq $_cur_patch ] ; then
+ return 0
+ fi
+ fi
+ fi
+ return 1
+}
+
+
+######################################
+# LOCATE_CONFIGURE_TEMPLATE FUNCTION #
+######################################
+locate_configure_template ( ) {
+ _pwd="`pwd`"
+ if test -f "./configure.ac" ; then
+ echo "./configure.ac"
+ elif test -f "./configure.in" ; then
+ echo "./configure.in"
+ elif test -f "$_pwd/configure.ac" ; then
+ echo "$_pwd/configure.ac"
+ elif test -f "$_pwd/configure.in" ; then
+ echo "$_pwd/configure.in"
+ elif test -f "$PATH_TO_AUTOGEN/configure.ac" ; then
+ echo "$PATH_TO_AUTOGEN/configure.ac"
+ elif test -f "$PATH_TO_AUTOGEN/configure.in" ; then
+ echo "$PATH_TO_AUTOGEN/configure.in"
+ fi
+}
+
+
+##################
+# argument check #
+##################
+ARGS="$*"
+PATH_TO_AUTOGEN="`dirname $0`"
+NAME_OF_AUTOGEN="`basename $0`"
+AUTOGEN_SH="$PATH_TO_AUTOGEN/$NAME_OF_AUTOGEN"
+
+LIBTOOL_M4="${PATH_TO_AUTOGEN}/misc/libtool.m4"
+
+if [ "x$HELP" = "x" ] ; then
+ HELP=no
+fi
+if [ "x$QUIET" = "x" ] ; then
+ QUIET=no
+fi
+if [ "x$VERBOSE" = "x" ] ; then
+ VERBOSE=no
+fi
+if [ "x$VERSION_ONLY" = "x" ] ; then
+ VERSION_ONLY=no
+fi
+if [ "x$DOWNLOAD" = "x" ] ; then
+ DOWNLOAD=no
+fi
+if [ "x$AUTORECONF_OPTIONS" = "x" ] ; then
+ AUTORECONF_OPTIONS="-i -f"
+fi
+if [ "x$AUTOCONF_OPTIONS" = "x" ] ; then
+ AUTOCONF_OPTIONS="-f"
+fi
+if [ "x$AUTOMAKE_OPTIONS" = "x" ] ; then
+ AUTOMAKE_OPTIONS="-a -c -f"
+fi
+ALT_AUTOMAKE_OPTIONS="-a -c"
+if [ "x$LIBTOOLIZE_OPTIONS" = "x" ] ; then
+ LIBTOOLIZE_OPTIONS="--automake -c -f"
+fi
+ALT_LIBTOOLIZE_OPTIONS="--automake --copy --force"
+if [ "x$ACLOCAL_OPTIONS" = "x" ] ; then
+ ACLOCAL_OPTIONS=""
+fi
+if [ "x$AUTOHEADER_OPTIONS" = "x" ] ; then
+ AUTOHEADER_OPTIONS=""
+fi
+if [ "x$CONFIG_GUESS_URL" = "x" ] ; then
+ CONFIG_GUESS_URL="http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=build-aux/config.guess;hb=HEAD"
+fi
+for arg in $ARGS ; do
+ case "x$arg" in
+ x--help) HELP=yes ;;
+ x-[hH]) HELP=yes ;;
+ x--quiet) QUIET=yes ;;
+ x-[qQ]) QUIET=yes ;;
+ x--verbose) VERBOSE=yes ;;
+ x-[dD]) DOWNLOAD=yes ;;
+ x--download) DOWNLOAD=yes ;;
+ x-[vV]) VERBOSE=yes ;;
+ x--version) VERSION_ONLY=yes ;;
+ *)
+ echo "Unknown option: $arg"
+ echo
+ usage
+ exit 1
+ ;;
+ esac
+done
+
+
+#####################
+# environment check #
+#####################
+
+# sanity check before recursions potentially begin
+if [ ! -f "$AUTOGEN_SH" ] ; then
+ echo "INTERNAL ERROR: $AUTOGEN_SH does not exist"
+ if [ ! "x$0" = "x$AUTOGEN_SH" ] ; then
+ echo "INTERNAL ERROR: dirname/basename inconsistency: $0 != $AUTOGEN_SH"
+ fi
+ exit 1
+fi
+
+# force locale setting to C so things like date output as expected
+LC_ALL=C
+
+# commands that this script expects
+for __cmd in echo head tail pwd ; do
+ echo "test" | $__cmd > /dev/null 2>&1
+ if [ $? != 0 ] ; then
+ echo "INTERNAL ERROR: '${__cmd}' command is required"
+ exit 2
+ fi
+done
+echo "test" | grep "test" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+ echo "INTERNAL ERROR: grep command is required"
+ exit 1
+fi
+echo "test" | sed "s/test/test/" > /dev/null 2>&1
+if test ! x$? = x0 ; then
+ echo "INTERNAL ERROR: sed command is required"
+ exit 1
+fi
+
+
+# determine the behavior of echo
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+# determine the behavior of head
+case "x`echo 'head' | head -n 1 2>&1`" in
+ *xhead*) HEAD_N="n " ;;
+ *) HEAD_N="" ;;
+esac
+
+# determine the behavior of tail
+case "x`echo 'tail' | tail -n 1 2>&1`" in
+ *xtail*) TAIL_N="n " ;;
+ *) TAIL_N="" ;;
+esac
+
+VERBOSE_ECHO=:
+ECHO=:
+if [ "x$QUIET" = "xyes" ] ; then
+ if [ "x$VERBOSE" = "xyes" ] ; then
+ echo "Verbose output quelled by quiet option. Further output disabled."
+ fi
+else
+ ECHO=echo
+ if [ "x$VERBOSE" = "xyes" ] ; then
+ echo "Verbose output enabled"
+ VERBOSE_ECHO=echo
+ fi
+fi
+
+
+# allow a recursive run to disable further recursions
+if [ "x$RUN_RECURSIVE" = "x" ] ; then
+ RUN_RECURSIVE=yes
+fi
+
+
+################################################
+# check for help arg and bypass version checks #
+################################################
+if [ "x`echo $ARGS | sed 's/.*[hH][eE][lL][pP].*/help/'`" = "xhelp" ] ; then
+ HELP=yes
+fi
+if [ "x$HELP" = "xyes" ] ; then
+ usage
+ $ECHO "---"
+ $ECHO "Help was requested. No preparation or configuration will be performed."
+ exit 0
+fi
+
+
+#######################
+# set up signal traps #
+#######################
+untrap_abnormal ( ) {
+ for sig in 1 2 13 15; do
+ trap - $sig
+ done
+}
+
+# do this cleanup whenever we exit.
+trap '
+ # start from the root
+ if test -d "$START_PATH" ; then
+ cd "$START_PATH"
+ fi
+
+ # restore/delete backup files
+ if test "x$PFC_INIT" = "x1" ; then
+ recursive_restore
+ fi
+' 0
+
+# trap SIGHUP (1), SIGINT (2), SIGPIPE (13), SIGTERM (15)
+for sig in 1 2 13 15; do
+ trap '
+ $ECHO ""
+ $ECHO "Aborting $NAME_OF_AUTOGEN: caught signal '$sig'"
+
+ # start from the root
+ if test -d "$START_PATH" ; then
+ cd "$START_PATH"
+ fi
+
+ # clean up on abnormal exit
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+
+ if test -f "acinclude.m4.$$.backup" ; then
+ $VERBOSE_ECHO "cat acinclude.m4.$$.backup > acinclude.m4"
+ chmod u+w acinclude.m4
+ cat acinclude.m4.$$.backup > acinclude.m4
+
+ $VERBOSE_ECHO "rm -f acinclude.m4.$$.backup"
+ rm -f acinclude.m4.$$.backup
+ fi
+
+ { (exit 1); exit 1; }
+' $sig
+done
+
+
+#############################
+# look for a configure file #
+#############################
+if [ "x$CONFIGURE" = "x" ] ; then
+ CONFIGURE="`locate_configure_template`"
+ if [ ! "x$CONFIGURE" = "x" ] ; then
+ $VERBOSE_ECHO "Found a configure template: $CONFIGURE"
+ fi
+else
+ $ECHO "Using CONFIGURE environment variable override: $CONFIGURE"
+fi
+if [ "x$CONFIGURE" = "x" ] ; then
+ if [ "x$VERSION_ONLY" = "xyes" ] ; then
+ CONFIGURE=/dev/null
+ else
+ $ECHO
+ $ECHO "A configure.ac or configure.in file could not be located implying"
+ $ECHO "that the GNU Build System is at least not used in this directory. In"
+ $ECHO "any case, there is nothing to do here without one of those files."
+ $ECHO
+ $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+ exit 1
+ fi
+fi
+
+####################
+# get project name #
+####################
+if [ "x$PROJECT" = "x" ] ; then
+ PROJECT="`grep AC_INIT $CONFIGURE | grep -v '.*#.*AC_INIT' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_INIT(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if [ "x$PROJECT" = "xAC_INIT" ] ; then
+ # projects might be using the older/deprecated arg-less AC_INIT .. look for AM_INIT_AUTOMAKE instead
+ PROJECT="`grep AM_INIT_AUTOMAKE $CONFIGURE | grep -v '.*#.*AM_INIT_AUTOMAKE' | tail -${TAIL_N}1 | sed 's/^[ ]*AM_INIT_AUTOMAKE(\([^,)]*\).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ fi
+ if [ "x$PROJECT" = "xAM_INIT_AUTOMAKE" ] ; then
+ PROJECT="project"
+ fi
+ if [ "x$PROJECT" = "x" ] ; then
+ PROJECT="project"
+ fi
+else
+ $ECHO "Using PROJECT environment variable override: $PROJECT"
+fi
+$ECHO "Preparing the $PROJECT build system...please wait"
+$ECHO
+
+
+########################
+# check for autoreconf #
+########################
+HAVE_AUTORECONF=no
+if [ "x$AUTORECONF" = "x" ] ; then
+ for AUTORECONF in autoreconf ; do
+ $VERBOSE_ECHO "Checking autoreconf version: $AUTORECONF --version"
+ $AUTORECONF --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ HAVE_AUTORECONF=yes
+ break
+ fi
+ done
+else
+ HAVE_AUTORECONF=yes
+ $ECHO "Using AUTORECONF environment variable override: $AUTORECONF"
+fi
+
+
+##########################
+# autoconf version check #
+##########################
+_acfound=no
+if [ "x$AUTOCONF" = "x" ] ; then
+ for AUTOCONF in autoconf ; do
+ $VERBOSE_ECHO "Checking autoconf version: $AUTOCONF --version"
+ $AUTOCONF --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ _acfound=yes
+ break
+ fi
+ done
+else
+ _acfound=yes
+ $ECHO "Using AUTOCONF environment variable override: $AUTOCONF"
+fi
+
+_report_error=no
+if [ ! "x$_acfound" = "xyes" ] ; then
+ $ECHO "ERROR: Unable to locate GNU Autoconf."
+ _report_error=yes
+else
+ _version="`$AUTOCONF --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Autoconf version $_version"
+ version_check "$AUTOCONF_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$AUTOCONF_VERSION" "GNU Autoconf"
+ exit 1
+fi
+
+
+##########################
+# automake version check #
+##########################
+_amfound=no
+if [ "x$AUTOMAKE" = "x" ] ; then
+ for AUTOMAKE in automake ; do
+ $VERBOSE_ECHO "Checking automake version: $AUTOMAKE --version"
+ $AUTOMAKE --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ _amfound=yes
+ break
+ fi
+ done
+else
+ _amfound=yes
+ $ECHO "Using AUTOMAKE environment variable override: $AUTOMAKE"
+fi
+
+
+_report_error=no
+if [ ! "x$_amfound" = "xyes" ] ; then
+ $ECHO
+ $ECHO "ERROR: Unable to locate GNU Automake."
+ _report_error=yes
+else
+ _version="`$AUTOMAKE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Automake version $_version"
+ version_check "$AUTOMAKE_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$AUTOMAKE_VERSION" "GNU Automake"
+ exit 1
+fi
+
+
+########################
+# check for libtoolize #
+########################
+HAVE_LIBTOOLIZE=yes
+HAVE_ALT_LIBTOOLIZE=no
+_ltfound=no
+if [ "x$LIBTOOLIZE" = "x" ] ; then
+ LIBTOOLIZE=libtoolize
+ $VERBOSE_ECHO "Checking libtoolize version: $LIBTOOLIZE --version"
+ $LIBTOOLIZE --version > /dev/null 2>&1
+ if [ ! $? = 0 ] ; then
+ HAVE_LIBTOOLIZE=no
+ $ECHO
+ if [ "x$HAVE_AUTORECONF" = "xno" ] ; then
+ $ECHO "Warning: libtoolize does not appear to be available."
+ else
+ $ECHO "Warning: libtoolize does not appear to be available. This means that"
+ $ECHO "the automatic build preparation via autoreconf will probably not work."
+ $ECHO "Preparing the build by running each step individually, however, should"
+ $ECHO "work and will be done automatically for you if autoreconf fails."
+ fi
+
+ # look for some alternates
+ for tool in glibtoolize libtoolize15 libtoolize14 libtoolize13 ; do
+ $VERBOSE_ECHO "Checking libtoolize alternate: $tool --version"
+ _glibtoolize="`$tool --version > /dev/null 2>&1`"
+ if [ $? = 0 ] ; then
+ $VERBOSE_ECHO "Found $tool --version"
+ _glti="`which $tool`"
+ if [ "x$_glti" = "x" ] ; then
+ $VERBOSE_ECHO "Cannot find $tool with which"
+ continue;
+ fi
+ if test ! -f "$_glti" ; then
+ $VERBOSE_ECHO "Cannot use $tool, $_glti is not a file"
+ continue;
+ fi
+ _gltidir="`dirname $_glti`"
+ if [ "x$_gltidir" = "x" ] ; then
+ $VERBOSE_ECHO "Cannot find $tool path with dirname of $_glti"
+ continue;
+ fi
+ if test ! -d "$_gltidir" ; then
+ $VERBOSE_ECHO "Cannot use $tool, $_gltidir is not a directory"
+ continue;
+ fi
+ HAVE_ALT_LIBTOOLIZE=yes
+ LIBTOOLIZE="$tool"
+ $ECHO
+ $ECHO "Fortunately, $tool was found which means that your system may simply"
+ $ECHO "have a non-standard or incomplete GNU Autotools install. If you have"
+ $ECHO "sufficient system access, it may be possible to quell this warning by"
+ $ECHO "running:"
+ $ECHO
+ sudo -V > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ $ECHO " sudo ln -s $_glti $_gltidir/libtoolize"
+ $ECHO
+ else
+ $ECHO " ln -s $_glti $_gltidir/libtoolize"
+ $ECHO
+ $ECHO "Run that as root or with proper permissions to the $_gltidir directory"
+ $ECHO
+ fi
+ _ltfound=yes
+ break
+ fi
+ done
+ else
+ _ltfound=yes
+ fi
+else
+ _ltfound=yes
+ $ECHO "Using LIBTOOLIZE environment variable override: $LIBTOOLIZE"
+fi
+
+
+############################
+# libtoolize version check #
+############################
+_report_error=no
+if [ ! "x$_ltfound" = "xyes" ] ; then
+ $ECHO
+ $ECHO "ERROR: Unable to locate GNU Libtool."
+ _report_error=yes
+else
+ _version="`$LIBTOOLIZE --version | head -${HEAD_N}1 | sed 's/[^0-9]*\([0-9\.][0-9\.]*\)/\1/'`"
+ if [ "x$_version" = "x" ] ; then
+ _version="0.0.0"
+ fi
+ $ECHO "Found GNU Libtool version $_version"
+ version_check "$LIBTOOL_VERSION" "$_version"
+ if [ $? -ne 0 ] ; then
+ _report_error=yes
+ fi
+fi
+if [ "x$_report_error" = "xyes" ] ; then
+ version_error "$LIBTOOL_VERSION" "GNU Libtool"
+ exit 1
+fi
+
+
+#####################
+# check for aclocal #
+#####################
+if [ "x$ACLOCAL" = "x" ] ; then
+ for ACLOCAL in aclocal ; do
+ $VERBOSE_ECHO "Checking aclocal version: $ACLOCAL --version"
+ $ACLOCAL --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ break
+ fi
+ done
+else
+ $ECHO "Using ACLOCAL environment variable override: $ACLOCAL"
+fi
+
+
+########################
+# check for autoheader #
+########################
+if [ "x$AUTOHEADER" = "x" ] ; then
+ for AUTOHEADER in autoheader ; do
+ $VERBOSE_ECHO "Checking autoheader version: $AUTOHEADER --version"
+ $AUTOHEADER --version > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ break
+ fi
+ done
+else
+ $ECHO "Using AUTOHEADER environment variable override: $AUTOHEADER"
+fi
+
+
+#########################
+# check if version only #
+#########################
+$VERBOSE_ECHO "Checking whether to only output version information"
+if [ "x$VERSION_ONLY" = "xyes" ] ; then
+ $ECHO
+ ident
+ $ECHO "---"
+ $ECHO "Version requested. No preparation or configuration will be performed."
+ exit 0
+fi
+
+
+#################################
+# PROTECT_FROM_CLOBBER FUNCTION #
+#################################
+protect_from_clobber ( ) {
+ PFC_INIT=1
+
+ # protect COPYING & INSTALL from overwrite by automake. the
+ # automake force option will (inappropriately) ignore the existing
+ # contents of a COPYING and/or INSTALL files (depending on the
+ # version) instead of just forcing *missing* files like it does
+ # for AUTHORS, NEWS, and README. this is broken but extremely
+ # prevalent behavior, so we protect against it by keeping a backup
+ # of the file that can later be restored.
+
+ for file in COPYING INSTALL ; do
+ if test -f ${file} ; then
+ if test -f ${file}.$$.protect_from_automake.backup ; then
+ $VERBOSE_ECHO "Already backed up ${file} in `pwd`"
+ else
+ $VERBOSE_ECHO "Backing up ${file} in `pwd`"
+ $VERBOSE_ECHO "cp -p ${file} ${file}.$$.protect_from_automake.backup"
+ cp -p ${file} ${file}.$$.protect_from_automake.backup
+ fi
+ fi
+ done
+}
+
+
+##############################
+# RECURSIVE_PROTECT FUNCTION #
+##############################
+recursive_protect ( ) {
+
+ # for projects using recursive configure, run the build
+ # preparation steps for the subdirectories. this function assumes
+ # START_PATH was set to pwd before recursion begins so that
+ # relative paths work.
+
+ # git 'r done, protect COPYING and INSTALL from being clobbered
+ protect_from_clobber
+
+ if test -d autom4te.cache ; then
+ $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+ fi
+
+ # find configure template
+ _configure="`locate_configure_template`"
+ if [ "x$_configure" = "x" ] ; then
+ return
+ fi
+ # $VERBOSE_ECHO "Looking for configure template found `pwd`/$_configure"
+
+ # look for subdirs
+ # $VERBOSE_ECHO "Looking for subdirs in `pwd`"
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ CHECK_DIRS=""
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+ fi
+ done
+
+ # process subdirs
+ if [ ! "x$CHECK_DIRS" = "x" ] ; then
+ $VERBOSE_ECHO "Recursively scanning the following directories:"
+ $VERBOSE_ECHO " $CHECK_DIRS"
+ for dir in $CHECK_DIRS ; do
+ $VERBOSE_ECHO "Protecting files from automake in $dir"
+ cd "$START_PATH"
+ eval "cd $dir"
+
+ # recursively git 'r done
+ recursive_protect
+ done
+ fi
+} # end of recursive_protect
+
+
+#############################
+# RESTORE_CLOBBERED FUNCION #
+#############################
+restore_clobbered ( ) {
+
+ # The automake (and autoreconf by extension) -f/--force-missing
+ # option may overwrite COPYING and INSTALL even if they do exist.
+ # Here we restore the files if necessary.
+
+ spacer=no
+
+ for file in COPYING INSTALL ; do
+ if test -f ${file}.$$.protect_from_automake.backup ; then
+ if test -f ${file} ; then
+ # compare entire content, restore if needed
+ if test "x`cat ${file}`" != "x`cat ${file}.$$.protect_from_automake.backup`" ; then
+ if test "x$spacer" = "xno" ; then
+ $VERBOSE_ECHO
+ spacer=yes
+ fi
+ # restore the backup
+ $VERBOSE_ECHO "Restoring ${file} from backup (automake -f likely clobbered it)"
+ $VERBOSE_ECHO "rm -f ${file}"
+ rm -f ${file}
+ $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
+ mv ${file}.$$.protect_from_automake.backup ${file}
+ fi # check contents
+ elif test -f ${file}.$$.protect_from_automake.backup ; then
+ $VERBOSE_ECHO "mv ${file}.$$.protect_from_automake.backup ${file}"
+ mv ${file}.$$.protect_from_automake.backup ${file}
+ fi # -f ${file}
+
+ # just in case
+ $VERBOSE_ECHO "rm -f ${file}.$$.protect_from_automake.backup"
+ rm -f ${file}.$$.protect_from_automake.backup
+ fi # -f ${file}.$$.protect_from_automake.backup
+ done
+
+ CONFIGURE="`locate_configure_template`"
+ if [ "x$CONFIGURE" = "x" ] ; then
+ return
+ fi
+
+ _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if test ! -d "$_aux_dir" ; then
+ _aux_dir=.
+ fi
+
+ for file in config.guess config.sub ltmain.sh ; do
+ if test -f "${_aux_dir}/${file}" ; then
+ $VERBOSE_ECHO "rm -f \"${_aux_dir}/${file}.backup\""
+ rm -f "${_aux_dir}/${file}.backup"
+ fi
+ done
+} # end of restore_clobbered
+
+
+##############################
+# RECURSIVE_RESTORE FUNCTION #
+##############################
+recursive_restore ( ) {
+
+ # restore COPYING and INSTALL from backup if they were clobbered
+ # for each directory recursively.
+
+ # git 'r undone
+ restore_clobbered
+
+ # find configure template
+ _configure="`locate_configure_template`"
+ if [ "x$_configure" = "x" ] ; then
+ return
+ fi
+
+ # look for subdirs
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $_configure | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ CHECK_DIRS=""
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ CHECK_DIRS="$CHECK_DIRS \"`pwd`/$dir\""
+ fi
+ done
+
+ # process subdirs
+ if [ ! "x$CHECK_DIRS" = "x" ] ; then
+ $VERBOSE_ECHO "Recursively scanning the following directories:"
+ $VERBOSE_ECHO " $CHECK_DIRS"
+ for dir in $CHECK_DIRS ; do
+ $VERBOSE_ECHO "Checking files for automake damage in $dir"
+ cd "$START_PATH"
+ eval "cd $dir"
+
+ # recursively git 'r undone
+ recursive_restore
+ done
+ fi
+} # end of recursive_restore
+
+
+#######################
+# INITIALIZE FUNCTION #
+#######################
+initialize ( ) {
+
+ # this routine performs a variety of directory-specific
+ # initializations. some are sanity checks, some are preventive,
+ # and some are necessary setup detection.
+ #
+ # this function sets:
+ # CONFIGURE
+ # SEARCH_DIRS
+ # CONFIG_SUBDIRS
+
+ ##################################
+ # check for a configure template #
+ ##################################
+ CONFIGURE="`locate_configure_template`"
+ if [ "x$CONFIGURE" = "x" ] ; then
+ $ECHO
+ $ECHO "A configure.ac or configure.in file could not be located implying"
+ $ECHO "that the GNU Build System is at least not used in this directory. In"
+ $ECHO "any case, there is nothing to do here without one of those files."
+ $ECHO
+ $ECHO "ERROR: No configure.in or configure.ac file found in `pwd`"
+ exit 1
+ fi
+
+ #####################
+ # detect an aux dir #
+ #####################
+ _aux_dir="`grep AC_CONFIG_AUX_DIR $CONFIGURE | grep -v '.*#.*AC_CONFIG_AUX_DIR' | tail -${TAIL_N}1 | sed 's/^[ ]*AC_CONFIG_AUX_DIR(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ if test ! -d "$_aux_dir" ; then
+ _aux_dir=.
+ else
+ $VERBOSE_ECHO "Detected auxillary directory: $_aux_dir"
+ fi
+
+ ################################
+ # detect a recursive configure #
+ ################################
+ CONFIG_SUBDIRS=""
+ _det_config_subdirs="`grep AC_CONFIG_SUBDIRS $CONFIGURE | grep -v '.*#.*AC_CONFIG_SUBDIRS' | sed 's/^[ ]*AC_CONFIG_SUBDIRS(\(.*\)).*/\1/' | sed 's/.*\[\(.*\)\].*/\1/'`"
+ for dir in $_det_config_subdirs ; do
+ if test -d "`pwd`/$dir" ; then
+ $VERBOSE_ECHO "Detected recursive configure directory: `pwd`/$dir"
+ CONFIG_SUBDIRS="$CONFIG_SUBDIRS `pwd`/$dir"
+ fi
+ done
+
+ ###########################################################
+ # make sure certain required files exist for GNU projects #
+ ###########################################################
+ _marker_found=""
+ _marker_found_message_intro='Detected non-GNU marker "'
+ _marker_found_message_mid='" in '
+ for marker in foreign cygnus ; do
+ _marker_found_message=${_marker_found_message_intro}${marker}${_marker_found_message_mid}
+ _marker_found="`grep 'AM_INIT_AUTOMAKE.*'${marker} $CONFIGURE`"
+ if [ ! "x$_marker_found" = "x" ] ; then
+ $VERBOSE_ECHO "${_marker_found_message}`basename \"$CONFIGURE\"`"
+ break
+ fi
+ if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
+ _marker_found="`grep 'AUTOMAKE_OPTIONS.*'${marker} Makefile.am`"
+ if [ ! "x$_marker_found" = "x" ] ; then
+ $VERBOSE_ECHO "${_marker_found_message}Makefile.am"
+ break
+ fi
+ fi
+ done
+ if [ "x${_marker_found}" = "x" ] ; then
+ _suggest_foreign=no
+ for file in AUTHORS COPYING ChangeLog INSTALL NEWS README ; do
+ if [ ! -f $file ] ; then
+ $VERBOSE_ECHO "Touching ${file} since it does not exist"
+ _suggest_foreign=yes
+ touch $file
+ fi
+ done
+
+ if [ "x${_suggest_foreign}" = "xyes" ] ; then
+ $ECHO
+ $ECHO "Warning: Several files expected of projects that conform to the GNU"
+ $ECHO "coding standards were not found. The files were automatically added"
+ $ECHO "for you since you do not have a 'foreign' declaration specified."
+ $ECHO
+ $ECHO "Considered adding 'foreign' to AM_INIT_AUTOMAKE in `basename \"$CONFIGURE\"`"
+ if test -f "`dirname \"$CONFIGURE\"/Makefile.am`" ; then
+ $ECHO "or to AUTOMAKE_OPTIONS in your top-level Makefile.am file."
+ fi
+ $ECHO
+ fi
+ fi
+
+ ##################################################
+ # make sure certain generated files do not exist #
+ ##################################################
+ for file in config.guess config.sub ltmain.sh ; do
+ if test -f "${_aux_dir}/${file}" ; then
+ $VERBOSE_ECHO "mv -f \"${_aux_dir}/${file}\" \"${_aux_dir}/${file}.backup\""
+ mv -f "${_aux_dir}/${file}" "${_aux_dir}/${file}.backup"
+ fi
+ done
+
+ ############################
+ # search alternate m4 dirs #
+ ############################
+ SEARCH_DIRS=""
+ for dir in m4 ; do
+ if [ -d $dir ] ; then
+ $VERBOSE_ECHO "Found extra aclocal search directory: $dir"
+ SEARCH_DIRS="$SEARCH_DIRS -I $dir"
+ fi
+ done
+
+ ######################################
+ # remove any previous build products #
+ ######################################
+ if test -d autom4te.cache ; then
+ $VERBOSE_ECHO "Found an autom4te.cache directory, deleting it"
+ $VERBOSE_ECHO "rm -rf autom4te.cache"
+ rm -rf autom4te.cache
+ fi
+# tcl/tk (and probably others) have a customized aclocal.m4, so can't delete it
+# if test -f aclocal.m4 ; then
+# $VERBOSE_ECHO "Found an aclocal.m4 file, deleting it"
+# $VERBOSE_ECHO "rm -f aclocal.m4"
+# rm -f aclocal.m4
+# fi
+
+} # end of initialize()
+
+
+##############
+# initialize #
+##############
+
+# stash path
+START_PATH="`pwd`"
+
+# Before running autoreconf or manual steps, some prep detection work
+# is necessary or useful. Only needs to occur once per directory, but
+# does need to traverse the entire subconfigure hierarchy to protect
+# files from being clobbered even by autoreconf.
+recursive_protect
+
+# start from where we started
+cd "$START_PATH"
+
+# get ready to process
+initialize
+
+
+#########################################
+# DOWNLOAD_GNULIB_CONFIG_GUESS FUNCTION #
+#########################################
+
+# TODO - should make sure wget/curl exist and/or work before trying to
+# use them.
+
+download_gnulib_config_guess () {
+ # abuse gitweb to download gnulib's latest config.guess via HTTP
+ config_guess_temp="config.guess.$$.download"
+ ret=1
+ for __cmd in wget curl fetch ; do
+ $VERBOSE_ECHO "Checking for command ${__cmd}"
+ ${__cmd} --version > /dev/null 2>&1
+ ret=$?
+ if [ ! $ret = 0 ] ; then
+ continue
+ fi
+
+ __cmd_version=`${__cmd} --version | head -n 1 | sed -e 's/^[^0-9]\+//' -e 's/ .*//'`
+ $VERBOSE_ECHO "Found ${__cmd} ${__cmd_version}"
+
+ opts=""
+ case ${__cmd} in
+ wget)
+ opts="-O"
+ ;;
+ curl)
+ opts="-o"
+ ;;
+ fetch)
+ opts="-t 5 -f"
+ ;;
+ esac
+
+ $VERBOSE_ECHO "Running $__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\""
+ eval "$__cmd \"${CONFIG_GUESS_URL}\" $opts \"${config_guess_temp}\"" > /dev/null 2>&1
+ if [ $? = 0 ] ; then
+ mv -f "${config_guess_temp}" ${_aux_dir}/config.guess
+ ret=0
+ break
+ fi
+ done
+
+ if [ ! $ret = 0 ] ; then
+ $ECHO "Warning: config.guess download failed from: $CONFIG_GUESS_URL"
+ rm -f "${config_guess_temp}"
+ fi
+}
+
+
+##############################
+# LIBTOOLIZE_NEEDED FUNCTION #
+##############################
+libtoolize_needed () {
+ ret=1 # means no, don't need libtoolize
+ for feature in AC_PROG_LIBTOOL AM_PROG_LIBTOOL LT_INIT ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ ret=0 # means yes, need to run libtoolize
+ break
+ fi
+ done
+ return ${ret}
+}
+
+
+
+############################################
+# prepare build via autoreconf or manually #
+############################################
+reconfigure_manually=no
+if [ "x$HAVE_AUTORECONF" = "xyes" ] ; then
+ $ECHO
+ $ECHO $ECHO_N "Automatically preparing build ... $ECHO_C"
+
+ $VERBOSE_ECHO "$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS"
+ autoreconf_output="`$AUTORECONF $SEARCH_DIRS $AUTORECONF_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoreconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+ if [ ! "x`echo \"$autoreconf_output\" | grep libtoolize | grep \"No such file or directory\"`" = "x" ] ; then
+ $ECHO
+ $ECHO "Warning: autoreconf failed but due to what is usually a common libtool"
+ $ECHO "misconfiguration issue. This problem is encountered on systems that"
+ $ECHO "have installed libtoolize under a different name without providing a"
+ $ECHO "symbolic link or without setting the LIBTOOLIZE environment variable."
+ $ECHO
+ $ECHO "Restarting the preparation steps with LIBTOOLIZE set to $LIBTOOLIZE"
+
+ export LIBTOOLIZE
+ RUN_RECURSIVE=no
+ export RUN_RECURSIVE
+ untrap_abnormal
+
+ $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ exit $?
+ fi
+ fi
+
+ $ECHO "Warning: $AUTORECONF failed"
+
+ if test -f ltmain.sh ; then
+ $ECHO "libtoolize being run by autoreconf is not creating ltmain.sh in the auxillary directory like it should"
+ fi
+
+ $ECHO "Attempting to run the preparation steps individually"
+ reconfigure_manually=yes
+ else
+ if [ "x$DOWNLOAD" = "xyes" ] ; then
+ if libtoolize_needed ; then
+ download_gnulib_config_guess
+ fi
+ fi
+ fi
+else
+ reconfigure_manually=yes
+fi
+
+
+############################
+# LIBTOOL_FAILURE FUNCTION #
+############################
+libtool_failure ( ) {
+
+ # libtool is rather error-prone in comparison to the other
+ # autotools and this routine attempts to compensate for some
+ # common failures. the output after a libtoolize failure is
+ # parsed for an error related to AC_PROG_LIBTOOL and if found, we
+ # attempt to inject a project-provided libtool.m4 file.
+
+ _autoconf_output="$1"
+
+ if [ "x$RUN_RECURSIVE" = "xno" ] ; then
+ # we already tried the libtool.m4, don't try again
+ return 1
+ fi
+
+ if test -f "$LIBTOOL_M4" ; then
+ found_libtool="`$ECHO $_autoconf_output | grep AC_PROG_LIBTOOL`"
+ if test ! "x$found_libtool" = "x" ; then
+ if test -f acinclude.m4 ; then
+ rm -f acinclude.m4.$$.backup
+ $VERBOSE_ECHO "cat acinclude.m4 > acinclude.m4.$$.backup"
+ cat acinclude.m4 > acinclude.m4.$$.backup
+ fi
+ $VERBOSE_ECHO "cat \"$LIBTOOL_M4\" >> acinclude.m4"
+ chmod u+w acinclude.m4
+ cat "$LIBTOOL_M4" >> acinclude.m4
+
+ # don't keep doing this
+ RUN_RECURSIVE=no
+ export RUN_RECURSIVE
+ untrap_abnormal
+
+ $ECHO
+ $ECHO "Restarting the preparation steps with libtool macros in acinclude.m4"
+ $VERBOSE_ECHO sh $AUTOGEN_SH "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ sh "$AUTOGEN_SH" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9"
+ exit $?
+ fi
+ fi
+}
+
+
+###########################
+# MANUAL_AUTOGEN FUNCTION #
+###########################
+manual_autogen ( ) {
+
+ ##################################################
+ # Manual preparation steps taken are as follows: #
+ # aclocal [-I m4] #
+ # libtoolize --automake -c -f #
+ # aclocal [-I m4] #
+ # autoconf -f #
+ # autoheader #
+ # automake -a -c -f #
+ ##################################################
+
+ ###########
+ # aclocal #
+ ###########
+ $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+ aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$aclocal_output"
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $ACLOCAL failed" && exit 2 ; fi
+
+ ##############
+ # libtoolize #
+ ##############
+ if libtoolize_needed ; then
+ if [ "x$HAVE_LIBTOOLIZE" = "xyes" ] ; then
+ $VERBOSE_ECHO "$LIBTOOLIZE $LIBTOOLIZE_OPTIONS"
+ libtoolize_output="`$LIBTOOLIZE $LIBTOOLIZE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$libtoolize_output"
+
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+ else
+ if [ "x$HAVE_ALT_LIBTOOLIZE" = "xyes" ] ; then
+ $VERBOSE_ECHO "$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS"
+ libtoolize_output="`$LIBTOOLIZE $ALT_LIBTOOLIZE_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$libtoolize_output"
+
+ if [ ! $ret = 0 ] ; then $ECHO "ERROR: $LIBTOOLIZE failed" && exit 2 ; fi
+ fi
+ fi
+
+ ###########
+ # aclocal #
+ ###########
+ # re-run again as instructed by libtoolize
+ $VERBOSE_ECHO "$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS"
+ aclocal_output="`$ACLOCAL $SEARCH_DIRS $ACLOCAL_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$aclocal_output"
+
+ # libtoolize might put ltmain.sh in the wrong place
+ if test -f ltmain.sh ; then
+ if test ! -f "${_aux_dir}/ltmain.sh" ; then
+ $ECHO
+ $ECHO "Warning: $LIBTOOLIZE is creating ltmain.sh in the wrong directory"
+ $ECHO
+ $ECHO "Fortunately, the problem can be worked around by simply copying the"
+ $ECHO "file to the appropriate location (${_aux_dir}/). This has been done for you."
+ $ECHO
+ $VERBOSE_ECHO "cp -p ltmain.sh \"${_aux_dir}/ltmain.sh\""
+ cp -p ltmain.sh "${_aux_dir}/ltmain.sh"
+ $ECHO $ECHO_N "Continuing build preparation ... $ECHO_C"
+ fi
+ fi # ltmain.sh
+
+ if [ "x$DOWNLOAD" = "xyes" ] ; then
+ download_gnulib_config_guess
+ fi
+ fi # libtoolize_needed
+
+ ############
+ # autoconf #
+ ############
+ $VERBOSE_ECHO
+ $VERBOSE_ECHO "$AUTOCONF $AUTOCONF_OPTIONS"
+ autoconf_output="`$AUTOCONF $AUTOCONF_OPTIONS 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ # retry without the -f and check for usage of macros that are too new
+ ac2_59_macros="AC_C_RESTRICT AC_INCLUDES_DEFAULT AC_LANG_ASSERT AC_LANG_WERROR AS_SET_CATFILE"
+ ac2_55_macros="AC_COMPILER_IFELSE AC_FUNC_MBRTOWC AC_HEADER_STDBOOL AC_LANG_CONFTEST AC_LANG_SOURCE AC_LANG_PROGRAM AC_LANG_CALL AC_LANG_FUNC_TRY_LINK AC_MSG_FAILURE AC_PREPROC_IFELSE"
+ ac2_54_macros="AC_C_BACKSLASH_A AC_CONFIG_LIBOBJ_DIR AC_GNU_SOURCE AC_PROG_EGREP AC_PROG_FGREP AC_REPLACE_FNMATCH AC_FUNC_FNMATCH_GNU AC_FUNC_REALLOC AC_TYPE_MBSTATE_T"
+
+ macros_to_search=""
+ ac_major="`echo ${AUTOCONF_VERSION}. | cut -d. -f1 | sed 's/[^0-9]//g'`"
+ ac_minor="`echo ${AUTOCONF_VERSION}. | cut -d. -f2 | sed 's/[^0-9]//g'`"
+
+ if [ $ac_major -lt 2 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+ else
+ if [ $ac_minor -lt 54 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros $ac2_54_macros"
+ elif [ $ac_minor -lt 55 ] ; then
+ macros_to_search="$ac2_59_macros $ac2_55_macros"
+ elif [ $ac_minor -lt 59 ] ; then
+ macros_to_search="$ac2_59_macros"
+ fi
+ fi
+
+ configure_ac_macros=__none__
+ for feature in $macros_to_search ; do
+ $VERBOSE_ECHO "Searching for $feature in $CONFIGURE"
+ found="`grep \"^$feature.*\" $CONFIGURE`"
+ if [ ! "x$found" = "x" ] ; then
+ if [ "x$configure_ac_macros" = "x__none__" ] ; then
+ configure_ac_macros="$feature"
+ else
+ configure_ac_macros="$feature $configure_ac_macros"
+ fi
+ fi
+ done
+ if [ ! "x$configure_ac_macros" = "x__none__" ] ; then
+ $ECHO
+ $ECHO "Warning: Unsupported macros were found in $CONFIGURE"
+ $ECHO
+ $ECHO "The `basename \"$CONFIGURE\"` file was scanned in order to determine if any"
+ $ECHO "unsupported macros are used that exceed the minimum version"
+ $ECHO "settings specified within this file. As such, the following macros"
+ $ECHO "should be removed from configure.ac or the version numbers in this"
+ $ECHO "file should be increased:"
+ $ECHO
+ $ECHO "$configure_ac_macros"
+ $ECHO
+ $ECHO $ECHO_N "Ignorantly continuing build preparation ... $ECHO_C"
+ fi
+
+ ###################
+ # autoconf, retry #
+ ###################
+ $VERBOSE_ECHO
+ $VERBOSE_ECHO "$AUTOCONF"
+ autoconf_output="`$AUTOCONF 2>&1`"
+ ret=$?
+ $VERBOSE_ECHO "$autoconf_output"
+
+ if [ ! $ret = 0 ] ; then
+ # test if libtool is busted
+ libtool_failure "$autoconf_output"
+
+ # let the user know what went wrong
+ cat <1, then adding their definitions to either the "foo1" translation unit or a completely new one, just add the new files with your functionality defined in them.
+
+The "huff" module is a simple huffman encryption C implementation you can use to encrypt plain text, like the message of the day included example command. Use it to do just that or maybe a list of passwords or whatever, or completly remove it and make the app even smaller. Warning: its just that; a simple huffman encoding implementaion. Please use AES or something like that for really sensitive data. Its meant soley as a way to make non-critical data readable only through the app.
+
+Action is a first match, arbitrary length thing; q, qu, and qui will all "quit" the interpreter, but quib will not. The logic that implements this is tiny (pretty terse pointer magic) and all contained in the main module1 (but not in the main function, there's like two or three static functions in the main module that handle all this. It shouldn't be a huge climb to follow. That same magic extends to commands as parameters to the help module; simply typing "h a" will return info on the arp command, simply being entered into the "commands" and "table" tables is snough to have the application treat them like any other command.
+
+As is the code compiles down to under 35K bytes3, filled out with some actually useful commands, stripped release, and an embedded clib, I figure you can have a board that can do things via this interpreter for well under 100KB or possibly less if you really watch your memory usage.
+
+Builds its own command menu automgically, no need to update that. Detailed descriptions will need updating in extra/interp.hlp however. Rename to your actual application name and place in the $USER home directory.
+
+Autoconf automatically provides an "install" command but there's no point in installing this. I'm going to remove the autoconf directive. You are welcome to use the autoconf install command if you want but its really kind of pointless. I would add that in your own finished package that uses this code.
+
+As well as typical scripts for autotools there's a qtcreator project file and a visual studio 2022 project configuration just for fun.
+
+Builds an ARMhf 32 bit target using the generic makefile in the linux subdirectory as long as you have the GCC ARMhf compiler installed on your system,
+can probably target other platforms using GCC or LLVM.
+
+To initiate an autotools build just run autogen.sh and follow the instructions, make sure that the execute bit is set on the automake and clean scripts. To build using the qtcreator project file load the project and select your build kit of choice. To build on Windows you'll need to reconfigure the project for your Win SDK, obviously. I'm on visual studio 2022, so you may need to create a project for your version and add the source modules manually. For visual studio code I have no idea, I rarely use that ide. For eclipse? Lift the voodoo curse off yourself and stop using it, you know that thing was originally written by IBM??
+
+autoclean.sh should reset the entire source directory to the "ground state." See "newcmds.md" for instruction on adding commands to the interpreter. Take special note
+of the user context struct, its key for makeing changes to the shell's environment without needing to re-start it.
+
+Again, and probably can't be stressed enough; this project is a "starter" project for your own command line processor engine or shell utiity; the included commands in the foo1 source are just examples; all of them need to be replaced with your required functionality and the extra/interp.help updated with actual descriptions of your new commands. MAKE INSTALL will do NOTHING USEFUL, but you can use it if for some reason you must. Never the less this is fully functioning code (the foo commands don't do anything though, not really) I simply consider it bad form to provide broken example code. That said, there maybe be some "slightly" broken code here and there. Pleaase write me about it or submit your own patch if you like.
+
+Depends on libconfig, barely. Can be easily removed if needed.
+
+7/12/2024 Fixed flaw with the help code.
+---
+1. The code now counts the number of members in the table static global member automatically, no need for this constant anymore. Also, the "commands" & "table" structs have been moved to include/parser.h.
+2. This has been eliminated in subsequent versions.
+3. Under 25K on windows.
+
+9/11/2024 Uncovered some serious issues
+---
+Turns out libconfig sucks. You can't put arbitrary characters in the ini files, it either needs to have legitmate alphanumeric characters or no file. If it encounters, say stars or hashbangs it spews core. Unacceptable. So I'm going back to my original idea of writing my own ini parser. When I can. Really busy right now.
+
+10/26/2024 Removed the libconfig dependancy
+---
+Got the above done.
diff --git a/extra/interp.hlp b/extra/interp.hlp
new file mode 100644
index 0000000..abb2e9c
--- /dev/null
+++ b/extra/interp.hlp
@@ -0,0 +1,19 @@
+aaa|===||aaa ||Development test command.
+access-lists|============||access-lists||List all access lists.
+elist|====||elist ||Edit the specified list or list all the available lists.
+amp|===||amp |Edit the properties of the specified amplifier.
+arp|===||arp|List the arp table.
+app|===||app ||Edit interpreter properites.
+badl|====||badl ||Mark a list as being bad. Will be deleted AFTER the next processing cycle.
+batch|=====||batch ||Execute a batch list.
+bert|====||bert (on/off)||Check the status or activate/deactivate the specified BERT device.
+help|====||help ||Get command list or detail on the specified topic.
+motd|====||motd ||Set the message of the day for the interpreter. The message must be quote surrounded.
+prompt|=====||prompt ||Set a new prompt for the interpreter. The prompt will be avilable when the interpreter is re-started. The new prompt must be quote surrounded.
+quit|====||quit||Exit the application.
+date|====||date||Output the current date.
+list|====||list ||Create a new batch list, or list all existing lists.
+time2|====||time2||Output the current time.
+loglevel|====||loglevel||Log verbosity; from 0 (Off) to 4 (Maximum).
+filet|====||filet||A fine cut of meat.
+
diff --git a/include/error_info.h b/include/error_info.h
new file mode 100644
index 0000000..94076a0
--- /dev/null
+++ b/include/error_info.h
@@ -0,0 +1,21 @@
+#pragma once
+
+typedef enum
+{
+ CMD_MISPARAM = -11, // inimissing paramt file
+ CMD_MEMERR = -10,
+ CMD_INITNF = -9, // initialization file not found
+ CMD_HELPERR = -8, // problem with help subsystem
+ CMD_QUOTES = -7, // argument not enclosed in quotes
+ CMD_IOERR = -6, // io error
+ CMD_BANNERPTH = -5, //
+ CMD_FILEEXST = -4, // file exists
+ CMD_FILENF = -3, // file not found
+ CMD_ARGS = -2, // error with command arguments
+ CMD_ERR = -1, // non-specific error
+ CMD_OK = 0, // command completed successfully
+ CMD_QUIT = 1, // exit application
+ CMD_NOOP = 2 // no operation
+} RET_CODE;
+
+// Hashmap of pointers to error text and codes whatever else.
diff --git a/include/foo1.h b/include/foo1.h
new file mode 100644
index 0000000..520a3cc
--- /dev/null
+++ b/include/foo1.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#ifndef _WIN32
+#include "utils/filesys.h"
+#endif
+#include "simple_strlib.h"
+#include "error_info.h"
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+int dummy(const char*);
+
+int aaa(const char*);
+int alist(const char*);
+int amp(const char*);
+
+int app(const char*);
+int arp(const char*);
+int badl(const char*);
+
+int batch(const char*);
+int bert(const char*);
+int date(const char*);
+
+int list(const char*);
+int time2(const char*);
+int loglevel(const char*);
+
+int filet(const char*);
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/include/intrinsic.h b/include/intrinsic.h
new file mode 100644
index 0000000..35a30ed
--- /dev/null
+++ b/include/intrinsic.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include "utils/filesys.h"
+#include "utils/huff.h"
+#include "error_info.h"
+
+#ifdef _MSC_VER
+#pragma warning( push )
+#pragma warning( disable : 4090 )
+#pragma warning( disable : 594 )
+#pragma warning( disable : 542 )
+#endif
+
+extern const char dateout[16];
+extern const char timeout[20];
+extern const char* commands[];
+extern const int noCmds;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int help(const char*);
+int find_help_section(const char*);
+int motd(const char*);
+
+int prompt(const char*);
+int quit(const char*);
+
+#ifdef _MSC_VER
+#pragma warning ( pop )
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/include/logger.h b/include/logger.h
new file mode 100644
index 0000000..b0b6702
--- /dev/null
+++ b/include/logger.h
@@ -0,0 +1,12 @@
+#pragma once
+
+
+/**
+ * Logging thread, complately alpha code- may completely change form in the near future.
+ */
+
+inline int
+StartLogger(int nLevel)
+{
+ return CMD_OK;
+}
\ No newline at end of file
diff --git a/include/parser.h b/include/parser.h
new file mode 100644
index 0000000..e956583
--- /dev/null
+++ b/include/parser.h
@@ -0,0 +1,141 @@
+#pragma once
+
+#include "foo1.h"
+#ifdef _MSC_VER
+#include
+#include "winunistd.h"
+#define STRLWR _strlwr
+extern size_t getline(char**, size_t*, FILE*);
+#else
+#include
+#include
+extern char* strlwr(char*);
+#define STRLWR strlwr
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MSC_VER
+#pragma warning( push )
+#pragma warning( disable : 4090 )
+#pragma warning( disable : 542 )
+#endif
+
+extern const char* strstrip(char*);
+
+// Just a simple jump table
+const char* commands[] = {
+ "dummy",
+ "help",
+ "aaa",
+ "access-lists",
+ "amplifiers",
+ "app",
+ "arp",
+ "badl",
+ "batch",
+ "bert",
+ "motd",
+ "prompt",
+ "quit",
+ "date",
+ "lists",
+ "time2",
+ "loglevel",
+ "filet",
+ "?" };
+
+int (*table[])() = {
+ dummy, help, aaa, alist, amp, app, arp, badl, batch, bert,
+ motd, prompt, quit, date, list, time2, loglevel, filet,
+};
+
+#ifdef _MSC_VER
+int const noCmds = _countof(table);
+#else
+const int noCmds = *(&table + 1) - table;
+#endif
+
+bool
+run_cmd(int nCmd, char* full_cmd)
+{
+ int n = 0;
+
+ if(nCmd == CMD_ERR) {
+ printf("\nUnknown command.\n");
+ return true;
+ }
+
+ // If help is invoked with '?'
+ if (nCmd == noCmds) nCmd = 1;
+ if((nCmd == 1) && (strlen(full_cmd) < 5)) {
+ printf("\n");
+
+ while (strchr(*(commands + ++n), '?') == NULL)
+ printf("- %s\n", *(commands + n));
+ printf("\n");
+ }
+
+ int nStatus = CMD_ERR;
+ int (**p)(char*);
+ p = table;
+
+ // command found, execute it.
+ nStatus = (*p[nCmd])(strstrip(full_cmd));
+ // if its the 'special' quit command; bail.
+ if (nStatus == CMD_QUIT)
+ return false;
+
+ return true;
+}
+
+/**
+ * @brief Process the commands rejecting those that aren't on the list
+ * This needs to be moved to another translation unit (completed 11/2023)
+ *
+ * @param char** inp The string to process
+ * @param int size The number of command elements
+ * @return boolean returns the status of the run_cmd
+ * function, which is only false if we
+ * process a quit command.
+ */
+bool
+proc_cmds(char** inp, int size)
+{
+ int n = 0, nCmd = 0;
+ bool bFound = false;
+ char full_cmd[200], cmd[40];
+
+ memset(full_cmd, '\n', sizeof(full_cmd));
+ memset(cmd, '\n', sizeof(cmd));
+
+ for(n = 0; n <= noCmds; n++) {
+ if(strncmp(inp[0],
+ *(commands + n),
+ strlen(strstrip(STRLWR(inp[0])))) == 0) {
+ bFound = true;
+ }
+ if(bFound) {
+ nCmd = n;
+ strcpy(full_cmd, *(commands + n));
+
+ for(int m = 1; m < size; m++) {
+ strcat(full_cmd, " ");
+ strcat(full_cmd, inp[m]);
+ }
+ printf("\n");
+ return run_cmd(nCmd, full_cmd);
+ }
+ }
+ return run_cmd(CMD_ERR, NULL);
+}
+
+#ifdef _MSC_VER
+#pragma warning ( pop )
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/include/simple_strlib.h b/include/simple_strlib.h
new file mode 100644
index 0000000..55b4d63
--- /dev/null
+++ b/include/simple_strlib.h
@@ -0,0 +1,266 @@
+#pragma once
+
+#ifdef _MSVC_VER
+#include
+#endif
+#include "support.h"
+
+/**
+ * @file simple_strlib.h
+ * @brief Some additions that seem to be missing or
+ * are missing from the clib port.
+ *
+ * @details We've rolled our own string functions, they
+ * were either missing or I didn't like the way
+ * they worked. (I tend to favor in-place string
+ * manips for embedded work.) Needs to be tested
+ * for thread safety.
+ *
+ * @author t.o.
+ * @bug
+ */
+
+#include
+#include
+#include
+#include
+#ifndef _WIN32
+#include
+#endif
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+/**
+ * @brief strlwr
+ *
+ * @details My own version of strlwr, becuase clib
+ * doesn't appear to have one.
+ *
+ * @param s The string to convert
+ * @return pointer to the modified string
+ */
+inline char*
+strlwr(char* s)
+{
+ char* p = s;
+
+ while(*p) {
+ *p = tolower((uint32_t)*p);
+ p++;
+ }
+ return s;
+}
+
+/**
+ * @brief countc
+ *
+ * @details Count the number of occurrences of c in s
+ *
+ * @param s The target string
+ * @param c The character to count
+ * @return the count
+ */
+static inline int
+countc(const char* s, char c)
+{
+ int n = 0;
+
+ while(*s++ != '\0') {
+ if(*s == c)
+ n++;
+ }
+ return n;
+}
+
+/**
+ * @brief Strip trailing whitespace from s inplace
+ *
+ * @param s The string to trim
+ * @return the modified string
+ */
+inline const char*
+strstrip(char* s)
+{
+ size_t size = strlen(s);
+ if (!size)
+ return s;
+
+ char* end;
+ end = s + size - 1;
+ while(end >= s && isspace(*end))
+ end--;
+
+ *(end + 1) = '\0';
+ while(*s && isspace(*s))
+ s++;
+
+ return s;
+}
+
+/**
+ * @brief Split the string at delimiters using a zero-copy in-place algorithm
+ *
+ * @param s The string to split (every delimiter will be replaced by NULL)
+ * @param delimiter The delimiter character to split at
+ * @param size The size of the return array w
+ * @return A list of char* (size stored in *size), pointing to all split results in order
+ * Don't forget to free the returned array in the caller
+ */
+static inline char**
+split(char* s, char delimiter, int* size)
+{
+ int numdelimiters = countc(s, delimiter);
+ char** splitresult = malloc((numdelimiters + 2) * sizeof(char*)); // +2 for the last substring and NULL terminator
+
+ if (splitresult == NULL) {
+ *size = 0;
+ return NULL; // Memory allocation failed
+ }
+
+ splitresult[0] = s;
+ int i = 1;
+ char* hit = s;
+
+ while ((hit = strchr(hit, delimiter)) != NULL) {
+ *hit++ = '\0';
+ splitresult[i++] = hit;
+ }
+ splitresult[i] = NULL; // Mark the end of the splitresult array with NULL
+ *size = numdelimiters + 1;
+
+ return splitresult;
+}
+
+/**
+ * @breif Remove the substring matched. Operates
+ * in-place.
+ *
+ * @param the string to operate on
+ * @param the character sequence to remove
+ */
+static inline void
+remove_first(char* str, const char* str_remove)
+{
+ size_t j;
+ size_t len;
+ size_t remove_len;
+ int found = 0;
+
+ len = strlen(str);
+ remove_len = strlen(str_remove);
+
+ for(size_t i = 0; i < len; i++) {
+ found = 1;
+ for(j = 0; j < remove_len; j++) {
+ if(*(str + i + j) != *(str_remove + j)) {
+ found = 0;
+ break;
+ }
+ }
+ // If word has been found then remove it by shifting characters
+ if(found == 1) {
+ for(j = i; j <= len - remove_len; j++)
+ *(str + j) = *(str + j + remove_len);
+
+ // Terminate from loop so only first occurrence is removed
+ break;
+ }
+ }
+}
+
+/**
+ * @brief find first index of character in string
+ *
+ * @param string string to operate on
+ * @param char char to find index of
+ * @return integer index of the first occurance of char
+ */
+static inline int
+find_ch_index(const char* string, char ch)
+{
+ int i = 0;
+
+ while(*(string + i) && *(string + i) != ch) i++;
+
+ return *(string + i) == ch ? -1 : i;
+}
+
+/**
+ * @brief pass a substring of the source string through an i/o parameter
+ *
+ * @param char pointer source string
+ * @param start index of starting character for subsctring in source
+ * @param length of source string
+ * @param char pointer to destination string buffer
+ * @param length of destination string buffer
+ */
+static inline void
+mid(const char* src, size_t start, size_t length, char* dst, size_t dstlen)
+{
+ size_t len = MIN(dstlen, length);
+ strncpy(dst, src + start, len);
+ // zero terminate because strncpy() didn't ?
+ if(len < length)
+ dst[dstlen - 1] = 0;
+}
+
+static char*
+right(char* string, int size)
+{
+ while(*string++);
+
+ return(string - size - 2);
+}
+
+static char*
+replace_char(char* str, char find, char replace){
+ char *current_pos = strchr(str,find);
+ while (current_pos) {
+ *current_pos = replace;
+ current_pos = strchr(current_pos,find);
+ }
+ return str;
+}
+
+static void
+substr(const char* inputString, char delimiter, char** resultBuffer) {
+ // Find the first occurrence of the delimiter
+ const char* firstOccurrence = strchr(inputString, delimiter);
+
+ if (firstOccurrence == NULL) {
+ // Delimiter not found
+ *resultBuffer = NULL;
+ return;
+ }
+
+ // Find the second occurrence of the delimiter
+ const char* secondOccurrence = strchr(firstOccurrence + 1, delimiter);
+
+ if (secondOccurrence == NULL) {
+ // Second occurrence not found
+ *resultBuffer = NULL;
+ return;
+ }
+
+ // Calculate the length of the substring
+ size_t substringLength = secondOccurrence - (firstOccurrence + 1);
+
+ // Allocate memory for the result buffer
+ *resultBuffer = (char*)malloc(substringLength + 1);
+
+ if (*resultBuffer == NULL) {
+ // Memory allocation failed
+ fprintf(stderr, "Memory allocation failed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ // Copy the substring into the result buffer
+ strncpy(*resultBuffer, firstOccurrence + 1, substringLength);
+ (*resultBuffer)[substringLength] = '\0'; // Null-terminate the string
+}
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/include/support.h b/include/support.h
new file mode 100644
index 0000000..0530fbf
--- /dev/null
+++ b/include/support.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#ifdef _MSC_VER
+#pragma warning( push )
+#pragma warning( disable : 5945 )
+#include
+#else
+#include
+#endif
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+/**
+ * Internal objects ONLY
+ */
+#define MAXMOTD 512
+#define MAXBUF 256
+
+#ifndef SIZEARRAY
+#define SIZEARRAY(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+typedef int (*command_object)(char*);
+//typedef enum {false, true} bool;
+
+struct uinfo {
+ struct passwd* info;
+};
+
+struct user_ctx
+{
+ int loglevel;
+ bool admin;
+ char prompt[16];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
diff --git a/include/threadpool.h b/include/threadpool.h
new file mode 100755
index 0000000..0e2c023
--- /dev/null
+++ b/include/threadpool.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#define DEFAULT_THREAD_POOL_SIZE 5
+
+typedef struct ThreadPool ThreadPool;
+typedef struct Task Task;
+
+struct Task {
+ void (*function)(void* arg);
+ void* arg;
+ Task* next;
+};
+
+struct ThreadPool {
+ mtx_t lock;
+ cnd_t condition;
+
+ Task* task_queue;
+ thrd_t* threads;
+ size_t num_threads;
+
+ bool shutdown;
+};
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+// Function to initialize a thread pool
+ThreadPool* thread_pool_init(size_t num_threads);
+
+// Function to destroy a thread pool
+void thread_pool_destroy(ThreadPool* pool);
+
+// Function to add a task to the thread pool
+bool thread_pool_enqueue(ThreadPool* pool, void (*function)(void*), void* arg);
+
+// Function executed by each thread in the pool
+int thread_function(void* arg);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/include/utils/config.h b/include/utils/config.h
new file mode 100644
index 0000000..b93b972
--- /dev/null
+++ b/include/utils/config.h
@@ -0,0 +1,111 @@
+#ifndef _TCONFIG_H_
+#define _TCONFIG_H_
+#include
+
+typedef struct ini_entry_s {
+ char* key;
+ char* value;
+} ini_entry_s;
+
+typedef struct ini_section_s {
+ char* name;
+ ini_entry_s* entry;
+ int size;
+} ini_section_s;
+
+typedef struct ini_table_s {
+ ini_section_s* section;
+ int size;
+} ini_table_s;
+
+/**
+ * @brief Creates an empty ini_table_s struct for writing new entries to.
+ * @return ini_table_s*
+ */
+ini_table_s* ini_table_create();
+
+/**
+ * @brief Free up all the allocated resources in the ini_table_s struct.
+ * @param table
+ */
+void ini_table_destroy(ini_table_s* table);
+
+/**
+ * @brief Creates an ini_table_s struct filled with data from the specified
+ * `file'. Returns NULL if the file can not be read.
+ * @param table
+ * @param file
+ * @return ini_table_s*
+ */
+bool ini_table_read_from_file(ini_table_s* table, const char* file);
+
+/**
+ * @brief Writes the specified ini_table_s struct to the specified `file'.
+ * Returns false if the file could not be opened for writing, otherwise
+ * true.
+ * @param table
+ * @param file
+ * @return bool
+ */
+bool ini_table_write_to_file(ini_table_s* table, const char* file);
+
+/**
+ * @brief Creates a new entry in the `table' containing the `key' and `value'
+ * provided if it does not exist. Otherwise, modifies an exsiting `key'
+ * with the new `value'
+ * @param table
+ * @param section_name
+ * @param key
+ * @param value
+ */
+void ini_table_create_entry(ini_table_s* table, const char* section_name,
+ const char* key, const char* value);
+
+/**
+ * @brief Checks for the existance of an entry in the specified `table'. Returns
+ * false if the entry does not exist, otherwise true.
+ * @param table
+ * @param section_name
+ * @param key
+ * @return bool
+ */
+bool ini_table_check_entry(ini_table_s* table, const char* section_name,
+ const char* key);
+
+/**
+ * @brief Retrieves the unmodified value of the specified `key' in `section_name'.
+ * Returns NULL if the entry does not exist, otherwise a pointer to the
+ * entry value data.
+ * @param table
+ * @param section_name
+ * @param key
+ * @return const char*
+ */
+const char* ini_table_get_entry(ini_table_s* table, const char* section_name,
+ const char* key);
+
+/**
+ * @brief Retrieves the value of the specified `key' in `section_name', converted
+ * to int. Returns false on failure, otherwise true.
+ * @param table
+ * @param section_name
+ * @param key
+ * @param [out]value
+ * @return int
+ */
+bool ini_table_get_entry_as_int(ini_table_s* table, const char* section_name,
+ const char* key, int* value);
+
+/**
+ * @brief Retrieves the value of the specified `key' in `section_name', converted
+ * to bool. Returns false on failure, true otherwise.
+ * @param table
+ * @param section_name
+ * @param key
+ * @param [out]value
+ * @return bool
+ */
+bool ini_table_get_entry_as_bool(ini_table_s* table, const char* section_name,
+ const char* key, bool* value);
+
+#endif//_TCONFIG_H_
diff --git a/include/utils/filesys.h b/include/utils/filesys.h
new file mode 100644
index 0000000..50681ee
--- /dev/null
+++ b/include/utils/filesys.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#ifndef _MSC_VER
+#include
+#include
+#else
+#include
+#include
+#endif
+#include "support.h"
+#include "simple_strlib.h"
+#include "error_info.h"
+
+#define MAXSIZE 256
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file filesys.h
+ * @author Tim O'Neil
+ * @date 3/9/2018
+ *
+ * @brief System-specific & near system-specific utilities
+ *
+ * @section DESCRIPTION
+ *
+ * Collection of functions that will be more system specific
+ * and non-portable (mostly file systems utils and such).
+ */
+int find_cfgfile(const char*, char*, bool);
+int create_cfgfile(const char*);
+void get_userdir(char*);
+
+int write_motd(char*, const char*);
+int read_motd(const char*);
+const char* get_keyvalue(const char*, const char*);
+
+int set_keyvalue(const char*, const char*);
+bool file_exists(const char*);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/include/utils/getopt.h b/include/utils/getopt.h
new file mode 100644
index 0000000..584561e
--- /dev/null
+++ b/include/utils/getopt.h
@@ -0,0 +1,84 @@
+/* A minimal POSIX getopt() implementation in ANSI C
+ *
+ * This is free and unencumbered software released into the public domain.
+ *
+ * This implementation supports the convention of resetting the option
+ * parser by assigning optind to 0. This resets the internal state
+ * appropriately.
+ *
+ * Ref: http://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html
+ */
+#ifndef GETOPT_H
+#define GETOPT_H
+
+#include
+#include
+#include
+
+int optind;
+int opterr = 1;
+int optopt;
+char *optarg;
+
+int
+getopt(int argc, char * const argv[], const char *optstring)
+{
+ static int optpos = 1;
+ const char *arg;
+
+ /* Reset? */
+ if (optind == 0) {
+ optind = !!argc;
+ optpos = 1;
+ }
+
+ arg = argv[optind];
+ if (arg && strcmp(arg, "--") == 0) {
+ optind++;
+ return -1;
+ } else if (!arg || arg[0] != '-' || !isalnum(arg[1])) {
+ return -1;
+ } else {
+ const char *opt = strchr(optstring, arg[optpos]);
+ optopt = arg[optpos];
+ if (!opt) {
+ if (opterr && *optstring != ':')
+ fprintf(stderr, "%s: illegal option: %c\n", argv[0], optopt);
+ if (!arg[++optpos]) {
+ optind++;
+ optpos = 1;
+ }
+ return '?';
+ } else if (opt[1] == ':') {
+ if (arg[optpos + 1]) {
+ optarg = (char *)arg + optpos + 1;
+ optind++;
+ optpos = 1;
+ return optopt;
+ } else if (argv[optind + 1]) {
+ optarg = (char *)argv[optind + 1];
+ optind += 2;
+ optpos = 1;
+ return optopt;
+ } else {
+ if (opterr && *optstring != ':')
+ fprintf(stderr,
+ "%s: option requires an argument: %c\n",
+ argv[0], optopt);
+ if (!arg[++optpos]) {
+ optind++;
+ optpos = 1;
+ }
+ return *optstring == ':' ? ':' : '?';
+ }
+ } else {
+ if (!arg[++optpos]) {
+ optind++;
+ optpos = 1;
+ }
+ return optopt;
+ }
+ }
+}
+
+#endif
diff --git a/include/utils/huff.h b/include/utils/huff.h
new file mode 100755
index 0000000..d3d9d3f
--- /dev/null
+++ b/include/utils/huff.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include
+#include
+#include
+
+/**
+ * @file huff.h
+ * @author Tim O'Neil
+ * @date 3/9/2018
+ *
+ * @brief Probably pretty portable Huffman coder
+ *
+ * @section DESCRIPTION
+ *
+ * Huffman ecoder to compress large blobs of text and anything
+ * meant to be read only through an application UI. This file
+ * defines some necessary structs and such. See the implementation
+ * file for more details.
+ */
+typedef struct node
+{
+ char c;
+ int freq;
+ struct node *link;
+ struct node *rlink;
+ struct node *llink;
+} node_t;
+
+typedef struct List
+{
+ node_t *head;
+} List_t;
+
+typedef struct
+{
+ unsigned char c : 8;
+} code_generated;
+
+typedef struct
+{
+ int freq;
+ unsigned char ch;
+} info;
+
+void decode(const char*);
+void encode(const char*, char*);
+void insert_in_list(List_t*, node_t*);
+
+void init_list(List_t *ptr);
+void disp_list(const List_t*);
+void make_node(List_t*, int, char);
+
+void make_list(List_t*, int*);
+void find_freq(char*, int*);
+void make_tree(List_t*, int*);
+
+void find_code(node_t*, char*, char codes[256][40]);
+void compress(char*, FILE*, char codes[256][40], int, info*, int*, int num);
+unsigned char convert_string_char(char string[8]);
+
+int find_no_of_chars(int*);
+void strre(char*, int);
+node_t* decode_char(FILE*, node_t*, int, int, node_t*, int*, int);
+
+int create_header(int*, int, info*);
diff --git a/linux/ARM_etc/Makefile b/linux/ARM_etc/Makefile
new file mode 100644
index 0000000..3cba3f0
--- /dev/null
+++ b/linux/ARM_etc/Makefile
@@ -0,0 +1,61 @@
+# This is a recursive makefile- do not use in production
+# convert to canonical make chain when time permits
+#
+# recursive file search
+rfilelist=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rfilelist,$d/,$2))
+# the name of program
+TARGET = interp
+CC = gcc
+# C++ compiler flags
+
+LIBS += -pthread -lconfig
+EXT = c
+# source files
+SRC = ./src
+SRCS := $(call rfilelist,$(SRC),*.$(EXT))
+INCLUDES = ./include ./include/utils
+CFLAGS := -Wall -std=c11 -c
+CFLAGS += $(addprefix -I,$(INCLUDES))
+OBJS = $(SRCS:%.$(EXT)=%.o)
+DEBUG_HELPERS = $(SRCS:%.$(EXT)=%.debug)
+OPTIMIZE_HELPERS = $(SRCS:%.$(EXT)=%.optim)
+OBJDEBOUT = $(@:%.debug=%.o)
+OBJOPTOUT = $(@:%.optim=%.o)
+DEBOUT = $(@:%.debug=%.$(EXT))
+OPTOUT = $(@:%.optim=%.$(EXT))
+
+# rules for debug build and optimized build
+%.debug: %.$(EXT)
+ $(CC) $(CFLAGS) -ggdb -D_DEBUG -DDEBUG -o $(OBJDEBOUT) $(DEBOUT)
+ rm -f $(@.debug=%.debug)
+ touch -f $@
+
+%.optim: %.$(EXT)
+ $(CC) $(CFLAGS) -O2 -DNDEBUG -o $(OBJOPTOUT) $(OPTOUT)
+ rm -f $(@.optim=%.optim)
+ touch -f $@
+
+# rules for object files
+%.o: %.$(EXT)
+ $(CC) $(CFLAGS) $?
+
+# default build
+all: debug
+
+# debug build
+debug: $(DEBUG_HELPERS)
+ test -s $@ || mkdir $@
+ $(CC) $(OBJS) -o debug/$(TARGET) $(LIBS)
+ rm -f $(DEBUG_HELPERS)
+
+# optimized build
+optim: $(OPTIMIZE_HELPERS)
+ test -s $@ || mkdir $@
+ $(CC) $(OBJS) -o optim/$(TARGET) $(LIBS)
+ rm -f $(OPTIMIZE_HELPERS)
+ strip optim/$(TARGET)
+
+# clean rule
+clean:
+ rm -f $(OBJS) $(DEBUG_HELPERS) $(OPTIMIZE_HELPERS)
+
diff --git a/linux/ARM_etc/README.md b/linux/ARM_etc/README.md
new file mode 100644
index 0000000..924be3a
--- /dev/null
+++ b/linux/ARM_etc/README.md
@@ -0,0 +1 @@
+Generic makefile, use this for hacking or building on other architectures. This will build on the Raspberry Pi as is.
diff --git a/linux/interp.pro b/linux/interp.pro
new file mode 100644
index 0000000..3cb6586
--- /dev/null
+++ b/linux/interp.pro
@@ -0,0 +1,39 @@
+TEMPLATE = app
+CONFIG += console
+CONFIG -= app_bundle
+CONFIG -= qt
+TARGET = interp
+
+QMAKE_CFLAGS += -Wall -std=c17 -Wno-unused-parameter
+QMAKE_CFLAGS += -Wno-unused-function -Wno-unused-result
+QMAKE_CFLAGS += -Wno-discarded-qualifiers -Wno-int-conversion
+QMAKE_CFLAGS += -Wno-incompatible-pointer-types
+
+INCLUDEPATH += $$PWD/../include
+
+LIBS += -lpthread
+
+SOURCES += \
+ ../src/foo1.c \
+ ../src/intrinsic.c \
+ ../src/support.c \
+ ../src/main.c \
+ ../src/support.c \
+ ../src/utils/filesys.c \
+ ../src/utils/huff.c \
+ ../src/utils/config.c
+
+HEADERS += \
+ ../include/commands.h \
+ ../include/foo1.h \
+ ../include/intrinsic.h \
+ ../include/parser.h \
+ ../include/simple_strlib.h \
+ ../include/support.h \
+ ../include/utils/filesys.h \
+ ../include/utils/huff.h \
+ ../include/utils/config.h
+
+CONFIG(release, debug|release) {
+QMAKE_POST_LINK=$(STRIP) $(TARGET)
+}
diff --git a/newcmds.md b/newcmds.md
new file mode 100644
index 0000000..398ffba
--- /dev/null
+++ b/newcmds.md
@@ -0,0 +1,46 @@
+### Adding New Commands to the Interpreter
+
+
The code consists of intrinsic commands and add-on commands. Intrinsic commands shouldn't be modified
+or expanded.
+
Add-on commands are all the other commands you can see in the "foo1" source file. Add-ons can be added to
+those existing sources, added to an entirely new translation unit, to a static library or a shared lib, whatever
+makes the most sense for your application. You're also welcome to remove all of those commands and add your own, of course.
+
+#### Adding a Log Level Command
+
+
In this example, we'll add a new command called `loglevel` that will set the logging level of the of the
+interpreter. There are lots of descisions to make in developing a loggng facility to an application; mostly
+its scope, where it'll be written and its persistence, but since we won't actually implement a working logging
+facility we'll just worry about adding the command to the interpreter.
+
+
Let's add the command "loglevel" with an integer parameter 0-4. 0 will mean logging off, and 4 is the highest
+level of verbosity:
+
+1. Add our new command "loglevel" to the "commands" double array in the "parser.h" file. The question mark must be the last command in the array, and help must always be first:
+ ``` c
+ const char* commands[] = { "dummy",
+ "help",
+ "aaa",
+ "access-lists",
+ "amplifiers",
+ "app",
+ <...>
+ "loglevel",
+ "?" };
+```
+2. Add a "loglevel" token to the "table" double array. Note, this is just an index token, not a string.
+Importance is its place in the table, not its value:
+``` c
+int (*table[])() = {
+ dummy, help, aaa, alist, amp, app, arp, badl, batch, bert,
+ help, motd, prompt, quit, date, list, time2, loglevel
+};
+```
+3. For statically linked commands define the function(s) for your new command in either foo1.c or add a new source file to the project. Add the new function prototypes to the appropriate header files. Use a pointer to the command invocation for access to parameters and delimeter processing; for example; if you want to use posix-style parameter marking ('-' & '--', and etc) define that in your new translation unit and pass all paramters through it before routing to the actual command definition. Look to the command examples in foo1.c for examples.
+
+4. Add a useful description of the new command the ./extra/help file.
+
+No modification of the parser code should be required for the UTF-8 charcter set-based projects. A list of all the commands is printed out when the user enters "help" or "?" and generated automtically from the available data. Command descriptions must be entered into the text file in the extra directory.
+
+The "dummy" command is nessessary for now, obviously an index is off, I'll be getting rid of that when I find the error.
+If you do see a problem a report would be appreciated. interval1066@gmail.com.
diff --git a/src/foo1.c b/src/foo1.c
new file mode 100644
index 0000000..a853c21
--- /dev/null
+++ b/src/foo1.c
@@ -0,0 +1,216 @@
+#include
+#include "foo1.h"
+#include "utils/filesys.h"
+
+const char dateout[16];
+const char timeout[20];
+extern const char* commands;
+extern const int noCmds;
+extern struct user_ctx user;
+
+#ifdef _MSC_VER
+#pragma warning( push )
+#pragma warning( disable : 836 )
+#pragma warning( disable : 594 )
+#pragma warning( disable : 4090 )
+#pragma warning( disable : 859 )
+#endif
+
+/**
+ * Command objects. Normally these would go into separate translation units.
+ * Quit is a special command that has its own return status, needed to signal
+ * to main that we are exiting.
+ * Callee functions are responsible for parsing and converting the opts string
+ * for any necessary arguments.
+ */
+int
+dummy(const char* opts)
+{
+ return CMD_OK;
+}
+
+int
+aaa(const char* opts)
+{
+ int size;
+ char** splitresult = split(opts, ' ', &size);
+
+ if(size < 2)
+ printf("%s called with no options.\n", splitresult[0]);
+
+ for(int n = 1; n < size; n++)
+ printf("%s called with option %s.\n", splitresult[0], splitresult[n]);
+
+ free(splitresult);
+
+ return CMD_OK;
+}
+
+int
+alist(const char* opts)
+{
+ int size;
+ char** splitresult = split(opts, ' ', &size);
+
+ for(int n = 1; n < size; n++)
+ printf("access_lists called with option %s.\n", splitresult[n]);
+
+ free(splitresult);
+
+ return CMD_OK;
+}
+
+int
+amp(const char* opts)
+{
+ printf("amp called with %s\n", opts);
+
+ return CMD_OK;
+}
+
+int
+app(const char* opts)
+{
+ printf("app called with %s\n", opts);
+ return CMD_OK;
+}
+
+int
+arp(const char* opts)
+{
+ printf("arp called with %s\n", opts);
+ return CMD_OK;
+}
+
+int
+badl(const char* opts)
+{
+ int n = 0;
+ char line[100];
+ FILE* fp = fopen("./list.dev", "r");
+
+ if(!fp) {
+ printf("No lists have been created yet\n");
+ return CMD_FILEEXST;
+ }
+
+ while(fgets(line, sizeof(line), fp))
+ if(strlen(line) > 1) printf("%d - %s", ++n, line);
+
+ fclose(fp);
+ return CMD_OK;
+}
+
+int
+batch(const char* opts)
+{
+ printf("batch called with %s\n", opts);
+ return CMD_OK;
+}
+
+int
+bert(const char* opts)
+{
+ printf("bert called with %s\n", opts);
+ return CMD_OK;
+}
+
+int
+get_date(const char* opts)
+{
+ sprintf(opts, "%s\n", __DATE__);
+ return CMD_OK;
+}
+
+int
+date(const char* opts)
+{
+ memset((const char*)dateout, '\0', sizeof(dateout));
+ get_date(dateout);
+ printf("%s\n", dateout);
+
+ return CMD_OK;
+}
+
+int
+get_time(const char* opts)
+{
+ sprintf(opts, "%s\n", __TIME__);
+ return CMD_OK;
+}
+
+int
+time2(const char* opts)
+{
+ memset((const uint64_t*)timeout, '\0', sizeof(timeout));
+ get_time(timeout);
+ printf("%s\n", timeout);
+ return CMD_OK;
+}
+
+int
+list(const char* opts)
+{
+ FILE* f = NULL;
+ char buf[16] = { 0 };
+ char dev[16] = { 0 };
+
+ strncpy(buf, right(opts, (int)strlen(opts) - 6), strlen(opts) - 2);
+ if(strlen(buf) == 2)
+ return CMD_ARGS;
+
+ printf("%s\n", buf);
+ if(strcmp(buf, "all") == 0) {
+ badl(NULL);
+ return CMD_OK;
+ }
+
+ printf("Adding new device %s to list.\n", buf);
+ f = fopen("./list.dev", "w");
+ if(f == NULL)
+ return CMD_IOERR;
+
+ do {
+ scanf("%15s", dev);
+ if(strcmp(dev, ".") != 0)
+ fwrite(&dev, strlen(dev), 1, f);
+ fprintf(f, "\n");
+ } while(strlen(dev) > 1);
+
+ if(f) fclose(f);
+
+ return CMD_OK;
+}
+
+int
+loglevel(const char* opts)
+{
+ int size;
+ char** splitresult = split((const char*)opts, ' ', &size);
+
+ if(size == 1) {
+ printf("Current log level: %i\n", user.loglevel);
+ free(splitresult);
+ return CMD_OK;
+ }
+
+ int m = atoi(splitresult[1]);
+ if((m < 0) && m > 4)
+ return CMD_ARGS;
+
+ user.loglevel = m;
+ free(splitresult);
+
+ return CMD_OK;
+}
+
+int
+filet(const char* opts)
+{
+ return CMD_OK;
+}
+
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
diff --git a/src/intrinsic.c b/src/intrinsic.c
new file mode 100644
index 0000000..221dad3
--- /dev/null
+++ b/src/intrinsic.c
@@ -0,0 +1,125 @@
+#include "intrinsic.h"
+
+extern struct user_ctx user;
+extern int writeconfig();
+
+#ifdef _MSC_VER
+#pragma warning( push )
+#pragma warning( disable : 4090 )
+#pragma warning( disable : 594 )
+#endif
+
+int
+help(const char* opts)
+{
+ int size;
+ int retcode;
+ char** splitresult = split(opts, ' ', &size);
+
+ if(size == 1)
+ retcode = find_help_section(opts);
+ else
+ retcode = find_help_section(splitresult[1]);
+
+ free(&splitresult[0]);
+
+ return retcode;
+}
+
+int
+find_help_section(const char* section)
+{
+ bool found = false;
+ FILE* fp = NULL;
+ char cfg_path[MAXBUF];
+ char chunk[MAXBUF];
+
+ memset(cfg_path, 0, sizeof(cfg_path));
+ get_userdir(cfg_path);
+#ifndef _MSC_VER
+ strcat(cfg_path, "/.interp.hlp");
+#else
+ strcat(cfg_path, "\\.interp.hlp");
+#endif
+
+ fp = fopen(cfg_path, "r");
+ if(!fp) return CMD_FILEEXST;
+
+ while(fgets(chunk, sizeof(chunk), fp) != NULL) {
+ int n = strncmp(section, chunk, strlen(section));
+
+ if(n == 0) {
+ replace_char(chunk, '|', '\n');
+ fputs(chunk, stdout);
+ found = true;
+ }
+ }
+
+ if((!found) && strncmp(section, "?", (size_t)1) != 0)
+ printf("Keyword not found\n");
+
+ if(fp) fclose(fp);
+
+ return CMD_OK;
+}
+
+int
+motd(const char* string)
+{
+ char cfg_path[MAXBUF];
+ char mot_path[MAXBUF];
+
+ if(strlen(string) < 3)
+ return CMD_ARGS;
+
+ if(strchr(string, '"') == NULL)
+ return CMD_QUOTES;
+
+ memset(cfg_path, 0, sizeof(cfg_path));
+ get_userdir(cfg_path);
+ strcpy(mot_path, cfg_path);
+
+ remove_first(mot_path, ".interp.ini");
+#ifndef _MSC_VER
+ strcat(mot_path, "/.motd");
+#else
+ strcat(mot_path, "\\.motd");
+#endif
+ char newm[MAXMOTD];
+ memset(newm, '\0', MAXMOTD);
+ mid(string, 6, find_ch_index(string, '"'), newm, strlen(string));
+
+ remove_first(newm, "\"");
+ encode(mot_path, newm);
+
+ return CMD_OK;
+}
+
+int
+prompt(const char* opts)
+{
+ char *buf;
+
+ int n = countc(opts, '\"');
+ if (n < 2)
+ return CMD_ARGS;
+
+ substr(opts, '\"', &buf);
+ memset(user.prompt, 0, strlen(user.prompt));
+ strcpy(user.prompt, buf);
+
+ free(buf);
+
+ return CMD_OK;
+}
+
+int
+quit(const char* opts)
+{
+ return CMD_QUIT;
+}
+
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..91de856
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,272 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "utils/config.h"
+#include "intrinsic.h"
+#include "support.h"
+#include "parser.h"
+#include "error_info.h"
+#include "logger.h"
+
+extern const int noCmds;
+extern int help(const char*);
+extern int StartLogger(int);
+extern bool proc_cmds(char**, int);
+struct user_ctx user;
+char str[1024];
+
+#define MAX_LINE 1024
+
+/**
+ * @file main.c
+ * @author Tim O'Neil
+ * @date 3/9/2018
+ *
+ * @brief Probably pretty portable CLI interpreter
+ *
+ * @section DESCRIPTION test
+ *
+ * Alternate command line interpreter prototype.
+ */
+
+/**
+ * Anticipate ctrl + c
+ *
+ * no signals we need to handle as far as we
+ * know right now. May change in the future.
+ */
+void
+sig_handler(int sign)
+{
+ signal(SIGINT, sig_handler);
+}
+
+/**
+* buffer sanitizer, on hold for now
+*/
+static int
+sanitize_buf()
+{
+ static char ok_chars[] = "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "1234567890_-.@";
+
+ char user_data[] = "Bad char 1:} Bad char 2:{";
+ char *cp = user_data; // Cursor into string
+ const char *end = user_data + strlen(user_data);
+
+ for (cp += strspn(cp, ok_chars); cp != end; cp += strspn(cp, ok_chars))
+ *cp = '_';
+
+ return CMD_OK;
+}
+
+static void
+reverse(char s[])
+{
+ size_t i, j;
+ char c;
+
+ for (i = 0, j = (int)strlen(s) - 1; i < j; i++, j--) {
+ c = s[i];
+ s[i] = s[j];
+ s[j] = c;
+ }
+}
+
+static void
+myitoa(int n, char s[])
+{
+ size_t i, sign;
+
+ if ((sign = n) < 0) /* record sign */
+ n = -n; /* make n positive */
+ i = 0;
+ do { /* generate digits in reverse order */
+ s[i++] = n % 10 + '0'; /* get next digit */
+ } while ((n /= 10) > 0); /* delete it */
+ if (sign < 0)
+ s[i++] = '-';
+ s[i] = '\0';
+ reverse(s);
+}
+
+/**
+ * initial state
+ *
+ * set defaults including prompt which is a "> " (greater than + space), can be changed
+ * via the 'prompt' intrinsic command.
+ */
+static int
+init(void)
+{
+ char tmp[16] = { 0 };
+ memset(user.prompt, 0, sizeof(user.prompt));
+ strcpy(tmp, get_keyvalue("prompt", ""));
+
+ if(strlen(tmp) == 0)
+ strcpy(tmp, get_keyvalue("prompt", ">"));
+
+#ifndef _MSC_VER
+ int len2 = (int)strlen(tmp);
+ tmp[len2 - 1] = '\0';
+#endif
+ char banner_path[MAXBUF];
+ char cfg_path[MAXBUF];
+ signal(SIGINT, sig_handler);
+ memset(cfg_path, 0, sizeof(cfg_path));
+
+ memset(banner_path, 0, sizeof(banner_path));
+ get_userdir(cfg_path);
+ strcpy(banner_path, cfg_path);
+
+#ifndef _MSC_VER
+ strcat(banner_path, "/.motd");
+ strcat(cfg_path, "/.interp.ini");
+#else
+ strcat(banner_path, "\\.motd");
+ strcat(cfg_path, "\\.interp.ini");
+#endif
+
+ if (access(banner_path, 0) == 0) {
+ decode(banner_path);
+ printf("\n");
+ }
+ return CMD_OK;
+}
+
+static int
+readconfig(void)
+{
+ user.admin = 0;
+ char adminperm[10] = { 0 };
+ char cfg_path[MAXBUF] = { 0 };
+
+ get_userdir(cfg_path);
+#ifndef _MSC_VER
+ strcat(cfg_path, "/.interp.ini");
+#else
+ strcat(cfg_path, "\\.interp.ini");
+#endif
+ ini_table_s* config = ini_table_create();
+ if(ini_table_read_from_file(config, cfg_path)) {
+
+ strcpy(user.prompt, ini_table_get_entry(config, "Main", "prompt"));
+ user.loglevel = atoi(ini_table_get_entry(config, "Main", "loglevel"));
+ strcpy(adminperm, ini_table_get_entry(config, "Main", "admin"));
+
+ if(strcmp(adminperm, "true") == 0)
+ user.admin = 1;
+ }
+ ini_table_destroy(config);
+
+ return CMD_OK;
+}
+
+static int
+writeconfig(void)
+{
+ char cfg_path[MAXBUF] = { 0 };
+ char astr[10] = { 0 };
+ get_userdir(cfg_path);
+
+#ifndef _MSC_VER
+ strcat(cfg_path, "/.interp.ini");
+#else
+ strcat(cfg_path, "\\.interp.ini");
+#endif
+
+ ini_table_s* config = ini_table_create();
+ if(ini_table_read_from_file(config, cfg_path)) {
+
+ ini_table_create_entry(config, "Main", "prompt", user.prompt);
+ myitoa(user.loglevel, astr);
+ ini_table_create_entry(config, "Main", "loglevel", astr);
+
+ if(user.admin == 1)
+ ini_table_create_entry(config, "Main", "admin", "true");
+ else
+ ini_table_create_entry(config, "Main", "admin", "false");
+
+ ini_table_write_to_file(config, cfg_path);
+ }
+
+ ini_table_destroy(config);
+
+ return CMD_OK;
+}
+
+#ifdef _MSV_VER
+char*
+get_cmdln(void)
+{
+ char ch;
+ int i = 0, flag = 0;
+
+ for (i = 0; i < MAX_LINE && flag == 0; ++i) {
+ ch = _getch();
+ switch (ch) {
+ case 13:
+ str[i] = '\0';
+ flag = 1;
+ break;
+
+ case '\b':
+ if (i > 0) i--;
+ str[i--] = '\0';
+ printf("\b \b");
+ break;
+
+ default:
+ str[i] = ch;
+ printf("%c", ch);
+ }
+ }
+ str[strlen(str)] = '\0';
+
+ return &str[0];
+}
+#endif
+
+/**
+ * main.c, pretty self explanitory I hope.
+ * Will be changed to take command line options eventually.
+ */
+int
+main(int argc, char** argv)
+{
+ size_t size, len;
+ bool bDo = true;
+ //char* cmd_string = NULL;
+
+ init();
+ readconfig();
+
+ do {
+ len = 0;
+ char* cmd_string = NULL;
+ printf("%s ", user.prompt);
+
+#ifdef _MSV_VER
+ cmd_string = get_cmdln();
+#else
+ getline(&cmd_string, &len, stdin);
+#endif
+ size = (size_t)strlen(cmd_string);
+ char** splitresult = split(cmd_string, ' ', &size);
+ bDo = proc_cmds(splitresult, size);
+
+ free(&splitresult[0]);
+ } while (bDo);
+
+ writeconfig();
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/src/threadpool.c b/src/threadpool.c
new file mode 100755
index 0000000..c7b6015
--- /dev/null
+++ b/src/threadpool.c
@@ -0,0 +1,141 @@
+#include "threadpool.h"
+
+ThreadPool*
+thread_pool_init(size_t num_threads)
+{
+ ThreadPool* pool = malloc(sizeof(ThreadPool));
+ if(!pool) {
+ perror("Memory allocation failed for thread pool");
+ return NULL;
+ }
+
+ if (mtx_init(&pool->lock, mtx_plain) != thrd_success) {
+ perror("Mutex initialization failed");
+ free(pool);
+
+ return NULL;
+ }
+
+ if (cnd_init(&pool->condition) != thrd_success) {
+ perror("Condition variable initialization failed");
+ mtx_destroy(&pool->lock);
+
+ free(pool);
+ return NULL;
+ }
+
+ pool->task_queue = NULL;
+ pool->num_threads = num_threads > 0 ? num_threads : DEFAULT_THREAD_POOL_SIZE;
+ pool->shutdown = false;
+
+ pool->threads = malloc(pool->num_threads * sizeof(thrd_t));
+ if (!pool->threads) {
+ perror("Memory allocation failed for thread pool threads");
+
+ mtx_destroy(&pool->lock);
+ cnd_destroy(&pool->condition);
+ free(pool);
+
+ return NULL;
+ }
+
+ for (size_t i = 0; i < pool->num_threads; ++i) {
+ if (thrd_create(&pool->threads[i], thread_function, pool) != thrd_success) {
+ perror("Thread creation failed");
+
+ thread_pool_destroy(pool);
+ return NULL;
+ }
+ }
+
+ return pool;
+}
+
+void
+thread_pool_destroy(ThreadPool* pool)
+{
+ if (!pool)
+ return;
+
+ mtx_lock(&pool->lock);
+ pool->shutdown = true;
+ mtx_unlock(&pool->lock);
+
+ cnd_broadcast(&pool->condition);
+
+ for (size_t i = 0; i < pool->num_threads; ++i) {
+ thrd_join(pool->threads[i], NULL);
+ printf("Thread destroyed\n");
+ }
+
+ free(pool->threads);
+ mtx_destroy(&pool->lock);
+ cnd_destroy(&pool->condition);
+
+ free(pool);
+}
+
+bool
+thread_pool_enqueue(ThreadPool* pool, void (*function)(void*), void* arg)
+{
+ if (!pool || !function)
+ return false;
+
+ Task* new_task = malloc(sizeof(Task));
+ if (!new_task) {
+ perror("Memory allocation failed for new task");
+ return false;
+ }
+
+ new_task->function = function;
+ new_task->arg = arg;
+ new_task->next = NULL;
+
+ mtx_lock(&pool->lock);
+
+ if (pool->task_queue == NULL)
+ pool->task_queue = new_task;
+ else {
+ Task* last_task = pool->task_queue;
+ while (last_task->next != NULL)
+ last_task = last_task->next;
+
+ last_task->next = new_task;
+ }
+
+ cnd_signal(&pool->condition);
+ mtx_unlock(&pool->lock);
+
+ return true;
+}
+
+int
+thread_function(void* arg)
+{
+ ThreadPool* pool = (ThreadPool*)arg;
+ while (true) {
+ mtx_lock(&pool->lock);
+
+ while (!pool->task_queue && !pool->shutdown)
+ cnd_wait(&pool->condition, &pool->lock);
+
+
+ if (pool->shutdown) {
+ mtx_unlock(&pool->lock);
+ return thrd_success;
+ }
+
+ Task* task = pool->task_queue;
+ if (task) {
+ pool->task_queue = task->next;
+ }
+
+ mtx_unlock(&pool->lock);
+
+ if (task) {
+ task->function(task->arg);
+ free(task);
+ }
+ }
+}
+
diff --git a/src/utils/config.c b/src/utils/config.c
new file mode 100644
index 0000000..e146d95
--- /dev/null
+++ b/src/utils/config.c
@@ -0,0 +1,300 @@
+#include
+#include
+#include
+#include
+#if defined(_WIN32) || defined(_WIN64)
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#else
+#include
+#endif
+#include "utils/config.h"
+
+static void print_log(int line, const char* msg, const char* extra)
+{
+ printf("TConfig [Line: %i]: ", line);
+ printf(msg, extra);
+ printf("\n");
+}
+
+static ini_entry_s* _ini_entry_create(ini_section_s* section,
+ const char* key, const char* value)
+{
+ if ((section->size % 10) == 0) {
+ section->entry =
+ realloc(section->entry, (10+section->size) * sizeof(ini_entry_s));
+ }
+ ini_entry_s* entry = §ion->entry[section->size++];
+ entry->key = malloc((strlen(key)+1)*sizeof(char));
+ entry->value = malloc((strlen(value)+1)*sizeof(char));
+
+ strcpy(entry->key, key);
+ strcpy(entry->value, value);
+
+ return entry;
+}
+
+static ini_section_s* _ini_section_create(ini_table_s* table,
+ const char* section_name)
+{
+ if ((table->size % 10) == 0) {
+ table->section =
+ realloc(table->section, (10+table->size) * sizeof(ini_section_s));
+ }
+ ini_section_s* section = &table->section[table->size++];
+ section->size = 0;
+ section->name = malloc((strlen(section_name)+1) * sizeof(char));
+
+ strcpy(section->name, section_name);
+ section->entry = malloc(10 * sizeof(ini_entry_s));
+
+ return section;
+}
+
+static ini_section_s* _ini_section_find(ini_table_s* table, const char* name)
+{
+ for (int i = 0; i < table->size; i++) {
+ if (strcmp(table->section[i].name, name) == 0) {
+ return &table->section[i];
+ }
+ }
+ return NULL;
+}
+
+static ini_entry_s* _ini_entry_find(ini_section_s* section, const char* key)
+{
+ for (int i = 0; i < section->size; i++) {
+ if (strcmp(section->entry[i].key, key) == 0) {
+ return §ion->entry[i];
+ }
+ }
+ return NULL;
+}
+
+static ini_entry_s* _ini_entry_get(ini_table_s* table, const char* section_name,
+ const char* key)
+{
+ ini_section_s* section = _ini_section_find(table, section_name);
+ if (section == NULL) {
+ return NULL;
+ }
+
+ ini_entry_s* entry = _ini_entry_find(section, key);
+ if (entry == NULL) {
+ return NULL;
+ }
+
+ return entry;
+}
+
+ini_table_s* ini_table_create()
+{
+ ini_table_s* table = malloc(sizeof(ini_table_s));
+ table->size = 0;
+ table->section = malloc(10 * sizeof(ini_section_s));
+
+ return table;
+}
+
+void ini_table_destroy(ini_table_s* table)
+{
+ for (int i = 0; i < table->size; i++) {
+ ini_section_s* section = &table->section[i];
+
+ for (int q = 0; q < section->size; q++) {
+ ini_entry_s* entry = §ion->entry[q];
+ free(entry->key);
+
+ free(entry->value);
+ }
+ free(section->entry);
+ free(section->name);
+ }
+ free(table->section);
+ free(table);
+}
+
+bool ini_table_read_from_file(ini_table_s* table, const char* file)
+{
+ FILE* f = fopen(file, "r");
+ if (f == NULL) return false;
+
+ enum {Section, Key, Value, Comment} state = Section;
+ int c;
+ int position = 0;
+ int spaces = 0;
+ int line = 0;
+ int buffer_size = 128 * sizeof(char);
+ char* buf = malloc(buffer_size);
+ char* value = NULL;
+
+ ini_section_s* current_section = NULL;
+
+ memset(buf, '\0', buffer_size);
+ while((c = getc(f)) != EOF) {
+ if (position > buffer_size-2) {
+
+ buffer_size += 128 * sizeof(char);
+ size_t value_offset = value == NULL ? 0 : value - buf;
+ buf = realloc(buf, buffer_size);
+
+ memset(buf+position, '\0', buffer_size-position);
+ if (value != NULL)
+ value = buf + value_offset;
+ }
+ switch(c) {
+ case ' ':
+ switch(state) {
+ case Value: if (value[0] != '\0') spaces++; break;
+ default: if (buf[0] != '\0') spaces++; break;
+ }
+ break;
+ case ';':
+ if (state == Value) {
+ buf[position++] = c;
+ break;
+ }
+ else {
+ state = Comment;
+ buf[position++] = c;
+
+ while (c != EOF && c != '\n') {
+ c = getc(f);
+ if (c != EOF && c != '\n') buf[position++] = c;
+ }
+ }
+ // fallthrough
+ case '\n':
+ // fallthrough
+ case EOF:
+ line++;
+ if (state == Value) {
+ if (current_section == NULL) {
+ current_section = _ini_section_create(table, "");
+ }
+ _ini_entry_create(current_section, buf, value);
+ value = NULL;
+ } else if (state == Comment) {
+ if (current_section == NULL) {
+ current_section = _ini_section_create(table, "");
+ }
+ _ini_entry_create(current_section, buf, "");
+ } else if (state == Section) {
+ print_log(line, "Section `%s' missing `]' operator.", buf);
+ } else if(state == Key && position) {
+ print_log(line, "Key `%s' missing `=' operator.", buf);
+ }
+ memset(buf, '\0', buffer_size);
+ state = Key;
+ position = 0;
+ spaces = 0;
+ break;
+ case '[':
+ state = Section;
+ break;
+ case ']':
+ current_section = _ini_section_create(table, buf);
+ memset(buf, '\0', buffer_size);
+ position = 0;
+ spaces = 0;
+ state = Key;
+ break;
+ case '=':
+ if (state == Key) {
+ state = Value;
+ buf[position++] = '\0';
+ value = buf + position;
+ spaces = 0;
+ continue;
+ }
+ default:
+ for(;spaces > 0; spaces--) buf[position++] = ' ';
+ buf[position++] = c;
+ break;
+ }
+ }
+ free(buf);
+ fclose(f);
+ return true;
+}
+
+bool ini_table_write_to_file(ini_table_s* table, const char* file)
+{
+ FILE* f = fopen(file, "w+");
+ if (f == NULL) return false;
+ for (int i = 0; i < table->size; i++) {
+ ini_section_s* section = &table->section[i];
+ fprintf(f, i > 0 ? "\n[%s]\n" : "[%s]\n", section->name);
+ for (int q = 0; q < section->size; q++) {
+ ini_entry_s* entry = §ion->entry[q];
+ if (entry->key[0] == ';') {
+ fprintf(f, "%s\n", entry->key);
+ } else {
+ fprintf(f, "%s = %s\n", entry->key, entry->value);
+ }
+ }
+ }
+ fclose(f);
+ return true;
+}
+
+
+void ini_table_create_entry(ini_table_s* table, const char* section_name,
+ const char* key, const char* value)
+{
+ ini_section_s* section = _ini_section_find(table, section_name);
+ if (section == NULL) {
+ section = _ini_section_create(table, section_name);
+ }
+ ini_entry_s* entry = _ini_entry_find(section, key);
+ if (entry == NULL) {
+ entry = _ini_entry_create(section, key, value);
+ }else {
+ free(entry->value);
+ entry->value = malloc((strlen(value)+1) * sizeof(char));
+ strcpy(entry->value, value);
+ }
+}
+
+bool ini_table_check_entry(ini_table_s* table, const char* section_name,
+ const char* key)
+{
+ return (_ini_entry_get(table, section_name, key) != NULL);
+}
+
+const char* ini_table_get_entry(ini_table_s* table, const char* section_name,
+ const char* key)
+{
+ ini_entry_s* entry = _ini_entry_get(table, section_name, key);
+ if (entry == NULL) {
+ return NULL;
+ }
+ return entry->value;
+}
+
+bool ini_table_get_entry_as_int(ini_table_s* table, const char* section_name,
+ const char* key, int* value)
+{
+ const char* val = ini_table_get_entry(table, section_name, key);
+ if (val == NULL) {
+ return false;
+ }
+ *value = atoi(val);
+ return true;
+}
+
+bool ini_table_get_entry_as_bool(ini_table_s* table, const char* section_name,
+ const char* key, bool* value)
+{
+ const char* val = ini_table_get_entry(table, section_name, key);
+ if (val == NULL) {
+ return false;
+ }
+ if (strcasecmp(val, "on") == 0 || strcasecmp(val, "true") == 0) {
+ *value = true;
+ }else {
+ *value = false;
+ }
+ return true;
+}
+
diff --git a/src/utils/filesys.c b/src/utils/filesys.c
new file mode 100644
index 0000000..13cbe0e
--- /dev/null
+++ b/src/utils/filesys.c
@@ -0,0 +1,232 @@
+#include "utils/filesys.h"
+#include "utils/config.h"
+
+#ifdef _MSC_VER
+#pragma warning( push )
+#pragma warning( disable : 594 )
+#pragma warning( disable : 4047 )
+#pragma warning( disable : 4024 )
+#endif
+
+
+/**
+ * @brief find_cfgfile search for ini in a few predefined locations
+ *
+ * @details make three attempts to locate the cfg file requested
+ * using three different strategies (/etc, $HOME, and
+ * the home for the current user using the passwd
+ * database), errors out with an integer code if
+ * finally unsuccessful. If the bUseDot is set the
+ * search will make the file name a dot file during
+ * search in the user directories
+ *
+ * @param const string file_name: name of cfg file to search for
+ * @param string path_found: result of successful search
+ * @param boolean make it a dot file flag
+ * @return integer status code, path_found is NULLed if unsuccessful
+ */
+#ifndef _MSC_VER
+int
+find_cfgfile(const char* file_name, char* path_found, bool bUseDot)
+{
+ if(file_name == NULL && path_found == NULL)
+ return CMD_ARGS;
+
+ char search_this_path[MAXSIZE];
+ char* sp = &search_this_path[0];
+ strcpy(sp, "/etc");
+ // Search /etc first for a plain,
+ // un-decorated (no dot prefix)
+ // file_name
+ memset(sp, 0, strlen(search_this_path));
+ sp = strcpy(search_this_path, "/etc");
+ sp = strcat(search_this_path, "/");
+
+ sp = strcat(sp, file_name);
+
+ if(access(sp, F_OK) != -1 ) {
+ strcpy(path_found, sp);
+ return CMD_OK;
+ }
+ // then try for whatever is in $HOME,
+ // if bUseDot == true prefix the file
+ // name with a dot.
+ char* homedir = getenv("HOME");
+ if(homedir != NULL) {
+ memset(sp, 0, strlen(search_this_path));
+
+ strcpy(sp, homedir);
+ strcat(sp, "/");
+ if(bUseDot) strcat(sp, ".");
+
+ strcat(sp, file_name);
+ if(access(sp, F_OK) != -1 ) {
+ strcpy(path_found, sp);
+
+ return CMD_OK;
+ }
+ }
+ // Finally try the user's directory as
+ // specified in the passwd data base
+ uid_t uid = getuid();
+ struct passwd* pw = getpwuid(uid);
+ if(pw != NULL) {
+
+ memset(sp, 0, strlen(search_this_path));
+ strcpy(sp, pw->pw_dir);
+ strcat(sp, "/");
+
+ if(bUseDot) strcat(sp, ".");
+ strcat(sp, file_name);
+ if(access(search_this_path, F_OK) != -1 ) {
+
+ strcpy(path_found, sp);
+ return CMD_OK;
+ }
+ }
+ // nothing worked, set the result string
+ // param to null and return an error code
+ strcpy(path_found, "\0");
+
+ return CMD_FILENF;
+}
+#endif
+
+/**
+ * @brief create_cfgfile create a new ini file
+ *
+ * @details copies the passed parameter to a buffer to
+ * protect against file names larger than 256
+ * chars.
+ *
+ * @param const char* the full path of the new file
+ * @return integer status code
+ */
+int
+create_cfgfile(const char* file_path)
+{
+ char cfg_path[MAXBUF];
+ memset(cfg_path, 0, sizeof(cfg_path));
+ get_userdir(cfg_path);
+
+#ifndef _MSC_VER
+ strcat(cfg_path, "/.interp.ini");
+#else
+ strcat(cfg_path, "\\.interp.ini");
+#endif
+
+ ini_table_s* config = ini_table_create();
+ if(!ini_table_read_from_file(config, cfg_path)) {
+ // create new table
+
+ ini_table_create_entry(config, "Main", "prompt", "> ");
+ ini_table_create_entry(config, "Main", "loglevel", "0");
+ ini_table_create_entry(config, "Main", "admin", "false");
+ ini_table_write_to_file(config, cfg_path);
+ }
+
+ ini_table_destroy(config);
+
+ return CMD_OK;
+}
+
+#ifndef _MSC_VER
+void
+get_userdir(char* path)
+{
+ uid_t uid = getuid();
+ struct passwd* pw = getpwuid(uid);
+ strcpy(path, pw->pw_dir);
+}
+#else
+void
+get_userdir(char* path)
+{
+ wchar_t wpath[MAXBUF * 2] = { 0 };
+
+ if (FAILED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, wpath)))
+ path = NULL;
+ else
+ wcstombs(path, wpath, MAXBUF);
+}
+#endif
+
+int
+write_motd(char* path, const char* motd)
+{
+ char newm[MAXMOTD];
+ memset(newm, '\0', MAXMOTD);
+ mid(motd, 6, find_ch_index(motd, '"'), newm, strlen(motd));
+
+ remove_first(newm, "\"");
+ FILE* fp = fopen(path, "w");
+ if (fp == NULL) return CMD_ERR;
+
+ fprintf(fp, "%s", newm);
+ fclose(fp);
+
+ return CMD_OK;
+}
+
+int
+read_motd(const char* path)
+{
+ FILE* stream;
+ char line[MAXMOTD];
+ int errno;
+
+ errno = CMD_OK;
+ if((stream = fopen(path, "r")) != NULL) {
+ if (NULL != fgets(&line, MAXMOTD, stream))
+ errno = CMD_FILEEXST;
+ fclose(stream);
+ }
+ return errno;
+}
+
+const char*
+get_keyvalue(const char* key, const char* def)
+{
+ //struct config_setting_t const *setting;
+ char cfg_path[MAXBUF];
+ memset(cfg_path, 0, sizeof(cfg_path));
+
+ get_userdir(cfg_path);
+#ifndef _MSC_VER
+ strcat(cfg_path, "/.interp.ini");
+#else
+ strcat(cfg_path, "\\.interp.ini");
+#endif
+
+ if(!file_exists(cfg_path))
+ create_cfgfile(cfg_path);
+
+ return "";
+}
+
+int
+set_keyvalue(const char* key, const char* value)
+{
+ char cfg_path[MAXBUF];
+ memset(cfg_path, 0, sizeof(cfg_path));
+ get_userdir(cfg_path);
+#ifndef _MSC_VER
+ strcat(cfg_path, "/.interp.ini");
+#else
+ strcat(cfg_path, "\\.interp.ini");
+#endif
+
+ return CMD_OK;
+}
+
+bool
+file_exists(const char* filename)
+{
+ struct stat buffer;
+ return (stat (filename, &buffer) == 0);
+}
+
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
diff --git a/src/utils/huff.c b/src/utils/huff.c
new file mode 100755
index 0000000..f7b28fc
--- /dev/null
+++ b/src/utils/huff.c
@@ -0,0 +1,378 @@
+#define __STDC_WANT_LIB_EXT1__ 1
+
+#include "utils/huff.h"
+#include
+
+/**
+ * @file huff.c
+ * @author Tim O'Neil
+ * @date 3/9/2018
+ *
+ * @brief Probably pretty portable huffman coder
+ *
+ * @section DESCRIPTION
+ *
+ * Huffman coder for making text blobs psuedo-encrypted
+ * and psuedo-compressed and editable only through this
+ * the application (more or less). This code is not
+ * tested with other implementations, so its totally
+ * possible to be a wonky, one-off, not comatible, non-
+ * standard implementation (if there is such a thing.)
+ */
+
+/**
+ * @brief encode coder entrypoint, this one of our public facing
+ * interfaces, the other being decode, below.
+ *
+ * @details Given a buffer of text encode it and write the output
+ * to a file.
+ * @param const char* file_name2: path of output file
+ * @param char* buf: string containing text to encode
+ */
+void
+encode(const char* file_name2, char* buf)
+{
+ FILE* en;
+ int freq[256] = {0};
+ info header[256];
+
+ char codes[256][40];
+ char code[40];
+ strcpy(code, "");
+
+ List_t mylist_en;
+ size_t num, no_of_chars;
+ en = fopen(file_name2, "w");
+
+ if(en) {
+ find_freq(buf, freq);
+ no_of_chars = strlen(buf);
+
+ if(no_of_chars > 0) {
+ make_tree(&mylist_en, freq);
+ num = create_header(freq, (int)no_of_chars, header);
+
+ find_code(mylist_en.head, code, codes);
+ compress(buf, en, codes, (int)no_of_chars, header, freq, (int)num);
+ }
+ }
+ fclose(en);
+}
+
+/**
+ * @brief decode: decode a buffer of huffman encoded text
+ *
+ * @details Decode a file and send the output to stdout
+ *
+ * @param const char*: file_name1: path of input file
+ */
+void
+decode(const char* file_name1)
+{
+ info y, * ptrrr;
+ FILE* en;
+ ptrrr = &y;
+
+ code_generated x, * ptrr;
+ ptrr = &x;
+ List_t mylist_de;
+
+ int i, num, no_of_chars, count = 0;
+ int freq[256] = {0};
+ en = fopen(file_name1, "r");
+
+ if(en) {
+ fread(ptrr, sizeof(code_generated), 1, en);
+ num = ptrr->c;
+
+ for(i = 0; i < num; ++i) {
+ fread(ptrrr, sizeof(info), 1, en);
+ freq[(int)ptrrr->ch] = ptrrr->freq;
+ }
+
+ make_tree(&mylist_de, freq);
+ no_of_chars = find_no_of_chars(freq);
+ node_t* root = mylist_de.head;
+
+ node_t* ptr = root;
+ while(fread(ptrr, sizeof(code_generated), 1, en) == 1)
+ ptr = decode_char(stdout, ptr, ptrr->c, 1, root, &count, no_of_chars);
+ }
+ fclose(en);
+}
+
+node_t*
+decode_char(FILE* de, node_t* ptr, int x, int y,
+ node_t* root, int* count, int no_of_chars)
+{
+ if(y != 9 &&(*count) != no_of_chars) {
+ if(ptr->llink == 0 && ptr->rlink == 0) {
+ fputc(ptr->c, de);
+
+ ptr = root;
+ ++(*count);
+ ptr = decode_char(de, ptr, x, y, root, count, no_of_chars);
+ }
+ else if(x % 2 == 0 && ptr->llink)
+ ptr = decode_char(de, ptr->llink, x / 2, ++y, root, count, no_of_chars);
+ else if(x%2!=0 && ptr->rlink)
+ ptr = decode_char(de, ptr->rlink, x / 2, ++y, root, count, no_of_chars);
+ }
+ return ptr;
+}
+
+void
+insert_in_list(List_t*, node_t*);
+
+void
+init_list(List_t* ptr)
+{
+ ptr->head = 0;
+}
+
+void
+disp_list(const List_t* ptr)
+{
+ node_t* temp = ptr->head;
+ while(temp) {
+ printf("%d( %c )\t",temp->freq,temp->c);
+ temp = temp->link;
+ }
+ printf("\n");
+}
+
+void
+make_node(List_t* ptr, int n, char c)
+{
+ node_t* temp;
+ temp = (node_t*)malloc(sizeof(node_t));
+ temp->freq = n;
+
+ temp->c = c;
+ temp->link = 0;
+ temp->rlink = 0;
+
+ temp->llink = 0;
+ insert_in_list(ptr, temp);
+ //free(temp);
+}
+
+void
+insert_in_list(List_t* ptr, node_t* t)
+{
+ if(ptr->head == 0)
+ ptr->head = t;
+ else {
+ node_t* prev = 0;
+ node_t* pres = ptr->head;
+
+ while(pres && pres->freq <= t->freq) {
+ prev = pres;
+ pres = pres->link;
+ }
+ if(pres == ptr->head) {
+ ptr->head = t;
+ t->link = pres;
+ }
+ else if(pres == 0)
+ prev->link = t;
+ else {
+ t->link = pres;
+ prev->link = t;
+ }
+ }
+}
+
+void
+make_list(List_t* ptr, int* freq)
+{
+ int i;
+ for(i = 0; i < 256; ++i) {
+ if(freq[i] != 0)
+ make_node(ptr, freq[i], i);
+ }
+}
+
+void
+find_freq(char* fp, int* freq)
+{
+ char c;
+ unsigned n = 0;
+
+ while(n < strlen(fp)) {
+ c = fp[n++];
+ ++freq[(int)c];
+ }
+}
+
+void
+make_tree(List_t* ptr, int* freq)
+{
+ node_t * t1, * t2;
+ init_list(ptr);
+ make_list(ptr,freq);
+
+ while(ptr->head->link != 0) {
+ t1=ptr->head;
+ t2=ptr->head->link;
+
+ ptr->head=t2->link;
+ t1->link = 0;
+ t2->link = 0;
+
+ node_t* temp;
+ temp=(node_t*)malloc(sizeof(node_t));
+ temp->freq = t1->freq + t2->freq;
+
+ temp->c = 0;
+ temp->link = 0;
+ temp->rlink = t2;
+
+ temp->llink = t1;
+ insert_in_list(ptr, temp);
+ }
+}
+
+void
+find_code(node_t* ptr, char* code, char codes[256][40])
+{
+ char temp[40];
+ if(ptr->rlink == 0 && ptr->llink == 0)
+ strcpy(codes[(int)(ptr->c)], code);
+
+ if(ptr->rlink != 0) {
+ strcpy(temp, code);
+ strcat(temp, "1");
+
+ find_code(ptr->rlink, temp, codes);
+ }
+ if(ptr->llink != 0) {
+ strcpy(temp, code);
+ strcat(temp, "0");
+
+ find_code(ptr->llink, temp, codes);
+ }
+}
+
+int
+find_no_of_chars(int* freq)
+{
+ int i, sum = 0;
+ for(i = 0; i < 256; ++i)
+ sum = sum + freq[i];
+
+ return sum;
+}
+
+void
+compress(char* fp, FILE* en, char codes[256][40],
+ int no_of_chars, info* header, int* freq, int num)
+{
+ char ch;
+ int n = 0, i = 0, j = 0, count = 0, k, l;
+ unsigned char c;
+ code_generated x;
+ char string_rev[9];
+ x.c = num;
+
+ fwrite(&x, sizeof(code_generated), 1 ,en);
+ for(k = 0, l = 0; k < 256; ++k) {
+ if(freq[k]) {
+ fwrite(&header[l], sizeof(info), 1, en);
+ ++l;
+ }
+ }
+ while((ch = fp[n++]) != EOF) {
+ j = 0;
+ ++count;
+
+ while(codes[(int)ch][j] != '\0') {
+ string_rev[i] = codes[(int)ch][j];
+ ++i; ++j;
+
+ if(i == 8) {
+ string_rev[i] = '\0';
+ strre(string_rev, (int)strlen(string_rev));
+
+ c = convert_string_char(string_rev);
+ strcpy(string_rev, "");
+ i = 0; x.c = c;
+
+ fwrite(&x,sizeof(x),1,en);
+ }
+ if(count == no_of_chars ) {
+ while(codes[(int)ch][j]) {
+ string_rev[i] = codes[(int)ch][j];
+
+ ++i; ++j;
+ if(i == 8) {
+ string_rev[i] = '\0';
+
+ strre(string_rev, (int)strlen(string_rev));
+ c = convert_string_char(string_rev);
+ strcpy(string_rev, "");
+
+ i = 0; x.c=c;
+ fwrite(&x,sizeof(x),1,en);
+ }
+ }
+ string_rev[i] = '\0';
+ strre(string_rev, (int)strlen(string_rev));
+ c = convert_string_char(string_rev);
+
+ strcpy(string_rev, "");
+ x.c = c;
+ fwrite(&x, sizeof(x), 1, en);
+ }
+ }
+ }
+}
+
+void
+strre(char* str, int n)
+{
+ int i = 0, j = n - 1;
+ char temp;
+
+ while(i < j) {
+ temp = str[i];
+ str[i] = str[j];
+ str[j] = temp;
+ ++i; --j;
+ }
+ str[n]='\0';
+}
+
+int
+create_header(int* freq, int no_of_chars, info* header)
+{
+ int i, j;
+
+ for(i = 0, j = 0; i < 256; ++i) {
+ if(freq[i] != 0) {
+ header[j].freq = freq[i];
+ header[j].ch = i;
+ ++j;
+ }
+ }
+ return j;
+}
+
+unsigned char
+convert_string_char(char string[8])
+{
+ int i=0;
+ unsigned char c = 0;
+
+ while(string[i]) {
+ if(string[i] == '1') {
+ c = c * 2;
+ c = c + 1;
+ }
+ else
+ c = c * 2;
+
+ ++i;
+ }
+ return c;
+}
diff --git a/windows/PropertySheet.props b/windows/PropertySheet.props
new file mode 100644
index 0000000..6f6a475
--- /dev/null
+++ b/windows/PropertySheet.props
@@ -0,0 +1,20 @@
+
+
+
+
+ $(ProjectDir)..\extra
+
+
+
+
+
+
+
+
+
+
+ $(InterpExtra)
+ true
+
+
+
\ No newline at end of file
diff --git a/windows/README.md b/windows/README.md
new file mode 100644
index 0000000..8d4442d
--- /dev/null
+++ b/windows/README.md
@@ -0,0 +1,18 @@
+# Building interp on Windows
+
+I configured this project with Visual Studio 2022, I hope you have few problems, I've been noticing
+over the last few years Microsoft has been pretty good about making visual studio projects back rev
+compatible, if you have problems would love to hear about them, send reports to
+interval1066@gmail.com.
+
+If you haven't done it yet configure vcpkg to work with vs2022 or your preferred version of VS, I
+understand versions after 2017 can do this with varying degrees of success.
+
+Follow the instructions here for installation and configuration of vcpckg:
+https://devblogs.microsoft.com/cppblog/vcpkg-is-now-included-with-visual-studio/
+
+That should be all you need to do. In visual studio look at the include list in filesys.h; line
+7 should be the include for libconfig.h. If its not underlined then the project is probably
+ready to build.
+
+(10/17/2024) All dependancies on libconfig have been removed.
diff --git a/windows/include/winthread.h b/windows/include/winthread.h
new file mode 100644
index 0000000..f4adbfe
--- /dev/null
+++ b/windows/include/winthread.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include
+
+#define MAX_THREADS 10
+
+typedef struct {
+ // Add any data members you need for your specific task
+ // ...
+
+ // Add synchronization objects if needed
+ CRITICAL_SECTION lock;
+} TaskData;
+
+typedef struct {
+ HANDLE hThread;
+ TaskData* pData;
+} ThreadPoolThread;
+
diff --git a/windows/include/winunistd.h b/windows/include/winunistd.h
new file mode 100644
index 0000000..1ae5b73
--- /dev/null
+++ b/windows/include/winunistd.h
@@ -0,0 +1,62 @@
+#ifndef _UNISTD_H
+#define _UNISTD_H 1
+
+/* This file intended to serve as a drop-in replacement for
+ * unistd.h on Windows.
+ * Please add functionality as neeeded.
+ * Original file from: http://stackoverflow.com/a/826027
+ */
+
+#include
+#include
+#ifndef _MSC_VER
+#include /* getopt at: https://gist.github.com/bikerm16/1b75e2dd20d839dcea58 */
+#endif
+#include /* for getpid() and the exec..() family */
+#include /* for _getcwd() and _chdir() */
+
+#define srandom srand
+#define random rand
+
+ /* Values for the second argument to access.
+ These may be OR'd together. */
+#define R_OK 4 /* Test for read permission. */
+#define W_OK 2 /* Test for write permission. */
+#define X_OK R_OK /* execute permission - unsupported in Windows,
+ use R_OK instead. */
+#define F_OK 0 /* Test for existence. */
+
+#define access _access
+#define dup2 _dup2
+#define execve _execve
+#define ftruncate _chsize
+#define unlink _unlink
+#define fileno _fileno
+#define getcwd _getcwd
+#define chdir _chdir
+#define isatty _isatty
+#define lseek _lseek
+ /* read, write, and close are NOT being #defined here,
+ * because while there are file handle specific versions for Windows,
+ * they probably don't work for sockets.
+ * You need to look at your app and consider whether
+ * to call e.g. closesocket().
+ */
+
+#define ssize_t int
+#define size_t ssize_t
+
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+ /* should be in some equivalent to */
+typedef __int8 int8_t;
+typedef __int16 int16_t;
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+
+#endif /* unistd.h */
diff --git a/windows/interp.csproj b/windows/interp.csproj
new file mode 100644
index 0000000..40c60dd
--- /dev/null
+++ b/windows/interp.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
diff --git a/windows/interp.sln b/windows/interp.sln
new file mode 100644
index 0000000..14ee837
--- /dev/null
+++ b/windows/interp.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.8.34112.27
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "interp", "interp.vcxproj", "{8BF84208-506F-49D2-B39B-4726A266C6E4}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8BF84208-506F-49D2-B39B-4726A266C6E4}.Debug|x64.ActiveCfg = Debug|x64
+ {8BF84208-506F-49D2-B39B-4726A266C6E4}.Debug|x64.Build.0 = Debug|x64
+ {8BF84208-506F-49D2-B39B-4726A266C6E4}.Debug|x86.ActiveCfg = Debug|Win32
+ {8BF84208-506F-49D2-B39B-4726A266C6E4}.Debug|x86.Build.0 = Debug|Win32
+ {8BF84208-506F-49D2-B39B-4726A266C6E4}.Release|x64.ActiveCfg = Release|x64
+ {8BF84208-506F-49D2-B39B-4726A266C6E4}.Release|x64.Build.0 = Release|x64
+ {8BF84208-506F-49D2-B39B-4726A266C6E4}.Release|x86.ActiveCfg = Release|Win32
+ {8BF84208-506F-49D2-B39B-4726A266C6E4}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {552FA45B-1EC0-4A26-B26B-CDB0DE0F816B}
+ EndGlobalSection
+EndGlobal
diff --git a/windows/interp.vcxproj b/windows/interp.vcxproj
new file mode 100644
index 0000000..2864a10
--- /dev/null
+++ b/windows/interp.vcxproj
@@ -0,0 +1,173 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 17.0
+ Win32Proj
+ {8bf84208-506f-49d2-b39b-4726a266c6e4}
+ interp
+ 10.0
+
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(VC_ExecutablePath_x64);$(CommonExecutablePath)
+ $(VC_IncludePath);$(WindowsSDK_IncludePath)
+ $(VC_IncludePath);$(WindowsSDK_IncludePath)
+
+
+ false
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ $(ProjectDir)..\include;$(ProjectDir)..\include\utils;$(ProjectDir)..\windows\include
+ 4996;4113;28251
+
+
+ Console
+ true
+ $(CoreLibraryDependencies);%(AdditionalDependencies);
+
+
+
+
+ Level3
+ true
+ true
+ true
+ _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ $(ProjectDir)..\include;$(ProjectDir)..\include\utils;$(ProjectDir)..\windows\include
+ stdc11
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/windows/interp.vcxproj.filters b/windows/interp.vcxproj.filters
new file mode 100644
index 0000000..762ae17
--- /dev/null
+++ b/windows/interp.vcxproj.filters
@@ -0,0 +1,81 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/windows/src/winstring_ext.c b/windows/src/winstring_ext.c
new file mode 100644
index 0000000..9d5b048
--- /dev/null
+++ b/windows/src/winstring_ext.c
@@ -0,0 +1,59 @@
+#include
+#include
+
+size_t
+getline(char** lineptr, size_t* n, FILE* stream)
+ {
+ char* bufptr = NULL, * p = bufptr;
+ size_t size;
+ int c;
+
+ if (lineptr == NULL)
+ return -1;
+
+ if (stream == NULL)
+ return -1;
+
+ if (n == NULL)
+ return -1;
+
+ bufptr = *lineptr;
+ size = *n;
+
+ c = fgetc(stream);
+ if (c == EOF)
+ return -1;
+
+ if (bufptr == NULL) {
+ bufptr = malloc(128);
+ if (bufptr == NULL)
+ return -1;
+
+ size = 128;
+ }
+
+ p = bufptr;
+ while (c != EOF) {
+ if ((size_t)(p - bufptr) > (size - 1)) {
+
+ size = size + 128;
+ bufptr = realloc(bufptr, size);
+
+ if (bufptr == NULL)
+ return -1;
+
+ p = bufptr + (size - 128);
+ }
+ *p++ = c;
+ if (c == '\n')
+ break;
+
+ c = fgetc(stream);
+ }
+
+ *p++ = '\0';
+ *lineptr = bufptr;
+ *n = size;
+
+ return p - bufptr - 1;
+ }
\ No newline at end of file
diff --git a/windows/src/winthread.c b/windows/src/winthread.c
new file mode 100644
index 0000000..ba3c4cd
--- /dev/null
+++ b/windows/src/winthread.c
@@ -0,0 +1,46 @@
+#include "winthread.h"
+
+TaskData yourTaskData;
+ThreadPoolThread g_ThreadPool[MAX_THREADS];
+
+DWORD WINAPI WorkerThread(LPVOID lpParam) {
+ TaskData* pData = (TaskData*)lpParam;
+
+ // Your thread's work goes here
+ // ...
+
+ // Release any resources or perform cleanup if needed
+ // ...
+
+ return 0;
+}
+
+void InitializeThreadPool() {
+ // Initialize the critical section
+ InitializeCriticalSection(&g_ThreadPool[0].pData->lock);
+
+ for (int i = 0; i < MAX_THREADS; ++i) {
+ // Initialize your task data structure and other necessary members
+ // ...
+
+ // Create threads
+ g_ThreadPool[i].pData = &yourTaskData; // Set your task data
+ g_ThreadPool[i].hThread = CreateThread(NULL, 0, WorkerThread, g_ThreadPool[i].pData, 0, NULL);
+ }
+}
+
+void CleanupThreadPool() {
+ for (int i = 0; i < MAX_THREADS; ++i) {
+ // Perform any cleanup or signal threads to exit gracefully
+ // ...
+
+ // Wait for thread termination
+ WaitForSingleObject(g_ThreadPool[i].hThread, INFINITE);
+
+ // Close thread handle
+ CloseHandle(g_ThreadPool[i].hThread);
+ }
+
+ // Delete the critical section
+ DeleteCriticalSection(&g_ThreadPool[0].pData->lock);
+}