Pārlūkot izejas kodu

Merge branch 'tcl'

* tcl:
  * Don't require TCL for compiling
  * Include the tcl path when compiling
  * Add TCL autoconf checks from Eggdrop 1.6
  * Add tcl 'privmsg' command as a PoC
  * Add tcl eval command over DCC
  * Add libtcl dynamic loading

Conflicts:
	doc/UPDATES
Bryan Drewery 15 gadi atpakaļ
vecāks
revīzija
9e4b92cd10
11 mainītis faili ar 1544 papildinājumiem un 6 dzēšanām
  1. 6 5
      Makefile.in
  2. 54 0
      autotools/configure.ac
  3. 565 0
      autotools/includes/libtcl.m4
  4. 9 0
      config.h.in
  5. 690 0
      configure
  6. 1 0
      doc/UPDATES
  7. 3 0
      doc/help.txt
  8. 2 1
      src/Makefile.in
  9. 22 0
      src/cmds.c
  10. 153 0
      src/libtcl.c
  11. 39 0
      src/libtcl.h

+ 6 - 5
Makefile.in

@@ -40,6 +40,7 @@ DIFF = @DIFF@
 
 #LIBS = @LIBS@ @ZLIB@
 LIBS = @LIBS@
+INCLUDES = -I@TCLINC@
 
 DEBCXXFLAGS = -DDEBUG -fno-inline -g3 -ggdb3 -O0 -Wshadow -Wpointer-arith -Wcast-align @GCC3DEB@ @GCC4DEB@
 CFLGS = @GCC3@ -fno-rtti @SSL_INCLUDES@
@@ -57,26 +58,26 @@ _CFLGS = -fno-strict-aliasing -W -Wformat \
 
 MAKEFLAGS = -s @MAKEJOBS@
 
-MAKE_STATIC = $(MAKE) 'MAKE=$(MAKE)' 'CXX=$(CXX)' 'LD=$(LD_STATIC)' \
+MAKE_STATIC = $(MAKE) 'MAKE=$(MAKE)' 'CXX=$(CXX)' 'LD=$(LD_STATIC)' 'INCLUDES=$(INCLUDES)' \
 'CCDEPMODE=$(CCDEPMODE)' 'BRANCH=$(BRANCH)' 'COMMIT=$(COMMIT_SHORT)' 'BUILDTS=$(BUILDTS)' 'VERSION=$(VERSION)' \
 'STRIP=$(STRIP)' 'CFLGS=$(CFLGS) -DBIN_TYPE_STATIC' \
 'LIBS=$(LIBS)' 'EGGEXEC=$(BINEXEC)' 'EGGBUILD=(wraith)'
 
-MAKE_DYNAMIC = $(MAKE) 'MAKE=$(MAKE)' 'CXX=$(CXX)' 'LD=$(LD_DYNAMIC)' \
+MAKE_DYNAMIC = $(MAKE) 'MAKE=$(MAKE)' 'CXX=$(CXX)' 'LD=$(LD_DYNAMIC)' 'INCLUDES=$(INCLUDES)' \
 'CCDEPMODE=$(CCDEPMODE)' 'BRANCH=$(BRANCH)' 'COMMIT=$(COMMIT_SHORT)' 'BUILDTS=$(BUILDTS)' 'VERSION=$(VERSION)' \
 'STRIP=$(STRIP)' 'CFLGS=$(CFLGS) -DBIN_TYPE_DYNAMIC' \
 'LIBS=$(LIBS)' 'EGGEXEC=$(BINEXEC)' 'EGGBUILD=(wraith)'
 
-MAKE_DEBUG = $(MAKE) 'MAKE=$(MAKE)' 'CXX=$(CCDEBUG)' 'LD=$(LD_DEBUG) -g' \
+MAKE_DEBUG = $(MAKE) 'MAKE=$(MAKE)' 'CXX=$(CCDEBUG)' 'LD=$(LD_DEBUG) -g' 'INCLUDES=$(INCLUDES)' \
 'CCDEPMODE=$(CCDEPMODE)' 'BRANCH=$(BRANCH)' 'COMMIT=$(COMMIT_SHORT)' 'BUILDTS=$(BUILDTS)' 'VERSION=$(VERSION)' \
 'STRIP=touch' 'CFLGS=$(CFLGS) $(DEBCXXFLAGS) -DBIN_TYPE_DYNAMIC' \
 'LIBS=$(LIBS)' 'EGGEXEC=$(BINEXEC)' 'EGGBUILD=(debug)'
 
-MAKE_UTILS = $(MAKE) 'MAKE=$(MAKE)' 'CXX=$(CCDEBUG)' 'STRIP=touch' \
+MAKE_UTILS = $(MAKE) 'MAKE=$(MAKE)' 'CXX=$(CCDEBUG)' 'STRIP=touch' 'INCLUDES=$(INCLUDES)' \
 'CCDEPMODE=$(CCDEPMODE)' \
 'CFLGS=$(CFLGS) $(DEBCXXFLAGS)' 'LIBS=$(LIBS)' 'LD=$(LD_DEBUG) -g'
 
-MAKE_UTILS_NR = $(MAKE) 'CXX=$(CXX)' 'STRIP=touch' \
+MAKE_UTILS_NR = $(MAKE) 'CXX=$(CXX)' 'STRIP=touch' 'INCLUDES=$(INCLUDES)' \
 'CCDEPMODE=$(CCDEPMODE)' \
 'CFLGS=$(CFLGS) $(DEBCXXFLAGS)' 'LIBS=$(LIBS)' 'LD=$(LD) -g'
 

+ 54 - 0
autotools/configure.ac

@@ -88,6 +88,60 @@ AC_CACHE_SAVE
 #EGG_CHECK_ZLIB
 CHECK_SSL
 
+# TCL checks
+
+# Tcl version to recommend if no Tcl is found, and the site where it can be
+# found for download.
+tclrecommendver="8.5.X"
+tclrecommendsite="ftp://tcl.activestate.com/pub/tcl/tcl8_5/"
+
+# Tcl library filename prefixes, suffixes, and search paths.
+tcllibnames="tcl8.5 tcl85 tcl8.4 tcl84 tcl8.3 tcl83 tcl8.2 tcl82 \
+             tcl8.1 tcl81 tcl8.0 tcl80 tcl tcl7.6 tcl76 tcl7.5 tcl75 \
+             tcl7.4 tcl74 tcl7.3 tcl73 tcl7.2 tcl72 tcl7.1 tcl71 \
+             tcl7.0 tcl70"
+
+tcllibextensions=".so .so.1 .so.1.0 .so.1.2 .dll .dylib .sl .a"
+
+tcllibpaths="$HOME/lib $HOME/tcl/lib \
+             /usr/local/lib /usr/lib /lib /lib64 /usr/lib64 \
+             /usr/local/lib/tcl8.5 /usr/lib/tcl8.5 \
+             /usr/local/lib/tcl8.4 /usr/lib/tcl8.4 \
+             /usr/local/lib/tcl8.3 /usr/lib/tcl8.3 \
+             /usr/local/pkgs/tcl/lib /sys/lib /usr/pkg/lib \
+             /usr/i486-linuxaout/lib /beos/system/lib $HOME"
+
+# Tcl header filenames and search paths.
+tclheadernames="tcl.h"
+tclheaderpaths="$HOME/include $HOME/tcl/include \
+                /usr/local/include /usr/include \
+                /usr/local/include/tcl8.5 /usr/include/tcl8.5 \
+                /usr/local/include/tcl8.4 /usr/include/tcl8.4 \
+                /usr/local/include/tcl8.3 /usr/include/tcl8.3 \
+                /usr/local/pkgs/tcl/include /sys/include \
+                /usr/pkg/lib /beos/system/include /beos/devel/include $HOME"
+
+
+# Misc Tcl checks.
+EGG_TCL_ARG_WITH
+EGG_TCL_ENV
+EGG_TCL_WITH_TCLLIB
+EGG_TCL_WITH_TCLINC
+EGG_TCL_FIND_LIBRARY
+EGG_TCL_FIND_HEADER
+EGG_TCL_CHECK_LIBRARY
+EGG_TCL_CHECK_HEADER
+EGG_TCL_DETECT_CHANGE
+EGG_TCL_CHECK_VERSION
+EGG_TCL_CHECK_PRE70
+EGG_TCL_TESTLIBS
+EGG_TCL_CHECK_FREE
+#EGG_TCL_CHECK_GETCURRENTTHREAD
+#EGG_TCL_CHECK_GETTHREADDATA
+EGG_TCL_CHECK_SETNOTIFIER
+EGG_TCL_LIB_REQS
+
+AC_CACHE_SAVE
 
 #AC_SUBST(ZLIB)dnl
 

