From 822df774aa79cbd4738ebf2624c7dbffe2ca26d9 Mon Sep 17 00:00:00 2001 From: Steve Bennett Date: Mon, 20 May 2019 15:36:32 +1000 Subject: Update autosetup to v0.6.9 --- autosetup/README.autosetup | 2 +- autosetup/autosetup | 13 +- autosetup/cc-shared.tcl | 15 +- autosetup/cc.tcl | 13 +- autosetup/jimsh0.c | 3275 ++++++++++++++++++++++++-------------------- autosetup/system.tcl | 173 ++- configure | 2 +- 7 files changed, 1893 insertions(+), 1600 deletions(-) diff --git a/autosetup/README.autosetup b/autosetup/README.autosetup index 2e077b8..a6215e8 100644 --- a/autosetup/README.autosetup +++ b/autosetup/README.autosetup @@ -1,4 +1,4 @@ -README.autosetup created by autosetup v0.6.8 +README.autosetup created by autosetup v0.6.9 This is the autosetup directory for a local install of autosetup. It contains autosetup, support files and loadable modules. diff --git a/autosetup/autosetup b/autosetup/autosetup index 63441bb..da3a835 100755 --- a/autosetup/autosetup +++ b/autosetup/autosetup @@ -5,7 +5,8 @@ # \ dir=`dirname "$0"`; exec "`$dir/autosetup-find-tclsh`" "$0" "$@" -set autosetup(version) 0.6.8 +# Note that the version has a trailing + on unreleased versions +set autosetup(version) 0.6.9 # Can be set to 1 to debug early-init problems set autosetup(debug) [expr {"--debug" in $argv}] @@ -213,6 +214,7 @@ proc main {argv} { # Log how we were invoked configlog "Invoked as: [getenv WRAPPER $::argv0] [quote-argv $autosetup(argv)]" + configlog "Tclsh: [info nameofexecutable]" # Note that auto.def is *not* loaded in the global scope source $autosetup(autodef) @@ -709,6 +711,9 @@ proc define-append {name args} { if {[get-define $name ""] ne ""} { # Avoid duplicates foreach arg $args { + if {$arg eq ""} { + continue + } set found 0 foreach str [split $::define($name) " "] { if {$str eq $arg} { @@ -1308,7 +1313,7 @@ set modsource(formatting.tcl) { # Module which provides common text formatting -# This is designed for documenation which looks like: +# This is designed for documentation which looks like: # code {...} # or # code { @@ -1699,7 +1704,7 @@ set modsource(install.tcl) { # Module which can install autosetup # autosetup(installed)=1 means that autosetup is not running from source -# autosetup(sysinstall)=1 means that autosetup is running from a sysinstall verion +# autosetup(sysinstall)=1 means that autosetup is running from a sysinstall version # shared=1 means that we are trying to do a sysinstall. This is only possible from the development source. proc autosetup_install {dir {shared 0}} { @@ -1860,7 +1865,7 @@ WRAPPER="$0"; export WRAPPER; "autosetup" "$@" writefile configure \ {#!/bin/sh dir="`dirname "$0"`/autosetup" -WRAPPER="$0"; export WRAPPER; exec "`$dir/autosetup-find-tclsh`" "$dir/autosetup" "$@" +WRAPPER="$0"; export WRAPPER; exec "`"$dir/autosetup-find-tclsh"`" "$dir/autosetup" "$@" } } catch {exec chmod 755 configure} diff --git a/autosetup/cc-shared.tcl b/autosetup/cc-shared.tcl index a803cf7..0d6d522 100644 --- a/autosetup/cc-shared.tcl +++ b/autosetup/cc-shared.tcl @@ -9,6 +9,7 @@ ## SH_CFLAGS Flags to use compiling sources destined for a shared library ## SH_LDFLAGS Flags to use linking (creating) a shared library ## SH_SOPREFIX Prefix to use to set the soname when creating a shared library +## SH_SOFULLPATH Set to 1 if the shared library soname should include the full install path ## SH_SOEXT Extension for shared libs ## SH_SOEXTVER Format for versioned shared libs - %s = version ## SHOBJ_CFLAGS Flags to use compiling sources destined for a shared object @@ -22,9 +23,9 @@ module-options {} # Defaults: gcc on unix -define SHOBJ_CFLAGS -fpic +define SHOBJ_CFLAGS -fPIC define SHOBJ_LDFLAGS -shared -define SH_CFLAGS -fpic +define SH_CFLAGS -fPIC define SH_LDFLAGS -shared define SH_LINKFLAGS -rdynamic define SH_LINKRPATH "-Wl,-rpath -Wl,%s" @@ -48,6 +49,7 @@ switch -glob -- [get-define host] { define SH_SOEXT .dylib define SH_SOEXTVER .%s.dylib define SH_SOPREFIX -Wl,-install_name, + define SH_SOFULLPATH define LD_LIBRARY_PATH DYLD_LIBRARY_PATH define STRIPLIBFLAGS -x } @@ -73,10 +75,6 @@ switch -glob -- [get-define host] { define SH_CFLAGS -KPIC define SH_LINKFLAGS -Wl,-export-dynamic define SH_SOPREFIX -Wl,-h, - } else { - # sparc has a very small GOT table limit, so use -fPIC - define SH_CFLAGS -fPIC - define SHOBJ_CFLAGS -fPIC } } *-*-solaris* { @@ -108,11 +106,6 @@ switch -glob -- [get-define host] { define SH_SOPREFIX "" define LD_LIBRARY_PATH LIBRARY_PATH } - microblaze* { - # Microblaze generally needs -fPIC rather than -fpic - define SHOBJ_CFLAGS -fPIC - define SH_CFLAGS -fPIC - } } if {![is-defined SHOBJ_LDFLAGS_R]} { diff --git a/autosetup/cc.tcl b/autosetup/cc.tcl index 40f2ae1..585d259 100644 --- a/autosetup/cc.tcl +++ b/autosetup/cc.tcl @@ -11,6 +11,7 @@ # ## CC - C compiler ## CXX - C++ compiler +## CPP - C preprocessor ## CCACHE - Set to "none" to disable automatic use of ccache ## CFLAGS - Additional C compiler flags ## CXXFLAGS - Additional C++ compiler flags @@ -465,7 +466,6 @@ proc cc-with {settings args} { # Any failures are recorded in 'config.log' # proc cctest {args} { - set src conftest__.c set tmp conftest__ # Easiest way to merge in the settings @@ -507,9 +507,11 @@ proc cctest {args} { lappend cmdline {*}[get-define CCACHE] switch -exact -- $opts(-lang) { c++ { + set src conftest__.cpp lappend cmdline {*}[get-define CXX] {*}[get-define CXXFLAGS] } c { + set src conftest__.c lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS] } default { @@ -697,6 +699,15 @@ if {[get-define CC] eq ""} { define CCACHE [find-an-executable [get-env CCACHE ccache]] +# If any of these are set in the environment, propagate them to the AUTOREMAKE commandline +foreach i {CC CXX CCACHE CPP CFLAGS CXXFLAGS CXXFLAGS LDFLAGS LIBS CROSS CPPFLAGS LINKFLAGS CC_FOR_BUILD LD} { + if {[env-is-set $i]} { + # Note: If the variable is set on the command line, get-env will return that value + # so the command line will continue to override the environment + define-append AUTOREMAKE [quote-if-needed $i=[get-env $i ""]] + } +} + # Initial cctest settings cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {} -nooutput 0} set autosetup(cc-include-deps) {} diff --git a/autosetup/jimsh0.c b/autosetup/jimsh0.c index fc2133f..63d4596 100644 --- a/autosetup/jimsh0.c +++ b/autosetup/jimsh0.c @@ -59,7 +59,7 @@ #define HAVE_UNISTD_H #define HAVE_UMASK #endif -#define JIM_VERSION 77 +#define JIM_VERSION 78 #ifndef JIM_WIN32COMPAT_H #define JIM_WIN32COMPAT_H @@ -129,11 +129,6 @@ DIR *opendir(const char *name); int closedir(DIR *dir); struct dirent *readdir(DIR *dir); -#elif defined(__MINGW32__) - -#include -#define strtod __strtod - #endif #endif @@ -376,6 +371,12 @@ typedef struct Jim_Obj { } twoPtrValue; struct { + void *ptr; + int int1; + int int2; + } ptrIntValue; + + struct { struct Jim_Var *varPtr; unsigned long callFrameId; int global; @@ -412,11 +413,6 @@ typedef struct Jim_Obj { struct Jim_Obj *varNameObjPtr; struct Jim_Obj *indexObjPtr; } dictSubstValue; - - struct { - void *compre; - unsigned flags; - } regexpValue; struct { int line; int argc; @@ -574,7 +570,7 @@ typedef struct Jim_Interp { unsigned long referenceNextId; struct Jim_HashTable references; unsigned long lastCollectId; /* reference max Id of the last GC - execution. It's set to -1 while the collection + execution. It's set to ~0 while the collection is running as sentinel to avoid to recursive calls via the [collect] command inside finalizers. */ @@ -639,7 +635,7 @@ JIM_EXPORT char *Jim_StrDupLen(const char *s, int l); JIM_EXPORT char **Jim_GetEnviron(void); JIM_EXPORT void Jim_SetEnviron(char **env); -JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template); +JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file); JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script); @@ -847,7 +843,7 @@ JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr, JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp, - Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr); + Jim_Obj *exprObjPtr); JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr); @@ -904,7 +900,8 @@ JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp); JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp); JIM_EXPORT void Jim_HistoryLoad(const char *filename); JIM_EXPORT void Jim_HistorySave(const char *filename); -JIM_EXPORT char *Jim_HistoryGetline(const char *prompt); +JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt); +JIM_EXPORT void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj); JIM_EXPORT void Jim_HistoryAdd(const char *line); JIM_EXPORT void Jim_HistoryShow(void); @@ -1055,6 +1052,79 @@ void regfree(regex_t *preg); #endif #endif +#ifndef JIM_SIGNAL_H +#define JIM_SIGNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +const char *Jim_SignalId(int sig); + +#ifdef __cplusplus +} +#endif + +#endif +#ifndef JIMIOCOMPAT_H +#define JIMIOCOMPAT_H + + +#include +#include + + +void Jim_SetResultErrno(Jim_Interp *interp, const char *msg); + +int Jim_OpenForWrite(const char *filename, int append); + +int Jim_OpenForRead(const char *filename); + +#if defined(__MINGW32__) + #ifndef STRICT + #define STRICT + #endif + #define WIN32_LEAN_AND_MEAN + #include + #include + #include + #include + + typedef HANDLE pidtype; + #define JIM_BAD_PID INVALID_HANDLE_VALUE + + #define JIM_NO_PID INVALID_HANDLE_VALUE + + + #define WIFEXITED(STATUS) (((STATUS) & 0xff00) == 0) + #define WEXITSTATUS(STATUS) ((STATUS) & 0x00ff) + #define WIFSIGNALED(STATUS) (((STATUS) & 0xff00) != 0) + #define WTERMSIG(STATUS) (((STATUS) >> 8) & 0xff) + #define WNOHANG 1 + + int Jim_Errno(void); + pidtype waitpid(pidtype pid, int *status, int nohang); + + #define HAVE_PIPE + #define pipe(P) _pipe((P), 0, O_NOINHERIT) + +#elif defined(HAVE_UNISTD_H) + #include + #include + #include + #include + + typedef int pidtype; + #define Jim_Errno() errno + #define JIM_BAD_PID -1 + #define JIM_NO_PID 0 + + #ifndef HAVE_EXECVPE + #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV) + #endif +#endif + +#endif int Jim_bootstrapInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG)) @@ -1063,11 +1133,15 @@ int Jim_bootstrapInit(Jim_Interp *interp) return Jim_EvalSource(interp, "bootstrap.tcl", 1, "\n" "\n" -"proc package {cmd pkg} {\n" +"proc package {cmd pkg args} {\n" " if {$cmd eq \"require\"} {\n" " foreach path $::auto_path {\n" -" if {[file exists $path/$pkg.tcl]} {\n" -" uplevel #0 [list source $path/$pkg.tcl]\n" +" set pkgpath $path/$pkg.tcl\n" +" if {$path eq \".\"} {\n" +" set pkgpath $pkg.tcl\n" +" }\n" +" if {[file exists $pkgpath]} {\n" +" uplevel #0 [list source $pkgpath]\n" " return\n" " }\n" " }\n" @@ -1374,6 +1448,13 @@ int Jim_stdlibInit(Jim_Interp *interp) return Jim_EvalSource(interp, "stdlib.tcl", 1, "\n" "\n" +"if {![exists -command ref]} {\n" +"\n" +" proc ref {args} {{count 0}} {\n" +" format %08x [incr count]\n" +" }\n" +"}\n" +"\n" "\n" "proc lambda {arglist args} {\n" " tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n" @@ -1435,6 +1516,13 @@ int Jim_stdlibInit(Jim_Interp *interp) "\n" "\n" "\n" +"proc defer {script} {\n" +" upvar jim::defer v\n" +" lappend v $script\n" +"}\n" +"\n" +"\n" +"\n" "proc errorInfo {msg {stacktrace \"\"}} {\n" " if {$stacktrace eq \"\"} {\n" "\n" @@ -1666,7 +1754,7 @@ int Jim_tclcompatInit(Jim_Interp *interp) "\n" "\n" "proc popen {cmd {mode r}} {\n" -" lassign [socket pipe] r w\n" +" lassign [pipe] r w\n" " try {\n" " if {[string match \"w*\" $mode]} {\n" " lappend cmd <@$r &\n" @@ -1683,11 +1771,26 @@ int Jim_tclcompatInit(Jim_Interp *interp) " if {$cmd eq \"pid\"} {\n" " return $pids\n" " }\n" +" if {$cmd eq \"getfd\"} {\n" +" $f getfd\n" +" }\n" " if {$cmd eq \"close\"} {\n" " $f close\n" "\n" -" foreach p $pids { os.wait $p }\n" -" return\n" +" set retopts {}\n" +" foreach p $pids {\n" +" lassign [wait $p] status - rc\n" +" if {$status eq \"CHILDSTATUS\"} {\n" +" if {$rc == 0} {\n" +" continue\n" +" }\n" +" set msg \"child process exited abnormally\"\n" +" } else {\n" +" set msg \"child killed: received signal\"\n" +" }\n" +" set retopts [list -code error -errorcode [list $status $p $rc] $msg]\n" +" }\n" +" return {*}$retopts\n" " }\n" " tailcall $f $cmd {*}$args\n" " }\n" @@ -1720,6 +1823,7 @@ int Jim_tclcompatInit(Jim_Interp *interp) "\n" "\n" "\n" +"\n" "proc try {args} {\n" " set catchopts {}\n" " while {[string match -* [lindex $args 0]]} {\n" @@ -1810,11 +1914,15 @@ int Jim_tclcompatInit(Jim_Interp *interp) #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H) #include #include +#include #include #include #ifdef HAVE_SYS_UN_H #include #endif +#define HAVE_SOCKETS +#elif defined (__MINGW32__) + #else #define JIM_ANSIC #endif @@ -1849,7 +1957,11 @@ int Jim_tclcompatInit(Jim_Interp *interp) #endif #endif -#define JimCheckStreamError(interp, af) af->fops->error(af) +#ifdef JIM_ANSIC + +#undef HAVE_PIPE +#undef HAVE_SOCKETPAIR +#endif struct AioFile; @@ -1956,6 +2068,15 @@ static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name) } } +static int JimCheckStreamError(Jim_Interp *interp, AioFile *af) +{ + int ret = af->fops->error(af); + if (ret) { + JimAioSetError(interp, af->filename); + } + return ret; +} + static void JimAioDelProc(Jim_Interp *interp, void *privData) { AioFile *af = privData; @@ -2068,6 +2189,16 @@ FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command) return af->fp; } +static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + AioFile *af = Jim_CmdPrivData(interp); + + fflush(af->fp); + Jim_SetResultInt(interp, fileno(af->fp)); + + return JIM_OK; +} + static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); @@ -2226,7 +2357,7 @@ static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv) static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc == 3) { -#if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN) +#if defined(HAVE_SOCKETS) && defined(HAVE_SHUTDOWN) static const char * const options[] = { "r", "w", NULL }; enum { OPT_R, OPT_W, }; int option; @@ -2317,6 +2448,7 @@ static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv) } #endif + #ifdef HAVE_FSYNC static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { @@ -2451,6 +2583,13 @@ static const jim_subcmd_type aio_command_table[] = { 2, }, + { "getfd", + NULL, + aio_cmd_getfd, + 0, + 0, + + }, { "gets", "?var?", aio_cmd_gets, @@ -2624,17 +2763,17 @@ static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *fi Jim_IncrRefCount(filename); if (fh == NULL) { -#if !defined(JIM_ANSIC) if (fd >= 0) { +#ifndef JIM_ANSIC fh = fdopen(fd, mode); +#endif } else -#endif fh = fopen(Jim_String(filename), mode); if (fh == NULL) { JimAioSetError(interp, filename); -#if !defined(JIM_ANSIC) +#ifndef JIM_ANSIC if (fd >= 0) { close(fd); } @@ -2648,14 +2787,16 @@ static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *fi af = Jim_Alloc(sizeof(*af)); memset(af, 0, sizeof(*af)); af->fp = fh; - af->fd = fileno(fh); af->filename = filename; + af->openFlags = openFlags; +#ifndef JIM_ANSIC + af->fd = fileno(fh); #ifdef FD_CLOEXEC if ((openFlags & AIO_KEEPOPEN) == 0) { (void)fcntl(af->fd, F_SETFD, FD_CLOEXEC); } #endif - af->openFlags = openFlags; +#endif af->addr_family = family; af->fops = &stdio_fops; af->ssl = NULL; @@ -2674,7 +2815,6 @@ static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename, if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) { Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0); Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp)); - if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) { Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp)); Jim_SetResult(interp, objPtr); @@ -2690,46 +2830,26 @@ static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename, } #endif - -int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template) +#ifdef HAVE_PIPE +static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { -#ifdef HAVE_MKSTEMP - int fd; - mode_t mask; - Jim_Obj *filenameObj; + int p[2]; + static const char *mode[2] = { "r", "w" }; - if (filename_template == NULL) { - const char *tmpdir = getenv("TMPDIR"); - if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) { - tmpdir = "/tmp/"; - } - filenameObj = Jim_NewStringObj(interp, tmpdir, -1); - if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') { - Jim_AppendString(interp, filenameObj, "/", 1); - } - Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1); - } - else { - filenameObj = Jim_NewStringObj(interp, filename_template, -1); + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, ""); + return JIM_ERR; } - - mask = umask(S_IXUSR | S_IRWXG | S_IRWXO); - fd = mkstemp(filenameObj->bytes); - umask(mask); - if (fd < 0) { - JimAioSetError(interp, filenameObj); - Jim_FreeNewObj(interp, filenameObj); - return -1; + if (pipe(p) != 0) { + JimAioSetError(interp, NULL); + return JIM_ERR; } - Jim_SetResult(interp, filenameObj); - return fd; -#else - Jim_SetResultString(interp, "platform has no tempfile support", -1); - return -1; -#endif + return JimMakeChannelPair(interp, p, argv[0], "aio.pipe%ld", 0, mode); } +#endif + int Jim_aioInit(Jim_Interp *interp) @@ -2742,9 +2862,12 @@ int Jim_aioInit(Jim_Interp *interp) #endif Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL); -#ifndef JIM_ANSIC +#ifdef HAVE_SOCKETS Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL); #endif +#ifdef HAVE_PIPE + Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL); +#endif JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r"); @@ -2828,8 +2951,8 @@ int Jim_readdirInit(Jim_Interp *interp) static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) { - regfree(objPtr->internalRep.regexpValue.compre); - Jim_Free(objPtr->internalRep.regexpValue.compre); + regfree(objPtr->internalRep.ptrIntValue.ptr); + Jim_Free(objPtr->internalRep.ptrIntValue.ptr); } static const Jim_ObjType regexpObjType = { @@ -2848,9 +2971,9 @@ static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned f if (objPtr->typePtr == ®expObjType && - objPtr->internalRep.regexpValue.compre && objPtr->internalRep.regexpValue.flags == flags) { + objPtr->internalRep.ptrIntValue.ptr && objPtr->internalRep.ptrIntValue.int1 == flags) { - return objPtr->internalRep.regexpValue.compre; + return objPtr->internalRep.ptrIntValue.ptr; } @@ -2872,8 +2995,8 @@ static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned f Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = ®expObjType; - objPtr->internalRep.regexpValue.flags = flags; - objPtr->internalRep.regexpValue.compre = compre; + objPtr->internalRep.ptrIntValue.int1 = flags; + objPtr->internalRep.ptrIntValue.ptr = compre; return compre; } @@ -3335,13 +3458,20 @@ int Jim_regexpInit(Jim_Interp *interp) # define MAXPATHLEN JIM_PATH_LEN # endif -#if defined(__MINGW32__) || defined(_MSC_VER) +#if defined(__MINGW32__) || defined(__MSYS__) || defined(_MSC_VER) #define ISWINDOWS 1 #else #define ISWINDOWS 0 #endif +#if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC) + #define STAT_MTIME_US(STAT) ((STAT).st_mtimespec.tv_sec * 1000000ll + (STAT).st_mtimespec.tv_nsec / 1000) +#elif defined(HAVE_STRUCT_STAT_ST_MTIM) + #define STAT_MTIME_US(STAT) ((STAT).st_mtim.tv_sec * 1000000ll + (STAT).st_mtim.tv_nsec / 1000) +#endif + + static const char *JimGetFileType(int mode) { if (S_ISREG(mode)) { @@ -3399,6 +3529,9 @@ static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat AppendStatElement(interp, listObj, "atime", sb->st_atime); AppendStatElement(interp, listObj, "mtime", sb->st_mtime); AppendStatElement(interp, listObj, "ctime", sb->st_ctime); +#ifdef STAT_MTIME_US + AppendStatElement(interp, listObj, "mtimeus", STAT_MTIME_US(*sb)); +#endif Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1)); Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1)); @@ -3715,7 +3848,7 @@ static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv) static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL); + int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL, 0); if (fd < 0) { return JIM_ERR; @@ -3833,37 +3966,62 @@ static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv) return JIM_OK; } +static int JimSetFileTimes(Jim_Interp *interp, const char *filename, jim_wide us) +{ +#ifdef HAVE_UTIMES + struct timeval times[2]; + + times[1].tv_sec = times[0].tv_sec = us / 1000000; + times[1].tv_usec = times[0].tv_usec = us % 1000000; + + if (utimes(filename, times) != 0) { + Jim_SetResultFormatted(interp, "can't set time on \"%s\": %s", filename, strerror(errno)); + return JIM_ERR; + } + return JIM_OK; +#else + Jim_SetResultString(interp, "Not implemented", -1); + return JIM_ERR; +#endif +} + static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { struct stat sb; if (argc == 2) { -#ifdef HAVE_UTIMES - jim_wide newtime; - struct timeval times[2]; - - if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) { + jim_wide secs; + if (Jim_GetWide(interp, argv[1], &secs) != JIM_OK) { return JIM_ERR; } + return JimSetFileTimes(interp, Jim_String(argv[0]), secs * 1000000); + } + if (file_stat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + Jim_SetResultInt(interp, sb.st_mtime); + return JIM_OK; +} - times[1].tv_sec = times[0].tv_sec = newtime; - times[1].tv_usec = times[0].tv_usec = 0; +#ifdef STAT_MTIME_US +static int file_cmd_mtimeus(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; - if (utimes(Jim_String(argv[0]), times) != 0) { - Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno)); + if (argc == 2) { + jim_wide us; + if (Jim_GetWide(interp, argv[1], &us) != JIM_OK) { return JIM_ERR; } -#else - Jim_SetResultString(interp, "Not implemented", -1); - return JIM_ERR; -#endif + return JimSetFileTimes(interp, Jim_String(argv[0]), us); } if (file_stat(interp, argv[0], &sb) != JIM_OK) { return JIM_ERR; } - Jim_SetResultInt(interp, sb.st_mtime); + Jim_SetResultInt(interp, STAT_MTIME_US(sb)); return JIM_OK; } +#endif static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { @@ -3988,6 +4146,15 @@ static const jim_subcmd_type file_command_table[] = { 2, }, +#ifdef STAT_MTIME_US + { "mtimeus", + "name ?time?", + file_cmd_mtimeus, + 1, + 2, + + }, +#endif { "copy", "?-force? source dest", file_cmd_copy, @@ -4286,83 +4453,22 @@ int Jim_execInit(Jim_Interp *interp) #include #include +#include -#if defined(__MINGW32__) - - #ifndef STRICT - #define STRICT - #endif - #define WIN32_LEAN_AND_MEAN - #include - #include - - typedef HANDLE fdtype; - typedef HANDLE pidtype; - #define JIM_BAD_FD INVALID_HANDLE_VALUE - #define JIM_BAD_PID INVALID_HANDLE_VALUE - #define JimCloseFd CloseHandle - - #define WIFEXITED(STATUS) 1 - #define WEXITSTATUS(STATUS) (STATUS) - #define WIFSIGNALED(STATUS) 0 - #define WTERMSIG(STATUS) 0 - #define WNOHANG 1 - - static fdtype JimFileno(FILE *fh); - static pidtype JimWaitPid(pidtype pid, int *status, int nohang); - static fdtype JimDupFd(fdtype infd); - static fdtype JimOpenForRead(const char *filename); - static FILE *JimFdOpenForRead(fdtype fd); - static int JimPipe(fdtype pipefd[2]); - static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, - fdtype inputId, fdtype outputId, fdtype errorId); - static int JimErrno(void); -#else - #include - #include - #include - #include - - typedef int fdtype; - typedef int pidtype; - #define JimPipe pipe - #define JimErrno() errno - #define JIM_BAD_FD -1 - #define JIM_BAD_PID -1 - #define JimFileno fileno - #define JimReadFd read - #define JimCloseFd close - #define JimWaitPid waitpid - #define JimDupFd dup - #define JimFdOpenForRead(FD) fdopen((FD), "r") - #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0) - - #ifndef HAVE_EXECVPE - #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV) - #endif -#endif +struct WaitInfoTable; -static const char *JimStrError(void); static char **JimOriginalEnviron(void); static char **JimSaveEnv(char **env); static void JimRestoreEnv(char **env); static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, - pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr); -static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr); + pidtype **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr); +static void JimDetachPids(struct WaitInfoTable *table, int numPids, const pidtype *pidPtr); static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj); -static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents, int len); -static fdtype JimOpenForWrite(const char *filename, int append); -static int JimRewindFd(fdtype fd); - -static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg) -{ - Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError()); -} +static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv); -static const char *JimStrError(void) -{ - return strerror(JimErrno()); -} +#if defined(__MINGW32__) +static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId); +#endif static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr) { @@ -4375,10 +4481,10 @@ static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr) } } -static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj) +static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj) { char buf[256]; - FILE *fh = JimFdOpenForRead(fd); + FILE *fh = fdopen(fd, "r"); int ret = 0; if (fh == NULL) { @@ -4454,31 +4560,16 @@ static void JimFreeEnv(char **env, char **original_environ) } } -#ifndef jim_ext_signal - -const char *Jim_SignalId(int sig) -{ - static char buf[10]; - snprintf(buf, sizeof(buf), "%d", sig); - return buf; -} - -const char *Jim_SignalName(int sig) -{ - return Jim_SignalId(sig); -} -#endif - -static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj) +static Jim_Obj *JimMakeErrorCode(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj) { - Jim_Obj *errorCode; + Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0); - if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) { - return JIM_OK; + if (pid == JIM_BAD_PID || pid == JIM_NO_PID) { + Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, -1)); } - errorCode = Jim_NewListObj(interp, NULL, 0); - - if (WIFEXITED(waitStatus)) { + else if (WIFEXITED(waitStatus)) { Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1)); Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid)); Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus))); @@ -4486,14 +4577,17 @@ static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, J else { const char *type; const char *action; + const char *signame; if (WIFSIGNALED(waitStatus)) { type = "CHILDKILLED"; action = "killed"; + signame = Jim_SignalId(WTERMSIG(waitStatus)); } else { type = "CHILDSUSP"; action = "suspended"; + signame = "none"; } Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1)); @@ -4503,10 +4597,17 @@ static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, J } Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid)); - Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalId(WTERMSIG(waitStatus)), -1)); - Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1)); + Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, signame, -1)); + } + return errorCode; +} + +static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj) +{ + if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) { + return JIM_OK; } - Jim_SetGlobalVariableStr(interp, "errorCode", errorCode); + Jim_SetGlobalVariableStr(interp, "errorCode", JimMakeErrorCode(interp, pid, waitStatus, errStrObj)); return JIM_ERR; } @@ -4519,10 +4620,12 @@ struct WaitInfo int flags; }; + struct WaitInfoTable { struct WaitInfo *info; int size; int used; + int refcount; }; @@ -4534,8 +4637,10 @@ static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData) { struct WaitInfoTable *table = privData; - Jim_Free(table->info); - Jim_Free(table); + if (--table->refcount == 0) { + Jim_Free(table->info); + Jim_Free(table); + } } static struct WaitInfoTable *JimAllocWaitInfoTable(void) @@ -4543,27 +4648,46 @@ static struct WaitInfoTable *JimAllocWaitInfoTable(void) struct WaitInfoTable *table = Jim_Alloc(sizeof(*table)); table->info = NULL; table->size = table->used = 0; + table->refcount = 1; return table; } -static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +static int JimWaitRemove(struct WaitInfoTable *table, pidtype pid) { - fdtype outputId; - fdtype errorId; - pidtype *pidPtr; - int numPids, result; - int child_siginfo = 1; - Jim_Obj *childErrObj; - Jim_Obj *errStrObj; + int i; - if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) { - Jim_Obj *listObj; - int i; - argc--; - numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL); - if (numPids < 0) { + for (i = 0; i < table->used; i++) { + if (pid == table->info[i].pid) { + if (i != table->used - 1) { + table->info[i] = table->info[table->used - 1]; + } + table->used--; + return 0; + } + } + return -1; +} + +static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int outputId; + int errorId; + pidtype *pidPtr; + int numPids, result; + int child_siginfo = 1; + Jim_Obj *childErrObj; + Jim_Obj *errStrObj; + struct WaitInfoTable *table = Jim_CmdPrivData(interp); + + if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) { + Jim_Obj *listObj; + int i; + + argc--; + numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL); + if (numPids < 0) { return JIM_ERR; } @@ -4572,7 +4696,7 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i])); } Jim_SetResult(interp, listObj); - JimDetachPids(interp, numPids, pidPtr); + JimDetachPids(table, numPids, pidPtr); Jim_Free(pidPtr); return JIM_OK; } @@ -4589,7 +4713,7 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) errStrObj = Jim_NewStringObj(interp, "", 0); - if (outputId != JIM_BAD_FD) { + if (outputId != -1) { if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) { result = JIM_ERR; Jim_SetResultErrno(interp, "error reading from output pipe"); @@ -4604,9 +4728,9 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) result = JIM_ERR; } - if (errorId != JIM_BAD_FD) { + if (errorId != -1) { int ret; - JimRewindFd(errorId); + lseek(errorId, 0, SEEK_SET); ret = JimAppendStreamToString(interp, errorId, errStrObj); if (ret < 0) { Jim_SetResultErrno(interp, "error reading from error pipe"); @@ -4633,6 +4757,50 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) return result; } +static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr) +{ + if (JimWaitRemove(table, pid) == 0) { + + waitpid(pid, statusPtr, 0); + return pid; + } + + + return JIM_BAD_PID; +} + +static void JimDetachPids(struct WaitInfoTable *table, int numPids, const pidtype *pidPtr) +{ + int j; + + for (j = 0; j < numPids; j++) { + + int i; + for (i = 0; i < table->used; i++) { + if (pidPtr[j] == table->info[i].pid) { + table->info[i].flags |= WI_DETACHED; + break; + } + } + } +} + +static int JimGetChannelFd(Jim_Interp *interp, const char *name) +{ + Jim_Obj *objv[2]; + + objv[0] = Jim_NewStringObj(interp, name, -1); + objv[1] = Jim_NewStringObj(interp, "getfd", -1); + + if (Jim_EvalObjVector(interp, 2, objv) == JIM_OK) { + jim_wide fd; + if (Jim_GetWide(interp, Jim_GetResult(interp), &fd) == JIM_OK) { + return fd; + } + } + return -1; +} + static void JimReapDetachedPids(struct WaitInfoTable *table) { struct WaitInfo *waitPtr; @@ -4648,7 +4816,7 @@ static void JimReapDetachedPids(struct WaitInfoTable *table) for (count = table->used; count > 0; waitPtr++, count--) { if (waitPtr->flags & WI_DETACHED) { int status; - pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG); + pidtype pid = waitpid(waitPtr->pid, &status, WNOHANG); if (pid == waitPtr->pid) { table->used--; @@ -4662,62 +4830,58 @@ static void JimReapDetachedPids(struct WaitInfoTable *table) } } -static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr) +static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - int i; - - - for (i = 0; i < table->used; i++) { - if (pid == table->info[i].pid) { - - JimWaitPid(pid, statusPtr, 0); + struct WaitInfoTable *table = Jim_CmdPrivData(interp); + int nohang = 0; + pidtype pid; + long pidarg; + int status; + Jim_Obj *errCodeObj; - if (i != table->used - 1) { - table->info[i] = table->info[table->used - 1]; - } - table->used--; - return pid; - } + if (argc == 1) { + JimReapDetachedPids(table); + return JIM_OK; } + if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) { + nohang = 1; + } + if (argc != nohang + 2) { + Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?"); + return JIM_ERR; + } + if (Jim_GetLong(interp, argv[nohang + 1], &pidarg) != JIM_OK) { + return JIM_ERR; + } - return JIM_BAD_PID; -} + pid = waitpid((pidtype)pidarg, &status, nohang ? WNOHANG : 0); -static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr) -{ - int j; - struct WaitInfoTable *table = Jim_CmdPrivData(interp); + errCodeObj = JimMakeErrorCode(interp, pid, status, NULL); - for (j = 0; j < numPids; j++) { + if (pid != JIM_BAD_PID && (WIFEXITED(status) || WIFSIGNALED(status))) { - int i; - for (i = 0; i < table->used; i++) { - if (pidPtr[j] == table->info[i].pid) { - table->info[i].flags |= WI_DETACHED; - break; - } - } + JimWaitRemove(table, pid); } + Jim_SetResult(interp, errCodeObj); + return JIM_OK; } -static FILE *JimGetAioFilehandle(Jim_Interp *interp, const char *name) +static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - FILE *fh; - Jim_Obj *fhObj; - - fhObj = Jim_NewStringObj(interp, name, -1); - Jim_IncrRefCount(fhObj); - fh = Jim_AioFilehandle(interp, fhObj); - Jim_DecrRefCount(interp, fhObj); + if (argc != 1) { + Jim_WrongNumArgs(interp, 1, argv, ""); + return JIM_ERR; + } - return fh; + Jim_SetResultInt(interp, (jim_wide)getpid()); + return JIM_OK; } static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr, - fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr) + int *inPipePtr, int *outPipePtr, int *errFilePtr) { pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all * the pids of child processes. */ @@ -4754,35 +4918,36 @@ JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype ** * or NULL if output goes to stdout/pipe. */ const char *error = NULL; /* Holds name of stderr file to pipe to, * or NULL if stderr goes to stderr/pipe. */ - fdtype inputId = JIM_BAD_FD; - fdtype outputId = JIM_BAD_FD; - fdtype errorId = JIM_BAD_FD; - fdtype lastOutputId = JIM_BAD_FD; - fdtype pipeIds[2]; + int inputId = -1; + int outputId = -1; + int errorId = -1; + int lastOutputId = -1; + int pipeIds[2]; int firstArg, lastArg; /* Indexes of first and last arguments in * current command. */ int lastBar; int i; pidtype pid; char **save_environ; +#ifndef __MINGW32__ + char **child_environ; +#endif struct WaitInfoTable *table = Jim_CmdPrivData(interp); char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1)); int arg_count = 0; - JimReapDetachedPids(table); - if (inPipePtr != NULL) { - *inPipePtr = JIM_BAD_FD; + *inPipePtr = -1; } if (outPipePtr != NULL) { - *outPipePtr = JIM_BAD_FD; + *outPipePtr = -1; } if (errFilePtr != NULL) { - *errFilePtr = JIM_BAD_FD; + *errFilePtr = -1; } - pipeIds[0] = pipeIds[1] = JIM_BAD_FD; + pipeIds[0] = pipeIds[1] = -1; cmdCount = 1; lastBar = -1; @@ -4881,100 +5046,103 @@ badargs: if (input != NULL) { if (inputFile == FILE_TEXT) { - inputId = JimCreateTemp(interp, input, input_len); - if (inputId == JIM_BAD_FD) { + inputId = Jim_MakeTempFile(interp, NULL, 1); + if (inputId == -1) { + goto error; + } + if (write(inputId, input, input_len) != input_len) { + Jim_SetResultErrno(interp, "couldn't write temp file"); + close(inputId); goto error; } + lseek(inputId, 0L, SEEK_SET); } else if (inputFile == FILE_HANDLE) { + int fd = JimGetChannelFd(interp, input); - FILE *fh = JimGetAioFilehandle(interp, input); - - if (fh == NULL) { + if (fd < 0) { goto error; } - inputId = JimDupFd(JimFileno(fh)); + inputId = dup(fd); } else { - inputId = JimOpenForRead(input); - if (inputId == JIM_BAD_FD) { - Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError()); + inputId = Jim_OpenForRead(input); + if (inputId == -1) { + Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, strerror(Jim_Errno())); goto error; } } } else if (inPipePtr != NULL) { - if (JimPipe(pipeIds) != 0) { + if (pipe(pipeIds) != 0) { Jim_SetResultErrno(interp, "couldn't create input pipe for command"); goto error; } inputId = pipeIds[0]; *inPipePtr = pipeIds[1]; - pipeIds[0] = pipeIds[1] = JIM_BAD_FD; + pipeIds[0] = pipeIds[1] = -1; } if (output != NULL) { if (outputFile == FILE_HANDLE) { - FILE *fh = JimGetAioFilehandle(interp, output); - if (fh == NULL) { + int fd = JimGetChannelFd(interp, output); + if (fd < 0) { goto error; } - fflush(fh); - lastOutputId = JimDupFd(JimFileno(fh)); + lastOutputId = dup(fd); } else { - lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND); - if (lastOutputId == JIM_BAD_FD) { - Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError()); + lastOutputId = Jim_OpenForWrite(output, outputFile == FILE_APPEND); + if (lastOutputId == -1) { + Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, strerror(Jim_Errno())); goto error; } } } else if (outPipePtr != NULL) { - if (JimPipe(pipeIds) != 0) { + if (pipe(pipeIds) != 0) { Jim_SetResultErrno(interp, "couldn't create output pipe"); goto error; } lastOutputId = pipeIds[1]; *outPipePtr = pipeIds[0]; - pipeIds[0] = pipeIds[1] = JIM_BAD_FD; + pipeIds[0] = pipeIds[1] = -1; } if (error != NULL) { if (errorFile == FILE_HANDLE) { if (strcmp(error, "1") == 0) { - if (lastOutputId != JIM_BAD_FD) { - errorId = JimDupFd(lastOutputId); + if (lastOutputId != -1) { + errorId = dup(lastOutputId); } else { error = "stdout"; } } - if (errorId == JIM_BAD_FD) { - FILE *fh = JimGetAioFilehandle(interp, error); - if (fh == NULL) { + if (errorId == -1) { + int fd = JimGetChannelFd(interp, error); + if (fd < 0) { goto error; } - fflush(fh); - errorId = JimDupFd(JimFileno(fh)); + errorId = dup(fd); } } else { - errorId = JimOpenForWrite(error, errorFile == FILE_APPEND); - if (errorId == JIM_BAD_FD) { - Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError()); + errorId = Jim_OpenForWrite(error, errorFile == FILE_APPEND); + if (errorId == -1) { + Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, strerror(Jim_Errno())); goto error; } } } else if (errFilePtr != NULL) { - errorId = JimCreateTemp(interp, NULL, 0); - if (errorId == JIM_BAD_FD) { + errorId = Jim_MakeTempFile(interp, NULL, 1); + if (errorId == -1) { goto error; } - *errFilePtr = JimDupFd(errorId); + *errFilePtr = dup(errorId); } @@ -4984,23 +5152,31 @@ badargs: } for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) { int pipe_dup_err = 0; - fdtype origErrorId = errorId; + int origErrorId = errorId; for (lastArg = firstArg; lastArg < arg_count; lastArg++) { - if (arg_array[lastArg][0] == '|') { - if (arg_array[lastArg][1] == '&') { - pipe_dup_err = 1; - } + if (strcmp(arg_array[lastArg], "|") == 0) { break; } + if (strcmp(arg_array[lastArg], "|&") == 0) { + pipe_dup_err = 1; + break; + } + } + + if (lastArg == firstArg) { + Jim_SetResultString(interp, "missing command to exec", -1); + goto error; } + arg_array[lastArg] = NULL; if (lastArg == arg_count) { outputId = lastOutputId; + lastOutputId = -1; } else { - if (JimPipe(pipeIds) != 0) { + if (pipe(pipeIds) != 0) { Jim_SetResultErrno(interp, "couldn't create pipe"); goto error; } @@ -5021,6 +5197,9 @@ badargs: goto error; } #else + i = strlen(arg_array[firstArg]); + + child_environ = Jim_GetEnviron(); pid = vfork(); if (pid < 0) { Jim_SetResultErrno(interp, "couldn't fork child process"); @@ -5029,21 +5208,44 @@ badargs: if (pid == 0) { - if (inputId != -1) dup2(inputId, 0); - if (outputId != -1) dup2(outputId, 1); - if (errorId != -1) dup2(errorId, 2); + if (inputId != -1) { + dup2(inputId, fileno(stdin)); + close(inputId); + } + if (outputId != -1) { + dup2(outputId, fileno(stdout)); + if (outputId != errorId) { + close(outputId); + } + } + if (errorId != -1) { + dup2(errorId, fileno(stderr)); + close(errorId); + } - for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) { - close(i); + if (outPipePtr) { + close(*outPipePtr); + } + if (errFilePtr) { + close(*errFilePtr); + } + if (pipeIds[0] != -1) { + close(pipeIds[0]); + } + if (lastOutputId != -1) { + close(lastOutputId); } (void)signal(SIGPIPE, SIG_DFL); - execvpe(arg_array[firstArg], &arg_array[firstArg], Jim_GetEnviron()); + execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ); + if (write(fileno(stderr), "couldn't exec \"", 15) && + write(fileno(stderr), arg_array[firstArg], i) && + write(fileno(stderr), "\"\n", 2)) { - fprintf(stderr, "couldn't exec \"%s\"\n", arg_array[firstArg]); + } #ifdef JIM_MAINTAINER { @@ -5072,28 +5274,27 @@ badargs: errorId = origErrorId; - if (inputId != JIM_BAD_FD) { - JimCloseFd(inputId); + if (inputId != -1) { + close(inputId); } - if (outputId != JIM_BAD_FD) { - JimCloseFd(outputId); - outputId = JIM_BAD_FD; + if (outputId != -1) { + close(outputId); } inputId = pipeIds[0]; - pipeIds[0] = pipeIds[1] = JIM_BAD_FD; + pipeIds[0] = pipeIds[1] = -1; } *pidArrayPtr = pidPtr; cleanup: - if (inputId != JIM_BAD_FD) { - JimCloseFd(inputId); + if (inputId != -1) { + close(inputId); } - if (lastOutputId != JIM_BAD_FD) { - JimCloseFd(lastOutputId); + if (lastOutputId != -1) { + close(lastOutputId); } - if (errorId != JIM_BAD_FD) { - JimCloseFd(errorId); + if (errorId != -1) { + close(errorId); } Jim_Free(arg_array); @@ -5103,28 +5304,28 @@ badargs: error: - if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) { - JimCloseFd(*inPipePtr); - *inPipePtr = JIM_BAD_FD; + if ((inPipePtr != NULL) && (*inPipePtr != -1)) { + close(*inPipePtr); + *inPipePtr = -1; } - if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) { - JimCloseFd(*outPipePtr); - *outPipePtr = JIM_BAD_FD; + if ((outPipePtr != NULL) && (*outPipePtr != -1)) { + close(*outPipePtr); + *outPipePtr = -1; } - if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) { - JimCloseFd(*errFilePtr); - *errFilePtr = JIM_BAD_FD; + if ((errFilePtr != NULL) && (*errFilePtr != -1)) { + close(*errFilePtr); + *errFilePtr = -1; } - if (pipeIds[0] != JIM_BAD_FD) { - JimCloseFd(pipeIds[0]); + if (pipeIds[0] != -1) { + close(pipeIds[0]); } - if (pipeIds[1] != JIM_BAD_FD) { - JimCloseFd(pipeIds[1]); + if (pipeIds[1] != -1) { + close(pipeIds[1]); } if (pidPtr != NULL) { for (i = 0; i < numPids; i++) { if (pidPtr[i] != JIM_BAD_PID) { - JimDetachPids(interp, 1, &pidPtr[i]); + JimDetachPids(table, 1, &pidPtr[i]); } } Jim_Free(pidPtr); @@ -5156,6 +5357,7 @@ static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, int Jim_execInit(Jim_Interp *interp) { + struct WaitInfoTable *waitinfo; if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG)) return JIM_ERR; @@ -5163,227 +5365,18 @@ int Jim_execInit(Jim_Interp *interp) (void)signal(SIGPIPE, SIG_IGN); #endif - Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable); + waitinfo = JimAllocWaitInfoTable(); + Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable); + waitinfo->refcount++; + Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable); + Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0); + return JIM_OK; } #if defined(__MINGW32__) -static SECURITY_ATTRIBUTES *JimStdSecAttrs(void) -{ - static SECURITY_ATTRIBUTES secAtts; - - secAtts.nLength = sizeof(SECURITY_ATTRIBUTES); - secAtts.lpSecurityDescriptor = NULL; - secAtts.bInheritHandle = TRUE; - return &secAtts; -} - -static int JimErrno(void) -{ - switch (GetLastError()) { - case ERROR_FILE_NOT_FOUND: return ENOENT; - case ERROR_PATH_NOT_FOUND: return ENOENT; - case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; - case ERROR_ACCESS_DENIED: return EACCES; - case ERROR_INVALID_HANDLE: return EBADF; - case ERROR_BAD_ENVIRONMENT: return E2BIG; - case ERROR_BAD_FORMAT: return ENOEXEC; - case ERROR_INVALID_ACCESS: return EACCES; - case ERROR_INVALID_DRIVE: return ENOENT; - case ERROR_CURRENT_DIRECTORY: return EACCES; - case ERROR_NOT_SAME_DEVICE: return EXDEV; - case ERROR_NO_MORE_FILES: return ENOENT; - case ERROR_WRITE_PROTECT: return EROFS; - case ERROR_BAD_UNIT: return ENXIO; - case ERROR_NOT_READY: return EBUSY; - case ERROR_BAD_COMMAND: return EIO; - case ERROR_CRC: return EIO; - case ERROR_BAD_LENGTH: return EIO; - case ERROR_SEEK: return EIO; - case ERROR_WRITE_FAULT: return EIO; - case ERROR_READ_FAULT: return EIO; - case ERROR_GEN_FAILURE: return EIO; - case ERROR_SHARING_VIOLATION: return EACCES; - case ERROR_LOCK_VIOLATION: return EACCES; - case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE; - case ERROR_HANDLE_DISK_FULL: return ENOSPC; - case ERROR_NOT_SUPPORTED: return ENODEV; - case ERROR_REM_NOT_LIST: return EBUSY; - case ERROR_DUP_NAME: return EEXIST; - case ERROR_BAD_NETPATH: return ENOENT; - case ERROR_NETWORK_BUSY: return EBUSY; - case ERROR_DEV_NOT_EXIST: return ENODEV; - case ERROR_TOO_MANY_CMDS: return EAGAIN; - case ERROR_ADAP_HDW_ERR: return EIO; - case ERROR_BAD_NET_RESP: return EIO; - case ERROR_UNEXP_NET_ERR: return EIO; - case ERROR_NETNAME_DELETED: return ENOENT; - case ERROR_NETWORK_ACCESS_DENIED: return EACCES; - case ERROR_BAD_DEV_TYPE: return ENODEV; - case ERROR_BAD_NET_NAME: return ENOENT; - case ERROR_TOO_MANY_NAMES: return ENFILE; - case ERROR_TOO_MANY_SESS: return EIO; - case ERROR_SHARING_PAUSED: return EAGAIN; - case ERROR_REDIR_PAUSED: return EAGAIN; - case ERROR_FILE_EXISTS: return EEXIST; - case ERROR_CANNOT_MAKE: return ENOSPC; - case ERROR_OUT_OF_STRUCTURES: return ENFILE; - case ERROR_ALREADY_ASSIGNED: return EEXIST; - case ERROR_INVALID_PASSWORD: return EPERM; - case ERROR_NET_WRITE_FAULT: return EIO; - case ERROR_NO_PROC_SLOTS: return EAGAIN; - case ERROR_DISK_CHANGE: return EXDEV; - case ERROR_BROKEN_PIPE: return EPIPE; - case ERROR_OPEN_FAILED: return ENOENT; - case ERROR_DISK_FULL: return ENOSPC; - case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE; - case ERROR_INVALID_TARGET_HANDLE: return EBADF; - case ERROR_INVALID_NAME: return ENOENT; - case ERROR_PROC_NOT_FOUND: return ESRCH; - case ERROR_WAIT_NO_CHILDREN: return ECHILD; - case ERROR_CHILD_NOT_COMPLETE: return ECHILD; - case ERROR_DIRECT_ACCESS_HANDLE: return EBADF; - case ERROR_SEEK_ON_DEVICE: return ESPIPE; - case ERROR_BUSY_DRIVE: return EAGAIN; - case ERROR_DIR_NOT_EMPTY: return EEXIST; - case ERROR_NOT_LOCKED: return EACCES; - case ERROR_BAD_PATHNAME: return ENOENT; - case ERROR_LOCK_FAILED: return EACCES; - case ERROR_ALREADY_EXISTS: return EEXIST; - case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG; - case ERROR_BAD_PIPE: return EPIPE; - case ERROR_PIPE_BUSY: return EAGAIN; - case ERROR_PIPE_NOT_CONNECTED: return EPIPE; - case ERROR_DIRECTORY: return ENOTDIR; - } - return EINVAL; -} - -static int JimPipe(fdtype pipefd[2]) -{ - if (CreatePipe(&pipefd[0], &pipefd[1], NULL, 0)) { - return 0; - } - return -1; -} - -static fdtype JimDupFd(fdtype infd) -{ - fdtype dupfd; - pidtype pid = GetCurrentProcess(); - - if (DuplicateHandle(pid, infd, pid, &dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS)) { - return dupfd; - } - return JIM_BAD_FD; -} - -static int JimRewindFd(fdtype fd) -{ - return SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ? -1 : 0; -} - -#if 0 -static int JimReadFd(fdtype fd, char *buffer, size_t len) -{ - DWORD num; - - if (ReadFile(fd, buffer, len, &num, NULL)) { - return num; - } - if (GetLastError() == ERROR_HANDLE_EOF || GetLastError() == ERROR_BROKEN_PIPE) { - return 0; - } - return -1; -} -#endif - -static FILE *JimFdOpenForRead(fdtype fd) -{ - return _fdopen(_open_osfhandle((int)fd, _O_RDONLY | _O_TEXT), "r"); -} - -static fdtype JimFileno(FILE *fh) -{ - return (fdtype)_get_osfhandle(_fileno(fh)); -} - -static fdtype JimOpenForRead(const char *filename) -{ - return CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, - JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); -} - -static fdtype JimOpenForWrite(const char *filename, int append) -{ - fdtype fd = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, - JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); - if (append && fd != JIM_BAD_FD) { - SetFilePointer(fd, 0, NULL, FILE_END); - } - return fd; -} - -static FILE *JimFdOpenForWrite(fdtype fd) -{ - return _fdopen(_open_osfhandle((int)fd, _O_TEXT), "w"); -} - -static pidtype JimWaitPid(pidtype pid, int *status, int nohang) -{ - DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE); - if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) { - - return JIM_BAD_PID; - } - GetExitCodeProcess(pid, &ret); - *status = ret; - CloseHandle(pid); - return pid; -} - -static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents, int len) -{ - char name[MAX_PATH]; - HANDLE handle; - - if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) { - return JIM_BAD_FD; - } - - handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, JimStdSecAttrs(), - CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, - NULL); - - if (handle == INVALID_HANDLE_VALUE) { - goto error; - } - - if (contents != NULL) { - - FILE *fh = JimFdOpenForWrite(JimDupFd(handle)); - if (fh == NULL) { - goto error; - } - - if (fwrite(contents, len, 1, fh) != 1) { - fclose(fh); - goto error; - } - fseek(fh, 0, SEEK_SET); - fclose(fh); - } - return handle; - - error: - Jim_SetResultErrno(interp, "failed to create temp file"); - CloseHandle(handle); - DeleteFile(name); - return JIM_BAD_FD; -} - static int JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH]) { @@ -5493,11 +5486,11 @@ JimWinBuildCommandLine(Jim_Interp *interp, char **argv) } static pidtype -JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, fdtype inputId, fdtype outputId, fdtype errorId) +JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId) { STARTUPINFO startInfo; PROCESS_INFORMATION procInfo; - HANDLE hProcess, h; + HANDLE hProcess; char execPath[MAX_PATH]; pidtype pid = JIM_BAD_PID; Jim_Obj *cmdLineObj; @@ -5519,38 +5512,31 @@ JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, fdtype inputId, startInfo.hStdOutput= INVALID_HANDLE_VALUE; startInfo.hStdError = INVALID_HANDLE_VALUE; - if (inputId == JIM_BAD_FD) { - if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) { - CloseHandle(h); - } - } else { - DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput, - 0, TRUE, DUPLICATE_SAME_ACCESS); + if (inputId == -1) { + inputId = _fileno(stdin); } - if (startInfo.hStdInput == JIM_BAD_FD) { + DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(inputId), hProcess, &startInfo.hStdInput, + 0, TRUE, DUPLICATE_SAME_ACCESS); + if (startInfo.hStdInput == INVALID_HANDLE_VALUE) { goto end; } - if (outputId == JIM_BAD_FD) { - startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0, - JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - } else { - DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput, - 0, TRUE, DUPLICATE_SAME_ACCESS); + if (outputId == -1) { + outputId = _fileno(stdout); } - if (startInfo.hStdOutput == JIM_BAD_FD) { + DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(outputId), hProcess, &startInfo.hStdOutput, + 0, TRUE, DUPLICATE_SAME_ACCESS); + if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) { goto end; } - if (errorId == JIM_BAD_FD) { - startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0, - JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - } else { - DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError, - 0, TRUE, DUPLICATE_SAME_ACCESS); + if (errorId == -1) { + errorId = _fileno(stderr); } - if (startInfo.hStdError == JIM_BAD_FD) { + DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(errorId), hProcess, &startInfo.hStdError, + 0, TRUE, DUPLICATE_SAME_ACCESS); + if (startInfo.hStdError == INVALID_HANDLE_VALUE) { goto end; } @@ -5578,46 +5564,19 @@ JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, fdtype inputId, end: Jim_FreeNewObj(interp, cmdLineObj); - if (startInfo.hStdInput != JIM_BAD_FD) { + if (startInfo.hStdInput != INVALID_HANDLE_VALUE) { CloseHandle(startInfo.hStdInput); } - if (startInfo.hStdOutput != JIM_BAD_FD) { + if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) { CloseHandle(startInfo.hStdOutput); } - if (startInfo.hStdError != JIM_BAD_FD) { + if (startInfo.hStdError != INVALID_HANDLE_VALUE) { CloseHandle(startInfo.hStdError); } return pid; } -#else - -static int JimOpenForWrite(const char *filename, int append) -{ - return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666); -} -static int JimRewindFd(int fd) -{ - return lseek(fd, 0L, SEEK_SET); -} - -static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len) -{ - int fd = Jim_MakeTempFile(interp, NULL); - - if (fd != JIM_BAD_FD) { - unlink(Jim_String(Jim_GetResult(interp))); - if (contents) { - if (write(fd, contents, len) != len) { - Jim_SetResultErrno(interp, "couldn't write temp file"); - close(fd); - return -1; - } - lseek(fd, 0L, SEEK_SET); - } - } - return fd; -} +#else static char **JimOriginalEnviron(void) { @@ -5640,9 +5599,17 @@ static void JimRestoreEnv(char **env) #endif + +#ifdef STRPTIME_NEEDS_XOPEN_SOURCE #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 500 #endif +#endif + + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif #include #include @@ -5654,30 +5621,60 @@ static void JimRestoreEnv(char **env) #include #endif +struct clock_options { + int gmt; + const char *format; +}; + +static int parse_clock_options(Jim_Interp *interp, int argc, Jim_Obj *const *argv, struct clock_options *opts) +{ + static const char * const options[] = { "-gmt", "-format", NULL }; + enum { OPT_GMT, OPT_FORMAT, }; + int i; + + for (i = 0; i < argc; i += 2) { + int option; + if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { + return JIM_ERR; + } + switch (option) { + case OPT_GMT: + if (Jim_GetBoolean(interp, argv[i + 1], &opts->gmt) != JIM_OK) { + return JIM_ERR; + } + break; + case OPT_FORMAT: + opts->format = Jim_String(argv[i + 1]); + break; + } + } + return JIM_OK; +} + static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { char buf[100]; time_t t; - long seconds; - - const char *format = "%a %b %d %H:%M:%S %Z %Y"; + jim_wide seconds; + struct clock_options options = { 0, "%a %b %d %H:%M:%S %Z %Y" }; + struct tm *tm; - if (argc == 2 || (argc == 3 && !Jim_CompareStringImmediate(interp, argv[1], "-format"))) { - return -1; + if (Jim_GetWide(interp, argv[0], &seconds) != JIM_OK) { + return JIM_ERR; } - - if (argc == 3) { - format = Jim_String(argv[2]); + if (argc % 2 == 0) { + return -1; } - - if (Jim_GetLong(interp, argv[0], &seconds) != JIM_OK) { + if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) { return JIM_ERR; } + t = seconds; + tm = options.gmt ? gmtime(&t) : localtime(&t); - if (strftime(buf, sizeof(buf), format, localtime(&t)) == 0) { - Jim_SetResultString(interp, "format string too long", -1); + if (tm == NULL || strftime(buf, sizeof(buf), options.format, tm) == 0) { + Jim_SetResultString(interp, "format string too long or invalid time", -1); return JIM_ERR; } @@ -5687,27 +5684,49 @@ static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv) } #ifdef HAVE_STRPTIME +static time_t jim_timegm(const struct tm *tm) +{ + int m = tm->tm_mon + 1; + int y = 1900 + tm->tm_year - (m <= 2); + int era = (y >= 0 ? y : y - 399) / 400; + unsigned yoe = (unsigned)(y - era * 400); + unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + tm->tm_mday - 1; + unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; + long days = (era * 146097 + (int)doe - 719468); + int secs = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec; + + return days * 24 * 60 * 60 + secs; +} + static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { char *pt; struct tm tm; - time_t now = time(0); + time_t now = time(NULL); + + struct clock_options options = { 0, NULL }; - if (!Jim_CompareStringImmediate(interp, argv[1], "-format")) { + if (argc % 2 == 0) { return -1; } + if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) { + return JIM_ERR; + } + if (options.format == NULL) { + return -1; + } localtime_r(&now, &tm); - pt = strptime(Jim_String(argv[0]), Jim_String(argv[2]), &tm); + pt = strptime(Jim_String(argv[0]), options.format, &tm); if (pt == 0 || *pt != 0) { Jim_SetResultString(interp, "Failed to parse time according to format", -1); return JIM_ERR; } - Jim_SetResultInt(interp, mktime(&tm)); + Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm)); return JIM_OK; } @@ -5743,13 +5762,6 @@ static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv) } static const jim_subcmd_type clock_command_table[] = { - { "seconds", - NULL, - clock_cmd_seconds, - 0, - 0, - - }, { "clicks", NULL, clock_cmd_micros, @@ -5757,6 +5769,13 @@ static const jim_subcmd_type clock_command_table[] = { 0, }, + { "format", + "seconds ?-format string? ?-gmt boolean?", + clock_cmd_format, + 1, + 5, + + }, { "microseconds", NULL, clock_cmd_micros, @@ -5771,22 +5790,22 @@ static const jim_subcmd_type clock_command_table[] = { 0, }, - { "format", - "seconds ?-format format?", - clock_cmd_format, - 1, - 3, - - }, #ifdef HAVE_STRPTIME { "scan", - "str -format format", + "str -format format ?-gmt boolean?", clock_cmd_scan, 3, - 3, + 5, }, #endif + { "seconds", + NULL, + clock_cmd_seconds, + 0, + 0, + + }, { NULL } }; @@ -6200,7 +6219,7 @@ first: pattern += utf8_tounicode_case(pattern, &start, nocase); if (pattern[0] == '-' && pattern[1]) { - pattern += utf8_tounicode(pattern, &pchar); + pattern++; pattern += utf8_tounicode_case(pattern, &end, nocase); @@ -7795,23 +7814,19 @@ static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc start = pc->tstart; end = pc->tend; - if (start > end) { + len = (end - start) + 1; + if (len < 0) { len = 0; - token = Jim_Alloc(1); - token[0] = '\0'; } - else { - len = (end - start) + 1; - token = Jim_Alloc(len + 1); - if (pc->tt != JIM_TT_ESC) { + token = Jim_Alloc(len + 1); + if (pc->tt != JIM_TT_ESC) { - memcpy(token, start, len); - token[len] = '\0'; - } - else { + memcpy(token, start, len); + token[len] = '\0'; + } + else { - len = JimEscape(token, start, len); - } + len = JimEscape(token, start, len); } return Jim_NewStringObjNoAlloc(interp, token, len); @@ -8010,7 +8025,6 @@ Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr) dupPtr->bytes = NULL; } else if (objPtr->length == 0) { - dupPtr->bytes = JimEmptyStringRep; dupPtr->length = 0; dupPtr->typePtr = NULL; @@ -8054,8 +8068,7 @@ int Jim_Length(Jim_Obj *objPtr) { if (objPtr->bytes == NULL) { - JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); - objPtr->typePtr->updateStringProc(objPtr); + Jim_GetString(objPtr, NULL); } return objPtr->length; } @@ -8065,9 +8078,7 @@ const char *Jim_String(Jim_Obj *objPtr) { if (objPtr->bytes == NULL) { - JimPanic((objPtr->typePtr == NULL, "UpdateStringProc called against typeless value.")); - JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); - objPtr->typePtr->updateStringProc(objPtr); + Jim_GetString(objPtr, NULL); } return objPtr->bytes; } @@ -8089,19 +8100,30 @@ static const Jim_ObjType dictSubstObjType = { JIM_TYPE_NONE, }; -static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) -{ - Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr); -} +static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); +static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static const Jim_ObjType interpolatedObjType = { "interpolated", FreeInterpolatedInternalRep, - NULL, + DupInterpolatedInternalRep, NULL, JIM_TYPE_NONE, }; +static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) +{ + Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr); +} + +static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) +{ + + dupPtr->internalRep = srcPtr->internalRep; + + Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr); +} + static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); @@ -8168,9 +8190,7 @@ Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len) objPtr->bytes = JimEmptyStringRep; } else { - objPtr->bytes = Jim_Alloc(len + 1); - memcpy(objPtr->bytes, s, len); - objPtr->bytes[len] = '\0'; + objPtr->bytes = Jim_StrDupLen(s, len); } objPtr->length = len; @@ -8458,8 +8478,6 @@ static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr) int len; const char *str; - SetStringFromAny(interp, strObjPtr); - str = Jim_GetString(strObjPtr, &len); #ifdef JIM_UTF8 @@ -8476,10 +8494,6 @@ static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr) const char *str; int len; - if (strObjPtr->typePtr != &stringObjType) { - SetStringFromAny(interp, strObjPtr); - } - str = Jim_GetString(strObjPtr, &len); #ifdef JIM_UTF8 @@ -8498,9 +8512,7 @@ static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr) const char *str; str = Jim_GetString(strObjPtr, &len); - if (len == 0) { - return strObjPtr; - } + #ifdef JIM_UTF8 len *= 2; #endif @@ -8750,9 +8762,7 @@ int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char * return 1; } else { - const char *objStr = Jim_String(objPtr); - - if (strcmp(str, objStr) != 0) + if (strcmp(str, Jim_String(objPtr)) != 0) return 0; if (objPtr->typePtr != &comparedStringObjType) { @@ -8948,7 +8958,7 @@ static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len t->line = line; } -static int JimCountWordTokens(ParseToken *t) +static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t) { int expand = 1; int count = 0; @@ -8960,6 +8970,13 @@ static int JimCountWordTokens(ParseToken *t) expand = -1; t++; } + else { + if (script->missing == ' ') { + + script->missing = '}'; + script->linenr = t[1].line; + } + } } @@ -9031,7 +9048,7 @@ static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, i++; } - wordtokens = JimCountWordTokens(tokenlist->list + i); + wordtokens = JimCountWordTokens(script, tokenlist->list + i); if (wordtokens == 0) { @@ -9104,7 +9121,7 @@ int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateChar if (stateCharPtr) { *stateCharPtr = script->missing; } - return (script->missing == ' '); + return script->missing == ' ' || script->missing == '}'; } static int JimParseCheckMissing(Jim_Interp *interp, int ch) @@ -9122,6 +9139,9 @@ static int JimParseCheckMissing(Jim_Interp *interp, int ch) case '{': msg = "missing close-brace"; break; + case '}': + msg = "extra characters after close-brace"; + break; case '"': default: msg = "missing quote"; @@ -9262,7 +9282,6 @@ static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr) } } - static void JimVariablesHTValDestructor(void *interp, void *val) { Jim_DecrRefCount(interp, ((Jim_Var *)val)->objPtr); @@ -9916,15 +9935,12 @@ int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objP int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val) { - Jim_Obj *nameObjPtr, *valObjPtr; + Jim_Obj *valObjPtr; int result; - nameObjPtr = Jim_NewStringObj(interp, name, -1); valObjPtr = Jim_NewStringObj(interp, val, -1); - Jim_IncrRefCount(nameObjPtr); Jim_IncrRefCount(valObjPtr); - result = Jim_SetVariable(interp, nameObjPtr, valObjPtr); - Jim_DecrRefCount(interp, nameObjPtr); + result = Jim_SetVariableStr(interp, name, valObjPtr); Jim_DecrRefCount(interp, valObjPtr); return result; } @@ -10237,14 +10253,13 @@ void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr); } -void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) +static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) { - JIM_NOTUSED(interp); - dupPtr->internalRep.dictSubstValue.varNameObjPtr = - srcPtr->internalRep.dictSubstValue.varNameObjPtr; - dupPtr->internalRep.dictSubstValue.indexObjPtr = srcPtr->internalRep.dictSubstValue.indexObjPtr; - dupPtr->typePtr = &dictSubstObjType; + dupPtr->internalRep = srcPtr->internalRep; + + Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr); + Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr); } @@ -10296,12 +10311,8 @@ static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr) static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr) { - Jim_Obj *resultObjPtr; - - if (Jim_EvalExpression(interp, objPtr, &resultObjPtr) == JIM_OK) { - - resultObjPtr->refCount--; - return resultObjPtr; + if (Jim_EvalExpression(interp, objPtr) == JIM_OK) { + return Jim_GetResult(interp); } return NULL; } @@ -10382,6 +10393,52 @@ static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands) return JIM_OK; } +static int JimInvokeDefer(Jim_Interp *interp, int retcode) +{ + Jim_Obj *objPtr; + + + if (Jim_FindHashEntry(&interp->framePtr->vars, "jim::defer") == NULL) { + return retcode; + } + + objPtr = Jim_GetVariableStr(interp, "jim::defer", JIM_NONE); + + if (objPtr) { + int ret = JIM_OK; + int i; + int listLen = Jim_ListLength(interp, objPtr); + Jim_Obj *resultObjPtr; + + Jim_IncrRefCount(objPtr); + + resultObjPtr = Jim_GetResult(interp); + Jim_IncrRefCount(resultObjPtr); + Jim_SetEmptyResult(interp); + + + for (i = listLen; i > 0; i--) { + + Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1); + ret = Jim_EvalObj(interp, scriptObjPtr); + if (ret != JIM_OK) { + break; + } + } + + if (ret == JIM_OK || retcode == JIM_ERR) { + + Jim_SetResult(interp, resultObjPtr); + } + else { + retcode = ret; + } + + Jim_DecrRefCount(interp, resultObjPtr); + Jim_DecrRefCount(interp, objPtr); + } + return retcode; +} #define JIM_FCF_FULL 0 #define JIM_FCF_REUSE 1 @@ -10495,6 +10552,8 @@ void Jim_FreeInterp(Jim_Interp *i) for (cf = i->framePtr; cf; cf = cfx) { + + JimInvokeDefer(i, JIM_OK); cfx = cf->parent; JimFreeCallFrame(i, cf, JIM_FCF_FULL); } @@ -10525,6 +10584,7 @@ void Jim_FreeInterp(Jim_Interp *i) printf("Objects still in the free list:\n"); while (objPtr) { const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string"; + Jim_String(objPtr); if (objPtr->bytes && strlen(objPtr->bytes) > 20) { printf("%p (%d) %-10s: '%.20s...'\n", @@ -10945,8 +11005,6 @@ static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr) jim_wide wideValue; const char *str; - str = Jim_String(objPtr); - #ifdef HAVE_LONG_LONG #define MIN_INT_IN_DOUBLE -(1LL << 53) @@ -10960,8 +11018,9 @@ static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr) objPtr->typePtr = &coercedDoubleObjType; return JIM_OK; } - else #endif + str = Jim_String(objPtr); + if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) { Jim_FreeIntRep(interp, objPtr); @@ -11554,7 +11613,11 @@ static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, ele[dst] = ele[src]; } - ele[++dst] = ele[src]; + + dst++; + if (dst < listObjPtr->internalRep.listValue.len) { + ele[dst] = ele[src]; + } listObjPtr->internalRep.listValue.len = dst; @@ -12331,7 +12394,7 @@ static const char * const jimReturnCodes[] = { NULL }; -#define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes)) +#define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1) static const Jim_ObjType returnCodeObjType = { "return-code", @@ -12384,12 +12447,11 @@ static int JimParseExprIrrational(struct JimParserCtx *pc); static int JimParseExprBoolean(struct JimParserCtx *pc); - - enum { + JIM_EXPROP_MUL = JIM_TT_EXPR_OP, JIM_EXPROP_DIV, JIM_EXPROP_MOD, @@ -12408,28 +12470,10 @@ enum JIM_EXPROP_BITAND, JIM_EXPROP_BITXOR, JIM_EXPROP_BITOR, - - JIM_EXPROP_LOGICAND, - JIM_EXPROP_LOGICAND_LEFT, - JIM_EXPROP_LOGICAND_RIGHT, - - JIM_EXPROP_LOGICOR, - JIM_EXPROP_LOGICOR_LEFT, - JIM_EXPROP_LOGICOR_RIGHT, - - - JIM_EXPROP_TERNARY, - JIM_EXPROP_TERNARY_LEFT, - JIM_EXPROP_TERNARY_RIGHT, - - JIM_EXPROP_COLON, - JIM_EXPROP_COLON_LEFT, - JIM_EXPROP_COLON_RIGHT, - JIM_EXPROP_POW, @@ -12445,8 +12489,7 @@ enum JIM_EXPROP_UNARYPLUS, - JIM_EXPROP_FUNC_FIRST, - JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST, + JIM_EXPROP_FUNC_INT, JIM_EXPROP_FUNC_WIDE, JIM_EXPROP_FUNC_ABS, JIM_EXPROP_FUNC_DOUBLE, @@ -12476,46 +12519,44 @@ enum JIM_EXPROP_FUNC_FMOD, }; -struct JimExprState -{ - Jim_Obj **stack; - int stacklen; - int opcode; - int skip; +struct JimExprNode { + int type; + struct Jim_Obj *objPtr; + + struct JimExprNode *left; + struct JimExprNode *right; + struct JimExprNode *ternary; }; typedef struct Jim_ExprOperator { const char *name; - int (*funcop) (Jim_Interp *interp, struct JimExprState * e); + int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode); unsigned char precedence; unsigned char arity; - unsigned char lazy; + unsigned char attr; unsigned char namelen; } Jim_ExprOperator; -static void ExprPush(struct JimExprState *e, Jim_Obj *obj) -{ - Jim_IncrRefCount(obj); - e->stack[e->stacklen++] = obj; -} +static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr); +static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node); +static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node); -static Jim_Obj *ExprPop(struct JimExprState *e) -{ - return e->stack[--e->stacklen]; -} - -static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e) +static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node) { int intresult = 1; - int rc = JIM_OK; - Jim_Obj *A = ExprPop(e); + int rc; double dA, dC = 0; jim_wide wA, wC = 0; + Jim_Obj *A; + + if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { + return rc; + } if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) { - switch (e->opcode) { + switch (node->type) { case JIM_EXPROP_FUNC_INT: case JIM_EXPROP_FUNC_WIDE: case JIM_EXPROP_FUNC_ROUND: @@ -12540,7 +12581,7 @@ static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e) } } else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) { - switch (e->opcode) { + switch (node->type) { case JIM_EXPROP_FUNC_INT: case JIM_EXPROP_FUNC_WIDE: wC = dA; @@ -12575,10 +12616,10 @@ static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e) if (rc == JIM_OK) { if (intresult) { - ExprPush(e, Jim_NewIntObj(interp, wC)); + Jim_SetResultInt(interp, wC); } else { - ExprPush(e, Jim_NewDoubleObj(interp, dC)); + Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC)); } } @@ -12595,20 +12636,25 @@ static double JimRandDouble(Jim_Interp *interp) return (double)x / (unsigned long)~0; } -static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e) +static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node) { - Jim_Obj *A = ExprPop(e); jim_wide wA; + Jim_Obj *A; + int rc; + + if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { + return rc; + } - int rc = Jim_GetWide(interp, A, &wA); + rc = Jim_GetWide(interp, A, &wA); if (rc == JIM_OK) { - switch (e->opcode) { + switch (node->type) { case JIM_EXPROP_BITNOT: - ExprPush(e, Jim_NewIntObj(interp, ~wA)); + Jim_SetResultInt(interp, ~wA); break; case JIM_EXPROP_FUNC_SRAND: JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA)); - ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp))); + Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp))); break; default: abort(); @@ -12620,25 +12666,29 @@ static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e) return rc; } -static int JimExprOpNone(Jim_Interp *interp, struct JimExprState *e) +static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node) { - JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()")); + JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()")); - ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp))); + Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp))); return JIM_OK; } #ifdef JIM_MATH_FUNCTIONS -static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e) +static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node) { int rc; - Jim_Obj *A = ExprPop(e); double dA, dC; + Jim_Obj *A; + + if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { + return rc; + } rc = Jim_GetDouble(interp, A, &dA); if (rc == JIM_OK) { - switch (e->opcode) { + switch (node->type) { case JIM_EXPROP_FUNC_SIN: dC = sin(dA); break; @@ -12687,7 +12737,7 @@ static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e) default: abort(); } - ExprPush(e, Jim_NewDoubleObj(interp, dC)); + Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC)); } Jim_DecrRefCount(interp, A); @@ -12697,19 +12747,28 @@ static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e) #endif -static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e) +static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node) { - Jim_Obj *B = ExprPop(e); - Jim_Obj *A = ExprPop(e); jim_wide wA, wB; - int rc = JIM_ERR; + int rc; + Jim_Obj *A, *B; + + if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { + return rc; + } + if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) { + Jim_DecrRefCount(interp, A); + return rc; + } + + rc = JIM_ERR; if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) { jim_wide wC; rc = JIM_OK; - switch (e->opcode) { + switch (node->type) { case JIM_EXPROP_LSHIFT: wC = wA << wB; break; @@ -12758,7 +12817,7 @@ static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e) uB %= S; - if (e->opcode == JIM_EXPROP_ROTR) { + if (node->type == JIM_EXPROP_ROTR) { uB = S - uB; } wC = (unsigned long)(uA << uB) | (uA >> (S - uB)); @@ -12767,8 +12826,7 @@ static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e) default: abort(); } - ExprPush(e, Jim_NewIntObj(interp, wC)); - + Jim_SetResultInt(interp, wC); } Jim_DecrRefCount(interp, A); @@ -12779,14 +12837,20 @@ static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e) -static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e) +static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node) { int rc = JIM_OK; double dA, dB, dC = 0; jim_wide wA, wB, wC = 0; + Jim_Obj *A, *B; - Jim_Obj *B = ExprPop(e); - Jim_Obj *A = ExprPop(e); + if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { + return rc; + } + if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) { + Jim_DecrRefCount(interp, A); + return rc; + } if ((A->typePtr != &doubleObjType || A->bytes) && (B->typePtr != &doubleObjType || B->bytes) && @@ -12794,7 +12858,7 @@ static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e) - switch (e->opcode) { + switch (node->type) { case JIM_EXPROP_POW: case JIM_EXPROP_FUNC_POW: if (wA == 0 && wB < 0) { @@ -12851,7 +12915,7 @@ static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e) } } if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) { - switch (e->opcode) { + switch (node->type) { #ifndef JIM_MATH_FUNCTIONS case JIM_EXPROP_POW: case JIM_EXPROP_FUNC_POW: @@ -12923,7 +12987,7 @@ static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e) int i = Jim_StringCompareObj(interp, A, B, 0); - switch (e->opcode) { + switch (node->type) { case JIM_EXPROP_LT: wC = i < 0; goto intresult; @@ -12951,10 +13015,10 @@ done: Jim_DecrRefCount(interp, B); return rc; intresult: - ExprPush(e, Jim_NewIntObj(interp, wC)); + Jim_SetResultInt(interp, wC); goto done; doubleresult: - ExprPush(e, Jim_NewDoubleObj(interp, dC)); + Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC)); goto done; } @@ -12972,18 +13036,27 @@ static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valOb return 0; } -static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e) -{ - Jim_Obj *B = ExprPop(e); - Jim_Obj *A = ExprPop(e); + +static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node) +{ + Jim_Obj *A, *B; jim_wide wC; + int rc; + + if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) { + return rc; + } + if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) { + Jim_DecrRefCount(interp, A); + return rc; + } - switch (e->opcode) { + switch (node->type) { case JIM_EXPROP_STREQ: case JIM_EXPROP_STRNE: wC = Jim_StringEqObj(A, B); - if (e->opcode == JIM_EXPROP_STRNE) { + if (node->type == JIM_EXPROP_STRNE) { wC = !wC; } break; @@ -12996,12 +13069,12 @@ static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e) default: abort(); } - ExprPush(e, Jim_NewIntObj(interp, wC)); + Jim_SetResultInt(interp, wC); Jim_DecrRefCount(interp, A); Jim_DecrRefCount(interp, B); - return JIM_OK; + return rc; } static int ExprBool(Jim_Interp *interp, Jim_Obj *obj) @@ -13009,166 +13082,82 @@ static int ExprBool(Jim_Interp *interp, Jim_Obj *obj) long l; double d; int b; + int ret = -1; + + + Jim_IncrRefCount(obj); if (Jim_GetLong(interp, obj, &l) == JIM_OK) { - return l != 0; + ret = (l != 0); } - if (Jim_GetDouble(interp, obj, &d) == JIM_OK) { - return d != 0; + else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) { + ret = (d != 0); } - if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) { - return b != 0; - } - return -1; -} - -static int JimExprOpAndLeft(Jim_Interp *interp, struct JimExprState *e) -{ - Jim_Obj *skip = ExprPop(e); - Jim_Obj *A = ExprPop(e); - int rc = JIM_OK; - - switch (ExprBool(interp, A)) { - case 0: - - e->skip = JimWideValue(skip); - ExprPush(e, Jim_NewIntObj(interp, 0)); - break; - - case 1: - - break; - - case -1: - - rc = JIM_ERR; + else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) { + ret = (b != 0); } - Jim_DecrRefCount(interp, A); - Jim_DecrRefCount(interp, skip); - return rc; + Jim_DecrRefCount(interp, obj); + return ret; } -static int JimExprOpOrLeft(Jim_Interp *interp, struct JimExprState *e) +static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node) { - Jim_Obj *skip = ExprPop(e); - Jim_Obj *A = ExprPop(e); - int rc = JIM_OK; - - switch (ExprBool(interp, A)) { - case 0: - break; - - case 1: - - e->skip = JimWideValue(skip); - ExprPush(e, Jim_NewIntObj(interp, 1)); - break; + int result = JimExprGetTermBoolean(interp, node->left); - case -1: + if (result == 1) { - rc = JIM_ERR; - break; + result = JimExprGetTermBoolean(interp, node->right); } - Jim_DecrRefCount(interp, A); - Jim_DecrRefCount(interp, skip); - - return rc; -} - -static int JimExprOpAndOrRight(Jim_Interp *interp, struct JimExprState *e) -{ - Jim_Obj *A = ExprPop(e); - int rc = JIM_OK; - - switch (ExprBool(interp, A)) { - case 0: - ExprPush(e, Jim_NewIntObj(interp, 0)); - break; - - case 1: - ExprPush(e, Jim_NewIntObj(interp, 1)); - break; - - case -1: - - rc = JIM_ERR; - break; + if (result == -1) { + return JIM_ERR; } - Jim_DecrRefCount(interp, A); - - return rc; + Jim_SetResultInt(interp, result); + return JIM_OK; } -static int JimExprOpTernaryLeft(Jim_Interp *interp, struct JimExprState *e) +static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node) { - Jim_Obj *skip = ExprPop(e); - Jim_Obj *A = ExprPop(e); - int rc = JIM_OK; - - - ExprPush(e, A); - - switch (ExprBool(interp, A)) { - case 0: - - e->skip = JimWideValue(skip); - - ExprPush(e, Jim_NewIntObj(interp, 0)); - break; - - case 1: - break; + int result = JimExprGetTermBoolean(interp, node->left); - case -1: + if (result == 0) { - rc = JIM_ERR; - break; + result = JimExprGetTermBoolean(interp, node->right); } - Jim_DecrRefCount(interp, A); - Jim_DecrRefCount(interp, skip); - - return rc; + if (result == -1) { + return JIM_ERR; + } + Jim_SetResultInt(interp, result); + return JIM_OK; } -static int JimExprOpColonLeft(Jim_Interp *interp, struct JimExprState *e) +static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node) { - Jim_Obj *skip = ExprPop(e); - Jim_Obj *B = ExprPop(e); - Jim_Obj *A = ExprPop(e); - - if (ExprBool(interp, A)) { + int result = JimExprGetTermBoolean(interp, node->left); - e->skip = JimWideValue(skip); + if (result == 1) { - ExprPush(e, B); + return JimExprEvalTermNode(interp, node->right); } + else if (result == 0) { - Jim_DecrRefCount(interp, skip); - Jim_DecrRefCount(interp, A); - Jim_DecrRefCount(interp, B); - return JIM_OK; -} + return JimExprEvalTermNode(interp, node->ternary); + } -static int JimExprOpNull(Jim_Interp *interp, struct JimExprState *e) -{ - return JIM_OK; + return JIM_ERR; } enum { - LAZY_NONE, - LAZY_OP, - LAZY_LEFT, - LAZY_RIGHT, - RIGHT_ASSOC, + OP_FUNC = 0x0001, + OP_RIGHT_ASSOC = 0x0002, }; #define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1} -#define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, LAZY_NONE) +#define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0) static const struct Jim_ExprOperator Jim_ExprOperators[] = { OPRINIT("*", 110, 2, JimExprOpBin), @@ -13196,24 +13185,13 @@ static const struct Jim_ExprOperator Jim_ExprOperators[] = { OPRINIT("^", 49, 2, JimExprOpIntBin), OPRINIT("|", 48, 2, JimExprOpIntBin), - OPRINIT_ATTR("&&", 10, 2, NULL, LAZY_OP), - OPRINIT_ATTR(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT), - OPRINIT_ATTR(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT), + OPRINIT("&&", 10, 2, JimExprOpAnd), + OPRINIT("||", 9, 2, JimExprOpOr), + OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC), + OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC), - OPRINIT_ATTR("||", 9, 2, NULL, LAZY_OP), - OPRINIT_ATTR(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT), - OPRINIT_ATTR(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT), - OPRINIT_ATTR("?", 5, 2, JimExprOpNull, LAZY_OP), - OPRINIT_ATTR(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT), - OPRINIT_ATTR(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT), - - OPRINIT_ATTR(":", 5, 2, JimExprOpNull, LAZY_OP), - OPRINIT_ATTR(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT), - OPRINIT_ATTR(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT), - - - OPRINIT_ATTR("**", 120, 2, JimExprOpBin, RIGHT_ASSOC), + OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC), OPRINIT("eq", 60, 2, JimExprOpStrBin), OPRINIT("ne", 60, 2, JimExprOpStrBin), @@ -13221,45 +13199,45 @@ static const struct Jim_ExprOperator Jim_ExprOperators[] = { OPRINIT("in", 55, 2, JimExprOpStrBin), OPRINIT("ni", 55, 2, JimExprOpStrBin), - OPRINIT("!", 150, 1, JimExprOpNumUnary), - OPRINIT("~", 150, 1, JimExprOpIntUnary), - OPRINIT(NULL, 150, 1, JimExprOpNumUnary), - OPRINIT(NULL, 150, 1, JimExprOpNumUnary), + OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC), + OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC), + OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC), + OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC), - OPRINIT("int", 200, 1, JimExprOpNumUnary), - OPRINIT("wide", 200, 1, JimExprOpNumUnary), - OPRINIT("abs", 200, 1, JimExprOpNumUnary), - OPRINIT("double", 200, 1, JimExprOpNumUnary), - OPRINIT("round", 200, 1, JimExprOpNumUnary), - OPRINIT("rand", 200, 0, JimExprOpNone), - OPRINIT("srand", 200, 1, JimExprOpIntUnary), + OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC), + OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC), + OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC), + OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC), + OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC), + OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC), + OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC), #ifdef JIM_MATH_FUNCTIONS - OPRINIT("sin", 200, 1, JimExprOpDoubleUnary), - OPRINIT("cos", 200, 1, JimExprOpDoubleUnary), - OPRINIT("tan", 200, 1, JimExprOpDoubleUnary), - OPRINIT("asin", 200, 1, JimExprOpDoubleUnary), - OPRINIT("acos", 200, 1, JimExprOpDoubleUnary), - OPRINIT("atan", 200, 1, JimExprOpDoubleUnary), - OPRINIT("atan2", 200, 2, JimExprOpBin), - OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary), - OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary), - OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary), - OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary), - OPRINIT("floor", 200, 1, JimExprOpDoubleUnary), - OPRINIT("exp", 200, 1, JimExprOpDoubleUnary), - OPRINIT("log", 200, 1, JimExprOpDoubleUnary), - OPRINIT("log10", 200, 1, JimExprOpDoubleUnary), - OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary), - OPRINIT("pow", 200, 2, JimExprOpBin), - OPRINIT("hypot", 200, 2, JimExprOpBin), - OPRINIT("fmod", 200, 2, JimExprOpBin), + OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC), + OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC), + OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC), + OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC), + OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC), #endif }; #undef OPRINIT -#undef OPRINIT_LAZY +#undef OPRINIT_ATTR #define JIM_EXPR_OPERATORS_NUM \ (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator)) @@ -13415,31 +13393,40 @@ static int JimParseExprBoolean(struct JimParserCtx *pc) return JIM_ERR; } +static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode) +{ + static Jim_ExprOperator dummy_op; + if (opcode < JIM_TT_EXPR_OP) { + return &dummy_op; + } + return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP]; +} + static int JimParseExprOperator(struct JimParserCtx *pc) { int i; - int bestIdx = -1, bestLen = 0; + const struct Jim_ExprOperator *bestOp = NULL; + int bestLen = 0; for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) { - const char * const opname = Jim_ExprOperators[i].name; - const int oplen = Jim_ExprOperators[i].namelen; + const struct Jim_ExprOperator *op = &Jim_ExprOperators[i]; - if (opname == NULL || opname[0] != pc->p[0]) { + if (op->name[0] != pc->p[0]) { continue; } - if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) { - bestIdx = i + JIM_TT_EXPR_OP; - bestLen = oplen; + if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) { + bestOp = op; + bestLen = op->namelen; } } - if (bestIdx == -1) { + if (bestOp == NULL) { return JIM_ERR; } - if (bestIdx >= JIM_EXPROP_FUNC_FIRST) { + if (bestOp->attr & OP_FUNC) { const char *p = pc->p + bestLen; int len = pc->len - bestLen; @@ -13455,19 +13442,10 @@ static int JimParseExprOperator(struct JimParserCtx *pc) pc->p += bestLen; pc->len -= bestLen; - pc->tt = bestIdx; + pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP; return JIM_OK; } -static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode) -{ - static Jim_ExprOperator dummy_op; - if (opcode < JIM_TT_EXPR_OP) { - return &dummy_op; - } - return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP]; -} - const char *jim_tt_name(int type) { static const char * const tt_names[JIM_TT_EXPR_OP] = @@ -13507,34 +13485,41 @@ static const Jim_ObjType exprObjType = { }; -typedef struct ExprByteCode +struct ExprTree { - ScriptToken *token; + struct JimExprNode *expr; + struct JimExprNode *nodes; int len; int inUse; -} ExprByteCode; +}; -static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr) +static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num) { int i; - - for (i = 0; i < expr->len; i++) { - Jim_DecrRefCount(interp, expr->token[i].objPtr); + for (i = 0; i < num; i++) { + if (nodes[i].objPtr) { + Jim_DecrRefCount(interp, nodes[i].objPtr); + } } - Jim_Free(expr->token); + Jim_Free(nodes); +} + +static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr) +{ + ExprTreeFreeNodes(interp, expr->nodes, expr->len); Jim_Free(expr); } static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) { - ExprByteCode *expr = (void *)objPtr->internalRep.ptr; + struct ExprTree *expr = (void *)objPtr->internalRep.ptr; if (expr) { if (--expr->inUse != 0) { return; } - ExprFreeByteCode(interp, expr); + ExprTreeFree(interp, expr); } } @@ -13547,260 +13532,142 @@ static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dup dupPtr->typePtr = NULL; } -static int ExprCheckCorrectness(Jim_Interp *interp, Jim_Obj *exprObjPtr, ExprByteCode * expr) +struct ExprBuilder { + int parencount; + int level; + ParseToken *token; + ParseToken *first_token; + Jim_Stack stack; + Jim_Obj *exprObjPtr; + Jim_Obj *fileNameObj; + struct JimExprNode *nodes; + struct JimExprNode *next; +}; + +#ifdef DEBUG_SHOW_EXPR +static void JimShowExprNode(struct JimExprNode *node, int level) { int i; - int stacklen = 0; - int ternary = 0; - int lasttt = JIM_TT_NONE; - const char *errmsg; - - for (i = 0; i < expr->len; i++) { - ScriptToken *t = &expr->token[i]; - const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type); - lasttt = t->type; - - stacklen -= op->arity; - - if (stacklen < 0) { - break; - } - if (t->type == JIM_EXPROP_TERNARY || t->type == JIM_EXPROP_TERNARY_LEFT) { - ternary++; - } - else if (t->type == JIM_EXPROP_COLON || t->type == JIM_EXPROP_COLON_LEFT) { - ternary--; - } - - - stacklen++; - } - if (stacklen == 1 && ternary == 0) { - return JIM_OK; + for (i = 0; i < level; i++) { + printf(" "); } - - if (stacklen <= 0) { - - if (lasttt >= JIM_EXPROP_FUNC_FIRST) { - errmsg = "too few arguments for math function"; - Jim_SetResultString(interp, "too few arguments for math function", -1); - } else { - errmsg = "premature end of expression"; + if (TOKEN_IS_EXPR_OP(node->type)) { + printf("%s\n", jim_tt_name(node->type)); + if (node->left) { + JimShowExprNode(node->left, level + 1); } - } - else if (stacklen > 1) { - if (lasttt >= JIM_EXPROP_FUNC_FIRST) { - errmsg = "too many arguments for math function"; - } else { - errmsg = "extra tokens at end of expression"; + if (node->right) { + JimShowExprNode(node->right, level + 1); + } + if (node->ternary) { + JimShowExprNode(node->ternary, level + 1); } } else { - errmsg = "invalid ternary expression"; + printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr)); } - Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": %s", exprObjPtr, errmsg); - return JIM_ERR; } +#endif -static int ExprAddLazyOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t) -{ - int i; - - int leftindex, arity, offset; - - - leftindex = expr->len - 1; - - arity = 1; - while (arity) { - ScriptToken *tt = &expr->token[leftindex]; - - if (tt->type >= JIM_TT_EXPR_OP) { - arity += JimExprOperatorInfoByOpcode(tt->type)->arity; - } - arity--; - if (--leftindex < 0) { - return JIM_ERR; - } - } - leftindex++; - - - memmove(&expr->token[leftindex + 2], &expr->token[leftindex], - sizeof(*expr->token) * (expr->len - leftindex)); - expr->len += 2; - offset = (expr->len - leftindex) - 1; - - expr->token[leftindex + 1].type = t->type + 1; - expr->token[leftindex + 1].objPtr = interp->emptyObj; - - expr->token[leftindex].type = JIM_TT_EXPR_INT; - expr->token[leftindex].objPtr = Jim_NewIntObj(interp, offset); - - - expr->token[expr->len].objPtr = interp->emptyObj; - expr->token[expr->len].type = t->type + 2; - expr->len++; - - - for (i = leftindex - 1; i > 0; i--) { - const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(expr->token[i].type); - if (op->lazy == LAZY_LEFT) { - if (JimWideValue(expr->token[i - 1].objPtr) + i - 1 >= leftindex) { - JimWideValue(expr->token[i - 1].objPtr) += 2; - } - } - } - return JIM_OK; -} +#define EXPR_UNTIL_CLOSE 0x0001 +#define EXPR_FUNC_ARGS 0x0002 +#define EXPR_TERNARY 0x0004 -static int ExprAddOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t) +static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms) { - struct ScriptToken *token = &expr->token[expr->len]; - const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type); + int rc; + struct JimExprNode *node; - if (op->lazy == LAZY_OP) { - if (ExprAddLazyOperator(interp, expr, t) != JIM_OK) { - Jim_SetResultFormatted(interp, "Expression has bad operands to %s", op->name); - return JIM_ERR; - } - } - else { - token->objPtr = interp->emptyObj; - token->type = t->type; - expr->len++; - } - return JIM_OK; -} + int exp_stacklen = builder->stack.len + exp_numterms; -static int ExprTernaryGetColonLeftIndex(ExprByteCode *expr, int right_index) -{ - int ternary_count = 1; + if (builder->level++ > 200) { + Jim_SetResultString(interp, "Expression too complex", -1); + return JIM_ERR; + } - right_index--; + while (builder->token->type != JIM_TT_EOL) { + ParseToken *t = builder->token++; + int prevtt; - while (right_index > 1) { - if (expr->token[right_index].type == JIM_EXPROP_TERNARY_LEFT) { - ternary_count--; - } - else if (expr->token[right_index].type == JIM_EXPROP_COLON_RIGHT) { - ternary_count++; + if (t == builder->first_token) { + prevtt = JIM_TT_NONE; } - else if (expr->token[right_index].type == JIM_EXPROP_COLON_LEFT && ternary_count == 1) { - return right_index; + else { + prevtt = t[-1].type; } - right_index--; - } - - return -1; -} - -static int ExprTernaryGetMoveIndices(ExprByteCode *expr, int right_index, int *prev_right_index, int *prev_left_index) -{ - int i = right_index - 1; - int ternary_count = 1; - - while (i > 1) { - if (expr->token[i].type == JIM_EXPROP_TERNARY_LEFT) { - if (--ternary_count == 0 && expr->token[i - 2].type == JIM_EXPROP_COLON_RIGHT) { - *prev_right_index = i - 2; - *prev_left_index = ExprTernaryGetColonLeftIndex(expr, *prev_right_index); - return 1; + if (t->type == JIM_TT_SUBEXPR_START) { + if (builder->stack.len == exp_stacklen) { + Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr); + return JIM_ERR; } - } - else if (expr->token[i].type == JIM_EXPROP_COLON_RIGHT) { - if (ternary_count == 0) { - return 0; + builder->parencount++; + rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1); + if (rc != JIM_OK) { + return rc; } - ternary_count++; - } - i--; - } - return 0; -} - -static void ExprTernaryReorderExpression(Jim_Interp *interp, ExprByteCode *expr) -{ - int i; - for (i = expr->len - 1; i > 1; i--) { - int prev_right_index; - int prev_left_index; - int j; - ScriptToken tmp; - - if (expr->token[i].type != JIM_EXPROP_COLON_RIGHT) { - continue; - } - - - if (ExprTernaryGetMoveIndices(expr, i, &prev_right_index, &prev_left_index) == 0) { - continue; } + else if (t->type == JIM_TT_SUBEXPR_END) { + if (!(flags & EXPR_UNTIL_CLOSE)) { + if (builder->stack.len == exp_stacklen && builder->level > 1) { + builder->token--; + builder->level--; + return JIM_OK; + } + Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr); + return JIM_ERR; + } + builder->parencount--; + if (builder->stack.len == exp_stacklen) { - tmp = expr->token[prev_right_index]; - for (j = prev_right_index; j < i; j++) { - expr->token[j] = expr->token[j + 1]; + break; + } } - expr->token[i] = tmp; - - JimWideValue(expr->token[prev_left_index-1].objPtr) += (i - prev_right_index); - - - i++; - } -} - -static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj) -{ - Jim_Stack stack; - ExprByteCode *expr; - int ok = 1; - int i; - int prevtt = JIM_TT_NONE; - int have_ternary = 0; - - - int count = tokenlist->count - 1; - - expr = Jim_Alloc(sizeof(*expr)); - expr->inUse = 1; - expr->len = 0; - - Jim_InitStack(&stack); - - for (i = 0; i < tokenlist->count; i++) { - ParseToken *t = &tokenlist->list[i]; - const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type); + else if (t->type == JIM_TT_SUBEXPR_COMMA) { + if (!(flags & EXPR_FUNC_ARGS)) { + if (builder->stack.len == exp_stacklen) { - if (op->lazy == LAZY_OP) { - count += 2; - - if (t->type == JIM_EXPROP_TERNARY) { - have_ternary = 1; + builder->token--; + builder->level--; + return JIM_OK; + } + Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr); + return JIM_ERR; } - } - } + else { - expr->token = Jim_Alloc(sizeof(ScriptToken) * count); + if (builder->stack.len > exp_stacklen) { + Jim_SetResultFormatted(interp, "too many arguments to math function"); + return JIM_ERR; + } + } - for (i = 0; i < tokenlist->count && ok; i++) { - ParseToken *t = &tokenlist->list[i]; + } + else if (t->type == JIM_EXPROP_COLON) { + if (!(flags & EXPR_TERNARY)) { + if (builder->level != 1) { + builder->token--; + builder->level--; + return JIM_OK; + } + Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr); + return JIM_ERR; + } + if (builder->stack.len == exp_stacklen) { - struct ScriptToken *token = &expr->token[expr->len]; + builder->token--; + builder->level--; + return JIM_OK; + } - if (t->type == JIM_TT_EOL) { - break; } - - if (TOKEN_IS_EXPR_OP(t->type)) { + else if (TOKEN_IS_EXPR_OP(t->type)) { const struct Jim_ExprOperator *op; - ParseToken *tt; - if (prevtt == JIM_TT_NONE || prevtt == JIM_TT_SUBEXPR_START || prevtt == JIM_TT_SUBEXPR_COMMA || prevtt >= JIM_TT_EXPR_OP) { + if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) { if (t->type == JIM_EXPROP_SUB) { t->type = JIM_EXPROP_UNARYMINUS; } @@ -13811,66 +13678,82 @@ static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList op = JimExprOperatorInfoByOpcode(t->type); + if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) { - while ((tt = Jim_StackPeek(&stack)) != NULL) { - const struct Jim_ExprOperator *tt_op = - JimExprOperatorInfoByOpcode(tt->type); - - - if (op->arity != 1 && tt_op->precedence >= op->precedence) { + builder->token--; + break; + } - if (tt_op->precedence == op->precedence && tt_op->lazy == RIGHT_ASSOC) { - break; - } - if (ExprAddOperator(interp, expr, tt) != JIM_OK) { - ok = 0; - goto err; - } - Jim_StackPop(&stack); + if (op->attr & OP_FUNC) { + if (builder->token->type != JIM_TT_SUBEXPR_START) { + Jim_SetResultString(interp, "missing arguments for math function", -1); + return JIM_ERR; } - else { - break; + builder->token++; + if (op->arity == 0) { + if (builder->token->type != JIM_TT_SUBEXPR_END) { + Jim_SetResultString(interp, "too many arguments for math function", -1); + return JIM_ERR; + } + builder->token++; + goto noargs; } + builder->parencount++; + + + rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity); } - Jim_StackPush(&stack, t); - } - else if (t->type == JIM_TT_SUBEXPR_START) { - Jim_StackPush(&stack, t); - } - else if (t->type == JIM_TT_SUBEXPR_END || t->type == JIM_TT_SUBEXPR_COMMA) { + else if (t->type == JIM_EXPROP_TERNARY) { - ok = 0; - while (Jim_StackLen(&stack)) { - ParseToken *tt = Jim_StackPop(&stack); + rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2); + } + else { + rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1); + } - if (tt->type == JIM_TT_SUBEXPR_START || tt->type == JIM_TT_SUBEXPR_COMMA) { - if (t->type == JIM_TT_SUBEXPR_COMMA) { + if (rc != JIM_OK) { + return rc; + } - Jim_StackPush(&stack, tt); - } - ok = 1; - break; +noargs: + node = builder->next++; + node->type = t->type; + + if (op->arity >= 3) { + node->ternary = Jim_StackPop(&builder->stack); + if (node->ternary == NULL) { + goto missingoperand; } - if (ExprAddOperator(interp, expr, tt) != JIM_OK) { - goto err; + } + if (op->arity >= 2) { + node->right = Jim_StackPop(&builder->stack); + if (node->right == NULL) { + goto missingoperand; } } - if (!ok) { - Jim_SetResultFormatted(interp, "Unexpected close parenthesis in expression: \"%#s\"", exprObjPtr); - goto err; + if (op->arity >= 1) { + node->left = Jim_StackPop(&builder->stack); + if (node->left == NULL) { +missingoperand: + Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr); + builder->next--; + return JIM_ERR; + + } } + + + Jim_StackPush(&builder->stack, node); } else { Jim_Obj *objPtr = NULL; - token->type = t->type; if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) { - Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", exprObjPtr); - ok = 0; - goto err; + Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr); + return JIM_ERR; } @@ -13889,64 +13772,103 @@ static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList } } - if (objPtr) { - token->objPtr = objPtr; - } - else { + if (!objPtr) { - token->objPtr = Jim_NewStringObj(interp, t->token, t->len); + objPtr = Jim_NewStringObj(interp, t->token, t->len); if (t->type == JIM_TT_CMD) { - JimSetSourceInfo(interp, token->objPtr, fileNameObj, t->line); + JimSetSourceInfo(interp, objPtr, builder->fileNameObj, t->line); } } - expr->len++; + + + node = builder->next++; + node->objPtr = objPtr; + Jim_IncrRefCount(node->objPtr); + node->type = t->type; + Jim_StackPush(&builder->stack, node); } - prevtt = t->type; } + if (builder->stack.len == exp_stacklen) { + builder->level--; + return JIM_OK; + } - while (Jim_StackLen(&stack)) { - ParseToken *tt = Jim_StackPop(&stack); - - if (tt->type == JIM_TT_SUBEXPR_START) { - ok = 0; - Jim_SetResultString(interp, "Missing close parenthesis", -1); - goto err; + if ((flags & EXPR_FUNC_ARGS)) { + Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many"); + } + else { + if (builder->stack.len < exp_stacklen) { + if (builder->level == 0) { + Jim_SetResultFormatted(interp, "empty expression"); + } + else { + Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr); + } } - if (ExprAddOperator(interp, expr, tt) != JIM_OK) { - ok = 0; - goto err; + else { + Jim_SetResultFormatted(interp, "extra terms after expression"); } } - if (have_ternary) { - ExprTernaryReorderExpression(interp, expr); - } + return JIM_ERR; +} - err: +static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj) +{ + struct ExprTree *expr; + struct ExprBuilder builder; + int rc; + struct JimExprNode *top = NULL; + + builder.parencount = 0; + builder.level = 0; + builder.token = builder.first_token = tokenlist->list; + builder.exprObjPtr = exprObjPtr; + builder.fileNameObj = fileNameObj; + + builder.nodes = malloc(sizeof(struct JimExprNode) * (tokenlist->count - 1)); + memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1)); + builder.next = builder.nodes; + Jim_InitStack(&builder.stack); - Jim_FreeStack(&stack); + rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1); - for (i = 0; i < expr->len; i++) { - Jim_IncrRefCount(expr->token[i].objPtr); + if (rc == JIM_OK) { + top = Jim_StackPop(&builder.stack); + + if (builder.parencount) { + Jim_SetResultString(interp, "missing close parenthesis", -1); + rc = JIM_ERR; + } } - if (!ok) { - ExprFreeByteCode(interp, expr); + + Jim_FreeStack(&builder.stack); + + if (rc != JIM_OK) { + ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes); return NULL; } + expr = Jim_Alloc(sizeof(*expr)); + expr->inUse = 1; + expr->expr = top; + expr->nodes = builder.nodes; + expr->len = builder.next - builder.nodes; + + assert(expr->len <= tokenlist->count - 1); + return expr; } - static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) { int exprTextLen; const char *exprText; struct JimParserCtx parser; - struct ExprByteCode *expr; + struct ExprTree *expr; ParseTokenList tokenlist; int line; Jim_Obj *fileNameObj; @@ -13999,7 +13921,7 @@ static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) } - expr = ExprCreateByteCode(interp, &tokenlist, objPtr, fileNameObj); + expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj); ScriptTokenListFree(&tokenlist); @@ -14009,26 +13931,10 @@ static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) } #ifdef DEBUG_SHOW_EXPR - { - int i; - - printf("==== Expr ====\n"); - for (i = 0; i < expr->len; i++) { - ScriptToken *t = &expr->token[i]; - - printf("[%2d] %s '%s'\n", i, jim_tt_name(t->type), Jim_String(t->objPtr)); - } - } + printf("==== Expr ====\n"); + JimShowExprNode(expr->expr, 0); #endif - - if (ExprCheckCorrectness(interp, objPtr, expr) != JIM_OK) { - - ExprFreeByteCode(interp, expr); - expr = NULL; - goto err; - } - rc = JIM_OK; err: @@ -14040,39 +13946,104 @@ static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) return rc; } -static ExprByteCode *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr) +static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr) { if (objPtr->typePtr != &exprObjType) { if (SetExprFromAny(interp, objPtr) != JIM_OK) { return NULL; } } - return (ExprByteCode *) Jim_GetIntRepPtr(objPtr); + return (struct ExprTree *) Jim_GetIntRepPtr(objPtr); } #ifdef JIM_OPTIMIZATION -static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, const ScriptToken *token) -{ - if (token->type == JIM_TT_EXPR_INT) - return token->objPtr; - else if (token->type == JIM_TT_VAR) - return Jim_GetVariable(interp, token->objPtr, JIM_NONE); - else if (token->type == JIM_TT_DICTSUGAR) - return JimExpandDictSugar(interp, token->objPtr); +static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node) +{ + if (node->type == JIM_TT_EXPR_INT) + return node->objPtr; + else if (node->type == JIM_TT_VAR) + return Jim_GetVariable(interp, node->objPtr, JIM_NONE); + else if (node->type == JIM_TT_DICTSUGAR) + return JimExpandDictSugar(interp, node->objPtr); else return NULL; } #endif -#define JIM_EE_STATICSTACK_LEN 10 -int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr) +static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node) { - ExprByteCode *expr; - Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN]; - int i; + if (TOKEN_IS_EXPR_OP(node->type)) { + const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type); + return op->funcop(interp, node); + } + else { + Jim_Obj *objPtr; + + + switch (node->type) { + case JIM_TT_EXPR_INT: + case JIM_TT_EXPR_DOUBLE: + case JIM_TT_EXPR_BOOLEAN: + case JIM_TT_STR: + Jim_SetResult(interp, node->objPtr); + return JIM_OK; + + case JIM_TT_VAR: + objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG); + if (objPtr) { + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + return JIM_ERR; + + case JIM_TT_DICTSUGAR: + objPtr = JimExpandDictSugar(interp, node->objPtr); + if (objPtr) { + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + return JIM_ERR; + + case JIM_TT_ESC: + if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) { + Jim_SetResult(interp, objPtr); + return JIM_OK; + } + return JIM_ERR; + + case JIM_TT_CMD: + return Jim_EvalObj(interp, node->objPtr); + + default: + + return JIM_ERR; + } + } +} + +static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr) +{ + int rc = JimExprEvalTermNode(interp, node); + if (rc == JIM_OK) { + *objPtrPtr = Jim_GetResult(interp); + Jim_IncrRefCount(*objPtrPtr); + } + return rc; +} + +static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node) +{ + if (JimExprEvalTermNode(interp, node) == JIM_OK) { + return ExprBool(interp, Jim_GetResult(interp)); + } + return -1; +} + +int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr) +{ + struct ExprTree *expr; int retcode = JIM_OK; - struct JimExprState e; expr = JimGetExpression(interp, exprObjPtr); if (!expr) { @@ -14086,35 +14057,33 @@ int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprRe switch (expr->len) { case 1: - objPtr = JimExprIntValOrVar(interp, &expr->token[0]); + objPtr = JimExprIntValOrVar(interp, expr->expr); if (objPtr) { - Jim_IncrRefCount(objPtr); - *exprResultPtrPtr = objPtr; + Jim_SetResult(interp, objPtr); return JIM_OK; } break; case 2: - if (expr->token[1].type == JIM_EXPROP_NOT) { - objPtr = JimExprIntValOrVar(interp, &expr->token[0]); + if (expr->expr->type == JIM_EXPROP_NOT) { + objPtr = JimExprIntValOrVar(interp, expr->expr->left); if (objPtr && JimIsWide(objPtr)) { - *exprResultPtrPtr = JimWideValue(objPtr) ? interp->falseObj : interp->trueObj; - Jim_IncrRefCount(*exprResultPtrPtr); + Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj); return JIM_OK; } } break; case 3: - objPtr = JimExprIntValOrVar(interp, &expr->token[0]); + objPtr = JimExprIntValOrVar(interp, expr->expr->left); if (objPtr && JimIsWide(objPtr)) { - Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, &expr->token[1]); + Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right); if (objPtr2 && JimIsWide(objPtr2)) { jim_wide wideValueA = JimWideValue(objPtr); jim_wide wideValueB = JimWideValue(objPtr2); int cmpRes; - switch (expr->token[2].type) { + switch (expr->expr->type) { case JIM_EXPROP_LT: cmpRes = wideValueA < wideValueB; break; @@ -14136,8 +14105,7 @@ int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprRe default: goto noopt; } - *exprResultPtrPtr = cmpRes ? interp->trueObj : interp->falseObj; - Jim_IncrRefCount(*exprResultPtrPtr); + Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj); return JIM_OK; } } @@ -14150,122 +14118,33 @@ noopt: expr->inUse++; - - if (expr->len > JIM_EE_STATICSTACK_LEN) - e.stack = Jim_Alloc(sizeof(Jim_Obj *) * expr->len); - else - e.stack = staticStack; - - e.stacklen = 0; - - - for (i = 0; i < expr->len && retcode == JIM_OK; i++) { - Jim_Obj *objPtr; - - switch (expr->token[i].type) { - case JIM_TT_EXPR_INT: - case JIM_TT_EXPR_DOUBLE: - case JIM_TT_EXPR_BOOLEAN: - case JIM_TT_STR: - ExprPush(&e, expr->token[i].objPtr); - break; - - case JIM_TT_VAR: - objPtr = Jim_GetVariable(interp, expr->token[i].objPtr, JIM_ERRMSG); - if (objPtr) { - ExprPush(&e, objPtr); - } - else { - retcode = JIM_ERR; - } - break; - - case JIM_TT_DICTSUGAR: - objPtr = JimExpandDictSugar(interp, expr->token[i].objPtr); - if (objPtr) { - ExprPush(&e, objPtr); - } - else { - retcode = JIM_ERR; - } - break; - - case JIM_TT_ESC: - retcode = Jim_SubstObj(interp, expr->token[i].objPtr, &objPtr, JIM_NONE); - if (retcode == JIM_OK) { - ExprPush(&e, objPtr); - } - break; - - case JIM_TT_CMD: - retcode = Jim_EvalObj(interp, expr->token[i].objPtr); - if (retcode == JIM_OK) { - ExprPush(&e, Jim_GetResult(interp)); - } - break; - - default:{ - - e.skip = 0; - e.opcode = expr->token[i].type; - - retcode = JimExprOperatorInfoByOpcode(e.opcode)->funcop(interp, &e); - - i += e.skip; - continue; - } - } - } + retcode = JimExprEvalTermNode(interp, expr->expr); expr->inUse--; - if (retcode == JIM_OK) { - *exprResultPtrPtr = ExprPop(&e); - } - else { - for (i = 0; i < e.stacklen; i++) { - Jim_DecrRefCount(interp, e.stack[i]); - } - } - if (e.stack != staticStack) { - Jim_Free(e.stack); - } return retcode; } int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr) { - int retcode; - jim_wide wideValue; - double doubleValue; - int booleanValue; - Jim_Obj *exprResultPtr; + int retcode = Jim_EvalExpression(interp, exprObjPtr); - retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr); - if (retcode != JIM_OK) - return retcode; + if (retcode == JIM_OK) { + switch (ExprBool(interp, Jim_GetResult(interp))) { + case 0: + *boolPtr = 0; + break; - if (JimGetWideNoErr(interp, exprResultPtr, &wideValue) != JIM_OK) { - if (Jim_GetDouble(interp, exprResultPtr, &doubleValue) != JIM_OK) { - if (Jim_GetBoolean(interp, exprResultPtr, &booleanValue) != JIM_OK) { - Jim_DecrRefCount(interp, exprResultPtr); - return JIM_ERR; - } else { - Jim_DecrRefCount(interp, exprResultPtr); - *boolPtr = booleanValue; - return JIM_OK; - } - } - else { - Jim_DecrRefCount(interp, exprResultPtr); - *boolPtr = doubleValue != 0; - return JIM_OK; + case 1: + *boolPtr = 1; + break; + + case -1: + retcode = JIM_ERR; + break; } } - *boolPtr = wideValue != 0; - - Jim_DecrRefCount(interp, exprResultPtr); - return JIM_OK; + return retcode; } @@ -14273,8 +14152,8 @@ int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr) typedef struct ScanFmtPartDescr { - char *arg; - char *prefix; + const char *arg; + const char *prefix; size_t width; int pos; char type; @@ -14336,8 +14215,8 @@ static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr) ScanFmtStringObj *fmtObj; char *buffer; int maxCount, i, approxSize, lastPos = -1; - const char *fmt = objPtr->bytes; - int maxFmtLen = objPtr->length; + const char *fmt = Jim_String(objPtr); + int maxFmtLen = Jim_Length(objPtr); const char *fmtEnd = fmt + maxFmtLen; int curr; @@ -14422,6 +14301,11 @@ static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr) return JIM_ERR; } } + if (descr->pos < 0) { + fmtObj->error = + "\"%n$\" conversion specifier is negative"; + return JIM_ERR; + } if (sscanf(fmt, "%d%n", &width, &skip) == 1) { descr->width = width; @@ -14473,9 +14357,14 @@ static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr) } else { - if (strchr("hlL", *fmt) != 0) + if (fmt < fmtEnd && strchr("hlL", *fmt)) descr->modifier = tolower((int)*fmt++); + if (fmt >= fmtEnd) { + fmtObj->error = "missing scan conversion character"; + return JIM_ERR; + } + descr->type = *fmt; if (strchr("efgcsndoxui", *fmt) == 0) { fmtObj->error = "bad scan conversion character"; @@ -14882,6 +14771,7 @@ static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv) { int retcode; Jim_Cmd *cmdPtr; + void *prevPrivData; #if 0 printf("invoke"); @@ -14911,6 +14801,7 @@ static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv) goto out; } interp->evalDepth++; + prevPrivData = interp->cmdPrivData; Jim_SetEmptyResult(interp); @@ -14921,6 +14812,7 @@ static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv) interp->cmdPrivData = cmdPtr->u.native.privData; retcode = cmdPtr->u.native.cmdProc(interp, objc, objv); } + interp->cmdPrivData = prevPrivData; interp->evalDepth--; out: @@ -15085,7 +14977,8 @@ static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * tok if (tokens == 1 && intv[0] && intv == sintv) { - Jim_DecrRefCount(interp, intv[0]); + + intv[0]->refCount--; return intv[0]; } @@ -15540,6 +15433,7 @@ static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj badargset: + retcode = JimInvokeDefer(interp, retcode); interp->framePtr = interp->framePtr->parent; JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE); @@ -15828,7 +15722,7 @@ void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const listObjPtr = Jim_NewListObj(interp, argv, argc); - if (*msg) { + if (msg && *msg) { Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1)); } Jim_IncrRefCount(listObjPtr); @@ -16068,8 +15962,13 @@ static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, i } if (op == JIM_EXPROP_SUB) res -= wideValue; - else + else { + if (wideValue == 0) { + Jim_SetResultString(interp, "Division by zero", -1); + return JIM_ERR; + } res /= wideValue; + } } Jim_SetResultInt(interp, res); return JIM_OK; @@ -16221,7 +16120,7 @@ static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv #ifdef JIM_OPTIMIZATION if (retval == JIM_OK && boolean) { ScriptObj *incrScript; - ExprByteCode *expr; + struct ExprTree *expr; jim_wide stop, currentVal; Jim_Obj *objPtr; int cmpOffset; @@ -16235,47 +16134,53 @@ static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv goto evalstart; } - if (incrScript->token[1].type != JIM_TT_ESC || - expr->token[0].type != JIM_TT_VAR || - (expr->token[1].type != JIM_TT_EXPR_INT && expr->token[1].type != JIM_TT_VAR)) { + if (incrScript->token[1].type != JIM_TT_ESC) { goto evalstart; } - if (expr->token[2].type == JIM_EXPROP_LT) { + if (expr->expr->type == JIM_EXPROP_LT) { cmpOffset = 0; } - else if (expr->token[2].type == JIM_EXPROP_LTE) { + else if (expr->expr->type == JIM_EXPROP_LTE) { cmpOffset = 1; } else { goto evalstart; } + if (expr->expr->left->type != JIM_TT_VAR) { + goto evalstart; + } + + if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) { + goto evalstart; + } + if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) { goto evalstart; } - if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->token[0].objPtr)) { + if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) { goto evalstart; } - if (expr->token[1].type == JIM_TT_EXPR_INT) { - if (Jim_GetWide(interp, expr->token[1].objPtr, &stop) == JIM_ERR) { + if (expr->expr->right->type == JIM_TT_EXPR_INT) { + if (Jim_GetWide(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) { goto evalstart; } } else { - stopVarNamePtr = expr->token[1].objPtr; + stopVarNamePtr = expr->expr->right->objPtr; Jim_IncrRefCount(stopVarNamePtr); stop = 0; } - varNamePtr = expr->token[0].objPtr; + varNamePtr = expr->expr->left->objPtr; Jim_IncrRefCount(varNamePtr); objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE); @@ -16482,7 +16387,7 @@ static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *arg } if (result != JIM_OK) { Jim_SetResultString(interp, "foreach varlist is empty", -1); - return result; + goto empty_varlist; } if (doMap) { @@ -16545,6 +16450,7 @@ static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *arg Jim_SetResult(interp, resultObj); err: Jim_DecrRefCount(interp, resultObj); + empty_varlist: if (numargs > 2) { Jim_Free(iters); } @@ -16671,15 +16577,13 @@ int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patter return eq; } -enum -{ SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD }; - static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { + enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD }; int matchOpt = SWITCH_EXACT, opt = 1, patCount, i; - Jim_Obj *command = 0, *const *caseList = 0, *strObj; - Jim_Obj *script = 0; + Jim_Obj *command = NULL, *scriptObj = NULL, *strObj; + Jim_Obj **caseList; if (argc < 3) { wrongnumargs: @@ -16720,16 +16624,13 @@ static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a strObj = argv[opt++]; patCount = argc - opt; if (patCount == 1) { - Jim_Obj **vector; - - JimListGetElements(interp, argv[opt], &patCount, &vector); - caseList = vector; + JimListGetElements(interp, argv[opt], &patCount, &caseList); } else - caseList = &argv[opt]; + caseList = (Jim_Obj **)&argv[opt]; if (patCount == 0 || patCount % 2 != 0) goto wrongnumargs; - for (i = 0; script == 0 && i < patCount; i += 2) { + for (i = 0; scriptObj == NULL && i < patCount; i += 2) { Jim_Obj *patObj = caseList[i]; if (!Jim_CompareStringImmediate(interp, patObj, "default") @@ -16737,11 +16638,11 @@ static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a switch (matchOpt) { case SWITCH_EXACT: if (Jim_StringEqObj(strObj, patObj)) - script = caseList[i + 1]; + scriptObj = caseList[i + 1]; break; case SWITCH_GLOB: if (Jim_StringMatchObj(interp, patObj, strObj, 0)) - script = caseList[i + 1]; + scriptObj = caseList[i + 1]; break; case SWITCH_RE: command = Jim_NewStringObj(interp, "regexp", -1); @@ -16750,34 +16651,31 @@ static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, 0); if (argc - opt == 1) { - Jim_Obj **vector; - - JimListGetElements(interp, argv[opt], &patCount, &vector); - caseList = vector; + JimListGetElements(interp, argv[opt], &patCount, &caseList); } if (rc < 0) { return -rc; } if (rc) - script = caseList[i + 1]; + scriptObj = caseList[i + 1]; break; } } } else { - script = caseList[i + 1]; + scriptObj = caseList[i + 1]; } } - for (; i < patCount && Jim_CompareStringImmediate(interp, script, "-"); i += 2) - script = caseList[i + 1]; - if (script && Jim_CompareStringImmediate(interp, script, "-")) { + for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2) + scriptObj = caseList[i + 1]; + if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) { Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]); return JIM_ERR; } Jim_SetEmptyResult(interp); - if (script) { - return Jim_EvalObj(interp, script); + if (scriptObj) { + return Jim_EvalObj(interp, scriptObj); } return JIM_OK; } @@ -17076,18 +16974,8 @@ static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const JimRelToAbsRange(len, &first, &last, &rangeLen); - - if (first < len) { - - } - else if (len == 0) { - - first = 0; - } - else { - Jim_SetResultString(interp, "list doesn't contain element ", -1); - Jim_AppendObj(interp, Jim_GetResult(interp), argv[2]); - return JIM_ERR; + if (first > len) { + first = len; } @@ -17131,6 +17019,7 @@ static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const arg Jim_Obj *resObj; int i; int retCode; + int shared; struct lsort_info info; @@ -17196,12 +17085,14 @@ static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const arg break; } } - resObj = Jim_DuplicateObj(interp, argv[argc - 1]); + resObj = argv[argc - 1]; + if ((shared = Jim_IsShared(resObj))) + resObj = Jim_DuplicateObj(interp, resObj); retCode = ListSortElements(interp, resObj, &info); if (retCode == JIM_OK) { Jim_SetResult(interp, resObj); } - else { + else if (shared) { Jim_FreeNewObj(interp, resObj); } return retCode; @@ -17249,6 +17140,7 @@ static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *a } + static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { #if !defined(JIM_DEBUG_COMMAND) @@ -17329,18 +17221,17 @@ static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const * static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - Jim_Obj *exprResultPtr; int retcode; if (argc == 2) { - retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr); + retcode = Jim_EvalExpression(interp, argv[1]); } else if (argc > 2) { Jim_Obj *objPtr; objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1); Jim_IncrRefCount(objPtr); - retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr); + retcode = Jim_EvalExpression(interp, objPtr); Jim_DecrRefCount(interp, objPtr); } else { @@ -17349,8 +17240,6 @@ static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg } if (retcode != JIM_OK) return retcode; - Jim_SetResult(interp, exprResultPtr); - Jim_DecrRefCount(interp, exprResultPtr); return JIM_OK; } @@ -19655,6 +19544,14 @@ int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const return JIM_ERR; } +static const Jim_ObjType getEnumObjType = { + "get-enum", + NULL, + NULL, + NULL, + JIM_TYPE_REFERENCES +}; + int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr, int *indexPtr, const char *name, int flags) { @@ -19663,15 +19560,24 @@ int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr, int i; int match = -1; int arglen; - const char *arg = Jim_GetString(objPtr, &arglen); + const char *arg; + + if (objPtr->typePtr == &getEnumObjType) { + if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) { + *indexPtr = objPtr->internalRep.ptrIntValue.int2; + return JIM_OK; + } + } + + arg = Jim_GetString(objPtr, &arglen); *indexPtr = -1; for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) { if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) { - *indexPtr = i; - return JIM_OK; + match = i; + goto found; } if (flags & JIM_ENUM_ABBREV) { if (strncmp(arg, *entryPtr, arglen) == 0) { @@ -19689,6 +19595,14 @@ int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr, if (match >= 0) { + found: + + Jim_FreeIntRep(interp, objPtr); + objPtr->typePtr = &getEnumObjType; + objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr; + objPtr->internalRep.ptrIntValue.int1 = flags; + objPtr->internalRep.ptrIntValue.int2 = match; + *indexPtr = match; return JIM_OK; } @@ -19820,18 +19734,14 @@ static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const c static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type, Jim_Obj *cmd, Jim_Obj *subcmd) { - Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); - Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), ", ", type, - " command \"", Jim_String(subcmd), "\": should be ", NULL); + Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be ", cmd, type, subcmd); add_commands(interp, command_table, ", "); } static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc, Jim_Obj *const *argv) { - Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); - Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]), - " command ... \", where command is one of: ", NULL); + Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: ", argv[0]); add_commands(interp, command_table, ", "); } @@ -19853,6 +19763,14 @@ static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_t Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); } +static const Jim_ObjType subcmdLookupObjType = { + "subcmd-lookup", + NULL, + NULL, + NULL, + JIM_TYPE_REFERENCES +}; + const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc, Jim_Obj *const *argv) { @@ -19861,22 +19779,25 @@ const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type int cmdlen; Jim_Obj *cmd; const char *cmdstr; - const char *cmdname; int help = 0; - cmdname = Jim_String(argv[0]); - if (argc < 2) { - Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); - Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname, - " command ...\"\n", NULL); - Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help ?command?\" for help", NULL); + Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n" + "Use \"%#s -help ?command?\" for help", argv[0], argv[0]); return 0; } cmd = argv[1]; + if (cmd->typePtr == &subcmdLookupObjType) { + if (cmd->internalRep.ptrIntValue.ptr == command_table) { + ct = command_table + cmd->internalRep.ptrIntValue.int1; + goto found; + } + } + + if (Jim_CompareStringImmediate(interp, cmd, "-help")) { if (argc == 2) { @@ -19944,6 +19865,13 @@ const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type } + Jim_FreeIntRep(interp, cmd); + cmd->typePtr = &subcmdLookupObjType; + cmd->internalRep.ptrIntValue.ptr = (void *)command_table; + cmd->internalRep.ptrIntValue.int1 = ct - command_table; + +found: + if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) { Jim_SetResultString(interp, "wrong # args: should be \"", -1); @@ -20135,7 +20063,8 @@ Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_ *p++ = ch; format += step; step = utf8_tounicode(format, &ch); - } while (sawFlag); + + } while (sawFlag && (p - spec <= 5)); width = 0; @@ -20334,6 +20263,13 @@ Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_ *p = '\0'; + if (width > 10000 || length > 10000 || precision > 10000) { + Jim_SetResultString(interp, "format too long", -1); + goto error; + } + + + if (width > length) { length = width; } @@ -20706,6 +20642,10 @@ static int regpiece(regex_t *preg, int *flagp) if (*end == '}') { max = min; } + else if (*end == '\0') { + preg->err = REG_ERR_UNMATCHED_BRACES; + return 0; + } else { preg->regparse = end; max = strtoul(preg->regparse + 1, &end, 10); @@ -20910,8 +20850,31 @@ static int regatom(regex_t *preg, int *flagp) int start; int end; + enum { + CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER, + CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT, + CC_NUM + }; + int cc; + pattern += reg_utf8_tounicode_case(pattern, &start, nocase); if (start == '\\') { + + switch (*pattern) { + case 's': + pattern++; + cc = CC_SPACE; + goto cc_switch; + case 'd': + pattern++; + cc = CC_DIGIT; + goto cc_switch; + case 'w': + pattern++; + reg_addrange(preg, '_', '_'); + cc = CC_ALNUM; + goto cc_switch; + } pattern += reg_decode_escape(pattern, &start); if (start == 0) { preg->err = REG_ERR_NULL_CHAR; @@ -20938,23 +20901,18 @@ static int regatom(regex_t *preg, int *flagp) ":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:", ":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:", }; - enum { - CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER, - CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT, - CC_NUM - }; - int i; - for (i = 0; i < CC_NUM; i++) { - n = strlen(character_class[i]); - if (strncmp(pattern, character_class[i], n) == 0) { + for (cc = 0; cc < CC_NUM; cc++) { + n = strlen(character_class[cc]); + if (strncmp(pattern, character_class[cc], n) == 0) { pattern += n + 1; break; } } - if (i != CC_NUM) { - switch (i) { + if (cc != CC_NUM) { +cc_switch: + switch (cc) { case CC_ALNUM: reg_addrange(preg, '0', '9'); @@ -21809,6 +21767,219 @@ void regfree(regex_t *preg) } #endif +#include + +void Jim_SetResultErrno(Jim_Interp *interp, const char *msg) +{ + Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno())); +} + +#if defined(__MINGW32__) +#include + +int Jim_Errno(void) +{ + switch (GetLastError()) { + case ERROR_FILE_NOT_FOUND: return ENOENT; + case ERROR_PATH_NOT_FOUND: return ENOENT; + case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; + case ERROR_ACCESS_DENIED: return EACCES; + case ERROR_INVALID_HANDLE: return EBADF; + case ERROR_BAD_ENVIRONMENT: return E2BIG; + case ERROR_BAD_FORMAT: return ENOEXEC; + case ERROR_INVALID_ACCESS: return EACCES; + case ERROR_INVALID_DRIVE: return ENOENT; + case ERROR_CURRENT_DIRECTORY: return EACCES; + case ERROR_NOT_SAME_DEVICE: return EXDEV; + case ERROR_NO_MORE_FILES: return ENOENT; + case ERROR_WRITE_PROTECT: return EROFS; + case ERROR_BAD_UNIT: return ENXIO; + case ERROR_NOT_READY: return EBUSY; + case ERROR_BAD_COMMAND: return EIO; + case ERROR_CRC: return EIO; + case ERROR_BAD_LENGTH: return EIO; + case ERROR_SEEK: return EIO; + case ERROR_WRITE_FAULT: return EIO; + case ERROR_READ_FAULT: return EIO; + case ERROR_GEN_FAILURE: return EIO; + case ERROR_SHARING_VIOLATION: return EACCES; + case ERROR_LOCK_VIOLATION: return EACCES; + case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE; + case ERROR_HANDLE_DISK_FULL: return ENOSPC; + case ERROR_NOT_SUPPORTED: return ENODEV; + case ERROR_REM_NOT_LIST: return EBUSY; + case ERROR_DUP_NAME: return EEXIST; + case ERROR_BAD_NETPATH: return ENOENT; + case ERROR_NETWORK_BUSY: return EBUSY; + case ERROR_DEV_NOT_EXIST: return ENODEV; + case ERROR_TOO_MANY_CMDS: return EAGAIN; + case ERROR_ADAP_HDW_ERR: return EIO; + case ERROR_BAD_NET_RESP: return EIO; + case ERROR_UNEXP_NET_ERR: return EIO; + case ERROR_NETNAME_DELETED: return ENOENT; + case ERROR_NETWORK_ACCESS_DENIED: return EACCES; + case ERROR_BAD_DEV_TYPE: return ENODEV; + case ERROR_BAD_NET_NAME: return ENOENT; + case ERROR_TOO_MANY_NAMES: return ENFILE; + case ERROR_TOO_MANY_SESS: return EIO; + case ERROR_SHARING_PAUSED: return EAGAIN; + case ERROR_REDIR_PAUSED: return EAGAIN; + case ERROR_FILE_EXISTS: return EEXIST; + case ERROR_CANNOT_MAKE: return ENOSPC; + case ERROR_OUT_OF_STRUCTURES: return ENFILE; + case ERROR_ALREADY_ASSIGNED: return EEXIST; + case ERROR_INVALID_PASSWORD: return EPERM; + case ERROR_NET_WRITE_FAULT: return EIO; + case ERROR_NO_PROC_SLOTS: return EAGAIN; + case ERROR_DISK_CHANGE: return EXDEV; + case ERROR_BROKEN_PIPE: return EPIPE; + case ERROR_OPEN_FAILED: return ENOENT; + case ERROR_DISK_FULL: return ENOSPC; + case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE; + case ERROR_INVALID_TARGET_HANDLE: return EBADF; + case ERROR_INVALID_NAME: return ENOENT; + case ERROR_PROC_NOT_FOUND: return ESRCH; + case ERROR_WAIT_NO_CHILDREN: return ECHILD; + case ERROR_CHILD_NOT_COMPLETE: return ECHILD; + case ERROR_DIRECT_ACCESS_HANDLE: return EBADF; + case ERROR_SEEK_ON_DEVICE: return ESPIPE; + case ERROR_BUSY_DRIVE: return EAGAIN; + case ERROR_DIR_NOT_EMPTY: return EEXIST; + case ERROR_NOT_LOCKED: return EACCES; + case ERROR_BAD_PATHNAME: return ENOENT; + case ERROR_LOCK_FAILED: return EACCES; + case ERROR_ALREADY_EXISTS: return EEXIST; + case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG; + case ERROR_BAD_PIPE: return EPIPE; + case ERROR_PIPE_BUSY: return EAGAIN; + case ERROR_PIPE_NOT_CONNECTED: return EPIPE; + case ERROR_DIRECTORY: return ENOTDIR; + } + return EINVAL; +} + +pidtype waitpid(pidtype pid, int *status, int nohang) +{ + DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE); + if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) { + + return JIM_BAD_PID; + } + GetExitCodeProcess(pid, &ret); + *status = ret; + CloseHandle(pid); + return pid; +} + +int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file) +{ + char name[MAX_PATH]; + HANDLE handle; + + if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) { + return -1; + } + + handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0), + NULL); + + if (handle == INVALID_HANDLE_VALUE) { + goto error; + } + + Jim_SetResultString(interp, name, -1); + return _open_osfhandle((int)handle, _O_RDWR | _O_TEXT); + + error: + Jim_SetResultErrno(interp, name); + DeleteFile(name); + return -1; +} + +int Jim_OpenForWrite(const char *filename, int append) +{ + if (strcmp(filename, "/dev/null") == 0) { + filename = "nul:"; + } + int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE); + if (fd >= 0 && append) { + + _lseek(fd, 0L, SEEK_END); + } + return fd; +} + +int Jim_OpenForRead(const char *filename) +{ + if (strcmp(filename, "/dev/null") == 0) { + filename = "nul:"; + } + return _open(filename, _O_RDONLY | _O_TEXT, 0); +} + +#elif defined(HAVE_UNISTD_H) + + + +int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file) +{ + int fd; + mode_t mask; + Jim_Obj *filenameObj; + + if (filename_template == NULL) { + const char *tmpdir = getenv("TMPDIR"); + if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) { + tmpdir = "/tmp/"; + } + filenameObj = Jim_NewStringObj(interp, tmpdir, -1); + if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') { + Jim_AppendString(interp, filenameObj, "/", 1); + } + Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1); + } + else { + filenameObj = Jim_NewStringObj(interp, filename_template, -1); + } + + + mask = umask(S_IXUSR | S_IRWXG | S_IRWXO); +#ifdef HAVE_MKSTEMP + fd = mkstemp(filenameObj->bytes); +#else + if (mktemp(filenameObj->bytes) == NULL) { + fd = -1; + } + else { + fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC); + } +#endif + umask(mask); + if (fd < 0) { + Jim_SetResultErrno(interp, Jim_String(filenameObj)); + Jim_FreeNewObj(interp, filenameObj); + return -1; + } + if (unlink_file) { + remove(Jim_String(filenameObj)); + } + + Jim_SetResult(interp, filenameObj); + return fd; +} + +int Jim_OpenForWrite(const char *filename, int append) +{ + return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666); +} + +int Jim_OpenForRead(const char *filename) +{ + return open(filename, O_RDONLY, 0); +} + +#endif #if defined(_WIN32) || defined(WIN32) #ifndef STRICT @@ -21927,6 +22098,32 @@ struct dirent *readdir(DIR * dir) } #endif #endif +#include +#include + + + + + + +#ifndef SIGPIPE +#define SIGPIPE 13 +#endif +#ifndef SIGINT +#define SIGINT 2 +#endif + +const char *Jim_SignalId(int sig) +{ + static char buf[10]; + switch (sig) { + case SIGINT: return "SIGINT"; + case SIGPIPE: return "SIGPIPE"; + + } + snprintf(buf, sizeof(buf), "%d", sig); + return buf; +} #ifndef JIM_BOOTSTRAP_LIB_ONLY #include #include @@ -21944,10 +22141,30 @@ struct dirent *readdir(DIR * dir) #define MAX_LINE_LEN 512 #endif -char *Jim_HistoryGetline(const char *prompt) +#ifdef USE_LINENOISE +static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata); +static const char completion_callback_assoc_key[] = "interactive-completion"; +#endif + +char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt) { #ifdef USE_LINENOISE - return linenoise(prompt); + struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key); + char *result; + Jim_Obj *objPtr; + long mlmode = 0; + if (compinfo) { + linenoiseSetCompletionCallback(JimCompletionCallback, compinfo); + } + objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE); + if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) { + linenoiseSetMultiLine(mlmode); + } + + result = linenoise(prompt); + + linenoiseSetCompletionCallback(NULL, NULL); + return result; #else int len; char *line = malloc(MAX_LINE_LEN); @@ -21991,7 +22208,7 @@ void Jim_HistorySave(const char *filename) #endif linenoiseHistorySave(filename); #ifdef HAVE_UMASK - mask = umask(mask); + umask(mask); #endif #endif } @@ -22015,7 +22232,7 @@ struct JimCompletionInfo { Jim_Obj *command; }; -void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata) +static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata) { struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata; Jim_Obj *objv[2]; @@ -22036,7 +22253,36 @@ void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void } } } + +static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data) +{ + struct JimCompletionInfo *compinfo = data; + + Jim_DecrRefCount(interp, compinfo->command); + + Jim_Free(compinfo); +} +#endif + +void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj) +{ +#ifdef USE_LINENOISE + if (commandObj) { + + Jim_IncrRefCount(commandObj); + } + + Jim_DeleteAssocData(interp, completion_callback_assoc_key); + + if (commandObj) { + struct JimCompletionInfo *compinfo = Jim_Alloc(sizeof(*compinfo)); + compinfo->interp = interp; + compinfo->command = commandObj; + + Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo); + } #endif +} int Jim_InteractivePrompt(Jim_Interp *interp) { @@ -22044,7 +22290,6 @@ int Jim_InteractivePrompt(Jim_Interp *interp) char *history_file = NULL; #ifdef USE_LINENOISE const char *home; - struct JimCompletionInfo compinfo; home = getenv("HOME"); if (home && isatty(STDIN_FILENO)) { @@ -22054,12 +22299,7 @@ int Jim_InteractivePrompt(Jim_Interp *interp) Jim_HistoryLoad(history_file); } - compinfo.interp = interp; - compinfo.command = Jim_NewStringObj(interp, "tcl::autocomplete", -1); - Jim_IncrRefCount(compinfo.command); - - - linenoiseSetCompletionCallback(JimCompletionCallback, &compinfo); + Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1)); #endif printf("Welcome to Jim version %d.%d\n", @@ -22092,7 +22332,7 @@ int Jim_InteractivePrompt(Jim_Interp *interp) char state; char *line; - line = Jim_HistoryGetline(prompt); + line = Jim_HistoryGetline(interp, prompt); if (line == NULL) { if (errno == EINTR) { continue; @@ -22142,11 +22382,6 @@ int Jim_InteractivePrompt(Jim_Interp *interp) out: Jim_Free(history_file); -#ifdef USE_LINENOISE - Jim_DecrRefCount(interp, compinfo.command); - linenoiseSetCompletionCallback(NULL, NULL); -#endif - return retcode; } diff --git a/autosetup/system.tcl b/autosetup/system.tcl index 37377eb..b98b5c0 100644 --- a/autosetup/system.tcl +++ b/autosetup/system.tcl @@ -133,6 +133,102 @@ proc write-if-changed {file buf {script {}}} { } } + +# @include-file infile mapping +# +# The core of make-template, called recursively for each @include +# directive found within that template so that this proc's result +# is the fully-expanded template. +# +# The mapping parameter is how we expand @varname@ within the template. +# We do that inline within this step only for @include directives which +# can have variables in the filename arg. A separate substitution pass +# happens when this recursive function returns, expanding the rest of +# the variables. +# +proc include-file {infile mapping} { + # A stack of true/false conditions, one for each nested conditional + # starting with "true" + set condstack {1} + set result {} + set linenum 0 + foreach line [split [readfile $infile] \n] { + incr linenum + if {[regexp {^@(if|else|endif)(\s*)(.*)} $line -> condtype condspace condargs]} { + if {$condtype eq "if"} { + if {[string length $condspace] == 0} { + autosetup-error "$infile:$linenum: Invalid expression: $line" + } + if {[llength $condargs] == 1} { + # ABC => [get-define ABC] ni {0 ""} + # !ABC => [get-define ABC] in {0 ""} + lassign $condargs condvar + if {[regexp {^!(.*)} $condvar -> condvar]} { + set op in + } else { + set op ni + } + set condexpr "\[[list get-define $condvar]\] $op {0 {}}" + } else { + # Translate alphanumeric ABC into [get-define ABC] and leave the + # rest of the expression untouched + regsub -all {([A-Z][[:alnum:]_]*)} $condargs {[get-define \1]} condexpr + } + if {[catch [list expr $condexpr] condval]} { + dputs $condval + autosetup-error "$infile:$linenum: Invalid expression: $line" + } + dputs "@$condtype: $condexpr => $condval" + } + if {$condtype ne "if"} { + if {[llength $condstack] <= 1} { + autosetup-error "$infile:$linenum: Error: @$condtype missing @if" + } elseif {[string length $condargs] && [string index $condargs 0] ne "#"} { + autosetup-error "$infile:$linenum: Error: Extra arguments after @$condtype" + } + } + switch -exact $condtype { + if { + # push condval + lappend condstack $condval + } + else { + # Toggle the last entry + set condval [lpop condstack] + set condval [expr {!$condval}] + lappend condstack $condval + } + endif { + if {[llength $condstack] == 0} { + user-notice "$infile:$linenum: Error: @endif missing @if" + } + lpop condstack + } + } + continue + } elseif {[regexp {^@include\s+(.*)} $line -> filearg]} { + set incfile [string map $mapping $filearg] + if {[file exists $incfile]} { + lappend ::autosetup(deps) [file-normalize $incfile] + lappend result {*}[include-file $incfile $mapping] + } else { + user-error "$infile:$linenum: Include file $incfile is missing" + } + continue + } elseif {[regexp {^@define\s+(\w+)\s+(.*)} $line -> var val]} { + define $var $val + continue + } + # Only output this line if the stack contains all "true" + if {"0" in $condstack} { + continue + } + lappend result $line + } + return $result +} + + # @make-template template ?outfile? # # Reads the input file '/$template' and writes the output file '$outfile' @@ -195,70 +291,23 @@ proc make-template {template {out {}}} { define srcdir [relative-path [file join $::autosetup(srcdir) $outdir] $outdir] define top_srcdir [relative-path $::autosetup(srcdir) $outdir] - set mapping {} - foreach {n v} [array get ::define] { - lappend mapping @$n@ $v - } - - # A stack of true/false conditions, one for each nested conditional - # starting with "true" - set condstack {1} - set result {} - set linenum 0 - foreach line [split [readfile $infile] \n] { - incr linenum - if {[regexp {^@(if|else|endif)\s*(.*)} $line -> condtype condargs]} { - if {$condtype eq "if"} { - if {[llength $condargs] == 1} { - # ABC => [get-define ABC] ni {0 ""} - # !ABC => [get-define ABC] in {0 ""} - lassign $condargs condvar - if {[regexp {^!(.*)} $condvar -> condvar]} { - set op in - } else { - set op ni - } - set condexpr "\[[list get-define $condvar]\] $op {0 {}}" - } else { - # Translate alphanumeric ABC into [get-define ABC] and leave the - # rest of the expression untouched - regsub -all {([A-Z][[:alnum:]_]*)} $condargs {[get-define \1]} condexpr - } - if {[catch [list expr $condexpr] condval]} { - dputs $condval - autosetup-error "$infile:$linenum: Invalid expression: $line" - } - dputs "@$condtype: $condexpr => $condval" - } - if {$condtype ne "if" && [llength $condstack] <= 1} { - autosetup-error "$infile:$linenum: Error: @$condtype missing @if" - } - switch -exact $condtype { - if { - # push condval - lappend condstack $condval - } - else { - # Toggle the last entry - set condval [lpop condstack] - set condval [expr {!$condval}] - lappend condstack $condval - } - endif { - if {[llength $condstack] == 0} { - user-notice "$infile:$linenum: Error: @endif missing @if" - } - lpop condstack - } - } - continue + # Build map from global defines to their values so they can be + # substituted into @include file names. + proc build-define-mapping {} { + set mapping {} + foreach {n v} [array get ::define] { + lappend mapping @$n@ $v } - # Only output this line if the stack contains all "true" - if {"0" in $condstack} { - continue - } - lappend result $line + return $mapping } + set mapping [build-define-mapping] + + set result [include-file $infile $mapping] + + # Rebuild the define mapping in case we ran across @define + # directives in the template or a file it @included, then + # apply that mapping to the expanded template. + set mapping [build-define-mapping] write-if-changed $out [string map $mapping [join $result \n]] { msg-result "Created [relative-path $out] from [relative-path $template]" } diff --git a/configure b/configure index 32c8cfd..adac37c 100755 --- a/configure +++ b/configure @@ -1,3 +1,3 @@ #!/bin/sh dir="`dirname "$0"`/autosetup" -WRAPPER="$0"; export WRAPPER; exec "`$dir/autosetup-find-tclsh`" "$dir/autosetup" "$@" +WRAPPER="$0"; export WRAPPER; exec "`"$dir/autosetup-find-tclsh"`" "$dir/autosetup" "$@" -- cgit v1.1