+ 565 - 0
autotools/includes/libtcl.m4

@@ -0,0 +1,565 @@
+dnl libtcl.m4
+dnl   macros autoconf uses when building configure from configure.in
+dnl   These are taken from Eggdrop
+dnl
+
+dnl EGG_TCL_ARG_WITH()
+dnl
+AC_DEFUN([EGG_TCL_ARG_WITH],
+[
+  AC_ARG_WITH(tcllib, [  --with-tcllib=PATH      full path to Tcl library], [tcllibname="$withval"])
+  AC_ARG_WITH(tclinc, [  --with-tclinc=PATH      full path to Tcl header],  [tclincname="$withval"])
+
+  WARN=0
+  # Make sure either both or neither $tcllibname and $tclincname are set
+  if test "x$tcllibname" != x; then
+    if test "x$tclincname" = x; then
+      WARN=1
+      tcllibname=""
+      TCLLIB=""
+      TCLINC=""
+    fi
+  else
+    if test "x$tclincname" != x; then
+      WARN=1
+      tclincname=""
+      TCLLIB=""
+      TCLINC=""
+    fi
+  fi
+
+  if test "$WARN" = 1; then
+    cat << 'EOF' >&2
+configure: WARNING:
+
+  You must specify both --with-tcllib and --with-tclinc for either to work.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+  fi
+])
+
+
+dnl EGG_TCL_ENV()
+dnl
+AC_DEFUN([EGG_TCL_ENV],
+[
+  WARN=0
+  # Make sure either both or neither $TCLLIB and $TCLINC are set
+  if test "x$TCLLIB" != x; then
+    if test "x$TCLINC" = x; then
+      WARN=1
+      WVAR1=TCLLIB
+      WVAR2=TCLINC
+      TCLLIB=""
+    fi
+  else
+    if test "x$TCLINC" != x; then
+      WARN=1
+      WVAR1=TCLINC
+      WVAR2=TCLLIB
+      TCLINC=""
+    fi
+  fi
+
+  if test "$WARN" = 1; then
+    cat << EOF >&2
+configure: WARNING:
+
+  Environment variable $WVAR1 was set, but I did not detect ${WVAR2}.
+  Please set both TCLLIB and TCLINC correctly if you wish to use them.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+  fi
+])
+
+
+dnl EGG_TCL_WITH_TCLLIB()
+dnl
+AC_DEFUN([EGG_TCL_WITH_TCLLIB],
+[
+  # Look for Tcl library: if $tcllibname is set, check there first
+  if test "x$tcllibname" != x; then
+    if test -f "$tcllibname" && test -r "$tcllibname"; then
+      TCLLIB=`echo $tcllibname | sed 's%/[[^/]][[^/]]*$%%'`
+      TCLLIBFN=`$BASENAME $tcllibname | cut -c4-`
+      TCLLIBEXT=".`echo $TCLLIBFN | $AWK '{j=split([$]1, i, "."); print i[[j]]}'`"
+      TCLLIBFNS=`$BASENAME $tcllibname $TCLLIBEXT | cut -c4-`
+    else
+      cat << EOF >&2
+configure: WARNING:
+
+  The file '$tcllibname' given to option --with-tcllib is not valid.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+      tcllibname=""
+      tclincname=""
+      TCLLIB=""
+      TCLLIBFN=""
+      TCLINC=""
+      TCLINCFN=""
+    fi
+  fi
+])
+
+
+dnl EGG_TCL_WITH_TCLINC()
+dnl
+AC_DEFUN([EGG_TCL_WITH_TCLINC],
+[
+  # Look for Tcl header: if $tclincname is set, check there first
+  if test "x$tclincname" != x; then
+    if test -f "$tclincname" && test -r "$tclincname"; then
+      TCLINC=`echo $tclincname | sed 's%/[[^/]][[^/]]*$%%'`
+      TCLINCFN=`$BASENAME $tclincname`
+    else
+      cat << EOF >&2
+configure: WARNING:
+
+  The file '$tclincname' given to option --with-tclinc is not valid.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+      tcllibname=""
+      tclincname=""
+      TCLLIB=""
+      TCLLIBFN=""
+      TCLINC=""
+      TCLINCFN=""
+    fi
+  fi
+])
+
+
+dnl EGG_TCL_FIND_LIBRARY()
+dnl
+AC_DEFUN([EGG_TCL_FIND_LIBRARY],
+[
+  # Look for Tcl library: if $TCLLIB is set, check there first
+  if test "x$TCLLIBFN" = x && test "x$TCLLIB" != x; then
+    if test -d "$TCLLIB"; then
+      for tcllibfns in $tcllibnames; do
+        for tcllibext in $tcllibextensions; do
+          if test -r "${TCLLIB}/lib${tcllibfns}${tcllibext}"; then
+            TCLLIBFN="${tcllibfns}${tcllibext}"
+            TCLLIBEXT="$tcllibext"
+            TCLLIBFNS="$tcllibfns"
+            break 2
+          fi
+        done
+      done
+    fi
+
+    if test "x$TCLLIBFN" = x; then
+      cat << 'EOF' >&2
+configure: WARNING:
+
+  Environment variable TCLLIB was set, but incorrectly.
+  Please set both TCLLIB and TCLINC correctly if you wish to use them.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+      TCLLIB=""
+      TCLLIBFN=""
+      TCLINC=""
+      TCLINCFN=""
+    fi
+  fi
+])
+
+
+dnl EGG_TCL_FIND_HEADER()
+dnl
+AC_DEFUN([EGG_TCL_FIND_HEADER],
+[
+  # Look for Tcl header: if $TCLINC is set, check there first
+  if test "x$TCLINCFN" = x && test "x$TCLINC" != x; then
+    if test -d "$TCLINC"; then
+      for tclheaderfn in $tclheadernames; do
+        if test -r "${TCLINC}/${tclheaderfn}"; then
+          TCLINCFN="$tclheaderfn"
+          break
+        fi
+      done
+    fi
+
+    if test "x$TCLINCFN" = x; then
+      cat << 'EOF' >&2
+configure: WARNING:
+
+  Environment variable TCLINC was set, but incorrectly.
+  Please set both TCLLIB and TCLINC correctly if you wish to use them.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+      TCLLIB=""
+      TCLLIBFN=""
+      TCLINC=""
+      TCLINCFN=""
+    fi
+  fi
+])
+
+
+dnl EGG_TCL_CHECK_LIBRARY()
+dnl
+AC_DEFUN([EGG_TCL_CHECK_LIBRARY],
+[
+  AC_MSG_CHECKING([for Tcl library])
+
+  # Attempt autodetect for $TCLLIBFN if it's not set
+  if test "x$TCLLIBFN" != x; then
+    AC_MSG_RESULT([using ${TCLLIB}/lib${TCLLIBFN}])
+  else
+    for tcllibfns in $tcllibnames; do
+      for tcllibext in $tcllibextensions; do
+        for tcllibpath in $tcllibpaths; do
+          if test -r "${tcllibpath}/lib${tcllibfns}${tcllibext}"; then
+            AC_MSG_RESULT([found ${tcllibpath}/lib${tcllibfns}${tcllibext}])
+            TCLLIB="$tcllibpath"
+            TCLLIBFN="${tcllibfns}${tcllibext}"
+            TCLLIBEXT="$tcllibext"
+            TCLLIBFNS="$tcllibfns"
+            break 3
+          fi
+        done
+      done
+    done
+  fi
+
+  # Show if $TCLLIBFN wasn't found
+  if test "x$TCLLIBFN" = x; then
+    AC_MSG_RESULT([not found])
+  fi
+
+  AC_SUBST(TCLLIB)
+  AC_SUBST(TCLLIBFN)
+])
+
+
+dnl EGG_TCL_CHECK_HEADER()
+dnl
+AC_DEFUN([EGG_TCL_CHECK_HEADER],
+[
+  AC_MSG_CHECKING([for Tcl header])
+
+  # Attempt autodetect for $TCLINCFN if it's not set
+  if test "x$TCLINCFN" != x; then
+    AC_MSG_RESULT([using ${TCLINC}/${TCLINCFN}])
+  else
+    for tclheaderpath in $tclheaderpaths; do
+      for tclheaderfn in $tclheadernames; do
+        if test -r "${tclheaderpath}/${tclheaderfn}"; then
+          AC_MSG_RESULT([found ${tclheaderpath}/${tclheaderfn}])
+          TCLINC="$tclheaderpath"
+          TCLINCFN="$tclheaderfn"
+          break 2
+        fi
+      done
+    done
+
+    # FreeBSD hack ...
+    if test "x$TCLINCFN" = x; then
+      for tcllibfns in $tcllibnames; do
+        for tclheaderpath in $tclheaderpaths; do
+          for tclheaderfn in $tclheadernames; do
+            if test -r "${tclheaderpath}/${tcllibfns}/${tclheaderfn}"; then
+              AC_MSG_RESULT([found ${tclheaderpath}/${tcllibfns}/${tclheaderfn}])
+              TCLINC="${tclheaderpath}/${tcllibfns}"
+              TCLINCFN="$tclheaderfn"
+              break 3
+            fi
+          done
+        done
+      done
+    fi
+  fi
+
+  if test "x$TCLINCFN" = x; then
+    AC_MSG_RESULT([not found])
+  fi
+
+  AC_SUBST(TCLINC)
+  AC_SUBST(TCLINCFN)
+])
+
+
+dnl EGG_CACHE_UNSET(CACHE-ID)
+dnl
+dnl Unsets a certain cache item. Typically called before using the AC_CACHE_*()
+dnl macros.
+dnl
+AC_DEFUN([EGG_CACHE_UNSET], [unset $1])
+
+
+dnl EGG_TCL_DETECT_CHANGE()
+dnl
+dnl Detect whether the Tcl system has changed since our last configure run.
+dnl Set egg_tcl_changed accordingly.
+dnl
+dnl Tcl related feature and version checks should re-run their checks as soon
+dnl as egg_tcl_changed is set to "yes".
+dnl
+AC_DEFUN([EGG_TCL_DETECT_CHANGE],
+[
+  dnl NOTE: autoconf 2.50+ disables config.cache by default.
+  dnl       These checks don't do us much good if cache is disabled.
+  AC_MSG_CHECKING([whether the Tcl system has changed])
+  egg_tcl_changed="yes"
+  egg_tcl_id="${TCLLIB}:${TCLLIBFN}:${TCLINC}:${TCLINCFN}"
+  if test "$egg_tcl_id" != ":::"; then
+    egg_tcl_cached="yes"
+    AC_CACHE_VAL(egg_cv_var_tcl_id, [
+      egg_cv_var_tcl_id="$egg_tcl_id"
+      egg_tcl_cached="no"
+    ])
+    if test "$egg_tcl_cached" = yes; then
+      if test "x$egg_cv_var_tcl_id" = "x$egg_tcl_id"; then
+        egg_tcl_changed="no"
+      else
+        egg_cv_var_tcl_id="$egg_tcl_id"
+      fi
+    fi
+  fi
+
+  if test "$egg_tcl_changed" = yes; then
+    AC_MSG_RESULT([yes])
+  else
+    AC_MSG_RESULT([no])
+  fi
+])
+
+
+dnl EGG_TCL_CHECK_VERSION()
+dnl
+AC_DEFUN([EGG_TCL_CHECK_VERSION],
+[
+  # Both TCLLIBFN & TCLINCFN must be set, or we bail
+  TCL_FOUND=0
+  if test "x$TCLLIBFN" != x && test "x$TCLINCFN" != x; then
+    TCL_FOUND=1
+
+    # Check Tcl's version
+    if test "$egg_tcl_changed" = yes; then
+      EGG_CACHE_UNSET(egg_cv_var_tcl_version)
+    fi
+
+    AC_MSG_CHECKING([for Tcl version])
+    AC_CACHE_VAL(egg_cv_var_tcl_version, [
+      egg_cv_var_tcl_version=`grep TCL_VERSION $TCLINC/$TCLINCFN | $HEAD_1 | $AWK '{gsub(/\"/, "", [$]3); print [$]3}'`
+    ])
+
+    if test "x$egg_cv_var_tcl_version" != x; then
+      AC_MSG_RESULT([$egg_cv_var_tcl_version])
+    else
+      AC_MSG_RESULT([not found])
+      TCL_FOUND=0
+    fi
+
+    # Check Tcl's patch level (if available)
+    if test "$egg_tcl_changed" = yes; then
+      EGG_CACHE_UNSET(egg_cv_var_tcl_patch_level)
+    fi
+    AC_MSG_CHECKING([for Tcl patch level])
+    AC_CACHE_VAL(egg_cv_var_tcl_patch_level, [
+      eval "egg_cv_var_tcl_patch_level=`grep TCL_PATCH_LEVEL $TCLINC/$TCLINCFN | $HEAD_1 | $AWK '{gsub(/\"/, "", [$]3); print [$]3}'`"
+    ])
+
+    if test "x$egg_cv_var_tcl_patch_level" != x; then
+      AC_MSG_RESULT([$egg_cv_var_tcl_patch_level])
+    else
+      egg_cv_var_tcl_patch_level="unknown"
+      AC_MSG_RESULT([unknown])
+    fi
+  fi
+
+  # Check if we found Tcl's version
+  if test "$TCL_FOUND" = 0; then
+    cat << 'EOF' >&2
+configure: error:
+
+  Tcl cannot be found on this system.
+
+  Tcl is not required. Wraith will be compiled without TCL support. If you
+  already have Tcl installed on this system, and I just wasn't looking in
+  the right place for it, re-run ./configure using the
+  --with-tcllib='/path/to/libtcl.so' and --with-tclinc='/path/to/tcl.h' options.
+
+EOF
+  else
+    AC_DEFINE(HAVE_LIBTCL, 1, [Define if you have support for libtcl])
+  fi
+])
+
+
+dnl EGG_TCL_CHECK_PRE70()
+dnl
+AC_DEFUN([EGG_TCL_CHECK_PRE70],
+[
+  # Is this version of Tcl too old for us to use ?
+  TCL_VER_PRE70=`echo $egg_cv_var_tcl_version | $AWK '{split([$]1, i, "."); if (i[[1]] < 7) print "yes"; else print "no"}'`
+  if test "$HAVE_LIBTCL" = 1 -a "$TCL_VER_PRE70" = yes; then
+    cat << EOF >&2
+configure: error:
+
+  Your Tcl version is much too old for Eggdrop to use. You should
+  download and compile a more recent version. The most reliable
+  current version is $tclrecommendver and can be downloaded from
+  ${tclrecommendsite}.
+
+  See doc/COMPILE-GUIDE's 'Tcl Detection and Installation' section
+  for more information.
+
+EOF
+    exit 1
+  fi
+])
+
+
+dnl EGG_TCL_TESTLIBS()
+dnl
+AC_DEFUN([EGG_TCL_TESTLIBS],
+[
+  # Set variables for Tcl library tests
+  TCL_TEST_LIB="$TCLLIBFNS"
+  TCL_TEST_OTHERLIBS="-L$TCLLIB $EGG_MATH_LIB"
+  if test "x$ac_cv_lib_pthread" != x; then
+    TCL_TEST_OTHERLIBS="$TCL_TEST_OTHERLIBS $ac_cv_lib_pthread"
+  fi
+])
+
+
+dnl EGG_TCL_CHECK_FREE()
+dnl
+AC_DEFUN([EGG_TCL_CHECK_FREE],
+[
+  if test "$egg_tcl_changed" = yes; then
+    EGG_CACHE_UNSET(egg_cv_var_tcl_free)
+  fi
+
+  # Check for Tcl_Free()
+  AC_CHECK_LIB($TCL_TEST_LIB, Tcl_Free, [egg_cv_var_tcl_free="yes"], [egg_cv_var_tcl_free="no"], $TCL_TEST_OTHERLIBS)
+
+  if test "$egg_cv_var_tcl_free" = yes; then
+    AC_DEFINE(HAVE_TCL_FREE, 1, [Define for Tcl that has Tcl_Free() (7.5p1 and later).])
+  fi
+])
+
+
+dnl EGG_TCL_CHECK_GETCURRENTTHREAD
+dnl
+AC_DEFUN([EGG_TCL_CHECK_GETCURRENTTHREAD],
+[
+  if test "$egg_tcl_changed" = yes; then
+    EGG_CACHE_UNSET(egg_cv_var_tcl_getcurrentthread)
+  fi
+
+  # Check for Tcl_GetCurrentThread()
+  AC_CHECK_LIB($TCL_TEST_LIB, Tcl_GetCurrentThread, [egg_cv_var_tcl_getcurrentthread="yes"], [egg_cv_var_tcl_getcurrentthread="no"], $TCL_TEST_OTHERLIBS)
+  if test "$egg_cv_var_tcl_getcurrentthread" = yes; then
+    AC_DEFINE(HAVE_TCL_GETCURRENTTHREAD, 1, [Define for Tcl that has Tcl_GetCurrentThread() (8.1a2 and later).])
+
+    # Add pthread library to $LIBS if we need it for threaded Tcl
+    if test "x$ac_cv_lib_pthread" != x; then
+      EGG_APPEND_VAR(LIBS, $ac_cv_lib_pthread)
+    fi
+  fi
+])
+
+
+dnl EGG_TCL_CHECK_GETTHREADDATA
+dnl
+AC_DEFUN([EGG_TCL_CHECK_GETTHREADDATA],
+[
+  if test "$egg_tcl_changed" = yes; then
+    EGG_CACHE_UNSET(egg_cv_var_tcl_getthreaddata)
+  fi
+
+  # Check for Tcl_GetThreadData()
+  AC_CHECK_LIB($TCL_TEST_LIB, Tcl_GetThreadData, [egg_cv_var_tcl_getthreaddata="yes"], [egg_cv_var_tcl_getthreaddata="no"], $TCL_TEST_OTHERLIBS)
+  if test "$egg_cv_var_tcl_getthreaddata" = yes; then
+    AC_DEFINE(HAVE_TCL_GETTHREADDATA, 1, [Define for Tcl that has Tcl_GetThreadData() (8.1a2 and later).])
+  fi
+])
+
+
+dnl EGG_TCL_CHECK_SETNOTIFIER
+dnl
+AC_DEFUN([EGG_TCL_CHECK_SETNOTIFIER],
+[
+  if test "$egg_tcl_changed" = yes; then
+    EGG_CACHE_UNSET(egg_cv_var_tcl_setnotifier)
+  fi
+
+  # Check for Tcl_SetNotifier()
+  AC_CHECK_LIB($TCL_TEST_LIB, Tcl_SetNotifier, [egg_cv_var_tcl_setnotifier="yes"], [egg_cv_var_tcl_setnotifier="no"], $TCL_TEST_OTHERLIBS)
+  if test "$egg_cv_var_tcl_setnotifier" = yes; then
+    AC_DEFINE(HAVE_TCL_SETNOTIFIER, 1, [Define for Tcl that has Tcl_SetNotifier() (8.2b1 and later).])
+  fi
+])
+
+
+dnl EGG_TCL_LIB_REQS()
+dnl
+AC_DEFUN([EGG_TCL_LIB_REQS],
+[
+  if test "$EGG_CYGWIN" = yes; then
+    TCL_REQS="${TCLLIB}/lib${TCLLIBFN}"
+    TCL_LIBS="-L$TCLLIB -l$TCLLIBFNS $EGG_MATH_LIB"
+  else
+    if test "$TCLLIBEXT" != ".a"; then
+      TCL_REQS="${TCLLIB}/lib${TCLLIBFN}"
+      TCL_LIBS="-L$TCLLIB -l$TCLLIBFNS $EGG_MATH_LIB"
+    else
+      # Set default make as static for unshared Tcl library
+      if test "$DEFAULT_MAKE" != static; then
+        cat << 'EOF' >&2
+configure: WARNING:
+
+  Your Tcl library is not a shared lib.
+  configure will now set default make type to static.
+
+EOF
+        DEFAULT_MAKE="static"
+        AC_SUBST(DEFAULT_MAKE)
+      fi
+
+      # Are we using a pre 7.4 Tcl version ?
+      TCL_VER_PRE74=`echo $egg_cv_var_tcl_version | $AWK '{split([$]1, i, "."); if (((i[[1]] == 7) && (i[[2]] < 4)) || (i[[1]] < 7)) print "yes"; else print "no"}'`
+      if test "$TCL_VER_PRE74" = no; then
+
+        # Was the --with-tcllib option given ?
+        if test "x$tcllibname" != x; then
+          TCL_REQS="${TCLLIB}/lib${TCLLIBFN}"
+          TCL_LIBS="${TCLLIB}/lib${TCLLIBFN} $EGG_MATH_LIB"
+        else
+          TCL_REQS="${TCLLIB}/lib${TCLLIBFN}"
+          TCL_LIBS="-L$TCLLIB -l$TCLLIBFNS $EGG_MATH_LIB"
+        fi
+      else
+        cat << EOF >&2
+configure: WARNING:
+
+  Your Tcl version ($egg_cv_var_tcl_version) is older than 7.4.
+  There are known problems, but we will attempt to work around them.
+
+EOF
+        TCL_REQS="libtcle.a"
+        TCL_LIBS="-L`pwd` -ltcle $EGG_MATH_LIB"
+      fi
+    fi
+  fi
+
+  AC_SUBST(TCL_REQS)
+  AC_SUBST(TCL_LIBS)
+])
+
+

+ 9 - 0
config.h.in

@@ -72,6 +72,9 @@
 /* Define to 1 if you have the `socket' library (-lsocket). */
 #undef HAVE_LIBSOCKET
 
+/* Define if you have support for libtcl */
+#undef HAVE_LIBTCL
+
 /* Define to 1 if you have the <limits.h> header file. */
 #undef HAVE_LIMITS_H
 
@@ -204,6 +207,12 @@
 /* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
 #undef HAVE_SYS_WAIT_H
 
+/* Define for Tcl that has Tcl_Free() (7.5p1 and later). */
+#undef HAVE_TCL_FREE
+
+/* Define for Tcl that has Tcl_SetNotifier() (8.2b1 and later). */
+#undef HAVE_TCL_SETNOTIFIER
+
 /* Define to 1 if you have struct timespec in sys/time.h */
 #undef HAVE_TIMESPEC
 

+ 690 - 0
configure

@@ -602,6 +602,13 @@ MOD_UPDIR
 NUMVER
 VERSION
 LIBOBJS
+TCL_LIBS
+TCL_REQS
+DEFAULT_MAKE
+TCLINCFN
+TCLINC
+TCLLIBFN
+TCLLIB
 SSL_LIBS
 SSL_INCLUDES
 MAKEJOBS
@@ -677,6 +684,8 @@ ac_user_opts='
 enable_option_checking
 enable_ipv6
 with_openssl
+with_tcllib
+with_tclinc
 '
       ac_precious_vars='build_alias
 host_alias
@@ -1303,6 +1312,8 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-openssl=DIR      Path to OpenSSL
+  --with-tcllib=PATH      full path to Tcl library
+  --with-tclinc=PATH      full path to Tcl header
 
 Some influential environment variables:
   CXX         C++ compiler command
@@ -5384,6 +5395,685 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
+# TCL checks
+
+# Tcl version to recommend if no Tcl is found, and the site where it can be
+# found for download.
+tclrecommendver="8.5.X"
+tclrecommendsite="ftp://tcl.activestate.com/pub/tcl/tcl8_5/"
+
+# Tcl library filename prefixes, suffixes, and search paths.
+tcllibnames="tcl8.5 tcl85 tcl8.4 tcl84 tcl8.3 tcl83 tcl8.2 tcl82 \
+             tcl8.1 tcl81 tcl8.0 tcl80 tcl tcl7.6 tcl76 tcl7.5 tcl75 \
+             tcl7.4 tcl74 tcl7.3 tcl73 tcl7.2 tcl72 tcl7.1 tcl71 \
+             tcl7.0 tcl70"
+
+tcllibextensions=".so .so.1 .so.1.0 .so.1.2 .dll .dylib .sl .a"
+
+tcllibpaths="$HOME/lib $HOME/tcl/lib \
+             /usr/local/lib /usr/lib /lib /lib64 /usr/lib64 \
+             /usr/local/lib/tcl8.5 /usr/lib/tcl8.5 \
+             /usr/local/lib/tcl8.4 /usr/lib/tcl8.4 \
+             /usr/local/lib/tcl8.3 /usr/lib/tcl8.3 \
+             /usr/local/pkgs/tcl/lib /sys/lib /usr/pkg/lib \
+             /usr/i486-linuxaout/lib /beos/system/lib $HOME"
+
+# Tcl header filenames and search paths.
+tclheadernames="tcl.h"
+tclheaderpaths="$HOME/include $HOME/tcl/include \
+                /usr/local/include /usr/include \
+                /usr/local/include/tcl8.5 /usr/include/tcl8.5 \
+                /usr/local/include/tcl8.4 /usr/include/tcl8.4 \
+                /usr/local/include/tcl8.3 /usr/include/tcl8.3 \
+                /usr/local/pkgs/tcl/include /sys/include \
+                /usr/pkg/lib /beos/system/include /beos/devel/include $HOME"
+
+
+# Misc Tcl checks.
+
+
+# Check whether --with-tcllib was given.
+if test "${with_tcllib+set}" = set; then :
+  withval=$with_tcllib; tcllibname="$withval"
+fi
+
+
+# Check whether --with-tclinc was given.
+if test "${with_tclinc+set}" = set; then :
+  withval=$with_tclinc; tclincname="$withval"
+fi
+
+
+  WARN=0
+  # Make sure either both or neither $tcllibname and $tclincname are set
+  if test "x$tcllibname" != x; then
+    if test "x$tclincname" = x; then
+      WARN=1
+      tcllibname=""
+      TCLLIB=""
+      TCLINC=""
+    fi
+  else
+    if test "x$tclincname" != x; then
+      WARN=1
+      tclincname=""
+      TCLLIB=""
+      TCLINC=""
+    fi
+  fi
+
+  if test "$WARN" = 1; then
+    cat << 'EOF' >&2
+configure: WARNING:
+
+  You must specify both --with-tcllib and --with-tclinc for either to work.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+  fi
+
+
+  WARN=0
+  # Make sure either both or neither $TCLLIB and $TCLINC are set
+  if test "x$TCLLIB" != x; then
+    if test "x$TCLINC" = x; then
+      WARN=1
+      WVAR1=TCLLIB
+      WVAR2=TCLINC
+      TCLLIB=""
+    fi
+  else
+    if test "x$TCLINC" != x; then
+      WARN=1
+      WVAR1=TCLINC
+      WVAR2=TCLLIB
+      TCLINC=""
+    fi
+  fi
+
+  if test "$WARN" = 1; then
+    cat << EOF >&2
+configure: WARNING:
+
+  Environment variable $WVAR1 was set, but I did not detect ${WVAR2}.
+  Please set both TCLLIB and TCLINC correctly if you wish to use them.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+  fi
+
+
+  # Look for Tcl library: if $tcllibname is set, check there first
+  if test "x$tcllibname" != x; then
+    if test -f "$tcllibname" && test -r "$tcllibname"; then
+      TCLLIB=`echo $tcllibname | sed 's%/[^/][^/]*$%%'`
+      TCLLIBFN=`$BASENAME $tcllibname | cut -c4-`
+      TCLLIBEXT=".`echo $TCLLIBFN | $AWK '{j=split($1, i, "."); print i[j]}'`"
+      TCLLIBFNS=`$BASENAME $tcllibname $TCLLIBEXT | cut -c4-`
+    else
+      cat << EOF >&2
+configure: WARNING:
+
+  The file '$tcllibname' given to option --with-tcllib is not valid.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+      tcllibname=""
+      tclincname=""
+      TCLLIB=""
+      TCLLIBFN=""
+      TCLINC=""
+      TCLINCFN=""
+    fi
+  fi
+
+
+  # Look for Tcl header: if $tclincname is set, check there first
+  if test "x$tclincname" != x; then
+    if test -f "$tclincname" && test -r "$tclincname"; then
+      TCLINC=`echo $tclincname | sed 's%/[^/][^/]*$%%'`
+      TCLINCFN=`$BASENAME $tclincname`
+    else
+      cat << EOF >&2
+configure: WARNING:
+
+  The file '$tclincname' given to option --with-tclinc is not valid.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+      tcllibname=""
+      tclincname=""
+      TCLLIB=""
+      TCLLIBFN=""
+      TCLINC=""
+      TCLINCFN=""
+    fi
+  fi
+
+
+  # Look for Tcl library: if $TCLLIB is set, check there first
+  if test "x$TCLLIBFN" = x && test "x$TCLLIB" != x; then
+    if test -d "$TCLLIB"; then
+      for tcllibfns in $tcllibnames; do
+        for tcllibext in $tcllibextensions; do
+          if test -r "${TCLLIB}/lib${tcllibfns}${tcllibext}"; then
+            TCLLIBFN="${tcllibfns}${tcllibext}"
+            TCLLIBEXT="$tcllibext"
+            TCLLIBFNS="$tcllibfns"
+            break 2
+          fi
+        done
+      done
+    fi
+
+    if test "x$TCLLIBFN" = x; then
+      cat << 'EOF' >&2
+configure: WARNING:
+
+  Environment variable TCLLIB was set, but incorrectly.
+  Please set both TCLLIB and TCLINC correctly if you wish to use them.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+      TCLLIB=""
+      TCLLIBFN=""
+      TCLINC=""
+      TCLINCFN=""
+    fi
+  fi
+
+
+  # Look for Tcl header: if $TCLINC is set, check there first
+  if test "x$TCLINCFN" = x && test "x$TCLINC" != x; then
+    if test -d "$TCLINC"; then
+      for tclheaderfn in $tclheadernames; do
+        if test -r "${TCLINC}/${tclheaderfn}"; then
+          TCLINCFN="$tclheaderfn"
+          break
+        fi
+      done
+    fi
+
+    if test "x$TCLINCFN" = x; then
+      cat << 'EOF' >&2
+configure: WARNING:
+
+  Environment variable TCLINC was set, but incorrectly.
+  Please set both TCLLIB and TCLINC correctly if you wish to use them.
+
+  configure will now attempt to autodetect both the Tcl library and header.
+
+EOF
+      TCLLIB=""
+      TCLLIBFN=""
+      TCLINC=""
+      TCLINCFN=""
+    fi
+  fi
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl library" >&5
+$as_echo_n "checking for Tcl library... " >&6; }
+
+  # Attempt autodetect for $TCLLIBFN if it's not set
+  if test "x$TCLLIBFN" != x; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: using ${TCLLIB}/lib${TCLLIBFN}" >&5
+$as_echo "using ${TCLLIB}/lib${TCLLIBFN}" >&6; }
+  else
+    for tcllibfns in $tcllibnames; do
+      for tcllibext in $tcllibextensions; do
+        for tcllibpath in $tcllibpaths; do
+          if test -r "${tcllibpath}/lib${tcllibfns}${tcllibext}"; then
+            { $as_echo "$as_me:${as_lineno-$LINENO}: result: found ${tcllibpath}/lib${tcllibfns}${tcllibext}" >&5
+$as_echo "found ${tcllibpath}/lib${tcllibfns}${tcllibext}" >&6; }
+            TCLLIB="$tcllibpath"
+            TCLLIBFN="${tcllibfns}${tcllibext}"
+            TCLLIBEXT="$tcllibext"
+            TCLLIBFNS="$tcllibfns"
+            break 3
+          fi
+        done
+      done
+    done
+  fi
+
+  # Show if $TCLLIBFN wasn't found
+  if test "x$TCLLIBFN" = x; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+$as_echo "not found" >&6; }
+  fi
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl header" >&5
+$as_echo_n "checking for Tcl header... " >&6; }
+
+  # Attempt autodetect for $TCLINCFN if it's not set
+  if test "x$TCLINCFN" != x; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: using ${TCLINC}/${TCLINCFN}" >&5
+$as_echo "using ${TCLINC}/${TCLINCFN}" >&6; }
+  else
+    for tclheaderpath in $tclheaderpaths; do
+      for tclheaderfn in $tclheadernames; do
+        if test -r "${tclheaderpath}/${tclheaderfn}"; then
+          { $as_echo "$as_me:${as_lineno-$LINENO}: result: found ${tclheaderpath}/${tclheaderfn}" >&5
+$as_echo "found ${tclheaderpath}/${tclheaderfn}" >&6; }
+          TCLINC="$tclheaderpath"
+          TCLINCFN="$tclheaderfn"
+          break 2
+        fi
+      done
+    done
+
+    # FreeBSD hack ...
+    if test "x$TCLINCFN" = x; then
+      for tcllibfns in $tcllibnames; do
+        for tclheaderpath in $tclheaderpaths; do
+          for tclheaderfn in $tclheadernames; do
+            if test -r "${tclheaderpath}/${tcllibfns}/${tclheaderfn}"; then
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: found ${tclheaderpath}/${tcllibfns}/${tclheaderfn}" >&5
+$as_echo "found ${tclheaderpath}/${tcllibfns}/${tclheaderfn}" >&6; }
+              TCLINC="${tclheaderpath}/${tcllibfns}"
+              TCLINCFN="$tclheaderfn"
+              break 3
+            fi
+          done
+        done
+      done
+    fi
+  fi
+
+  if test "x$TCLINCFN" = x; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+$as_echo "not found" >&6; }
+  fi
+
+
+
+
+
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the Tcl system has changed" >&5
+$as_echo_n "checking whether the Tcl system has changed... " >&6; }
+  egg_tcl_changed="yes"
+  egg_tcl_id="${TCLLIB}:${TCLLIBFN}:${TCLINC}:${TCLINCFN}"
+  if test "$egg_tcl_id" != ":::"; then
+    egg_tcl_cached="yes"
+    if test "${egg_cv_var_tcl_id+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+      egg_cv_var_tcl_id="$egg_tcl_id"
+      egg_tcl_cached="no"
+
+fi
+
+    if test "$egg_tcl_cached" = yes; then
+      if test "x$egg_cv_var_tcl_id" = "x$egg_tcl_id"; then
+        egg_tcl_changed="no"
+      else
+        egg_cv_var_tcl_id="$egg_tcl_id"
+      fi
+    fi
+  fi
+
+  if test "$egg_tcl_changed" = yes; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+  fi
+
+
+  # Both TCLLIBFN & TCLINCFN must be set, or we bail
+  TCL_FOUND=0
+  if test "x$TCLLIBFN" != x && test "x$TCLINCFN" != x; then
+    TCL_FOUND=1
+
+    # Check Tcl's version
+    if test "$egg_tcl_changed" = yes; then
+      unset egg_cv_var_tcl_version
+    fi
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl version" >&5
+$as_echo_n "checking for Tcl version... " >&6; }
+    if test "${egg_cv_var_tcl_version+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+      egg_cv_var_tcl_version=`grep TCL_VERSION $TCLINC/$TCLINCFN | $HEAD_1 | $AWK '{gsub(/\"/, "", $3); print $3}'`
+
+fi
+
+
+    if test "x$egg_cv_var_tcl_version" != x; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: $egg_cv_var_tcl_version" >&5
+$as_echo "$egg_cv_var_tcl_version" >&6; }
+    else
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+$as_echo "not found" >&6; }
+      TCL_FOUND=0
+    fi
+
+    # Check Tcl's patch level (if available)
+    if test "$egg_tcl_changed" = yes; then
+      unset egg_cv_var_tcl_patch_level
+    fi
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl patch level" >&5
+$as_echo_n "checking for Tcl patch level... " >&6; }
+    if test "${egg_cv_var_tcl_patch_level+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+
+      eval "egg_cv_var_tcl_patch_level=`grep TCL_PATCH_LEVEL $TCLINC/$TCLINCFN | $HEAD_1 | $AWK '{gsub(/\"/, "", $3); print $3}'`"
+
+fi
+
+
+    if test "x$egg_cv_var_tcl_patch_level" != x; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: $egg_cv_var_tcl_patch_level" >&5
+$as_echo "$egg_cv_var_tcl_patch_level" >&6; }
+    else
+      egg_cv_var_tcl_patch_level="unknown"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: unknown" >&5
+$as_echo "unknown" >&6; }
+    fi
+  fi
+
+  # Check if we found Tcl's version
+  if test "$TCL_FOUND" = 0; then
+    cat << 'EOF' >&2
+configure: error:
+
+  Tcl cannot be found on this system.
+
+  Tcl is not required. Wraith will be compiled without TCL support. If you
+  already have Tcl installed on this system, and I just wasn't looking in
+  the right place for it, re-run ./configure using the
+  --with-tcllib='/path/to/libtcl.so' and --with-tclinc='/path/to/tcl.h' options.
+
+EOF
+  else
+
+$as_echo "#define HAVE_LIBTCL 1" >>confdefs.h
+
+  fi
+
+
+  # Is this version of Tcl too old for us to use ?
+  TCL_VER_PRE70=`echo $egg_cv_var_tcl_version | $AWK '{split($1, i, "."); if (i[1] < 7) print "yes"; else print "no"}'`
+  if test "$HAVE_LIBTCL" = 1 -a "$TCL_VER_PRE70" = yes; then
+    cat << EOF >&2
+configure: error:
+
+  Your Tcl version is much too old for Eggdrop to use. You should
+  download and compile a more recent version. The most reliable
+  current version is $tclrecommendver and can be downloaded from
+  ${tclrecommendsite}.
+
+  See doc/COMPILE-GUIDE's 'Tcl Detection and Installation' section
+  for more information.
+
+EOF
+    exit 1
+  fi
+
+
+  # Set variables for Tcl library tests
+  TCL_TEST_LIB="$TCLLIBFNS"
+  TCL_TEST_OTHERLIBS="-L$TCLLIB $EGG_MATH_LIB"
+  if test "x$ac_cv_lib_pthread" != x; then
+    TCL_TEST_OTHERLIBS="$TCL_TEST_OTHERLIBS $ac_cv_lib_pthread"
+  fi
+
+
+  if test "$egg_tcl_changed" = yes; then
+    unset egg_cv_var_tcl_free
+  fi
+
+  # Check for Tcl_Free()
+  as_ac_Lib=`$as_echo "ac_cv_lib_$TCL_TEST_LIB''_Tcl_Free" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl_Free in -l$TCL_TEST_LIB" >&5
+$as_echo_n "checking for Tcl_Free in -l$TCL_TEST_LIB... " >&6; }
+if eval "test \"\${$as_ac_Lib+set}\"" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$TCL_TEST_LIB $TCL_TEST_OTHERLIBS $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char Tcl_Free ();
+int
+main ()
+{
+return Tcl_Free ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  eval "$as_ac_Lib=yes"
+else
+  eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+  egg_cv_var_tcl_free="yes"
+else
+  egg_cv_var_tcl_free="no"
+fi
+
+
+  if test "$egg_cv_var_tcl_free" = yes; then
+
+$as_echo "#define HAVE_TCL_FREE 1" >>confdefs.h
+
+  fi
+
+#EGG_TCL_CHECK_GETCURRENTTHREAD
+#EGG_TCL_CHECK_GETTHREADDATA
+
+  if test "$egg_tcl_changed" = yes; then
+    unset egg_cv_var_tcl_setnotifier
+  fi
+
+  # Check for Tcl_SetNotifier()
+  as_ac_Lib=`$as_echo "ac_cv_lib_$TCL_TEST_LIB''_Tcl_SetNotifier" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Tcl_SetNotifier in -l$TCL_TEST_LIB" >&5
+$as_echo_n "checking for Tcl_SetNotifier in -l$TCL_TEST_LIB... " >&6; }
+if eval "test \"\${$as_ac_Lib+set}\"" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$TCL_TEST_LIB $TCL_TEST_OTHERLIBS $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char Tcl_SetNotifier ();
+int
+main ()
+{
+return Tcl_SetNotifier ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  eval "$as_ac_Lib=yes"
+else
+  eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+  egg_cv_var_tcl_setnotifier="yes"
+else
+  egg_cv_var_tcl_setnotifier="no"
+fi
+
+  if test "$egg_cv_var_tcl_setnotifier" = yes; then
+
+$as_echo "#define HAVE_TCL_SETNOTIFIER 1" >>confdefs.h
+
+  fi
+
+
+  if test "$EGG_CYGWIN" = yes; then
+    TCL_REQS="${TCLLIB}/lib${TCLLIBFN}"
+    TCL_LIBS="-L$TCLLIB -l$TCLLIBFNS $EGG_MATH_LIB"
+  else
+    if test "$TCLLIBEXT" != ".a"; then
+      TCL_REQS="${TCLLIB}/lib${TCLLIBFN}"
+      TCL_LIBS="-L$TCLLIB -l$TCLLIBFNS $EGG_MATH_LIB"
+    else
+      # Set default make as static for unshared Tcl library
+      if test "$DEFAULT_MAKE" != static; then
+        cat << 'EOF' >&2
+configure: WARNING:
+
+  Your Tcl library is not a shared lib.
+  configure will now set default make type to static.
+
+EOF
+        DEFAULT_MAKE="static"
+
+      fi
+
+      # Are we using a pre 7.4 Tcl version ?
+      TCL_VER_PRE74=`echo $egg_cv_var_tcl_version | $AWK '{split($1, i, "."); if (((i[1] == 7) && (i[2] < 4)) || (i[1] < 7)) print "yes"; else print "no"}'`
+      if test "$TCL_VER_PRE74" = no; then
+
+        # Was the --with-tcllib option given ?
+        if test "x$tcllibname" != x; then
+          TCL_REQS="${TCLLIB}/lib${TCLLIBFN}"
+          TCL_LIBS="${TCLLIB}/lib${TCLLIBFN} $EGG_MATH_LIB"
+        else
+          TCL_REQS="${TCLLIB}/lib${TCLLIBFN}"
+          TCL_LIBS="-L$TCLLIB -l$TCLLIBFNS $EGG_MATH_LIB"
+        fi
+      else
+        cat << EOF >&2
+configure: WARNING:
+
+  Your Tcl version ($egg_cv_var_tcl_version) is older than 7.4.
+  There are known problems, but we will attempt to work around them.
+
+EOF
+        TCL_REQS="libtcle.a"
+        TCL_LIBS="-L`pwd` -ltcle $EGG_MATH_LIB"
+      fi
+    fi
+  fi
+
+
+
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    test "x$cache_file" != "x/dev/null" &&
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+    cat confcache >$cache_file
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
 
 #AC_SUBST(ZLIB)dnl
 

+ 1 - 0
doc/UPDATES

@@ -18,6 +18,7 @@
   * Added SSL support
     This includes new set options: 'servers-ssl', 'servers6-ssl', 'server-port-ssl', 'server-use-ssl'
   * libcrypto (openssl) is now loaded at startup and is required.
+  * Added TCL support. This is *only* a .tcl command currently, no scripts are loadable yet.
 
 1.3.1 - http://wraith.botpack.net/milestone/1.3.1
   * Fix crash related to slowpart

+ 3 - 0
doc/help.txt

@@ -1845,6 +1845,9 @@ See also: die, conf, botcmd
     -Users will not see channels that are +s or +p unless they have op access there
  
 See also: find
+::tcl
+###  $btcl$b script
+   Executes the given string and returns the result.
 :leaf:topic
 ###  $btopic$b <text>
    Changes the channel's topic, assuming the bot is a chanop or the

+ 2 - 1
src/Makefile.in

@@ -8,7 +8,7 @@ depcomp = /bin/sh $(top_srcdir)/autotools/depcomp
 
 @SET_MAKE@
 
-CXXFLAGS = @CXXFLAGS@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_srcdir)/pack @DEFS@ $(CFLGS) '-DCOMMIT="$(COMMIT)"' '-DBRANCH="$(BRANCH)"' -DBUILDTS=$(BUILDTS) '-DVERSION="$(VERSION)"'
+CXXFLAGS = @CXXFLAGS@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_srcdir)/pack @DEFS@ $(INCLUDES) $(CFLGS) '-DCOMMIT="$(COMMIT)"' '-DBRANCH="$(BRANCH)"' -DBUILDTS=$(BUILDTS) '-DVERSION="$(VERSION)"'
 CPPFLAGS = @CPPFLAGS@
 
 OBJCOPY = @OBJCOPY@
@@ -37,6 +37,7 @@ OBJS = auth.o \
 	garble.o \
 	libcrypto.o \
 	libssl.o \
+	libtcl.o \
 	log.o \
 	main.o \
 	match.o \

+ 22 - 0
src/cmds.c

@@ -57,6 +57,7 @@
 #include "socket.h"
 #include "traffic.h" /* egg_traffic_t */
 #include "core_binds.h"
+#include "libtcl.h"
 #include "src/mod/console.mod/console.h"
 #include "src/mod/server.mod/server.h"
 #include "src/mod/irc.mod/irc.h"
@@ -4536,6 +4537,24 @@ void cmd_test(int idx, char *par)
   putlog(LOG_CMDS, "*", "#%s# test", dcc[idx].nick);
 }
 
+#ifdef HAVE_LIBTCL
+void cmd_tcl(int idx, char *par)
+{
+  if (!isowner(dcc[idx].nick)) {
+    dprintf(idx, "tcl is only available to permanent owners.\n");
+    return;
+  }
+
+  putlog(LOG_CMDS, "*", "#%s# tcl", dcc[idx].nick);
+
+  bd::String result(tcl_eval(par));
+  if (dcc[idx].irc && strcmp(dcc[idx].u.chat->con_chan, "*")) {
+      privmsg(dcc[idx].u.chat->con_chan, tcl_eval(par).c_str(), DP_SERVER);
+  } else
+    dprintf(idx, result.c_str(), DP_SERVER);
+}
+#endif
+
 void cmd_botlink(int idx, char *par)
 {
   putlog(LOG_CMDS, "*", "#%s# botlink %s", dcc[idx].nick, par);
@@ -4658,6 +4677,9 @@ cmd_t C_dcc[] =
   {"w", 		"n", 	(Function) cmd_w, 		NULL, 0},
   {"channels", 		"", 	(Function) cmd_channels, 	NULL, 0},
   {"test",		"",	(Function) cmd_test,		NULL, 0},
+#ifdef HAVE_LIBTCL
+  {"tcl",		"a",	(Function) cmd_tcl,		NULL, AUTH_ALL},
+#endif
   {"botlink",		"a",	(Function) cmd_botlink,		NULL, 0},
   {"randstring", 	"", 	(Function) cmd_randstring, 	NULL, AUTH_ALL},
   {"hash",		"",	(Function) cmd_hash,		NULL, AUTH_ALL},

+ 153 - 0
src/libtcl.c

@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 1997 Robey Pointer
+ * Copyright (C) 1999 - 2002 Eggheads Development Team
+ * Copyright (C) 2002 - 2010 Bryan Drewery
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/*
+ * tcl.c -- handles:
+ *   libtcl handling
+ *
+ */
+
+
+#include "common.h"
+#include "main.h"
+#include "dl.h"
+#include <bdlib/src/String.h>
+#include <bdlib/src/Array.h>
+
+#include "libtcl.h"
+
+#ifdef HAVE_LIBTCL
+Tcl_Interp *global_interp = NULL;
+#endif
+
+void *libtcl_handle = NULL;
+static bd::Array<bd::String> my_symbols;
+
+void initialize_binds_tcl();
+
+static int load_symbols(void *handle) {
+#ifdef HAVE_LIBTCL
+  const char *dlsym_error = NULL;
+
+  DLSYM_GLOBAL(handle, Tcl_Eval);
+  DLSYM_GLOBAL(handle, Tcl_GetStringResult);
+  DLSYM_GLOBAL(handle, Tcl_DeleteInterp);
+  DLSYM_GLOBAL(handle, Tcl_CreateCommand);
+  DLSYM_GLOBAL(handle, Tcl_AppendResult);
+  DLSYM_GLOBAL(handle, Tcl_CreateInterp);
+  DLSYM_GLOBAL(handle, Tcl_FindExecutable);
+  DLSYM_GLOBAL(handle, Tcl_Init);
+#endif
+
+  return 0;
+}
+
+int load_libtcl() {
+#ifndef HAVE_LIBTCL
+  sdprintf("Not compiled with TCL support");
+  return 1;
+#else
+  if (global_interp) {
+    return 0;
+  }
+#endif
+
+  bd::Array<bd::String> libs_list(bd::String("libtcl.so libtcl83.so libtcl8.3.so libtcl84.so libtcl8.4.so libtcl85.so libtcl8.5.so").split(' '));
+
+  for (size_t i = 0; i < libs_list.length(); ++i) {
+    dlerror(); // Clear Errors
+    libtcl_handle = dlopen(bd::String(libs_list[i]).c_str(), RTLD_LAZY);
+    if (libtcl_handle) break;
+  }
+  if (!libtcl_handle) {
+    sdprintf("Unable to find libtcl");
+    return 1;
+  }
+
+  load_symbols(libtcl_handle);
+
+#ifdef HAVE_LIBTCL
+  // create interp
+  global_interp = Tcl_CreateInterp();
+  Tcl_FindExecutable(binname);
+
+  if (Tcl_Init(global_interp) != TCL_OK) {
+    sdprintf("Tcl_Init error: %s", Tcl_GetStringResult(global_interp));
+    return 1;
+  }
+
+  initialize_binds_tcl();
+#endif
+  return 0;
+}
+
+#ifdef HAVE_LIBTCL
+
+#include "chanprog.h"
+static int cmd_privmsg STDVAR {
+  BADARGS(3, 999, " channel string");
+  bd::String str = argv[2];
+  for (int i = 3; i < argc; ++i)
+    str += " " + bd::String(argv[i]);
+  privmsg(argv[1], str.c_str(), DP_SERVER);
+
+  return TCL_OK;
+}
+
+void initialize_binds_tcl() {
+  Tcl_CreateCommand(global_interp, "privmsg", (Tcl_CmdProc*) cmd_privmsg, NULL, NULL);
+}
+
+#endif
+
+int unload_libtcl() {
+  if (libtcl_handle) {
+#ifdef HAVE_LIBTCL
+    if (global_interp) {
+      Tcl_DeleteInterp(global_interp);
+      global_interp = NULL;
+    }
+#endif
+
+    // Cleanup symbol table
+    for (size_t i = 0; i < my_symbols.length(); ++i) {
+      dl_symbol_table.remove(my_symbols[i]);
+      static_cast<bd::String>(my_symbols[i]).clear();
+    }
+    my_symbols.clear();
+
+    dlclose(libtcl_handle);
+    libtcl_handle = NULL;
+    return 0;
+  }
+  return 1;
+}
+
+#ifdef HAVE_LIBTCL
+bd::String tcl_eval(const bd::String& str) {
+  load_libtcl();
+  if (!global_interp) return bd::String();
+  if (Tcl_Eval(global_interp, str.c_str()) == TCL_OK) {
+    return Tcl_GetStringResult(global_interp);
+  } else
+    return tcl_eval("set errorInfo");
+  return bd::String();
+}
+#endif

+ 39 - 0
src/libtcl.h

@@ -0,0 +1,39 @@
+#ifndef _LIBTCL_H
+#define _LIBTCL_H
+
+#include "common.h"
+#include "dl.h"
+#include <bdlib/src/String.h>
+#ifdef HAVE_LIBTCL
+#include <tcl.h>
+
+typedef int (*Tcl_Eval_t)(Tcl_Interp*, const char*);
+typedef void (*Tcl_AppendResult_t)(Tcl_Interp*, ...);
+typedef void (*Tcl_CreateCommand_t)(Tcl_Interp*, const char*, Tcl_CmdProc*, ClientData, Tcl_CmdDeleteProc*);
+typedef const char* (*Tcl_GetStringResult_t)(Tcl_Interp*);
+typedef int (*Tcl_DeleteInterp_t)(Tcl_Interp*);
+typedef Tcl_Interp* (*Tcl_CreateInterp_t)();
+typedef void (*Tcl_FindExecutable_t)(const char*);
+typedef int (*Tcl_Init_t)(Tcl_Interp*);
+
+#include ".defs/libtcl_defs.h"
+
+#define STDVAR (ClientData cd, Tcl_Interp *interp, int argc, const char *argv[])
+
+#define BADARGS(nl, nh, example) do {                               \
+	if ((argc < (nl)) || (argc > (nh))) {                       \
+		Tcl_AppendResult(interp, "wrong # args: should be \"", \
+			argv[0], (example), "\"", NULL);            \
+		return TCL_ERROR;                                   \
+	}                                                           \
+} while (0)
+
+extern Tcl_Interp *global_interp;
+bd::String tcl_eval(const bd::String&);
+#endif
+
+int load_libtcl();
+int unload_libtcl();
+
+
+#endif /* !_LIBTCL_H */