Index: auto.def ================================================================== --- auto.def +++ auto.def @@ -87,34 +87,34 @@ user-error "zlib not found please install it or specify the location with --with-zlib" } set tclpath [opt-val with-tcl] if {$tclpath ne ""} { - # Note parse-tclconfig-sh is in autosetup/local.tcl + # Note parse-tclconfig-sh is in autosetup/local.tcl if {$tclpath eq "1"} { # Use the system Tcl. Look in some likely places. array set tclconfig [parse-tclconfig-sh /usr /usr/local /usr/share /opt/local] - set msg "on your system" - } else { + set msg "on your system" + } else { array set tclconfig [parse-tclconfig-sh $tclpath] - set msg "at $tclpath" - } - if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} { - user-error "Cannot find Tcl $msg" - } - set cflags $tclconfig(TCL_INCLUDE_SPEC) - set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)" - cc-with [list -cflags $cflags -libs $libs] { - if {![cc-check-functions Tcl_CreateInterp]} { - user-error "Cannot find a usable Tcl $msg" - } - } - set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL) - msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)" - define-append LIBS $libs - define-append EXTRA_CFLAGS $cflags - define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS) + set msg "at $tclpath" + } + if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} { + user-error "Cannot find Tcl $msg" + } + set cflags $tclconfig(TCL_INCLUDE_SPEC) + set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)" + cc-with [list -cflags $cflags -libs $libs] { + if {![cc-check-functions Tcl_CreateInterp]} { + user-error "Cannot find a usable Tcl $msg" + } + } + set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL) + msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)" + define-append LIBS $libs + define-append EXTRA_CFLAGS $cflags + define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS) define FOSSIL_ENABLE_TCL } # Helper for openssl checking Index: autosetup/README.autosetup ================================================================== --- autosetup/README.autosetup +++ autosetup/README.autosetup @@ -1,1 +1,1 @@ -This is autosetup v0.6.2. See http://msteveb.github.com/autosetup/ +This is autosetup v0.6.3. See http://msteveb.github.com/autosetup/ Index: autosetup/autosetup ================================================================== --- autosetup/autosetup +++ autosetup/autosetup @@ -3,11 +3,11 @@ # All rights reserved # vim:se syntax=tcl: # \ dir=`dirname "$0"`; exec "`$dir/find-tclsh`" "$0" "$@" -set autosetup(version) 0.6.2 +set autosetup(version) 0.6.3 # Can be set to 1 to debug early-init problems set autosetup(debug) 0 ################################################################## @@ -516,16 +516,20 @@ } # @env-is-set name # # Returns 1 if the $name was specified on the command line or in the environment. +# Note that an empty environment variable is not considered to be set. # proc env-is-set {name} { if {[dict exists $::autosetup(cmdline) $name]} { return 1 } - info exists ::env($name) + if {[getenv $name ""] ne ""} { + return 1 + } + return 0 } # @readfile filename ?default=""? # # Return the contents of the file, without the trailing newline. @@ -1541,19 +1545,10 @@ # On Windows, backslash convert all environment variables # (Assume that Tcl does this for us) proc getenv {name args} { string map {\\ /} [env $name {*}$args] } - # Jim uses system() for exec under mingw, so - # we need to fetch the output ourselves - proc exec-with-stderr {args} { - set tmpfile auto[format %04x [rand 10000]].tmp - set rc [catch [list exec {*}$args >$tmpfile 2>&1] result] - set result [readfile $tmpfile] - file delete $tmpfile - return -code $rc $result - } } else { # Jim on unix is simple alias getenv env } Index: autosetup/cc-shared.tcl ================================================================== --- autosetup/cc-shared.tcl +++ autosetup/cc-shared.tcl @@ -56,8 +56,8 @@ # Generic Unix settings define SH_LINKFLAGS -rdynamic define SH_CFLAGS -fpic define SH_LDFLAGS -shared define SHOBJ_CFLAGS -fpic - define SHOBJ_LDFLAGS "-shared -nostartfiles" + define SHOBJ_LDFLAGS -shared } } Index: autosetup/cc.tcl ================================================================== --- autosetup/cc.tcl +++ autosetup/cc.tcl @@ -372,11 +372,11 @@ } else { set save [cc-add-settings $settings] set rc [catch {uplevel 1 [lindex $args 0]} result info] cc-store-settings $save if {$rc != 0} { - return $result -code [dict get $info -code] + return -code [dict get $info -code] $result } return $result } } Index: autosetup/find-tclsh ================================================================== --- autosetup/find-tclsh +++ autosetup/find-tclsh @@ -1,15 +1,16 @@ #!/bin/sh # Looks for a suitable tclsh or jimsh in the PATH # If not found, builds a bootstrap jimsh from source d=`dirname "$0"` +{ "$d/jimsh0" "$d/test-tclsh"; } 2>/dev/null && exit 0 PATH="$PATH:$d" -for tclsh in jimsh tclsh tclsh8.5 tclsh8.6 jimsh0; do +for tclsh in jimsh tclsh tclsh8.5 tclsh8.6; do { $tclsh "$d/test-tclsh"; } 2>/dev/null && exit 0 done echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" for cc in ${CC_FOR_BUILD:-cc} gcc; do { $cc -o "$d/jimsh0" "$d/jimsh0.c"; } 2>/dev/null || continue "$d/jimsh0" "$d/test-tclsh" && exit 0 done echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc." echo false Index: autosetup/jimsh0.c ================================================================== --- autosetup/jimsh0.c +++ autosetup/jimsh0.c @@ -16,193 +16,122 @@ #define jim_ext_exec #define jim_ext_clock #define jim_ext_array #define jim_ext_stdlib #define jim_ext_tclcompat -#if defined(__MINGW32__) +#if defined(_MSC_VER) +#define TCL_PLATFORM_OS "windows" +#define TCL_PLATFORM_PLATFORM "windows" +#define TCL_PLATFORM_PATH_SEPARATOR ";" +#define HAVE_MKDIR_ONE_ARG +#define HAVE_SYSTEM +#elif defined(__MINGW32__) #define TCL_PLATFORM_OS "mingw" #define TCL_PLATFORM_PLATFORM "windows" #define TCL_PLATFORM_PATH_SEPARATOR ";" #define HAVE_MKDIR_ONE_ARG #define HAVE_SYSTEM +#define HAVE_SYS_TIME_H +#define HAVE_DIRENT_H +#define HAVE_UNISTD_H #else #define TCL_PLATFORM_OS "unknown" #define TCL_PLATFORM_PLATFORM "unix" #define TCL_PLATFORM_PATH_SEPARATOR ":" #define HAVE_VFORK #define HAVE_WAITPID +#define HAVE_SYS_TIME_H +#define HAVE_DIRENT_H +#define HAVE_UNISTD_H +#endif +#ifndef JIM_WIN32COMPAT_H +#define JIM_WIN32COMPAT_H + + + + +#if defined(_WIN32) || defined(WIN32) + +#define HAVE_DLOPEN +void *dlopen(const char *path, int mode); +int dlclose(void *handle); +void *dlsym(void *handle, const char *symbol); +char *dlerror(void); + +#ifdef _MSC_VER + + +#if _MSC_VER >= 1000 + #pragma warning(disable:4146) +#endif + +#include <limits.h> +#define jim_wide _int64 +#ifndef LLONG_MAX + #define LLONG_MAX 9223372036854775807I64 +#endif +#ifndef LLONG_MIN + #define LLONG_MIN (-LLONG_MAX - 1I64) +#endif +#define JIM_WIDE_MIN LLONG_MIN +#define JIM_WIDE_MAX LLONG_MAX +#define JIM_WIDE_MODIFIER "I64d" +#define strcasecmp _stricmp +#define strtoull _strtoui64 +#define snprintf _snprintf + +#include <io.h> + +struct timeval { + long tv_sec; + long tv_usec; +}; + +int gettimeofday(struct timeval *tv, void *unused); + +#define HAVE_OPENDIR +struct dirent { + char *d_name; +}; + +typedef struct DIR { + long handle; + struct _finddata_t info; + struct dirent result; + char *name; +} DIR; + +DIR *opendir(const char *name); +int closedir(DIR *dir); +struct dirent *readdir(DIR *dir); +#endif + +#endif + #endif #ifndef UTF8_UTIL_H #define UTF8_UTIL_H -/** - * UTF-8 utility functions - * - * (c) 2010 Steve Bennett <steveb@workware.net.au> - * - * See LICENCE for licence details. - */ -/** - * Converts the given unicode codepoint (0 - 0xffff) to utf-8 - * and stores the result at 'p'. - * - * Returns the number of utf-8 characters (1-3). - */ int utf8_fromunicode(char *p, unsigned short uc); #ifndef JIM_UTF8 #include <ctype.h> -/* No utf-8 support. 1 byte = 1 char */ -#define utf8_strlen(S, B) (B) < 0 ? strlen(S) : (B) -#define utf8_tounicode(S, CP) (*(CP) = *(S), 1) + +#define utf8_strlen(S, B) ((B) < 0 ? strlen(S) : (B)) +#define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1) #define utf8_upper(C) toupper(C) #define utf8_lower(C) tolower(C) #define utf8_index(C, I) (I) #define utf8_charlen(C) 1 #define utf8_prev_len(S, L) 1 #else -/** - * Returns the length of the utf-8 sequence starting with 'c'. - * - * Returns 1-4, or -1 if this is not a valid start byte. - * - * Note that charlen=4 is not supported by the rest of the API. - */ -int utf8_charlen(int c); - -/** - * Returns the number of characters in the utf-8 - * string of the given byte length. - * - * Any bytes which are not part of an valid utf-8 - * sequence are treated as individual characters. - * - * The string *must* be null terminated. - * - * Does not support unicode code points > \uffff - */ -int utf8_strlen(const char *str, int bytelen); - -/** - * Returns the byte index of the given character in the utf-8 string. - * - * The string *must* be null terminated. - * - * This will return the byte length of a utf-8 string - * if given the char length. - */ -int utf8_index(const char *str, int charindex); - -/** - * Returns the unicode codepoint corresponding to the - * utf-8 sequence 'str'. - * - * Stores the result in *uc and returns the number of bytes - * consumed. - * - * If 'str' is null terminated, then an invalid utf-8 sequence - * at the end of the string will be returned as individual bytes. - * - * If it is not null terminated, the length *must* be checked first. - * - * Does not support unicode code points > \uffff - */ -int utf8_tounicode(const char *str, int *uc); - -/** - * Returns the number of bytes before 'str' that the previous - * utf-8 character sequence starts (which may be the middle of a sequence). - * - * Looks back at most 'len' bytes backwards, which must be > 0. - * If no start char is found, returns -len - */ -int utf8_prev_len(const char *str, int len); - -/** - * Returns the upper-case variant of the given unicode codepoint. - * - * Does not support unicode code points > \uffff - */ -int utf8_upper(int uc); - -/** - * Returns the lower-case variant of the given unicode codepoint. - * - * NOTE: Use utf8_upper() in preference for case-insensitive matching. - * - * Does not support unicode code points > \uffff - */ -int utf8_lower(int uc); - -#endif - -#endif -/* Jim - A small embeddable Tcl interpreter - * - * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org> - * Copyright 2005 Clemens Hintze <c.hintze@gmx.net> - * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net> - * Copyright 2008 oharboe - �yvind Harboe - oyvind.harboe@zylin.com - * Copyright 2008 Andrew Lunn <andrew@lunn.ch> - * Copyright 2008 Duane Ellis <openocd@duaneellis.com> - * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of the Jim Tcl Project. - * - *--- Inline Header File Documentation --- - * [By Duane Ellis, openocd@duaneellis.com, 8/18/8] - * - * Belief is "Jim" would greatly benifit if Jim Internals where - * documented in some way - form whatever, and perhaps - the package: - * 'doxygen' is the correct approach to do that. - * - * Details, see: http://www.stack.nl/~dimitri/doxygen/ - * - * To that end please follow these guide lines: - * - * (A) Document the PUBLIC api in the .H file. - * - * (B) Document JIM Internals, in the .C file. - * - * (C) Remember JIM is embedded in other packages, to that end do - * not assume that your way of documenting is the right way, Jim's - * public documentation should be agnostic, such that it is some - * what agreeable with the "package" that is embedding JIM inside - * of it's own doxygen documentation. - * - * (D) Use minimal Doxygen tags. - * - * This will be an "ongoing work in progress" for some time. - **/ + +#endif + +#endif #ifndef __JIM__H #define __JIM__H #ifdef __cplusplus @@ -209,27 +138,20 @@ extern "C" { #endif #include <time.h> #include <limits.h> -#include <stdio.h> /* for the FILE typedef definition */ -#include <stdlib.h> /* In order to export the Jim_Free() macro */ -#include <stdarg.h> /* In order to get type va_list */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> -/* ----------------------------------------------------------------------------- - * System configuration - * autoconf (configure) will set these - * ---------------------------------------------------------------------------*/ #ifndef HAVE_NO_AUTOCONF #endif -/* ----------------------------------------------------------------------------- - * Compiler specific fixes. - * ---------------------------------------------------------------------------*/ -/* Long Long type and related issues */ + #ifndef jim_wide # ifdef HAVE_LONG_LONG # define jim_wide long long # ifndef LLONG_MAX # define LLONG_MAX 9223372036854775807LL @@ -243,13 +165,10 @@ # define jim_wide long # define JIM_WIDE_MIN LONG_MIN # define JIM_WIDE_MAX LONG_MAX # endif -/* ----------------------------------------------------------------------------- - * LIBC specific fixes - * ---------------------------------------------------------------------------*/ # ifdef HAVE_LONG_LONG # define JIM_WIDE_MODIFIER "lld" # else # define JIM_WIDE_MODIFIER "ld" @@ -257,80 +176,66 @@ # endif #endif #define UCHAR(c) ((unsigned char)(c)) -/* ----------------------------------------------------------------------------- - * Exported defines - * ---------------------------------------------------------------------------*/ -/* Jim version numbering: every version of jim is marked with a - * successive integer number. This is version 0. The first - * stable version will be 1, then 2, 3, and so on. */ -#define JIM_VERSION 71 +#define JIM_VERSION 73 #define JIM_OK 0 #define JIM_ERR 1 #define JIM_RETURN 2 #define JIM_BREAK 3 #define JIM_CONTINUE 4 #define JIM_SIGNAL 5 #define JIM_EXIT 6 -/* The following are internal codes and should never been seen/used */ + #define JIM_EVAL 7 -#define JIM_MAX_NESTING_DEPTH 1000 /* default max nesting depth */ +#define JIM_MAX_NESTING_DEPTH 1000 -/* Some function get an integer argument with flags to change - * the behaviour. */ -#define JIM_NONE 0 /* no flags set */ -#define JIM_ERRMSG 1 /* set an error message in the interpreter. */ +#define JIM_NONE 0 +#define JIM_ERRMSG 1 -#define JIM_UNSHARED 4 /* Flag to Jim_GetVariable() */ +#define JIM_UNSHARED 4 -/* Flags for Jim_SubstObj() */ -#define JIM_SUBST_NOVAR 1 /* don't perform variables substitutions */ -#define JIM_SUBST_NOCMD 2 /* don't perform command substitutions */ -#define JIM_SUBST_NOESC 4 /* don't perform escapes substitutions */ -#define JIM_SUBST_FLAG 128 /* flag to indicate that this is a real substition object */ -/* Unused arguments generate annoying warnings... */ +#define JIM_SUBST_NOVAR 1 +#define JIM_SUBST_NOCMD 2 +#define JIM_SUBST_NOESC 4 +#define JIM_SUBST_FLAG 128 + + #define JIM_NOTUSED(V) ((void) V) -/* Flags for Jim_GetEnum() */ -#define JIM_ENUM_ABBREV 2 /* Allow unambiguous abbreviation */ -/* Flags used by API calls getting a 'nocase' argument. */ -#define JIM_CASESENS 0 /* case sensitive */ -#define JIM_NOCASE 1 /* no case */ +#define JIM_ENUM_ABBREV 2 -/* Filesystem related */ + +#define JIM_CASESENS 0 +#define JIM_NOCASE 1 + + #define JIM_PATH_LEN 1024 -/* Newline, some embedded system may need -DJIM_CRLF */ + #ifdef JIM_CRLF #define JIM_NL "\r\n" #else #define JIM_NL "\n" #endif #define JIM_LIBPATH "auto_path" #define JIM_INTERACTIVE "tcl_interactive" -/* ----------------------------------------------------------------------------- - * Stack - * ---------------------------------------------------------------------------*/ typedef struct Jim_Stack { int len; int maxlen; void **vector; } Jim_Stack; -/* ----------------------------------------------------------------------------- - * Hash table - * ---------------------------------------------------------------------------*/ typedef struct Jim_HashEntry { const void *key; union { void *val; @@ -362,14 +267,14 @@ Jim_HashTable *ht; int index; Jim_HashEntry *entry, *nextEntry; } Jim_HashTableIterator; -/* This is the initial size of every hash table */ + #define JIM_HT_INITIAL_SIZE 16 -/* ------------------------------- Macros ------------------------------------*/ + #define Jim_FreeEntryVal(ht, entry) \ if ((ht)->type->valDestructor) \ (ht)->type->valDestructor((ht)->privdata, (entry)->u.val) #define Jim_SetHashVal(ht, entry, _val_) do { \ @@ -401,405 +306,334 @@ #define Jim_GetHashEntryVal(he) ((he)->val) #define Jim_GetHashTableCollisions(ht) ((ht)->collisions) #define Jim_GetHashTableSize(ht) ((ht)->size) #define Jim_GetHashTableUsed(ht) ((ht)->used) -/* ----------------------------------------------------------------------------- - * Jim_Obj structure - * ---------------------------------------------------------------------------*/ -/* ----------------------------------------------------------------------------- - * Jim object. This is mostly the same as Tcl_Obj itself, - * with the addition of the 'prev' and 'next' pointers. - * In Jim all the objects are stored into a linked list for GC purposes, - * so that it's possible to access every object living in a given interpreter - * sequentially. When an object is freed, it's moved into a different - * linked list, used as object pool. - * - * The refcount of a freed object is always -1. - * ---------------------------------------------------------------------------*/ typedef struct Jim_Obj { - int refCount; /* reference count */ - char *bytes; /* string representation buffer. NULL = no string repr. */ - int length; /* number of bytes in 'bytes', not including the numterm. */ - const struct Jim_ObjType *typePtr; /* object type. */ - /* Internal representation union */ + int refCount; + char *bytes; + int length; + const struct Jim_ObjType *typePtr; + union { - /* integer number type */ + jim_wide wideValue; - /* hashed object type value */ + int hashValue; - /* index type */ + int indexValue; - /* return code type */ + int returnCode; - /* double number type */ + double doubleValue; - /* Generic pointer */ + void *ptr; - /* Generic two pointers value */ + struct { void *ptr1; void *ptr2; } twoPtrValue; - /* Variable object */ + struct { unsigned jim_wide callFrameId; struct Jim_Var *varPtr; } varValue; - /* Command object */ + struct { unsigned jim_wide procEpoch; struct Jim_Cmd *cmdPtr; } cmdValue; - /* List object */ + struct { - struct Jim_Obj **ele; /* Elements vector */ - int len; /* Length */ - int maxLen; /* Allocated 'ele' length */ + struct Jim_Obj **ele; + int len; + int maxLen; } listValue; - /* String type */ + struct { int maxLength; - int charLength; /* utf-8 char length. -1 if unknown */ + int charLength; } strValue; - /* Reference type */ + struct { jim_wide id; struct Jim_Reference *refPtr; } refValue; - /* Source type */ + struct { - const char *fileName; + struct Jim_Obj *fileNameObj; int lineNumber; } sourceValue; - /* Dict substitution type */ + struct { struct Jim_Obj *varNameObjPtr; struct Jim_Obj *indexObjPtr; } dictSubstValue; - /* tagged binary type */ + struct { unsigned char *data; size_t len; } binaryValue; - /* Regular expression pattern */ + struct { unsigned flags; - void *compre; /* really an allocated (regex_t *) */ + void *compre; } regexpValue; struct { int line; int argc; } scriptLineValue; } internalRep; - /* This are 8 or 16 bytes more for every object - * but this is required for efficient garbage collection - * of Jim references. */ - struct Jim_Obj *prevObjPtr; /* pointer to the prev object. */ - struct Jim_Obj *nextObjPtr; /* pointer to the next object. */ + struct Jim_Obj *prevObjPtr; + struct Jim_Obj *nextObjPtr; } Jim_Obj; -/* Jim_Obj related macros */ + #define Jim_IncrRefCount(objPtr) \ ++(objPtr)->refCount #define Jim_DecrRefCount(interp, objPtr) \ if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr) #define Jim_IsShared(objPtr) \ ((objPtr)->refCount > 1) -/* This macro is used when we allocate a new object using - * Jim_New...Obj(), but for some error we need to destroy it. - * Instead to use Jim_IncrRefCount() + Jim_DecrRefCount() we - * can just call Jim_FreeNewObj. To call Jim_Free directly - * seems too raw, the object handling may change and we want - * that Jim_FreeNewObj() can be called only against objects - * that are belived to have refcount == 0. */ #define Jim_FreeNewObj Jim_FreeObj -/* Free the internal representation of the object. */ + #define Jim_FreeIntRep(i,o) \ if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \ (o)->typePtr->freeIntRepProc(i, o) -/* Get the internal representation pointer */ + #define Jim_GetIntRepPtr(o) (o)->internalRep.ptr -/* Set the internal representation pointer */ + #define Jim_SetIntRepPtr(o, p) \ (o)->internalRep.ptr = (p) -/* The object type structure. - * There are four methods. - * - * - FreeIntRep is used to free the internal representation of the object. - * Can be NULL if there is nothing to free. - * - DupIntRep is used to duplicate the internal representation of the object. - * If NULL, when an object is duplicated, the internalRep union is - * directly copied from an object to another. - * Note that it's up to the caller to free the old internal repr of the - * object before to call the Dup method. - * - UpdateString is used to create the string from the internal repr. - * - setFromAny is used to convert the current object into one of this type. - */ struct Jim_Interp; typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp, struct Jim_Obj *objPtr); typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp, struct Jim_Obj *srcPtr, Jim_Obj *dupPtr); typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr); - + typedef struct Jim_ObjType { - const char *name; /* The name of the type. */ + const char *name; Jim_FreeInternalRepProc *freeIntRepProc; Jim_DupInternalRepProc *dupIntRepProc; Jim_UpdateStringProc *updateStringProc; int flags; } Jim_ObjType; -/* Jim_ObjType flags */ -#define JIM_TYPE_NONE 0 /* No flags */ -#define JIM_TYPE_REFERENCES 1 /* The object may contain referneces. */ -/* Starting from 1 << 20 flags are reserved for private uses of - * different calls. This way the same 'flags' argument may be used - * to pass both global flags and private flags. */ +#define JIM_TYPE_NONE 0 +#define JIM_TYPE_REFERENCES 1 + #define JIM_PRIV_FLAG_SHIFT 20 -/* ----------------------------------------------------------------------------- - * Call frame, vars, commands structures - * ---------------------------------------------------------------------------*/ -/* Call frame */ + typedef struct Jim_CallFrame { - unsigned jim_wide id; /* Call Frame ID. Used for caching. */ - int level; /* Level of this call frame. 0 = global */ - struct Jim_HashTable vars; /* Where local vars are stored */ - struct Jim_HashTable *staticVars; /* pointer to procedure static vars */ + unsigned jim_wide id; + int level; + struct Jim_HashTable vars; + struct Jim_HashTable *staticVars; struct Jim_CallFrame *parentCallFrame; - Jim_Obj *const *argv; /* object vector of the current procedure call. */ - int argc; /* number of args of the current procedure call. */ - Jim_Obj *procArgsObjPtr; /* arglist object of the running procedure */ - Jim_Obj *procBodyObjPtr; /* body object of the running procedure */ + Jim_Obj *const *argv; + int argc; + Jim_Obj *procArgsObjPtr; + Jim_Obj *procBodyObjPtr; struct Jim_CallFrame *nextFramePtr; - const char *filename; /* file and line of caller of this proc (if available) */ + Jim_Obj *fileNameObj; int line; } Jim_CallFrame; -/* The var structure. It just holds the pointer of the referenced - * object. If linkFramePtr is not NULL the variable is a link - * to a variable of name store on objPtr living on the given callframe - * (this happens when the [global] or [upvar] command is used). - * The interp in order to always know how to free the Jim_Obj associated - * with a given variable because In Jim objects memory managment is - * bound to interpreters. */ typedef struct Jim_Var { Jim_Obj *objPtr; struct Jim_CallFrame *linkFramePtr; } Jim_Var; -/* The cmd structure. */ + typedef int (*Jim_CmdProc)(struct Jim_Interp *interp, int argc, Jim_Obj *const *argv); typedef void (*Jim_DelCmdProc)(struct Jim_Interp *interp, void *privData); -/* A command is implemented in C if funcPtr is != NULL, otherwise - * it's a Tcl procedure with the arglist and body represented by the - * two objects referenced by arglistObjPtr and bodyoObjPtr. */ typedef struct Jim_Cmd { - int inUse; /* Reference count */ - int isproc; /* Is this a procedure? */ + int inUse; + int isproc; union { struct { - /* native (C) command */ - Jim_CmdProc cmdProc; /* The command implementation */ - Jim_DelCmdProc delProc; /* Called when the command is deleted if != NULL */ - void *privData; /* command-private data available via Jim_CmdPrivData() */ + + Jim_CmdProc cmdProc; + Jim_DelCmdProc delProc; + void *privData; } native; struct { - /* Tcl procedure */ + Jim_Obj *argListObjPtr; Jim_Obj *bodyObjPtr; - Jim_HashTable *staticVars; /* Static vars hash table. NULL if no statics. */ - struct Jim_Cmd *prevCmd; /* Previous command defn if proc created 'local' */ - int argListLen; /* Length of argListObjPtr */ - int reqArity; /* Number of required parameters */ - int optArity; /* Number of optional parameters */ - int argsPos; /* Position of 'args', if specified, or -1 */ - int upcall; /* True if proc is currently in upcall */ + Jim_HashTable *staticVars; + struct Jim_Cmd *prevCmd; + int argListLen; + int reqArity; + int optArity; + int argsPos; + int upcall; struct Jim_ProcArg { - Jim_Obj *nameObjPtr; /* Name of this arg */ - Jim_Obj *defaultObjPtr; /* Default value, (or rename for $args) */ + Jim_Obj *nameObjPtr; + Jim_Obj *defaultObjPtr; } *arglist; } proc; } u; } Jim_Cmd; -/* Pseudo Random Number Generator State structure */ + typedef struct Jim_PrngState { unsigned char sbox[256]; unsigned int i, j; } Jim_PrngState; -/* ----------------------------------------------------------------------------- - * Jim interpreter structure. - * Fields similar to the real Tcl interpreter structure have the same names. - * ---------------------------------------------------------------------------*/ typedef struct Jim_Interp { - Jim_Obj *result; /* object returned by the last command called. */ - int errorLine; /* Error line where an error occurred. */ - char *errorFileName; /* Error file where an error occurred. */ - int addStackTrace; /* > 0 If a level should be added to the stack trace */ - int maxNestingDepth; /* Used for infinite loop detection. */ - int returnCode; /* Completion code to return on JIM_RETURN. */ - int returnLevel; /* Current level of 'return -level' */ - int exitCode; /* Code to return to the OS on JIM_EXIT. */ - long id; /* Hold unique id for various purposes */ - int signal_level; /* A nesting level of catch -signal */ - jim_wide sigmask; /* Bit mask of caught signals, or 0 if none */ - int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask); /* Set a result for the sigmask */ - Jim_CallFrame *framePtr; /* Pointer to the current call frame */ - Jim_CallFrame *topFramePtr; /* toplevel/global frame pointer. */ - struct Jim_HashTable commands; /* Commands hash table */ + Jim_Obj *result; + int errorLine; + Jim_Obj *errorFileNameObj; + int addStackTrace; + int maxNestingDepth; + int returnCode; + int returnLevel; + int exitCode; + long id; + int signal_level; + jim_wide sigmask; + int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask); + Jim_CallFrame *framePtr; + Jim_CallFrame *topFramePtr; + struct Jim_HashTable commands; unsigned jim_wide procEpoch; /* Incremented every time the result of procedures names lookup caching may no longer be valid. */ unsigned jim_wide callFrameEpoch; /* Incremented every time a new callframe is created. This id is used for the 'ID' field contained in the Jim_CallFrame structure. */ - int local; /* If 'local' is in effect, newly defined procs keep a reference to the old defn */ - Jim_Obj *liveList; /* Linked list of all the live objects. */ - Jim_Obj *freeList; /* Linked list of all the unused objects. */ - Jim_Obj *currentScriptObj; /* Script currently in execution. */ - Jim_Obj *emptyObj; /* Shared empty string object. */ - Jim_Obj *trueObj; /* Shared true int object. */ - Jim_Obj *falseObj; /* Shared false int object. */ - unsigned jim_wide referenceNextId; /* Next id for reference. */ - struct Jim_HashTable references; /* References hash table. */ + int local; + Jim_Obj *liveList; + Jim_Obj *freeList; + Jim_Obj *currentScriptObj; + Jim_Obj *emptyObj; + Jim_Obj *trueObj; + Jim_Obj *falseObj; + unsigned jim_wide referenceNextId; + struct Jim_HashTable references; jim_wide lastCollectId; /* reference max Id of the last GC execution. It's set to -1 while the collection is running as sentinel to avoid to recursive calls via the [collect] command inside finalizers. */ - time_t lastCollectTime; /* unix time of the last GC execution */ - struct Jim_HashTable sharedStrings; /* Shared Strings hash table */ - Jim_Obj *stackTrace; /* Stack trace object. */ - Jim_Obj *errorProc; /* Name of last procedure which returned an error */ - Jim_Obj *unknown; /* Unknown command cache */ - int unknown_called; /* The unknown command has been invoked */ - int errorFlag; /* Set if an error occurred during execution. */ + time_t lastCollectTime; + Jim_Obj *stackTrace; + Jim_Obj *errorProc; + Jim_Obj *unknown; + int unknown_called; + int errorFlag; void *cmdPrivData; /* Used to pass the private data pointer to a command. It is set to what the user specified via Jim_CreateCommand(). */ - struct Jim_CallFrame *freeFramesList; /* list of CallFrame structures. */ - struct Jim_HashTable assocData; /* per-interp storage for use by packages */ - Jim_PrngState *prngState; /* per interpreter Random Number Gen. state. */ - struct Jim_HashTable packages; /* Provided packages hash table */ - Jim_Stack *localProcs; /* procs to be destroyed on end of evaluation */ - Jim_Stack *loadHandles; /* handles of loaded modules [load] */ + struct Jim_CallFrame *freeFramesList; + struct Jim_HashTable assocData; + Jim_PrngState *prngState; + struct Jim_HashTable packages; + Jim_Stack *localProcs; + Jim_Stack *loadHandles; } Jim_Interp; -/* Currently provided as macro that performs the increment. - * At some point may be a real function doing more work. - * The proc epoch is used in order to know when a command lookup - * cached can no longer considered valid. */ #define Jim_InterpIncrProcEpoch(i) (i)->procEpoch++ #define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l)) #define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval)) -/* Note: Using trueObj and falseObj here makes some things slower...*/ + #define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b) #define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj) #define Jim_GetResult(i) ((i)->result) #define Jim_CmdPrivData(i) ((i)->cmdPrivData) #define Jim_String(o) Jim_GetString((o), NULL) -/* Note that 'o' is expanded only one time inside this macro, - * so it's safe to use side effects. */ #define Jim_SetResult(i,o) do { \ Jim_Obj *_resultObjPtr_ = (o); \ Jim_IncrRefCount(_resultObjPtr_); \ Jim_DecrRefCount(i,(i)->result); \ (i)->result = _resultObjPtr_; \ } while(0) -/* Use this for filehandles, etc. which need a unique id */ + #define Jim_GetId(i) (++(i)->id) -/* Reference structure. The interpreter pointer is held within privdata member in HashTable */ + #define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference string representation must be fixed length. */ typedef struct Jim_Reference { Jim_Obj *objPtr; Jim_Obj *finalizerCmdNamePtr; char tag[JIM_REFERENCE_TAGLEN+1]; } Jim_Reference; -/* ----------------------------------------------------------------------------- - * Exported API prototypes. - * ---------------------------------------------------------------------------*/ -/* Macros that are common for extensions and core. */ + #define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0) -/* The core includes real prototypes, extensions instead - * include a global function pointer for every function exported. - * Once the extension calls Jim_InitExtension(), the global - * functon pointers are set to the value of the STUB table - * contained in the Jim_Interp structure. - * - * This makes Jim able to load extensions even if it is statically - * linked itself, and to load extensions compiled with different - * versions of Jim (as long as the API is still compatible.) */ -/* Macros are common for core and extensions */ + #define Jim_FreeHashTableIterator(iter) Jim_Free(iter) #define JIM_EXPORT -/* Memory allocation */ + JIM_EXPORT void *Jim_Alloc (int size); JIM_EXPORT void *Jim_Realloc(void *ptr, int size); JIM_EXPORT void Jim_Free (void *ptr); JIM_EXPORT char * Jim_StrDup (const char *s); JIM_EXPORT char *Jim_StrDupLen(const char *s, int l); -/* environment */ + JIM_EXPORT char **Jim_GetEnviron(void); JIM_EXPORT void Jim_SetEnviron(char **env); -/* evaluation */ + JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script); -/* in C code, you can do this and get better error messages */ -/* Jim_Eval_Named( interp, "some tcl commands", __FILE__, __LINE__ ); */ -JIM_EXPORT int Jim_Eval_Named(Jim_Interp *interp, const char *script,const char *filename, int lineno); + + +JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script); + +#define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S)) + JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script); JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename); JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename); JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr); JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc, Jim_Obj *const *objv); -JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, const char *prefix, +JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv); +#define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov)) JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags); -/* stack */ + JIM_EXPORT void Jim_InitStack(Jim_Stack *stack); JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack); JIM_EXPORT int Jim_StackLen(Jim_Stack *stack); JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element); JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack); JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack); JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr)); -/* hash table */ + JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht, const Jim_HashTableType *type, void *privdata); JIM_EXPORT int Jim_ExpandHashTable (Jim_HashTable *ht, unsigned int size); JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key, @@ -815,11 +649,11 @@ JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator (Jim_HashTable *ht); JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry (Jim_HashTableIterator *iter); -/* objects */ + JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp); JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr); JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr); JIM_EXPORT void Jim_InitStringRep (Jim_Obj *objPtr, const char *bytes, int length); @@ -827,11 +661,11 @@ Jim_Obj *objPtr); JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr, int *lenPtr); JIM_EXPORT int Jim_Length(Jim_Obj *objPtr); -/* string object */ + JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp, const char *s, int len); JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen); JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp, @@ -856,33 +690,33 @@ Jim_Obj *objPtr, const char *str); JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase); JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr); -/* reference object */ + JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr); JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp, Jim_Obj *objPtr); JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr); JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr); -/* interpreter */ + JIM_EXPORT Jim_Interp * Jim_CreateInterp (void); JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i); JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp); JIM_EXPORT const char *Jim_ReturnCode(int code); JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...); -/* commands */ + JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp); -JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp, +JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp, const char *cmdName, Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc); JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp, const char *cmdName); -JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp, +JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp, const char *oldName, const char *newName); JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp, Jim_Obj *objPtr, int flags); JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr); @@ -904,23 +738,23 @@ JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp, const char *name, int flags); JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags); -/* call frame */ + JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr); -/* garbage collection */ + JIM_EXPORT int Jim_Collect (Jim_Interp *interp); JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp); -/* index object */ + JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr); -/* list object */ + JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp, Jim_Obj *const *elements, int len); JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp, Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec); JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp, @@ -934,204 +768,146 @@ Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr); JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc, Jim_Obj *const *objv); -/* dict object */ + JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp, Jim_Obj *const *elements, int len); JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags); JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags); JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp, Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc, - Jim_Obj *newObjPtr); + Jim_Obj *newObjPtr, int flags); JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len); JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr); JIM_EXPORT int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj); JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr); -/* return code object */ + JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr); -/* expression object */ + JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr); JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr); -/* integer object */ + JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr); JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr); #define Jim_NewWideObj Jim_NewIntObj JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp, jim_wide wideValue); -/* double object */ + JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr); JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue); JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue); -/* shared strings */ -JIM_EXPORT const char * Jim_GetSharedString (Jim_Interp *interp, + +JIM_EXPORT const char * Jim_GetSharedString (Jim_Interp *interp, const char *str); JIM_EXPORT void Jim_ReleaseSharedString (Jim_Interp *interp, const char *str); -/* commands utilities */ + JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg); JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr, const char * const *tablePtr, int *indexPtr, const char *name, int flags); JIM_EXPORT int Jim_ScriptIsComplete (const char *s, int len, char *stateCharPtr); -/** - * Find a matching name in the array of the given length. - * - * NULL entries are ignored. - * - * Returns the matching index if found, or -1 if not. - */ JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len); -/* package utilities */ + typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data); JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key); JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc *delProc, void *data); JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key); -/* Packages C API */ -/* jim-package.c */ + + JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp, const char *name, const char *ver, int flags); JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp, const char *name, int flags); -/* error messages */ + JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp); -/* interactive mode */ + JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp); -/* Misc */ + JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp); JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base); -/* jim-load.c */ + JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName); JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp); -/* jim-aio.c */ + JIM_EXPORT FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command); -/* type inspection - avoid where possible */ + JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr); JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr); #ifdef __cplusplus } #endif -#endif /* __JIM__H */ +#endif -/* - * Local Variables: *** - * c-basic-offset: 4 *** - * tab-width: 4 *** - * End: *** - */ -/* Provides a common approach to implementing Tcl commands - * which implement subcommands - */ #ifndef JIM_SUBCMD_H #define JIM_SUBCMD_H -#define JIM_MODFLAG_HIDDEN 0x0001 /* Don't show the subcommand in usage or commands */ -#define JIM_MODFLAG_FULLARGV 0x0002 /* Subcmd proc gets called with full argv */ +#ifdef __cplusplus +extern "C" { +#endif -/* Custom flags start at 0x0100 */ -/** - * Returns JIM_OK if OK, JIM_ERR (etc.) on error, break, continue, etc. - * Returns -1 if invalid args. - */ +#define JIM_MODFLAG_HIDDEN 0x0001 +#define JIM_MODFLAG_FULLARGV 0x0002 + + + typedef int tclmod_cmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv); typedef struct { - const char *cmd; /* Name of the (sub)command */ - const char *args; /* Textual description of allowed args */ - tclmod_cmd_function *function; /* Function implementing the subcommand */ - short minargs; /* Minimum required arguments */ - short maxargs; /* Maximum allowed arguments or -1 if no limit */ - unsigned flags; /* JIM_MODFLAG_... plus custom flags */ - const char *description; /* Description of the subcommand */ + const char *cmd; + const char *args; + tclmod_cmd_function *function; + short minargs; + short maxargs; + unsigned short flags; } jim_subcmd_type; -/** - * Looks up the appropriate subcommand in the given command table and return - * the command function which implements the subcommand. - * NULL will be returned and an appropriate error will be set if the subcommand or - * arguments are invalid. - * - * Typical usage is: - * { - * const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, command_table, argc, argv); - * - * return Jim_CallSubCmd(interp, ct, argc, argv); - * } - * - */ const jim_subcmd_type * Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv); -/** - * Parses the args against the given command table and executes the subcommand if found - * or sets an appropriate error if the subcommand or arguments is invalid. - * - * Can be used directly with Jim_CreateCommand() where the ClientData is the command table. - * - * e.g. Jim_CreateCommand(interp, "mycmd", Jim_SubCmdProc, command_table, NULL); - */ int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv); -/** - * Invokes the given subcmd with the given args as returned - * by Jim_ParseSubCmd() - * - * If ct is NULL, returns JIM_ERR, leaving any message. - * Otherwise invokes ct->function - * - * If ct->function returns -1, sets an error message and returns JIM_ERR. - * Otherwise returns the result of ct->function. - */ int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv); -/** - * Standard processing for a command. - * - * This does the '-help' and '-usage' check and the number of args checks. - * for a top level command against a single 'jim_subcmd_type' structure. - * - * Additionally, if command_table->function is set, it should point to a sub command table - * and '-subhelp ?subcmd?', '-subusage' and '-subcommands' are then also recognised. - * - * Returns 0 if user requested usage, -1 on arg error, 1 if OK to process. - */ -int -Jim_CheckCmdUsage(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv); +#ifdef __cplusplus +} +#endif #endif #ifndef JIMREGEXP_H #define JIMREGEXP_H @@ -1138,78 +914,51 @@ #ifndef _JIMAUTOCONF_H #error Need jimautoconf.h #endif #if defined(HAVE_REGCOMP) && !defined(JIM_REGEXP) -/* Use POSIX regex */ + #include <regex.h> #else #include <stdlib.h> -/* - * Definitions etc. for regexp(3) routines. - * - * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], - * not the System V one. - * - * 11/04/02 (seiwald) - const-ing for string literals - */ typedef struct { int rm_so; int rm_eo; } regmatch_t; -/* - * The "internal use only" fields in regexp.h are present to pass info from - * compile to execute that permits the execute phase to run lots faster on - * simple cases. They are: - * - * regstart char that must begin a match; '\0' if none obvious - * reganch is the match anchored (at beginning-of-line only)? - * regmust string (pointer into program) that match must include, or NULL - * regmlen length of regmust string - * - * Regstart and reganch permit very fast decisions on suitable starting points - * for a match, cutting down the work a lot. Regmust permits fast rejection - * of lines that cannot possibly match. The regmust tests are costly enough - * that regcomp() supplies a regmust only if the r.e. contains something - * potentially expensive (at present, the only such thing detected is * or + - * at the start of the r.e., which can involve a lot of backup). Regmlen is - * supplied because the test in regexec() needs it and regcomp() is computing - * it anyway. - */ typedef struct regexp { - /* -- public -- */ - int re_nsub; /* number of parenthesized subexpressions */ - - /* -- private -- */ - int cflags; /* Flags used when compiling */ - int err; /* Any error which occurred during compile */ - int regstart; /* Internal use only. */ - int reganch; /* Internal use only. */ - int regmust; /* Internal use only. */ - int regmlen; /* Internal use only. */ - int *program; /* Allocated */ - - /* working state - compile */ - const char *regparse; /* Input-scan pointer. */ - int p; /* Current output pos in program */ - int proglen; /* Allocated program size */ - - /* working state - exec */ - int eflags; /* Flags used when executing */ - const char *start; /* Initial string pointer. */ - const char *reginput; /* Current input pointer. */ - const char *regbol; /* Beginning of input, for ^ check. */ - - /* Input to regexec() */ - regmatch_t *pmatch; /* submatches will be stored here */ - int nmatch; /* size of pmatch[] */ + + int re_nsub; + + + int cflags; + int err; + int regstart; + int reganch; + int regmust; + int regmlen; + int *program; + + + const char *regparse; + int p; + int proglen; + + + int eflags; + const char *start; + const char *reginput; + const char *regbol; + + + regmatch_t *pmatch; + int nmatch; } regexp; typedef regexp regex_t; #define REG_EXTENDED 0 @@ -1217,13 +966,13 @@ #define REG_ICASE 2 #define REG_NOTBOL 16 enum { - REG_NOERROR, /* Success. */ - REG_NOMATCH, /* Didn't find a match (for regexec). */ - REG_BADPAT, /* >= REG_BADPAT is an error */ + REG_NOERROR, + REG_NOMATCH, + REG_BADPAT, REG_ERR_NULL_ARGUMENT, REG_ERR_UNKNOWN, REG_ERR_TOO_BIG, REG_ERR_NOMEM, REG_ERR_TOO_MANY_PAREN, @@ -1252,22 +1001,22 @@ int Jim_bootstrapInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG)) return JIM_ERR; - return Jim_Eval_Named(interp, + return Jim_EvalSource(interp, "bootstrap.tcl", 1, "\n" "\n" "proc package {args} {}\n" -,"bootstrap.tcl", 1); +); } int Jim_initjimshInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG)) return JIM_ERR; - return Jim_Eval_Named(interp, + return Jim_EvalSource(interp, "initjimsh.tcl", 1, "\n" "\n" "\n" "proc _jimsh_init {} {\n" " rename _jimsh_init {}\n" @@ -1291,18 +1040,18 @@ "if {$tcl_platform(platform) eq \"windows\"} {\n" " set jim_argv0 [string map {\\\\ /} $jim_argv0]\n" "}\n" "\n" "_jimsh_init\n" -,"initjimsh.tcl", 1); +); } int Jim_globInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG)) return JIM_ERR; - return Jim_Eval_Named(interp, + return Jim_EvalSource(interp, "glob.tcl", 1, "\n" "\n" "\n" "\n" "\n" @@ -1428,18 +1177,18 @@ " return -code error \"no files matched glob patterns\"\n" " }\n" "\n" " return $result\n" "}\n" -,"glob.tcl", 1); +); } int Jim_stdlibInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG)) return JIM_ERR; - return Jim_Eval_Named(interp, + return Jim_EvalSource(interp, "stdlib.tcl", 1, "\n" "\n" "\n" "proc alias {name args} {\n" " set prefix $args\n" @@ -1543,11 +1292,11 @@ " if {[info exists ::jim_argv0]} {\n" " if {[string match \"*/*\" $::jim_argv0]} {\n" " return [file join [pwd] $::jim_argv0]\n" " }\n" " foreach path [split [env PATH \"\"] $::tcl_platform(pathSeparator)] {\n" -" set exec [file join [pwd] $path $::jim_argv0]\n" +" set exec [file join [pwd] [string map {\\\\ /} $path] $::jim_argv0]\n" " if {[file executable $exec]} {\n" " return $exec\n" " }\n" " }\n" " }\n" @@ -1586,18 +1335,18 @@ " dict set dict $k $v\n" " }\n" " }\n" " return $dict\n" "}\n" -,"stdlib.tcl", 1); +); } int Jim_tclcompatInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG)) return JIM_ERR; - return Jim_Eval_Named(interp, + return Jim_EvalSource(interp, "tclcompat.tcl", 1, "\n" "\n" "\n" "\n" "\n" @@ -1873,53 +1622,14 @@ " foreach e [readdir $path] {\n" " file delete -force $path/$e\n" " }\n" " file delete $path\n" "}\n" -,"tclcompat.tcl", 1); +); } -/* Jim - A small embeddable Tcl interpreter - * - * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org> - * Copyright 2005 Clemens Hintze <c.hintze@gmx.net> - * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net> - * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com - * Copyright 2008 Andrew Lunn <andrew@lunn.ch> - * Copyright 2008 Duane Ellis <openocd@duaneellis.com> - * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of the Jim Tcl Project. - **/ -#include <unistd.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <fcntl.h> @@ -1927,20 +1637,21 @@ #if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H) #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> +#include <unistd.h> #ifdef HAVE_SYS_UN_H #include <sys/un.h> #endif #else #define JIM_ANSIC #endif -#define AIO_CMD_LEN 32 /* e.g. aio.handleXXXXXX */ -#define AIO_BUF_LEN 256 /* Can keep this small and rely on stdio buffering */ +#define AIO_CMD_LEN 32 +#define AIO_BUF_LEN 256 #define AIO_KEEPOPEN 1 #if defined(JIM_IPV6) #define IPV6 1 @@ -1949,194 +1660,31 @@ #ifndef PF_INET6 #define PF_INET6 0 #endif #endif -#ifndef JIM_ANSIC -union sockaddr_any { - struct sockaddr sa; - struct sockaddr_in sin; -#if IPV6 - struct sockaddr_in6 sin6; -#endif -}; - -#ifndef HAVE_INET_NTOP -const char *inet_ntop(int af, const void *src, char *dst, int size) -{ - if (af != PF_INET) { - return NULL; - } - snprintf(dst, size, "%s", inet_ntoa(((struct sockaddr_in *)src)->sin_addr)); - return dst; -} -#endif -#endif typedef struct AioFile { FILE *fp; Jim_Obj *filename; int type; - int OpenFlags; /* AIO_KEEPOPEN? keep FILE* */ + int OpenFlags; int fd; #ifdef O_NDELAY int flags; #endif Jim_Obj *rEvent; Jim_Obj *wEvent; Jim_Obj *eEvent; -#ifndef JIM_ANSIC int addr_family; -#endif } AioFile; static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv); - -#ifndef JIM_ANSIC -static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen) -{ -#if IPV6 - /* - * An IPv6 addr/port looks like: - * [::1] - * [::1]:2000 - * [fe80::223:6cff:fe95:bdc0%en1]:2000 - * [::]:2000 - * 2000 - * - * Note that the "any" address is ::, which is the same as when no address is specified. - */ - char *sthost = NULL; - const char *stport; - int ret = JIM_OK; - struct addrinfo req; - struct addrinfo *ai; - - stport = strrchr(hostport, ':'); - if (!stport) { - /* No : so, the whole thing is the port */ - stport = hostport; - hostport = "::"; - sthost = Jim_StrDup(hostport); - } - else { - stport++; - } - - if (*hostport == '[') { - /* This is a numeric ipv6 address */ - char *pt = strchr(++hostport, ']'); - if (pt) { - sthost = Jim_StrDupLen(hostport, pt - hostport); - } - } - - if (!sthost) { - sthost = Jim_StrDupLen(hostport, stport - hostport - 1); - } - - memset(&req, '\0', sizeof(req)); - req.ai_family = PF_INET6; - - if (getaddrinfo(sthost, NULL, &req, &ai)) { - Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport); - ret = JIM_ERR; - } - else { - memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen); - *salen = ai->ai_addrlen; - - sa->sin.sin_port = htons(atoi(stport)); - - freeaddrinfo(ai); - } - Jim_Free(sthost); - - return ret; -#else - Jim_SetResultString(interp, "ipv6 not supported", -1); - return JIM_ERR; -#endif -} - -static int JimParseIpAddress(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen) -{ - /* An IPv4 addr/port looks like: - * 192.168.1.5 - * 192.168.1.5:2000 - * 2000 - * - * If the address is missing, INADDR_ANY is used. - * If the port is missing, 0 is used (only useful for server sockets). - */ - char *sthost = NULL; - const char *stport; - int ret = JIM_OK; - - stport = strrchr(hostport, ':'); - if (!stport) { - /* No : so, the whole thing is the port */ - stport = hostport; - sthost = Jim_StrDup("0.0.0.0"); - } - else { - sthost = Jim_StrDupLen(hostport, stport - hostport); - stport++; - } - - { -#ifdef HAVE_GETADDRINFO - struct addrinfo req; - struct addrinfo *ai; - memset(&req, '\0', sizeof(req)); - req.ai_family = PF_INET; - - if (getaddrinfo(sthost, NULL, &req, &ai)) { - ret = JIM_ERR; - } - else { - memcpy(&sa->sin, ai->ai_addr, ai->ai_addrlen); - *salen = ai->ai_addrlen; - freeaddrinfo(ai); - } -#else - struct hostent *he; - - ret = JIM_ERR; - - if ((he = gethostbyname(sthost)) != NULL) { - if (he->h_length == sizeof(sa->sin.sin_addr)) { - *salen = sizeof(sa->sin); - sa->sin.sin_family= he->h_addrtype; - memcpy(&sa->sin.sin_addr, he->h_addr, he->h_length); /* set address */ - ret = JIM_OK; - } - } -#endif - - sa->sin.sin_port = htons(atoi(stport)); - } - Jim_Free(sthost); - - if (ret != JIM_OK) { - Jim_SetResultFormatted(interp, "Not a valid address: %s", hostport); - } - - return ret; -} - -#ifdef HAVE_SYS_UN_H -static int JimParseDomainAddress(Jim_Interp *interp, const char *path, struct sockaddr_un *sa) -{ - sa->sun_family = PF_UNIX; - snprintf(sa->sun_path, sizeof(sa->sun_path), "%s", path); - - return JIM_OK; -} -#endif -#endif +static int JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename, + const char *hdlfmt, int family, const char *mode); + static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name) { if (name) { Jim_SetResultFormatted(interp, "%#s: %s", name, strerror(errno)); @@ -2150,17 +1698,18 @@ { AioFile *af = privData; JIM_NOTUSED(interp); - Jim_DecrRefCount(interp, af->filename); - if (!(af->OpenFlags & AIO_KEEPOPEN)) { fclose(af->fp); } + + Jim_DecrRefCount(interp, af->filename); + #ifdef jim_ext_eventloop - /* remove existing EventHandlers */ + if (af->rEvent) { Jim_DeleteFileHandler(interp, af->fp); } if (af->wEvent) { Jim_DeleteFileHandler(interp, af->fp); @@ -2176,11 +1725,11 @@ { AioFile *af = Jim_CmdPrivData(interp); char buf[AIO_BUF_LEN]; Jim_Obj *objPtr; int nonewline = 0; - int neededLen = -1; /* -1 is "read as much as possible" */ + int neededLen = -1; if (argc && Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) { nonewline = 1; argv++; argc--; @@ -2218,16 +1767,16 @@ } } if (retval != readlen) break; } - /* Check for error conditions */ + if (ferror(af->fp)) { clearerr(af->fp); - /* eof and EAGAIN are not error conditions */ + if (!feof(af->fp) && errno != EAGAIN) { - /* I/O error */ + Jim_FreeNewObj(interp, objPtr); JimAioSetError(interp, af->filename); return JIM_ERR; } } @@ -2290,62 +1839,56 @@ static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); char buf[AIO_BUF_LEN]; Jim_Obj *objPtr; + int len; errno = 0; objPtr = Jim_NewStringObj(interp, NULL, 0); while (1) { - int more = 0; - buf[AIO_BUF_LEN - 1] = '_'; if (fgets(buf, AIO_BUF_LEN, af->fp) == NULL) break; - if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') - more = 1; - if (more) { + + if (buf[AIO_BUF_LEN - 1] == '\0' && buf[AIO_BUF_LEN - 2] != '\n') { Jim_AppendString(interp, objPtr, buf, AIO_BUF_LEN - 1); } else { - int len = strlen(buf); + len = strlen(buf); - if (len) { - int hasnl = (buf[len - 1] == '\n'); + if (len && (buf[len - 1] == '\n')) { + + len--; + } - /* strip "\n" */ - Jim_AppendString(interp, objPtr, buf, strlen(buf) - hasnl); - } + Jim_AppendString(interp, objPtr, buf, len); + break; } - if (!more) - break; } if (ferror(af->fp) && errno != EAGAIN && errno != EINTR) { - /* I/O error */ + Jim_FreeNewObj(interp, objPtr); JimAioSetError(interp, af->filename); clearerr(af->fp); return JIM_ERR; } - /* On EOF returns -1 if varName was specified, or the empty string. */ - if (feof(af->fp) && Jim_Length(objPtr) == 0) { - Jim_FreeNewObj(interp, objPtr); - if (argc) { - Jim_SetResultInt(interp, -1); - } - return JIM_OK; - } + if (argc) { - int totLen; - - Jim_GetString(objPtr, &totLen); if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) { Jim_FreeNewObj(interp, objPtr); return JIM_ERR; } - Jim_SetResultInt(interp, totLen); + + len = Jim_Length(objPtr); + + if (len == 0 && feof(af->fp)) { + + len = -1; + } + Jim_SetResultInt(interp, len); } else { Jim_SetResult(interp, objPtr); } return JIM_OK; @@ -2376,131 +1919,10 @@ } JimAioSetError(interp, af->filename); return JIM_ERR; } -#ifndef JIM_ANSIC -static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv) -{ - AioFile *af = Jim_CmdPrivData(interp); - char *buf; - union sockaddr_any sa; - long len; - socklen_t salen = sizeof(sa); - int rlen; - - if (Jim_GetLong(interp, argv[0], &len) != JIM_OK) { - return JIM_ERR; - } - - buf = Jim_Alloc(len + 1); - - rlen = recvfrom(fileno(af->fp), buf, len, 0, &sa.sa, &salen); - if (rlen < 0) { - Jim_Free(buf); - JimAioSetError(interp, NULL); - return JIM_ERR; - } - buf[rlen] = 0; - Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, rlen)); - - if (argc > 1) { - /* INET6_ADDRSTRLEN is 46. Add some for [] and port */ - char addrbuf[60]; - -#if IPV6 - if (sa.sa.sa_family == PF_INET6) { - addrbuf[0] = '['; - /* Allow 9 for []:65535\0 */ - inet_ntop(sa.sa.sa_family, &sa.sin6.sin6_addr, addrbuf + 1, sizeof(addrbuf) - 9); - snprintf(addrbuf + strlen(addrbuf), 8, "]:%d", ntohs(sa.sin.sin_port)); - } - else -#endif - { - /* Allow 7 for :65535\0 */ - inet_ntop(sa.sa.sa_family, &sa.sin.sin_addr, addrbuf, sizeof(addrbuf) - 7); - snprintf(addrbuf + strlen(addrbuf), 7, ":%d", ntohs(sa.sin.sin_port)); - } - - if (Jim_SetVariable(interp, argv[1], Jim_NewStringObj(interp, addrbuf, -1)) != JIM_OK) { - return JIM_ERR; - } - } - - return JIM_OK; -} - - -static int aio_cmd_sendto(Jim_Interp *interp, int argc, Jim_Obj *const *argv) -{ - AioFile *af = Jim_CmdPrivData(interp); - int wlen; - int len; - const char *wdata; - union sockaddr_any sa; - const char *addr = Jim_String(argv[1]); - int salen; - - if (IPV6 && af->addr_family == PF_INET6) { - if (JimParseIPv6Address(interp, addr, &sa, &salen) != JIM_OK) { - return JIM_ERR; - } - } - else if (JimParseIpAddress(interp, addr, &sa, &salen) != JIM_OK) { - return JIM_ERR; - } - wdata = Jim_GetString(argv[0], &wlen); - - /* Note that we don't validate the socket type. Rely on sendto() failing if appropriate */ - len = sendto(fileno(af->fp), wdata, wlen, 0, &sa.sa, salen); - if (len < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - Jim_SetResultInt(interp, len); - return JIM_OK; -} - -static int aio_cmd_accept(Jim_Interp *interp, int argc, Jim_Obj *const *argv) -{ - AioFile *serv_af = Jim_CmdPrivData(interp); - int sock; - union sockaddr_any sa; - socklen_t addrlen = sizeof(sa); - AioFile *af; - char buf[AIO_CMD_LEN]; - - sock = accept(serv_af->fd, &sa.sa, &addrlen); - if (sock < 0) - return JIM_ERR; - - /* Create the file command */ - af = Jim_Alloc(sizeof(*af)); - af->fd = sock; -#ifdef FD_CLOEXEC - fcntl(af->fd, F_SETFD, FD_CLOEXEC); -#endif - af->filename = Jim_NewStringObj(interp, "accept", -1); - Jim_IncrRefCount(af->filename); - af->fp = fdopen(sock, "r+"); - - af->OpenFlags = 0; -#ifdef O_NDELAY - af->flags = fcntl(af->fd, F_GETFL); -#endif - af->rEvent = NULL; - af->wEvent = NULL; - af->eEvent = NULL; - af->addr_family = serv_af->addr_family; - snprintf(buf, sizeof(buf), "aio.sockstream%ld", Jim_GetId(interp)); - Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc); - Jim_SetResultString(interp, buf, -1); - return JIM_OK; -} - -#endif static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { AioFile *af = Jim_CmdPrivData(interp); @@ -2649,31 +2071,31 @@ int argc, Jim_Obj * const *argv) { int scriptlen = 0; if (argc == 0) { - /* Return current script */ + if (*scriptHandlerObj) { Jim_SetResult(interp, *scriptHandlerObj); } return JIM_OK; } if (*scriptHandlerObj) { - /* Delete old handler */ + Jim_DeleteFileHandler(interp, af->fp); *scriptHandlerObj = NULL; } - /* Now possibly add the new script(s) */ + Jim_GetString(argv[0], &scriptlen); if (scriptlen == 0) { - /* Empty script, so done */ + return JIM_OK; } - /* A new script to add */ + Jim_IncrRefCount(argv[0]); *scriptHandlerObj = argv[0]; Jim_CreateFileHandler(interp, af->fp, mask, JimAioFileEventHandler, *scriptHandlerObj, JimAioFileEventFinalizer); @@ -2702,512 +2124,215 @@ return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, &af->wEvent, argc, argv); } #endif static const jim_subcmd_type aio_command_table[] = { - { .cmd = "read", - .args = "?-nonewline? ?len?", - .function = aio_cmd_read, - .minargs = 0, - .maxargs = 2, - .description = "Read and return bytes from the stream. To eof if no len." - }, - { .cmd = "copyto", - .args = "handle ?size?", - .function = aio_cmd_copy, - .minargs = 1, - .maxargs = 2, - .description = "Copy up to 'size' bytes to the given filehandle, or to eof if no size." - }, - { .cmd = "gets", - .args = "?var?", - .function = aio_cmd_gets, - .minargs = 0, - .maxargs = 1, - .description = "Read one line and return it or store it in the var" - }, - { .cmd = "puts", - .args = "?-nonewline? str", - .function = aio_cmd_puts, - .minargs = 1, - .maxargs = 2, - .description = "Write the string, with newline unless -nonewline" - }, -#ifndef JIM_ANSIC - { .cmd = "recvfrom", - .args = "len ?addrvar?", - .function = aio_cmd_recvfrom, - .minargs = 1, - .maxargs = 2, - .description = "Receive up to 'len' bytes on the socket. Sets 'addrvar' with receive address, if set" - }, - { .cmd = "sendto", - .args = "str address", - .function = aio_cmd_sendto, - .minargs = 2, - .maxargs = 2, - .description = "Send 'str' to the given address (dgram only)" - }, - { .cmd = "accept", - .function = aio_cmd_accept, - .description = "Server socket only: Accept a connection and return stream" - }, -#endif - { .cmd = "flush", - .function = aio_cmd_flush, - .description = "Flush the stream" - }, - { .cmd = "eof", - .function = aio_cmd_eof, - .description = "Returns 1 if stream is at eof" - }, - { .cmd = "close", - .flags = JIM_MODFLAG_FULLARGV, - .function = aio_cmd_close, - .description = "Closes the stream" - }, - { .cmd = "seek", - .args = "offset ?start|current|end", - .function = aio_cmd_seek, - .minargs = 1, - .maxargs = 2, - .description = "Seeks in the stream (default 'current')" - }, - { .cmd = "tell", - .function = aio_cmd_tell, - .description = "Returns the current seek position" - }, - { .cmd = "filename", - .function = aio_cmd_filename, - .description = "Returns the original filename" + { "read", + "?-nonewline? ?len?", + aio_cmd_read, + 0, + 2, + + }, + { "copyto", + "handle ?size?", + aio_cmd_copy, + 1, + 2, + + }, + { "gets", + "?var?", + aio_cmd_gets, + 0, + 1, + + }, + { "puts", + "?-nonewline? str", + aio_cmd_puts, + 1, + 2, + + }, + { "flush", + NULL, + aio_cmd_flush, + 0, + 0, + + }, + { "eof", + NULL, + aio_cmd_eof, + 0, + 0, + + }, + { "close", + NULL, + aio_cmd_close, + 0, + 0, + JIM_MODFLAG_FULLARGV, + + }, + { "seek", + "offset ?start|current|end", + aio_cmd_seek, + 1, + 2, + + }, + { "tell", + NULL, + aio_cmd_tell, + 0, + 0, + + }, + { "filename", + NULL, + aio_cmd_filename, + 0, + 0, + }, #ifdef O_NDELAY - { .cmd = "ndelay", - .args = "?0|1?", - .function = aio_cmd_ndelay, - .minargs = 0, - .maxargs = 1, - .description = "Set O_NDELAY (if arg). Returns current/new setting." + { "ndelay", + "?0|1?", + aio_cmd_ndelay, + 0, + 1, + }, #endif - { .cmd = "buffering", - .args = "none|line|full", - .function = aio_cmd_buffering, - .minargs = 1, - .maxargs = 1, - .description = "Sets buffering" + { "buffering", + "none|line|full", + aio_cmd_buffering, + 1, + 1, + }, #ifdef jim_ext_eventloop - { .cmd = "readable", - .args = "?readable-script?", - .minargs = 0, - .maxargs = 1, - .function = aio_cmd_readable, - .description = "Returns script, or invoke readable-script when readable, {} to remove", - }, - { .cmd = "writable", - .args = "?writable-script?", - .minargs = 0, - .maxargs = 1, - .function = aio_cmd_writable, - .description = "Returns script, or invoke writable-script when writable, {} to remove", - }, - { .cmd = "onexception", - .args = "?exception-script?", - .minargs = 0, - .maxargs = 1, - .function = aio_cmd_onexception, - .description = "Returns script, or invoke exception-script when oob data, {} to remove", - }, -#endif - { 0 } + { "readable", + "?readable-script?", + aio_cmd_readable, + 0, + 1, + + }, + { "writable", + "?writable-script?", + aio_cmd_writable, + 0, + 1, + + }, + { "onexception", + "?exception-script?", + aio_cmd_onexception, + 0, + 1, + + }, +#endif + { NULL } }; static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv); } -static int JimAioOpenCommand(Jim_Interp *interp, int argc, +static int JimAioOpenCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - FILE *fp; - AioFile *af; - char buf[AIO_CMD_LEN]; - int OpenFlags = 0; - const char *cmdname; + const char *mode; + const char *filename; if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 1, argv, "filename ?mode?"); return JIM_ERR; } - cmdname = Jim_String(argv[1]); - if (Jim_CompareStringImmediate(interp, argv[1], "stdin")) { - OpenFlags |= AIO_KEEPOPEN; - fp = stdin; - } - else if (Jim_CompareStringImmediate(interp, argv[1], "stdout")) { - OpenFlags |= AIO_KEEPOPEN; - fp = stdout; - } - else if (Jim_CompareStringImmediate(interp, argv[1], "stderr")) { - OpenFlags |= AIO_KEEPOPEN; - fp = stderr; - } - else { - const char *mode = (argc == 3) ? Jim_String(argv[2]) : "r"; - const char *filename = Jim_String(argv[1]); + + mode = (argc == 3) ? Jim_String(argv[2]) : "r"; + filename = Jim_String(argv[1]); #ifdef jim_ext_tclcompat - /* If the filename starts with '|', use popen instead */ - if (*filename == '|') { - Jim_Obj *evalObj[3]; - - evalObj[0] = Jim_NewStringObj(interp, "popen", -1); - evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1); - evalObj[2] = Jim_NewStringObj(interp, mode, -1); - - return Jim_EvalObjVector(interp, 3, evalObj); - } -#endif - fp = fopen(filename, mode); - if (fp == NULL) { - JimAioSetError(interp, argv[1]); - return JIM_ERR; - } - /* Get the next file id */ - snprintf(buf, sizeof(buf), "aio.handle%ld", Jim_GetId(interp)); - cmdname = buf; - } - - /* Create the file command */ + + if (*filename == '|') { + Jim_Obj *evalObj[3]; + + evalObj[0] = Jim_NewStringObj(interp, "popen", -1); + evalObj[1] = Jim_NewStringObj(interp, filename + 1, -1); + evalObj[2] = Jim_NewStringObj(interp, mode, -1); + + return Jim_EvalObjVector(interp, 3, evalObj); + } +#endif + return JimMakeChannel(interp, NULL, -1, argv[1], "aio.handle%ld", 0, mode); +} + +static int JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename, + const char *hdlfmt, int family, const char *mode) +{ + AioFile *af; + char buf[AIO_CMD_LEN]; + int OpenFlags = 0; + + if (filename == NULL) { + filename = Jim_NewStringObj(interp, hdlfmt, -1); + } + + Jim_IncrRefCount(filename); + + if (fh == NULL) { + if (fd < 0) { + fh = fopen(Jim_String(filename), mode); + } + else { + fh = fdopen(fd, mode); + } + } + else { + OpenFlags = AIO_KEEPOPEN; + } + + if (fh == NULL) { + JimAioSetError(interp, filename); + if (fd >= 0) { + close(fd); + } + Jim_DecrRefCount(interp, filename); + return JIM_ERR; + } + + af = Jim_Alloc(sizeof(*af)); - af->fp = fp; - af->fd = fileno(fp); + memset(af, 0, sizeof(*af)); + af->fp = fh; + af->fd = fileno(fh); + af->filename = filename; #ifdef FD_CLOEXEC if ((OpenFlags & AIO_KEEPOPEN) == 0) { fcntl(af->fd, F_SETFD, FD_CLOEXEC); } #endif + af->OpenFlags = OpenFlags; #ifdef O_NDELAY af->flags = fcntl(af->fd, F_GETFL); #endif - af->filename = argv[1]; - Jim_IncrRefCount(af->filename); - af->OpenFlags = OpenFlags; - af->rEvent = NULL; - af->wEvent = NULL; - af->eEvent = NULL; - Jim_CreateCommand(interp, cmdname, JimAioSubCmdProc, af, JimAioDelProc); - Jim_SetResultString(interp, cmdname, -1); - return JIM_OK; -} - -#ifndef JIM_ANSIC - -/** - * Creates a channel for fd. - * - * hdlfmt is a sprintf format for the filehandle. Anything with %ld at the end will do. - * mode is usual "r+", but may be another fdopen() mode as required. - * - * Creates the command and lappends the name of the command to the current result. - * - */ -static int JimMakeChannel(Jim_Interp *interp, Jim_Obj *filename, const char *hdlfmt, int fd, int family, - const char *mode) -{ - AioFile *af; - char buf[AIO_CMD_LEN]; - - FILE *fp = fdopen(fd, mode); - - if (fp == NULL) { - close(fd); - JimAioSetError(interp, NULL); - return JIM_ERR; - } - - /* Create the file command */ - af = Jim_Alloc(sizeof(*af)); - af->fp = fp; - af->fd = fd; - fcntl(af->fd, F_SETFD, FD_CLOEXEC); - af->OpenFlags = 0; - af->filename = filename; - Jim_IncrRefCount(af->filename); -#ifdef O_NDELAY - af->flags = fcntl(af->fd, F_GETFL); -#endif - af->rEvent = NULL; - af->wEvent = NULL; - af->eEvent = NULL; af->addr_family = family; snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp)); Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc); - Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, buf, -1)); - - return JIM_OK; -} - -static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) -{ - const char *hdlfmt = "aio.unknown%ld"; - const char *socktypes[] = { - "unix", - "unix.server", - "dgram", - "dgram.server", - "stream", - "stream.server", - "pipe", - NULL - }; - enum - { - SOCK_UNIX, - SOCK_UNIX_SERVER, - SOCK_DGRAM_CLIENT, - SOCK_DGRAM_SERVER, - SOCK_STREAM_CLIENT, - SOCK_STREAM_SERVER, - SOCK_STREAM_PIPE, - SOCK_DGRAM6_CLIENT, - SOCK_DGRAM6_SERVER, - SOCK_STREAM6_CLIENT, - SOCK_STREAM6_SERVER, - }; - int socktype; - int sock; - const char *hostportarg = NULL; - int res; - int on = 1; - const char *mode = "r+"; - int family = PF_INET; - Jim_Obj *argv0 = argv[0]; - int ipv6 = 0; - - if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-ipv6")) { - if (!IPV6) { - Jim_SetResultString(interp, "ipv6 not supported", -1); - return JIM_ERR; - } - ipv6 = 1; - family = PF_INET6; - } - argc -= ipv6; - argv += ipv6; - - if (argc < 2) { - wrongargs: - Jim_WrongNumArgs(interp, 1, &argv0, "?-ipv6? type ?address?"); - return JIM_ERR; - } - - if (Jim_GetEnum(interp, argv[1], socktypes, &socktype, "socket type", JIM_ERRMSG) != JIM_OK) - return JIM_ERR; - - Jim_SetResultString(interp, "", 0); - - hdlfmt = "aio.sock%ld"; - - if (argc > 2) { - hostportarg = Jim_String(argv[2]); - } - - switch (socktype) { - case SOCK_DGRAM_CLIENT: - if (argc == 2) { - /* No address, so an unconnected dgram socket */ - sock = socket(family, SOCK_DGRAM, 0); - if (sock < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - break; - } - /* fall through */ - case SOCK_STREAM_CLIENT: - { - union sockaddr_any sa; - int salen; - - if (argc != 3) { - goto wrongargs; - } - - if (ipv6) { - if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) { - return JIM_ERR; - } - } - else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) { - return JIM_ERR; - } - sock = socket(family, (socktype == SOCK_DGRAM_CLIENT) ? SOCK_DGRAM : SOCK_STREAM, 0); - if (sock < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - res = connect(sock, &sa.sa, salen); - if (res) { - JimAioSetError(interp, argv[2]); - close(sock); - return JIM_ERR; - } - } - break; - - case SOCK_STREAM_SERVER: - case SOCK_DGRAM_SERVER: - { - union sockaddr_any sa; - int salen; - - if (argc != 3) { - goto wrongargs; - } - - if (ipv6) { - if (JimParseIPv6Address(interp, hostportarg, &sa, &salen) != JIM_OK) { - return JIM_ERR; - } - } - else if (JimParseIpAddress(interp, hostportarg, &sa, &salen) != JIM_OK) { - return JIM_ERR; - } - sock = socket(family, (socktype == SOCK_DGRAM_SERVER) ? SOCK_DGRAM : SOCK_STREAM, 0); - if (sock < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - - /* Enable address reuse */ - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); - - res = bind(sock, &sa.sa, salen); - if (res) { - JimAioSetError(interp, argv[2]); - close(sock); - return JIM_ERR; - } - if (socktype == SOCK_STREAM_SERVER) { - res = listen(sock, 5); - if (res) { - JimAioSetError(interp, NULL); - close(sock); - return JIM_ERR; - } - } - hdlfmt = "aio.socksrv%ld"; - } - break; - -#ifdef HAVE_SYS_UN_H - case SOCK_UNIX: - { - struct sockaddr_un sa; - socklen_t len; - - if (argc != 3 || ipv6) { - goto wrongargs; - } - - if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) { - JimAioSetError(interp, argv[2]); - return JIM_ERR; - } - family = PF_UNIX; - sock = socket(PF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family); - res = connect(sock, (struct sockaddr *)&sa, len); - if (res) { - JimAioSetError(interp, argv[2]); - close(sock); - return JIM_ERR; - } - hdlfmt = "aio.sockunix%ld"; - break; - } - - case SOCK_UNIX_SERVER: - { - struct sockaddr_un sa; - socklen_t len; - - if (argc != 3 || ipv6) { - goto wrongargs; - } - - if (JimParseDomainAddress(interp, hostportarg, &sa) != JIM_OK) { - JimAioSetError(interp, argv[2]); - return JIM_ERR; - } - family = PF_UNIX; - sock = socket(PF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - len = strlen(sa.sun_path) + 1 + sizeof(sa.sun_family); - res = bind(sock, (struct sockaddr *)&sa, len); - if (res) { - JimAioSetError(interp, argv[2]); - close(sock); - return JIM_ERR; - } - res = listen(sock, 5); - if (res) { - JimAioSetError(interp, NULL); - close(sock); - return JIM_ERR; - } - hdlfmt = "aio.sockunixsrv%ld"; - break; - } -#endif - -#ifdef HAVE_PIPE - case SOCK_STREAM_PIPE: - { - int p[2]; - - if (argc != 2 || ipv6) { - goto wrongargs; - } - - if (pipe(p) < 0) { - JimAioSetError(interp, NULL); - return JIM_ERR; - } - - hdlfmt = "aio.pipe%ld"; - if (JimMakeChannel(interp, argv[1], hdlfmt, p[0], family, "r") != JIM_OK) { - close(p[0]); - close(p[1]); - JimAioSetError(interp, NULL); - return JIM_ERR; - } - /* Note, if this fails it will leave p[0] open, but this should never happen */ - mode = "w"; - sock = p[1]; - } - break; -#endif - default: - Jim_SetResultString(interp, "Unsupported socket type", -1); - return JIM_ERR; - } - - return JimMakeChannel(interp, argv[1], hdlfmt, sock, family, mode); -} -#endif + Jim_SetResultString(interp, buf, -1); + + return JIM_OK; +} + FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command) { Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG); @@ -3226,79 +2351,28 @@ Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL); #ifndef JIM_ANSIC Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL); #endif - /* Takeover stdin, stdout and stderr */ - Jim_EvalGlobal(interp, "open stdin; open stdout; open stderr"); - - return JIM_OK; -} - -/* - * Tcl readdir command. - * - * (c) 2008 Steve Bennett <steveb@worware.net.au> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of the Jim Tcl Project. - * - * Based on original work by: - *----------------------------------------------------------------------------- - * Copyright 1991-1994 Karl Lehenbauer and Mark Diekhans. - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose and without fee is hereby granted, provided - * that the above copyright notice appear in all copies. Karl Lehenbauer and - * Mark Diekhans make no representations about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - *----------------------------------------------------------------------------- - */ + + JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r"); + JimMakeChannel(interp, stdout, -1, NULL, "stdout", 0, "w"); + JimMakeChannel(interp, stderr, -1, NULL, "stderr", 0, "w"); + + return JIM_OK; +} + #include <errno.h> #include <stdio.h> #include <string.h> + + +#ifdef HAVE_DIRENT_H #include <dirent.h> +#endif - -/* - *----------------------------------------------------------------------------- - * - * Jim_ReaddirCmd -- - * Implements the rename TCL command: - * readdir ?-nocomplain? dirPath - * - * Results: - * Standard TCL result. - *----------------------------------------------------------------------------- - */ int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *dirPath; DIR *dirPtr; struct dirent *entryPtr; @@ -3348,56 +2422,10 @@ return JIM_ERR; Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL); return JIM_OK; } -/* - * Implements the regexp and regsub commands for Jim - * - * (c) 2008 Steve Bennett <steveb@workware.net.au> - * - * Uses C library regcomp()/regexec() for the matching. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of the Jim Tcl Project. - * - * Based on code originally from Tcl 6.7: - * - * Copyright 1987-1991 Regents of the University of California - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without - * fee is hereby granted, provided that the above copyright - * notice appear in all copies. The University of California - * makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without - * express or implied warranty. - */ #include <stdlib.h> #include <string.h> @@ -3419,24 +2447,20 @@ { regex_t *compre; const char *pattern; int ret; - /* Check if the object is already an uptodate variable */ + if (objPtr->typePtr == ®expObjType && objPtr->internalRep.regexpValue.compre && objPtr->internalRep.regexpValue.flags == flags) { - /* nothing to do */ + return objPtr->internalRep.regexpValue.compre; } - /* Not a regexp or the flags do not match */ - if (objPtr->typePtr == ®expObjType) { - FreeRegexpInternalRep(interp, objPtr); - objPtr->typePtr = NULL; - } + - /* Get the string representation */ + pattern = Jim_String(objPtr); compre = Jim_Alloc(sizeof(regex_t)); if ((ret = regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) { char buf[100]; @@ -3445,10 +2469,12 @@ Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf); regfree(compre); Jim_Free(compre); return NULL; } + + Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = ®expObjType; objPtr->internalRep.regexpValue.flags = flags; objPtr->internalRep.regexpValue.compre = compre; @@ -3556,13 +2582,10 @@ num_vars = regex->re_nsub + 1; } pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch)); - /* If an offset has been specified, adjust for that now. - * If it points past the end of the string, point to the terminating null - */ if (offset) { if (offset < 0) { offset += source_len + 1; } if (offset > source_len) { @@ -3594,18 +2617,14 @@ } num_matches++; if (opt_all && !opt_inline) { - /* Just count the number of matches, so skip the substitution h */ + goto try_next_match; } - /* - * If additional variable names have been specified, return - * index information in those variables. - */ j = 0; for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) { Jim_Obj *resultObj; @@ -3638,11 +2657,11 @@ if (opt_inline) { Jim_ListAppendElement(interp, resultListObj, resultObj); } else { - /* And now set the result variable */ + result = Jim_SetVariable(interp, argv[i], resultObj); if (result != JIM_OK) { Jim_FreeObj(interp, resultObj); break; @@ -3765,16 +2784,13 @@ source_str = Jim_GetString(argv[i + 1], &source_len); replace_str = Jim_GetString(argv[i + 2], &replace_len); varname = argv[i + 3]; - /* Create the result string */ + resultObj = Jim_NewStringObj(interp, "", 0); - /* If an offset has been specified, adjust for that now. - * If it points past the end of the string, point to the terminating null - */ if (offset) { if (offset < 0) { offset += source_len + 1; } if (offset > source_len) { @@ -3783,19 +2799,13 @@ else if (offset < 0) { offset = 0; } } - /* Copy the part before -start */ + Jim_AppendString(interp, resultObj, source_str, offset); - /* - * The following loop is to handle multiple matches within the - * same source string; each iteration handles one match and its - * corresponding substitution. If "-all" hasn't been specified - * then the loop body only gets executed once. - */ n = source_len - offset; p = source_str + offset; do { int match = regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags); @@ -3811,22 +2821,12 @@ break; } num_matches++; - /* - * Copy the portion of the source string before the match to the - * result variable. - */ Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so); - /* - * Append the subSpec (replace_str) argument to the variable, making appropriate - * substitutions. This code is a bit hairy because of the backslash - * conventions and because the code saves up ranges of characters in - * subSpec to reduce the number of calls to Jim_SetVar. - */ for (j = 0; j < replace_len; j++) { int idx; int c = replace_str[j]; @@ -3858,38 +2858,34 @@ } p += pmatch[0].rm_eo; n -= pmatch[0].rm_eo; - /* If -all is not specified, or there is no source left, we are done */ + if (!opt_all || n == 0) { break; } - /* An anchored pattern without -line must be done */ + if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') { break; } - /* If the pattern is empty, need to step forwards */ + if (pattern[0] == '\0' && n) { - /* Need to copy the char we are moving over */ + Jim_AppendString(interp, resultObj, p, 1); p++; n--; } - + regexec_flags |= REG_NOTBOL; } while (n); - /* - * Copy the portion of the string after the last match to the - * result variable. - */ Jim_AppendString(interp, resultObj, p, -1); - /* And now set or return the result variable */ + if (argc - i == 4) { result = Jim_SetVariable(interp, varname, resultObj); if (result == JIM_OK) { Jim_SetResultInt(interp, num_matches); @@ -3913,142 +2909,82 @@ Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL); Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL); return JIM_OK; } -/* - * Implements the file command for jim - * - * (c) 2008 Steve Bennett <steveb@workware.net.au> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of the Jim Tcl Project. - * - * Based on code originally from Tcl 6.7: - * - * Copyright 1987-1991 Regents of the University of California - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without - * fee is hereby granted, provided that the above copyright - * notice appear in all copies. The University of California - * makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without - * express or implied warranty. - */ #include <limits.h> #include <stdlib.h> #include <string.h> #include <stdio.h> -#include <unistd.h> #include <errno.h> #include <sys/stat.h> -#include <sys/param.h> + +#ifdef HAVE_UTIMES +#include <sys/time.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#elif defined(_MSC_VER) +#include <direct.h> +#define F_OK 0 +#define W_OK 2 +#define R_OK 4 +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif # ifndef MAXPATHLEN # define MAXPATHLEN JIM_PATH_LEN # endif -/* - *---------------------------------------------------------------------- - * - * JimGetFileType -- - * - * Given a mode word, returns a string identifying the type of a - * file. - * - * Results: - * A static text string giving the file type from mode. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ static const char *JimGetFileType(int mode) { if (S_ISREG(mode)) { return "file"; } else if (S_ISDIR(mode)) { return "directory"; } +#ifdef S_ISCHR else if (S_ISCHR(mode)) { return "characterSpecial"; } +#endif +#ifdef S_ISBLK else if (S_ISBLK(mode)) { return "blockSpecial"; } +#endif +#ifdef S_ISFIFO else if (S_ISFIFO(mode)) { return "fifo"; + } +#endif #ifdef S_ISLNK - } else if (S_ISLNK(mode)) { return "link"; + } #endif #ifdef S_ISSOCK - } else if (S_ISSOCK(mode)) { return "socket"; + } #endif - } return "unknown"; } -/* - *---------------------------------------------------------------------- - * - * StoreStatData -- - * - * This is a utility procedure that breaks out the fields of a - * "stat" structure and stores them in textual form into the - * elements of an associative array. - * - * Results: - * Returns a standard Tcl return value. If an error occurs then - * a message is left in interp->result. - * - * Side effects: - * Elements of the associative array given by "varName" are modified. - * - *---------------------------------------------------------------------- - */ static int set_array_int_value(Jim_Interp *interp, Jim_Obj *container, const char *key, jim_wide value) { Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1); Jim_Obj *valobj = Jim_NewWideObj(interp, value); - if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj) != JIM_OK) { + if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) { Jim_FreeObj(interp, nameobj); Jim_FreeObj(interp, valobj); return JIM_ERR; } return JIM_OK; @@ -4058,11 +2994,11 @@ const char *value) { Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1); Jim_Obj *valobj = Jim_NewStringObj(interp, value, -1); - if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj) != JIM_OK) { + if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) { Jim_FreeObj(interp, nameobj); Jim_FreeObj(interp, valobj); return JIM_ERR; } return JIM_OK; @@ -4083,11 +3019,11 @@ set_array_int_value(interp, varName, "atime", sb->st_atime); set_array_int_value(interp, varName, "mtime", sb->st_mtime); set_array_int_value(interp, varName, "ctime", sb->st_ctime); set_array_string_value(interp, varName, "type", JimGetFileType((int)sb->st_mode)); - /* And also return the value */ + Jim_SetResult(interp, Jim_GetVariable(interp, varName, 0)); return JIM_OK; } @@ -4100,13 +3036,13 @@ Jim_SetResultString(interp, ".", -1); } else if (p == path) { Jim_SetResultString(interp, "/", -1); } -#if defined(__MINGW32__) +#if defined(__MINGW32__) || defined(_MSC_VER) else if (p[-1] == ':') { - /* z:/dir => z:/ */ + Jim_SetResultString(interp, path, p - path + 1); } #endif else { Jim_SetResultString(interp, path, p - path); @@ -4182,37 +3118,37 @@ char *newname = Jim_Alloc(MAXPATHLEN + 1); char *last = newname; *newname = 0; - /* Simple implementation for now */ + for (i = 0; i < argc; i++) { int len; const char *part = Jim_GetString(argv[i], &len); if (*part == '/') { - /* Absolute component, so go back to the start */ + last = newname; } -#if defined(__MINGW32__) +#if defined(__MINGW32__) || defined(_MSC_VER) else if (strchr(part, ':')) { - /* Absolute compontent on mingw, so go back to the start */ + last = newname; } #endif else if (part[0] == '.') { if (part[1] == '/') { part += 2; len -= 2; } else if (part[1] == 0 && last != newname) { - /* Adding '.' to an existing path does nothing */ + continue; } } - /* Add a slash if needed */ + if (last != newname && last[-1] != '/') { *last++ = '/'; } if (len) { @@ -4223,19 +3159,19 @@ } memcpy(last, part, len); last += len; } - /* Remove a slash if needed */ + if (last > newname + 1 && last[-1] == '/') { *--last = 0; } } *last = 0; - /* Probably need to handle some special cases ... */ + Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname)); return JIM_OK; } @@ -4260,11 +3196,16 @@ return file_access(interp, argv[0], W_OK); } static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { +#ifdef X_OK return file_access(interp, argv[0], X_OK); +#else + Jim_SetResultBool(interp, 1); + return JIM_OK; +#endif } static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { return file_access(interp, argv[0], F_OK); @@ -4282,12 +3223,12 @@ while (argc--) { const char *path = Jim_String(argv[0]); if (unlink(path) == -1 && errno != ENOENT) { if (rmdir(path) == -1) { - /* Maybe try using the script helper */ - if (!force || Jim_EvalObjPrefix(interp, "file delete force", 1, argv) != JIM_OK) { + + if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) { Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path, strerror(errno)); return JIM_ERR; } } @@ -4301,26 +3242,19 @@ #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME) #else #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755) #endif -/** - * Create directory, creating all intermediate paths if necessary. - * - * Returns 0 if OK or -1 on failure (and sets errno) - * - * Note: The path may be modified. - */ static int mkdir_all(char *path) { int ok = 1; - /* First time just try to make the dir */ + goto first; while (ok--) { - /* Must have failed the first time, so recursively make the parent and try again */ + char *slash = strrchr(path, '/'); if (slash && slash != path) { *slash = 0; if (mkdir_all(path) != 0) { @@ -4331,24 +3265,24 @@ first: if (MKDIR_DEFAULT(path) == 0) { return 0; } if (errno == ENOENT) { - /* Create the parent and try again */ + continue; } - /* Maybe it already exists as a directory */ + if (errno == EEXIST) { struct stat sb; if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { return 0; } - /* Restore errno */ + errno = EEXIST; } - /* Failed */ + break; } return -1; } @@ -4465,20 +3399,41 @@ 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) { + return JIM_ERR; + } + + times[1].tv_sec = times[0].tv_sec = newtime; + times[1].tv_usec = times[0].tv_usec = 0; + + if (utimes(Jim_String(argv[0]), times) != 0) { + Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno)); + return JIM_ERR; + } +#else + Jim_SetResultString(interp, "Not implemented", -1); + return JIM_ERR; +#endif + } if (file_stat(interp, argv[0], &sb) != JIM_OK) { return JIM_ERR; } Jim_SetResultInt(interp, sb.st_mtime); return JIM_OK; } static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - return Jim_EvalObjPrefix(interp, "file copy", argc, argv); + return Jim_EvalPrefix(interp, "file copy", argc, argv); } static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { struct stat sb; @@ -4536,11 +3491,11 @@ int linkLength = readlink(path, linkValue, MAXPATHLEN); if (linkLength == -1) { Jim_Free(linkValue); - Jim_SetResultFormatted(interp, "couldn't readlink \"%s\": %s", argv[0], strerror(errno)); + Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno)); return JIM_ERR; } linkValue[linkLength] = 0; Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength)); return JIM_OK; @@ -4577,193 +3532,193 @@ } return StoreStatData(interp, argv[1], &sb); } static const jim_subcmd_type file_command_table[] = { - { .cmd = "atime", - .args = "name", - .function = file_cmd_atime, - .minargs = 1, - .maxargs = 1, - .description = "Last access time" - }, - { .cmd = "mtime", - .args = "name", - .function = file_cmd_mtime, - .minargs = 1, - .maxargs = 1, - .description = "Last modification time" - }, - { .cmd = "copy", - .args = "?-force? source dest", - .function = file_cmd_copy, - .minargs = 2, - .maxargs = 3, - .description = "Copy source file to destination file" - }, - { .cmd = "dirname", - .args = "name", - .function = file_cmd_dirname, - .minargs = 1, - .maxargs = 1, - .description = "Directory part of the name" - }, - { .cmd = "rootname", - .args = "name", - .function = file_cmd_rootname, - .minargs = 1, - .maxargs = 1, - .description = "Name without any extension" - }, - { .cmd = "extension", - .args = "name", - .function = file_cmd_extension, - .minargs = 1, - .maxargs = 1, - .description = "Last extension including the dot" - }, - { .cmd = "tail", - .args = "name", - .function = file_cmd_tail, - .minargs = 1, - .maxargs = 1, - .description = "Last component of the name" - }, - { .cmd = "normalize", - .args = "name", - .function = file_cmd_normalize, - .minargs = 1, - .maxargs = 1, - .description = "Normalized path of name" - }, - { .cmd = "join", - .args = "name ?name ...?", - .function = file_cmd_join, - .minargs = 1, - .maxargs = -1, - .description = "Join multiple path components" - }, - { .cmd = "readable", - .args = "name", - .function = file_cmd_readable, - .minargs = 1, - .maxargs = 1, - .description = "Is file readable" - }, - { .cmd = "writable", - .args = "name", - .function = file_cmd_writable, - .minargs = 1, - .maxargs = 1, - .description = "Is file writable" - }, - { .cmd = "executable", - .args = "name", - .function = file_cmd_executable, - .minargs = 1, - .maxargs = 1, - .description = "Is file executable" - }, - { .cmd = "exists", - .args = "name", - .function = file_cmd_exists, - .minargs = 1, - .maxargs = 1, - .description = "Does file exist" - }, - { .cmd = "delete", - .args = "?-force|--? name ...", - .function = file_cmd_delete, - .minargs = 1, - .maxargs = -1, - .description = "Deletes the files or directories (must be empty unless -force)" - }, - { .cmd = "mkdir", - .args = "dir ...", - .function = file_cmd_mkdir, - .minargs = 1, - .maxargs = -1, - .description = "Creates the directories" + { "atime", + "name", + file_cmd_atime, + 1, + 1, + + }, + { "mtime", + "name ?time?", + file_cmd_mtime, + 1, + 2, + + }, + { "copy", + "?-force? source dest", + file_cmd_copy, + 2, + 3, + + }, + { "dirname", + "name", + file_cmd_dirname, + 1, + 1, + + }, + { "rootname", + "name", + file_cmd_rootname, + 1, + 1, + + }, + { "extension", + "name", + file_cmd_extension, + 1, + 1, + + }, + { "tail", + "name", + file_cmd_tail, + 1, + 1, + + }, + { "normalize", + "name", + file_cmd_normalize, + 1, + 1, + + }, + { "join", + "name ?name ...?", + file_cmd_join, + 1, + -1, + + }, + { "readable", + "name", + file_cmd_readable, + 1, + 1, + + }, + { "writable", + "name", + file_cmd_writable, + 1, + 1, + + }, + { "executable", + "name", + file_cmd_executable, + 1, + 1, + + }, + { "exists", + "name", + file_cmd_exists, + 1, + 1, + + }, + { "delete", + "?-force|--? name ...", + file_cmd_delete, + 1, + -1, + + }, + { "mkdir", + "dir ...", + file_cmd_mkdir, + 1, + -1, + }, #ifdef HAVE_MKSTEMP - { .cmd = "tempfile", - .args = "?template?", - .function = file_cmd_tempfile, - .minargs = 0, - .maxargs = 1, - .description = "Creates a temporary filename" + { "tempfile", + "?template?", + file_cmd_tempfile, + 0, + 1, + }, #endif - { .cmd = "rename", - .args = "?-force? source dest", - .function = file_cmd_rename, - .minargs = 2, - .maxargs = 3, - .description = "Renames a file" + { "rename", + "?-force? source dest", + file_cmd_rename, + 2, + 3, + }, #if defined(HAVE_READLINK) - { .cmd = "readlink", - .args = "name", - .function = file_cmd_readlink, - .minargs = 1, - .maxargs = 1, - .description = "Value of the symbolic link" + { "readlink", + "name", + file_cmd_readlink, + 1, + 1, + }, #endif - { .cmd = "size", - .args = "name", - .function = file_cmd_size, - .minargs = 1, - .maxargs = 1, - .description = "Size of file" + { "size", + "name", + file_cmd_size, + 1, + 1, + + }, + { "stat", + "name var", + file_cmd_stat, + 2, + 2, + }, - { .cmd = "stat", - .args = "name var", - .function = file_cmd_stat, - .minargs = 2, - .maxargs = 2, - .description = "Stores results of stat in var array" + { "lstat", + "name var", + file_cmd_lstat, + 2, + 2, + }, - { .cmd = "lstat", - .args = "name var", - .function = file_cmd_lstat, - .minargs = 2, - .maxargs = 2, - .description = "Stores results of lstat in var array" - }, - { .cmd = "type", - .args = "name", - .function = file_cmd_type, - .minargs = 1, - .maxargs = 1, - .description = "Returns type of the file" + { "type", + "name", + file_cmd_type, + 1, + 1, + }, #ifdef HAVE_GETEUID - { .cmd = "owned", - .args = "name", - .function = file_cmd_owned, - .minargs = 1, - .maxargs = 1, - .description = "Returns 1 if owned by the current owner" + { "owned", + "name", + file_cmd_owned, + 1, + 1, + }, #endif - { .cmd = "isdirectory", - .args = "name", - .function = file_cmd_isdirectory, - .minargs = 1, - .maxargs = 1, - .description = "Returns 1 if name is a directory" + { "isdirectory", + "name", + file_cmd_isdirectory, + 1, + 1, + }, - { .cmd = "isfile", - .args = "name", - .function = file_cmd_isfile, - .minargs = 1, - .maxargs = 1, - .description = "Returns 1 if name is a file" + { "isfile", + "name", + file_cmd_isfile, + 1, + 1, + }, { - .cmd = 0 + NULL } }; static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { @@ -4791,13 +3746,13 @@ if (getcwd(cwd, cwd_len) == NULL) { Jim_SetResultString(interp, "Failed to get pwd", -1); return JIM_ERR; } -#if defined(__MINGW32__) +#if defined(__MINGW32__) || defined(_MSC_VER) { - /* Try to keep backlashes out of paths */ + char *p = cwd; while ((p = strchr(p, '\\')) != NULL) { *p++ = '/'; } } @@ -4818,54 +3773,142 @@ Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL); Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL); return JIM_OK; } -/* - * (c) 2008 Steve Bennett <steveb@workware.net.au> - * - * Implements the exec command for Jim - * - * Based on code originally from Tcl 6.7 by John Ousterhout. - * From that code: - * - * The Tcl_Fork and Tcl_WaitPids procedures are based on code - * contributed by Karl Lehenbauer, Mark Diekhans and Peter - * da Silva. - * - * Copyright 1987-1991 Regents of the University of California - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without - * fee is hereby granted, provided that the above copyright - * notice appear in all copies. The University of California - * makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without - * express or implied warranty. - */ - #include <string.h> +#include <ctype.h> + + +#if (!defined(HAVE_VFORK) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__) +static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp); + int i, j; + int rc; + + + for (i = 1; i < argc; i++) { + int len; + const char *arg = Jim_GetString(argv[i], &len); + + if (i > 1) { + Jim_AppendString(interp, cmdlineObj, " ", 1); + } + if (strpbrk(arg, "\\\" ") == NULL) { + + Jim_AppendString(interp, cmdlineObj, arg, len); + continue; + } + + Jim_AppendString(interp, cmdlineObj, "\"", 1); + for (j = 0; j < len; j++) { + if (arg[j] == '\\' || arg[j] == '"') { + Jim_AppendString(interp, cmdlineObj, "\\", 1); + } + Jim_AppendString(interp, cmdlineObj, &arg[j], 1); + } + Jim_AppendString(interp, cmdlineObj, "\"", 1); + } + rc = system(Jim_String(cmdlineObj)); + + Jim_FreeNewObj(interp, cmdlineObj); + + if (rc) { + Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0); + Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc)); + Jim_SetGlobalVariableStr(interp, "errorCode", errorCode); + return JIM_ERR; + } + + return JIM_OK; +} + +int Jim_execInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG)) + return JIM_ERR; + Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL); + return JIM_OK; +} +#else + + +#include <errno.h> #include <signal.h> - -#if defined(HAVE_VFORK) && defined(HAVE_WAITPID) - - -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/wait.h> - -#if defined(__GNUC__) && !defined(__clang__) -#define IGNORE_RC(EXPR) ((EXPR) < 0 ? -1 : 0) -#else -#define IGNORE_RC(EXPR) EXPR -#endif - -/* These two could be moved into the Tcl core */ +#if defined(__MINGW32__) + + #ifndef STRICT + #define STRICT + #endif + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + #include <fcntl.h> + + 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 <unistd.h> + #include <fcntl.h> + #include <sys/wait.h> + + 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) +#endif + +static const char *JimStrError(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); +static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId); +static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents); +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, strerror(errno)); + Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError()); +} + +static const char *JimStrError(void) +{ + return strerror(JimErrno()); } static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr) { int len; @@ -4875,40 +3918,32 @@ objPtr->length--; objPtr->bytes[objPtr->length] = '\0'; } } -/** - * Read from 'fd' and append the data to strObj - * Returns JIM_OK if OK, or JIM_ERR on error. - */ -static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj) +static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj) { + char buf[256]; + FILE *fh = JimFdOpenForRead(fd); + if (fh == NULL) { + return JIM_ERR; + } + while (1) { - char buffer[256]; - int count; - - count = read(fd, buffer, sizeof(buffer)); - - if (count == 0) { - Jim_RemoveTrailingNewline(strObj); - return JIM_OK; + int retval = fread(buf, 1, sizeof(buf), fh); + if (retval > 0) { + Jim_AppendString(interp, strObj, buf, retval); } - if (count < 0) { - return JIM_ERR; + if (retval != sizeof(buf)) { + break; } - Jim_AppendString(interp, strObj, buffer, count); } + Jim_RemoveTrailingNewline(strObj); + fclose(fh); + return JIM_OK; } -/* - * If the last character of the result is a newline, then remove - * the newline character (the newline would just confuse things). - * - * Note: Ideally we could do this by just reducing the length of stringrep - * by 1, but there is no API for this :-( - */ static void JimTrimTrailingNewline(Jim_Interp *interp) { int len; const char *p = Jim_GetString(Jim_GetResult(interp), &len); @@ -4915,89 +3950,72 @@ if (len > 0 && p[len - 1] == '\n') { Jim_SetResultString(interp, p, len - 1); } } -/** - * Builds the environment array from $::env - * - * If $::env is not set, simply returns environ. - * - * Otherwise allocates the environ array from the contents of $::env - * - * If the exec fails, memory can be freed via JimFreeEnv() - */ static char **JimBuildEnv(Jim_Interp *interp) { -#ifdef jim_ext_tclcompat +#if defined(jim_ext_tclcompat) int i; - int len; + int size; + int num; int n; - char **env; + char **envptr; + char *envdata; Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE); if (!objPtr) { return Jim_GetEnviron(); } - /* Calculate the required size */ - len = Jim_ListLength(interp, objPtr); - if (len % 2) { - len--; + + + num = Jim_ListLength(interp, objPtr); + if (num % 2) { + num--; } + size = Jim_Length(objPtr); + size++; - env = Jim_Alloc(sizeof(*env) * (len / 2 + 1)); + envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size); + envdata = (char *)&envptr[num / 2 + 1]; n = 0; - for (i = 0; i < len; i += 2) { - int l1, l2; + for (i = 0; i < num; i += 2) { const char *s1, *s2; Jim_Obj *elemObj; Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE); - s1 = Jim_GetString(elemObj, &l1); + s1 = Jim_String(elemObj); Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE); - s2 = Jim_GetString(elemObj, &l2); + s2 = Jim_String(elemObj); - env[n] = Jim_Alloc(l1 + l2 + 2); - sprintf(env[n], "%s=%s", s1, s2); + envptr[n] = envdata; + envdata += sprintf(envdata, "%s=%s", s1, s2); + envdata++; n++; } - env[n] = NULL; + envptr[n] = NULL; + *envdata = 0; - return env; + return envptr; #else return Jim_GetEnviron(); #endif } -/** - * Frees the environment allocated by JimBuildEnv() - * - * Must pass original_environ. - */ -static void JimFreeEnv(Jim_Interp *interp, char **env, char **original_environ) +static void JimFreeEnv(char **env, char **original_environ) { #ifdef jim_ext_tclcompat if (env != original_environ) { - int i; - for (i = 0; env[i]; i++) { - Jim_Free(env[i]); - } Jim_Free(env); } #endif } -/* - * Create error messages for unusual process exits. An - * extra newline gets appended to each error message, but - * it gets removed below (in the same fashion that an - * extra newline in the command's output is removed). - */ -static int JimCheckWaitStatus(Jim_Interp *interp, int pid, int waitStatus) +static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus) { Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0); int rc = JIM_ERR; if (WIFEXITED(waitStatus)) { @@ -5005,11 +4023,11 @@ Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1)); rc = JIM_OK; } else { Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1)); - Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid)); Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus))); } } else { const char *type; @@ -5032,43 +4050,32 @@ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid)); Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1)); #else Jim_SetResultFormatted(interp, "child %s by signal %d", action, WTERMSIG(waitStatus)); Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus))); - Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid)); + Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid)); Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WTERMSIG(waitStatus))); #endif } Jim_SetGlobalVariableStr(interp, "errorCode", errorCode); return rc; } -/* - * Data structures of the following type are used by JimFork and - * JimWaitPids to keep track of child processes. - */ struct WaitInfo { - int pid; /* Process id of child. */ - int status; /* Status returned when child exited or suspended. */ - int flags; /* Various flag bits; see below for definitions. */ + pidtype pid; + int status; + int flags; }; struct WaitInfoTable { struct WaitInfo *info; int size; int used; }; -/* - * Flag bits in WaitInfo structures: - * - * WI_DETACHED - Non-zero means no-one cares about the - * process anymore. Ignore it until it - * exits, then forget about it. - */ #define WI_DETACHED 2 #define WAIT_TABLE_GROW_BY 4 @@ -5087,78 +4094,63 @@ table->size = table->used = 0; return table; } -static int Jim_CreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, - int **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr); -static void JimDetachPids(Jim_Interp *interp, int numPids, const int *pidPtr); -static int Jim_CleanupChildren(Jim_Interp *interp, int numPids, int *pidPtr, int errorId); - static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - int outputId; /* File id for output pipe. -1 + fdtype outputId; /* File id for output pipe. -1 * means command overrode. */ - int errorId; /* File id for temporary file + fdtype errorId; /* File id for temporary file * containing error output. */ - int *pidPtr; + pidtype *pidPtr; int numPids, result; - /* - * See if the command is to be run in background; if so, create - * the command, detach it, and return. - */ if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) { Jim_Obj *listObj; int i; argc--; - numPids = Jim_CreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL); + numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL); if (numPids < 0) { return JIM_ERR; } - /* The return value is a list of the pids */ + listObj = Jim_NewListObj(interp, NULL, 0); for (i = 0; i < numPids; i++) { - Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, pidPtr[i])); + Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i])); } Jim_SetResult(interp, listObj); JimDetachPids(interp, numPids, pidPtr); Jim_Free(pidPtr); return JIM_OK; } - /* - * Create the command's pipeline. - */ numPids = - Jim_CreatePipeline(interp, argc - 1, argv + 1, &pidPtr, (int *)NULL, &outputId, &errorId); + JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId); + if (numPids < 0) { return JIM_ERR; } - /* - * Read the child's output (if any) and put it into the result. - */ Jim_SetResultString(interp, "", 0); result = JIM_OK; - if (outputId != -1) { + if (outputId != JIM_BAD_FD) { result = JimAppendStreamToString(interp, outputId, Jim_GetResult(interp)); if (result < 0) { Jim_SetResultErrno(interp, "error reading from output pipe"); } - close(outputId); } - if (Jim_CleanupChildren(interp, numPids, pidPtr, errorId) != JIM_OK) { + if (JimCleanupChildren(interp, numPids, pidPtr, errorId) != JIM_OK) { result = JIM_ERR; } return result; } -void Jim_ReapDetachedPids(struct WaitInfoTable *table) +static void JimReapDetachedPids(struct WaitInfoTable *table) { struct WaitInfo *waitPtr; int count; if (!table) { @@ -5166,76 +4158,52 @@ } for (waitPtr = table->info, count = table->used; count > 0; waitPtr++, count--) { if (waitPtr->flags & WI_DETACHED) { int status; - int pid = waitpid(waitPtr->pid, &status, WNOHANG); - if (pid > 0) { + pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG); + if (pid != JIM_BAD_PID) { if (waitPtr != &table->info[table->used - 1]) { *waitPtr = table->info[table->used - 1]; } table->used--; } } } } -/** - * Does waitpid() on the given pid, and then removes the - * entry from the wait table. - * - * Returns the pid if OK and updates *statusPtr with the status, - * or -1 if the pid was not in the table. - */ -static int JimWaitPid(struct WaitInfoTable *table, int pid, int *statusPtr) +static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr) { int i; - /* Find it in the table */ + for (i = 0; i < table->used; i++) { if (pid == table->info[i].pid) { - /* wait for it */ - waitpid(pid, statusPtr, 0); + + JimWaitPid(pid, statusPtr, 0); - /* Remove it from the table */ + if (i != table->used - 1) { table->info[i] = table->info[table->used - 1]; } table->used--; return pid; } } - /* Not found */ - return -1; + + return JIM_BAD_PID; } -/* - *---------------------------------------------------------------------- - * - * JimDetachPids -- - * - * This procedure is called to indicate that one or more child - * processes have been placed in background and are no longer - * cared about. These children can be cleaned up with JimReapDetachedPids(). - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ -static void JimDetachPids(Jim_Interp *interp, int numPids, const int *pidPtr) +static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr) { int j; struct WaitInfoTable *table = Jim_CmdPrivData(interp); for (j = 0; j < numPids; j++) { - /* Find it in the table */ + int i; for (i = 0; i < table->used; i++) { if (pidPtr[j] == table->info[i].pid) { table->info[i].flags |= WI_DETACHED; break; @@ -5242,56 +4210,28 @@ } } } } -/* - *---------------------------------------------------------------------- - * - * Jim_CreatePipeline -- - * - * Given an argc/argv array, instantiate a pipeline of processes - * as described by the argv. - * - * Results: - * The return value is a count of the number of new processes - * created, or -1 if an error occurred while creating the pipeline. - * *pidArrayPtr is filled in with the address of a dynamically - * allocated array giving the ids of all of the processes. It - * is up to the caller to free this array when it isn't needed - * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in - * with the file id for the input pipe for the pipeline (if any): - * the caller must eventually close this file. If outPipePtr - * isn't NULL, then *outPipePtr is filled in with the file id - * for the output pipe from the pipeline: the caller must close - * this file. If errFilePtr isn't NULL, then *errFilePtr is filled - * with a file id that may be used to read error output after the - * pipeline completes. - * - * Side effects: - * Processes and pipes are created. - * - *---------------------------------------------------------------------- - */ static int -Jim_CreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int **pidArrayPtr, - int *inPipePtr, int *outPipePtr, int *errFilePtr) +JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr, + fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr) { - int *pidPtr = NULL; /* Points to malloc-ed array holding all + pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all * the pids of child processes. */ int numPids = 0; /* Actual number of processes that exist * at *pidPtr right now. */ int cmdCount; /* Count of number of distinct commands * found in argc/argv. */ const char *input = NULL; /* Describes input for pipeline, depending * on "inputFile". NULL means take input * from stdin/pipe. */ -#define FILE_NAME 0 /* input/output: filename */ -#define FILE_APPEND 1 /* output only: filename, append */ -#define FILE_HANDLE 2 /* input/output: filehandle */ -#define FILE_TEXT 3 /* input only: input is actual text */ +#define FILE_NAME 0 +#define FILE_APPEND 1 +#define FILE_HANDLE 2 +#define FILE_TEXT 3 int inputFile = FILE_NAME; /* 1 means input is name of input file. * 2 means input is filehandle name. * 0 means input holds actual * text to be input to command. */ @@ -5308,55 +4248,40 @@ */ const char *output = NULL; /* Holds name of output file to pipe to, * 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. */ - int inputId = -1; /* Readable file id input to current command in - * pipeline (could be file or pipe). -1 - * means use stdin. */ - int outputId = -1; /* Writable file id for output from current - * command in pipeline (could be file or pipe). - * -1 means use stdout. */ - int errorId = -1; /* Writable file id for all standard error - * output from all commands in pipeline. -1 - * means use stderr. */ - int lastOutputId = -1; /* Write file id for output from last command - * in pipeline (could be file or pipe). - * -1 means use stdout. */ - int pipeIds[2]; /* File ids for pipe that's being created. */ + fdtype inputId = JIM_BAD_FD; + fdtype outputId = JIM_BAD_FD; + fdtype errorId = JIM_BAD_FD; + fdtype lastOutputId = JIM_BAD_FD; + fdtype pipeIds[2]; int firstArg, lastArg; /* Indexes of first and last arguments in * current command. */ int lastBar; - char *execName; - int i, pid; - char **orig_environ; + int i; + pidtype pid; + char **save_environ; struct WaitInfoTable *table = Jim_CmdPrivData(interp); - /* Holds the args which will be used to exec */ + char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1)); int arg_count = 0; - Jim_ReapDetachedPids(table); + JimReapDetachedPids(table); if (inPipePtr != NULL) { - *inPipePtr = -1; + *inPipePtr = JIM_BAD_FD; } if (outPipePtr != NULL) { - *outPipePtr = -1; + *outPipePtr = JIM_BAD_FD; } if (errFilePtr != NULL) { - *errFilePtr = -1; + *errFilePtr = JIM_BAD_FD; } - pipeIds[0] = pipeIds[1] = -1; + pipeIds[0] = pipeIds[1] = JIM_BAD_FD; - /* - * First, scan through all the arguments to figure out the structure - * of the pipeline. Count the number of distinct processes (it's the - * number of "|" arguments). If there are "<", "<<", or ">" arguments - * then make note of input and output redirection and remove these - * arguments and the arguments that follow them. - */ cmdCount = 1; lastBar = -1; for (i = 0; i < argc; i++) { const char *arg = Jim_String(argv[i]); @@ -5385,11 +4310,11 @@ if (*output == '>') { outputFile = FILE_APPEND; output++; } if (*output == '&') { - /* Redirect stderr too */ + output++; dup_error = 1; } if (*output == '@') { outputFile = FILE_HANDLE; @@ -5426,11 +4351,11 @@ goto badargs; } lastBar = i; cmdCount++; } - /* Either |, |& or a "normal" arg, so store it in the arg array */ + arg_array[arg_count++] = (char *)arg; continue; } if (i >= argc) { @@ -5444,82 +4369,49 @@ badargs: Jim_Free(arg_array); return -1; } - /* Must do this before vfork(), so do it now */ - orig_environ = Jim_GetEnviron(); - Jim_SetEnviron(JimBuildEnv(interp)); + + save_environ = JimSaveEnv(JimBuildEnv(interp)); - /* - * Set up the redirected input source for the pipeline, if - * so requested. - */ if (input != NULL) { if (inputFile == FILE_TEXT) { - /* - * Immediate data in command. Create temporary file and - * put data into file. - */ - -#define TMP_STDIN_NAME "/tmp/tcl.in.XXXXXX" - char inName[sizeof(TMP_STDIN_NAME) + 1]; - int length; - - strcpy(inName, TMP_STDIN_NAME); - inputId = mkstemp(inName); - if (inputId < 0) { - Jim_SetResultErrno(interp, "couldn't create input file for command"); - goto error; - } - length = strlen(input); - if (write(inputId, input, length) != length) { - Jim_SetResultErrno(interp, "couldn't write file input for command"); - goto error; - } - if (lseek(inputId, 0L, SEEK_SET) == -1 || unlink(inName) == -1) { - Jim_SetResultErrno(interp, "couldn't reset or remove input file for command"); + inputId = JimCreateTemp(interp, input); + if (inputId == JIM_BAD_FD) { goto error; } } else if (inputFile == FILE_HANDLE) { - /* Should be a file descriptor */ + Jim_Obj *fhObj = Jim_NewStringObj(interp, input, -1); FILE *fh = Jim_AioFilehandle(interp, fhObj); Jim_FreeNewObj(interp, fhObj); if (fh == NULL) { goto error; } - inputId = dup(fileno(fh)); + inputId = JimDupFd(JimFileno(fh)); } else { - /* - * File redirection. Just open the file. - */ - inputId = open(input, O_RDONLY, 0); - if (inputId < 0) { - Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, - strerror(errno)); + inputId = JimOpenForRead(input); + if (inputId == JIM_BAD_FD) { + Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError()); goto error; } } } else if (inPipePtr != NULL) { - if (pipe(pipeIds) != 0) { + if (JimPipe(pipeIds) != 0) { Jim_SetResultErrno(interp, "couldn't create input pipe for command"); goto error; } inputId = pipeIds[0]; *inPipePtr = pipeIds[1]; - pipeIds[0] = pipeIds[1] = -1; + pipeIds[0] = pipeIds[1] = JIM_BAD_FD; } - /* - * Set up the redirected output sink for the pipeline from one - * of two places, if requested. - */ if (output != NULL) { if (outputFile == FILE_HANDLE) { Jim_Obj *fhObj = Jim_NewStringObj(interp, output, -1); FILE *fh = Jim_AioFilehandle(interp, fhObj); @@ -5526,207 +4418,144 @@ Jim_FreeNewObj(interp, fhObj); if (fh == NULL) { goto error; } fflush(fh); - lastOutputId = dup(fileno(fh)); + lastOutputId = JimDupFd(JimFileno(fh)); } else { - /* - * Output is to go to a file. - */ - int mode = O_WRONLY | O_CREAT | O_TRUNC; - - if (outputFile == FILE_APPEND) { - mode = O_WRONLY | O_CREAT | O_APPEND; - } - - lastOutputId = open(output, mode, 0666); - if (lastOutputId < 0) { - Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, - strerror(errno)); + lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND); + if (lastOutputId == JIM_BAD_FD) { + Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError()); goto error; } } } else if (outPipePtr != NULL) { - /* - * Output is to go to a pipe. - */ - if (pipe(pipeIds) != 0) { + if (JimPipe(pipeIds) != 0) { Jim_SetResultErrno(interp, "couldn't create output pipe"); goto error; } lastOutputId = pipeIds[1]; *outPipePtr = pipeIds[0]; - pipeIds[0] = pipeIds[1] = -1; + pipeIds[0] = pipeIds[1] = JIM_BAD_FD; } - - /* If we are redirecting stderr with 2>filename or 2>@fileId, then we ignore errFilePtr */ + if (error != NULL) { if (errorFile == FILE_HANDLE) { if (strcmp(error, "1") == 0) { - /* Special 2>@1 */ - if (lastOutputId >= 0) { - errorId = dup(lastOutputId); + + if (lastOutputId != JIM_BAD_FD) { + errorId = JimDupFd(lastOutputId); } else { - /* No redirection of stdout, so just use 2>@stdout */ + error = "stdout"; } } - if (errorId < 0) { + if (errorId == JIM_BAD_FD) { Jim_Obj *fhObj = Jim_NewStringObj(interp, error, -1); FILE *fh = Jim_AioFilehandle(interp, fhObj); Jim_FreeNewObj(interp, fhObj); if (fh == NULL) { goto error; } fflush(fh); - errorId = dup(fileno(fh)); + errorId = JimDupFd(JimFileno(fh)); } } else { - /* - * Output is to go to a file. - */ - int mode = O_WRONLY | O_CREAT | O_TRUNC; - - if (errorFile == FILE_APPEND) { - mode = O_WRONLY | O_CREAT | O_APPEND; - } - - errorId = open(error, mode, 0666); - if (errorId < 0) { - Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, - strerror(errno)); + errorId = JimOpenForWrite(error, errorFile == FILE_APPEND); + if (errorId == JIM_BAD_FD) { + Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError()); + goto error; } } } else if (errFilePtr != NULL) { - /* - * Set up the standard error output sink for the pipeline, if - * requested. Use a temporary file which is opened, then deleted. - * Could potentially just use pipe, but if it filled up it could - * cause the pipeline to deadlock: we'd be waiting for processes - * to complete before reading stderr, and processes couldn't complete - * because stderr was backed up. - */ - -#define TMP_STDERR_NAME "/tmp/tcl.err.XXXXXX" - char errName[sizeof(TMP_STDERR_NAME) + 1]; - - strcpy(errName, TMP_STDERR_NAME); - errorId = mkstemp(errName); - if (errorId < 0) { - errFileError: - Jim_SetResultErrno(interp, "couldn't create error file for command"); + errorId = JimCreateTemp(interp, NULL); + if (errorId == JIM_BAD_FD) { goto error; } - *errFilePtr = open(errName, O_RDONLY, 0); - if (*errFilePtr < 0) { - goto errFileError; - } - if (unlink(errName) == -1) { - Jim_SetResultErrno(interp, "couldn't remove error file for command"); - goto error; - } + *errFilePtr = JimDupFd(errorId); } - /* - * Scan through the argc array, forking off a process for each - * group of arguments between "|" arguments. - */ - pidPtr = (int *)Jim_Alloc(cmdCount * sizeof(*pidPtr)); + pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr)); for (i = 0; i < numPids; i++) { - pidPtr[i] = -1; + pidPtr[i] = JIM_BAD_PID; } for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) { int pipe_dup_err = 0; - int origErrorId = errorId; - char execerr[64]; - int execerrlen; + fdtype origErrorId = errorId; for (lastArg = firstArg; lastArg < arg_count; lastArg++) { if (arg_array[lastArg][0] == '|') { if (arg_array[lastArg][1] == '&') { pipe_dup_err = 1; } break; } } - /* Replace | with NULL for execv() */ + arg_array[lastArg] = NULL; if (lastArg == arg_count) { outputId = lastOutputId; } else { - if (pipe(pipeIds) != 0) { + if (JimPipe(pipeIds) != 0) { Jim_SetResultErrno(interp, "couldn't create pipe"); goto error; } outputId = pipeIds[1]; } - execName = arg_array[firstArg]; - /* Now fork the child */ + - /* - * Disable SIGPIPE signals: if they were allowed, this process - * might go away unexpectedly if children misbehave. This code - * can potentially interfere with other application code that - * expects to handle SIGPIPEs; what's really needed is an - * arbiter for signals to allow them to be "shared". - */ +#ifdef __MINGW32__ + pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId); + if (pid == JIM_BAD_PID) { + Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]); + goto error; + } +#else if (table->info == NULL) { (void)signal(SIGPIPE, SIG_IGN); } - /* Need to do this befor vfork() */ + if (pipe_dup_err) { errorId = outputId; } - /* Need to prep an error message before vfork(), just in case */ - snprintf(execerr, sizeof(execerr), "couldn't exec \"%s\"", execName); - execerrlen = strlen(execerr); - - /* - * Make a new process and enter it into the table if the fork - * is successful. - */ pid = vfork(); if (pid < 0) { Jim_SetResultErrno(interp, "couldn't fork child process"); goto error; } if (pid == 0) { - /* Child */ + if (inputId != -1) dup2(inputId, 0); if (outputId != -1) dup2(outputId, 1); if (errorId != -1) dup2(errorId, 2); for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) { close(i); } - execvp(execName, &arg_array[firstArg]); + execvp(arg_array[firstArg], &arg_array[firstArg]); - /* we really can ignore the error here! */ - IGNORE_RC(write(2, execerr, execerrlen)); + + fprintf(stderr, "couldn't exec \"%s\"", arg_array[firstArg]); _exit(127); } +#endif - /* parent */ + - /* - * Enlarge the wait table if there isn't enough space for a new - * entry. - */ if (table->used == table->size) { table->size += WAIT_TABLE_GROW_BY; table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info)); } @@ -5734,136 +4563,96 @@ table->info[table->used].flags = 0; table->used++; pidPtr[numPids] = pid; - /* Restore in case of pipe_dup_err */ + errorId = origErrorId; - /* - * Close off our copies of file descriptors that were set up for - * this child, then set up the input for the next child. - */ - if (inputId != -1) { - close(inputId); + if (inputId != JIM_BAD_FD) { + JimCloseFd(inputId); } - if (outputId != -1) { - close(outputId); + if (outputId != JIM_BAD_FD) { + JimCloseFd(outputId); } inputId = pipeIds[0]; - pipeIds[0] = pipeIds[1] = -1; + pipeIds[0] = pipeIds[1] = JIM_BAD_FD; } *pidArrayPtr = pidPtr; - /* - * All done. Cleanup open files lying around and then return. - */ cleanup: - if (inputId != -1) { - close(inputId); + if (inputId != JIM_BAD_FD) { + JimCloseFd(inputId); } - if (lastOutputId != -1) { - close(lastOutputId); + if (lastOutputId != JIM_BAD_FD) { + JimCloseFd(lastOutputId); } - if (errorId != -1) { - close(errorId); + if (errorId != JIM_BAD_FD) { + JimCloseFd(errorId); } Jim_Free(arg_array); - JimFreeEnv(interp, Jim_GetEnviron(), orig_environ); - Jim_SetEnviron(orig_environ); + JimRestoreEnv(save_environ); return numPids; - /* - * An error occurred. There could have been extra files open, such - * as pipes between children. Clean them all up. Detach any child - * processes that have been created. - */ error: - if ((inPipePtr != NULL) && (*inPipePtr != -1)) { - close(*inPipePtr); - *inPipePtr = -1; + if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) { + JimCloseFd(*inPipePtr); + *inPipePtr = JIM_BAD_FD; } - if ((outPipePtr != NULL) && (*outPipePtr != -1)) { - close(*outPipePtr); - *outPipePtr = -1; + if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) { + JimCloseFd(*outPipePtr); + *outPipePtr = JIM_BAD_FD; } - if ((errFilePtr != NULL) && (*errFilePtr != -1)) { - close(*errFilePtr); - *errFilePtr = -1; + if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) { + JimCloseFd(*errFilePtr); + *errFilePtr = JIM_BAD_FD; } - if (pipeIds[0] != -1) { - close(pipeIds[0]); + if (pipeIds[0] != JIM_BAD_FD) { + JimCloseFd(pipeIds[0]); } - if (pipeIds[1] != -1) { - close(pipeIds[1]); + if (pipeIds[1] != JIM_BAD_FD) { + JimCloseFd(pipeIds[1]); } if (pidPtr != NULL) { for (i = 0; i < numPids; i++) { - if (pidPtr[i] != -1) { + if (pidPtr[i] != JIM_BAD_PID) { JimDetachPids(interp, 1, &pidPtr[i]); } } Jim_Free(pidPtr); } numPids = -1; goto cleanup; } -/* - *---------------------------------------------------------------------- - * - * CleanupChildren -- - * - * This is a utility procedure used to wait for child processes - * to exit, record information about abnormal exits, and then - * collect any stderr output generated by them. - * - * Results: - * The return value is a standard Tcl result. If anything at - * weird happened with the child processes, JIM_ERROR is returned - * and a message is left in interp->result. - * - * Side effects: - * If the last character of interp->result is a newline, then it - * is removed. File errorId gets closed, and pidPtr is freed - * back to the storage allocator. - * - *---------------------------------------------------------------------- - */ -static int Jim_CleanupChildren(Jim_Interp *interp, int numPids, int *pidPtr, int errorId) +static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId) { struct WaitInfoTable *table = Jim_CmdPrivData(interp); int result = JIM_OK; int i; for (i = 0; i < numPids; i++) { int waitStatus = 0; - if (JimWaitPid(table, pidPtr[i], &waitStatus) > 0) { + if (JimWaitForProcess(table, pidPtr[i], &waitStatus) != JIM_BAD_PID) { if (JimCheckWaitStatus(interp, pidPtr[i], waitStatus) != JIM_OK) { result = JIM_ERR; } } } Jim_Free(pidPtr); - /* - * Read the standard error file. If there's anything there, - * then add the file's contents to the result - * string. - */ - if (errorId >= 0) { + if (errorId != JIM_BAD_FD) { + JimRewindFd(errorId); if (JimAppendStreamToString(interp, errorId, Jim_GetResult(interp)) != JIM_OK) { - Jim_SetResultErrno(interp, "error reading from stderr output file"); result = JIM_ERR; } - close(errorId); } JimTrimTrailingNewline(interp); return result; @@ -5871,96 +4660,486 @@ int Jim_execInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG)) return JIM_ERR; - Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable); return JIM_OK; } -#else -/* e.g. Windows. Poor mans implementation of exec with system() - * The system() call *may* do command line redirection, etc. - * The standard output is not available. - * Can't redirect filehandles. - */ -static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) -{ - Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp); - int i, j; - int rc; - - /* Create a quoted command line */ - for (i = 1; i < argc; i++) { - int len; - const char *arg = Jim_GetString(argv[i], &len); - - if (i > 1) { - Jim_AppendString(interp, cmdlineObj, " ", 1); - } - if (strpbrk(arg, "\\\" ") == NULL) { - /* No quoting required */ - Jim_AppendString(interp, cmdlineObj, arg, len); - continue; - } - - Jim_AppendString(interp, cmdlineObj, "\"", 1); - for (j = 0; j < len; j++) { - if (arg[j] == '\\' || arg[j] == '"') { - Jim_AppendString(interp, cmdlineObj, "\\", 1); - } - Jim_AppendString(interp, cmdlineObj, &arg[j], 1); - } - Jim_AppendString(interp, cmdlineObj, "\"", 1); - } - rc = system(Jim_String(cmdlineObj)); - - Jim_FreeNewObj(interp, cmdlineObj); - - if (rc) { - Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0); - Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1)); - Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0)); - Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc)); - Jim_SetGlobalVariableStr(interp, "errorCode", errorCode); - return JIM_ERR; - } - - return JIM_OK; -} - -int Jim_execInit(Jim_Interp *interp) -{ - if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG)) - return JIM_ERR; - - Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL); - return JIM_OK; -} -#endif - -/* - * tcl_clock.c - * - * Implements the clock command - */ - -/* For strptime() */ + +#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, 0, NULL); +} + +static fdtype JimOpenForWrite(const char *filename, int append) +{ + return CreateFile(filename, append ? FILE_APPEND_DATA : GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, 0, (HANDLE) NULL); +} + +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) +{ + 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, strlen(contents), 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]) +{ + int i; + static char extensions[][5] = {".exe", "", ".bat"}; + + for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) { + lstrcpyn(fullPath, originalName, MAX_PATH - 5); + lstrcat(fullPath, extensions[i]); + + if (SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, NULL) == 0) { + continue; + } + if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) { + continue; + } + return 0; + } + + return -1; +} + +static char **JimSaveEnv(char **env) +{ + return env; +} + +static void JimRestoreEnv(char **env) +{ + JimFreeEnv(env, NULL); +} + +static Jim_Obj * +JimWinBuildCommandLine(Jim_Interp *interp, char **argv) +{ + char *start, *special; + int quote, i; + + Jim_Obj *strObj = Jim_NewStringObj(interp, "", 0); + + for (i = 0; argv[i]; i++) { + if (i > 0) { + Jim_AppendString(interp, strObj, " ", 1); + } + + if (argv[i][0] == '\0') { + quote = 1; + } + else { + quote = 0; + for (start = argv[i]; *start != '\0'; start++) { + if (isspace(UCHAR(*start))) { + quote = 1; + break; + } + } + } + if (quote) { + Jim_AppendString(interp, strObj, "\"" , 1); + } + + start = argv[i]; + for (special = argv[i]; ; ) { + if ((*special == '\\') && (special[1] == '\\' || + special[1] == '"' || (quote && special[1] == '\0'))) { + Jim_AppendString(interp, strObj, start, special - start); + start = special; + while (1) { + special++; + if (*special == '"' || (quote && *special == '\0')) { + + Jim_AppendString(interp, strObj, start, special - start); + break; + } + if (*special != '\\') { + break; + } + } + Jim_AppendString(interp, strObj, start, special - start); + start = special; + } + if (*special == '"') { + if (special == start) { + Jim_AppendString(interp, strObj, "\"", 1); + } + else { + Jim_AppendString(interp, strObj, start, special - start); + } + Jim_AppendString(interp, strObj, "\\\"", 2); + start = special + 1; + } + if (*special == '\0') { + break; + } + special++; + } + Jim_AppendString(interp, strObj, start, special - start); + if (quote) { + Jim_AppendString(interp, strObj, "\"", 1); + } + } + return strObj; +} + +static pidtype +JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId) +{ + STARTUPINFO startInfo; + PROCESS_INFORMATION procInfo; + HANDLE hProcess, h; + char execPath[MAX_PATH]; + char *originalName; + pidtype pid = JIM_BAD_PID; + Jim_Obj *cmdLineObj; + + if (JimWinFindExecutable(argv[0], execPath) < 0) { + return JIM_BAD_PID; + } + originalName = argv[0]; + argv[0] = execPath; + + hProcess = GetCurrentProcess(); + cmdLineObj = JimWinBuildCommandLine(interp, argv); + + + ZeroMemory(&startInfo, sizeof(startInfo)); + startInfo.cb = sizeof(startInfo); + startInfo.dwFlags = STARTF_USESTDHANDLES; + startInfo.hStdInput = INVALID_HANDLE_VALUE; + 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 (startInfo.hStdInput == JIM_BAD_FD) { + 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 (startInfo.hStdOutput == JIM_BAD_FD) { + 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 (startInfo.hStdError == JIM_BAD_FD) { + goto end; + } + + if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE, + 0, env, NULL, &startInfo, &procInfo)) { + goto end; + } + + + WaitForInputIdle(procInfo.hProcess, 5000); + CloseHandle(procInfo.hThread); + + pid = procInfo.hProcess; + + end: + Jim_FreeNewObj(interp, cmdLineObj); + if (startInfo.hStdInput != JIM_BAD_FD) { + CloseHandle(startInfo.hStdInput); + } + if (startInfo.hStdOutput != JIM_BAD_FD) { + CloseHandle(startInfo.hStdOutput); + } + if (startInfo.hStdError != JIM_BAD_FD) { + 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) +{ + char inName[] = "/tmp/tcl.tmp.XXXXXX"; + + int fd = mkstemp(inName); + if (fd == JIM_BAD_FD) { + Jim_SetResultErrno(interp, "couldn't create temp file"); + return -1; + } + unlink(inName); + if (contents) { + int length = strlen(contents); + if (write(fd, contents, length) != length) { + Jim_SetResultErrno(interp, "couldn't write temp file"); + close(fd); + return -1; + } + lseek(fd, 0L, SEEK_SET); + } + return fd; +} + +static char **JimSaveEnv(char **env) +{ + char **saveenv = Jim_GetEnviron(); + Jim_SetEnviron(env); + return saveenv; +} + +static void JimRestoreEnv(char **env) +{ + JimFreeEnv(Jim_GetEnviron(), env); + Jim_SetEnviron(env); +} +#endif +#endif + + + #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 500 #endif #include <stdlib.h> #include <string.h> #include <stdio.h> #include <time.h> + + +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> - +#endif static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - /* How big is big enough? */ + char buf[100]; time_t t; long seconds; const char *format = "%a %b %d %H:%M:%S %Z %Y"; @@ -5994,20 +5173,20 @@ if (!Jim_CompareStringImmediate(interp, argv[1], "-format")) { return -1; } - /* Initialise with the current date/time */ + localtime_r(&now, &tm); pt = strptime(Jim_String(argv[0]), Jim_String(argv[2]), &tm); if (pt == 0 || *pt != 0) { Jim_SetResultString(interp, "Failed to parse time according to format", -1); return JIM_ERR; } - /* Now convert into a time_t */ + Jim_SetResultInt(interp, mktime(&tm)); return JIM_OK; } #endif @@ -6040,51 +5219,55 @@ return JIM_OK; } static const jim_subcmd_type clock_command_table[] = { - { .cmd = "seconds", - .function = clock_cmd_seconds, - .minargs = 0, - .maxargs = 0, - .description = "Returns the current time as seconds since the epoch" + { "seconds", + NULL, + clock_cmd_seconds, + 0, + 0, + + }, + { "clicks", + NULL, + clock_cmd_micros, + 0, + 0, + }, - { .cmd = "clicks", - .function = clock_cmd_micros, - .minargs = 0, - .maxargs = 0, - .description = "Returns the current time in 'clicks'" + { "microseconds", + NULL, + clock_cmd_micros, + 0, + 0, + }, - { .cmd = "microseconds", - .function = clock_cmd_micros, - .minargs = 0, - .maxargs = 0, - .description = "Returns the current time in microseconds" + { "milliseconds", + NULL, + clock_cmd_millis, + 0, + 0, + }, - { .cmd = "milliseconds", - .function = clock_cmd_millis, - .minargs = 0, - .maxargs = 0, - .description = "Returns the current time in milliseconds" - }, - { .cmd = "format", - .args = "seconds ?-format format?", - .function = clock_cmd_format, - .minargs = 1, - .maxargs = 3, - .description = "Format the given time" + { "format", + "seconds ?-format format?", + clock_cmd_format, + 1, + 3, + }, #ifdef HAVE_STRPTIME - { .cmd = "scan", - .args = "str -format format", - .function = clock_cmd_scan, - .minargs = 3, - .maxargs = 3, - .description = "Determine the time according to the given format" + { "scan", + "str -format format", + clock_cmd_scan, + 3, + 3, + }, #endif - { 0 } + { NULL } }; int Jim_clockInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG)) @@ -6092,66 +5275,21 @@ Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL); return JIM_OK; } -/* - * Implements the array command for jim - * - * (c) 2008 Steve Bennett <steveb@workware.net.au> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of the Jim Tcl Project. - * - * Based on code originally from Tcl 6.7: - * - * Copyright 1987-1991 Regents of the University of California - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without - * fee is hereby granted, provided that the above copyright - * notice appear in all copies. The University of California - * makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without - * express or implied warranty. - */ #include <limits.h> #include <stdlib.h> #include <string.h> #include <stdio.h> -#include <unistd.h> #include <errno.h> static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - /* Just a regular [info exists] */ + Jim_SetResultInt(interp, Jim_GetVariable(interp, argv[0], 0) != 0); return JIM_OK; } static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv) @@ -6170,11 +5308,11 @@ if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) { all = 1; } - /* If it is a dictionary or list with an even number of elements, nothing else to do */ + if (all) { if (Jim_IsDict(objPtr) || (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0)) { Jim_SetResult(interp, objPtr); return JIM_OK; } @@ -6187,15 +5325,15 @@ if (Jim_DictPairs(interp, dictObj, &dictValuesObj, &len) != JIM_OK) { return JIM_ERR; } if (all) { - /* Return the whole array */ + Jim_SetResult(interp, dictObj); } else { - /* Only return the matching values */ + resultObj = Jim_NewListObj(interp, NULL, 0); for (i = 0; i < len; i += 2) { if (Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) { Jim_ListAppendElement(interp, resultObj, dictValuesObj[i]); @@ -6229,11 +5367,11 @@ Jim_Obj *objPtr; Jim_Obj *dictObj; Jim_Obj **dictValuesObj; if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) { - /* Unset the whole array */ + Jim_UnsetVariable(interp, argv[0], JIM_NONE); return JIM_OK; } objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); @@ -6244,11 +5382,11 @@ if (Jim_DictPairs(interp, dictObj, &dictValuesObj, &len) != JIM_OK) { return JIM_ERR; } - /* Create a new object with the values which don't match */ + resultObj = Jim_NewDictObj(interp, NULL, 0); for (i = 0; i < len; i += 2) { if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) { Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]); @@ -6263,11 +5401,11 @@ static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr; int len = 0; - /* Not found means zero length */ + objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); if (objPtr) { len = Jim_DictSize(interp, objPtr); if (len < 0) { return JIM_ERR; @@ -6285,11 +5423,11 @@ int len; int rc = JIM_OK; Jim_Obj *listObj = argv[1]; if (Jim_GetVariable(interp, argv[0], JIM_NONE) == NULL) { - /* Doesn't exist, so just set the list directly */ + return Jim_SetVariable(interp, argv[0], listObj); } len = Jim_ListLength(interp, listObj); if (len % 2) { @@ -6301,60 +5439,60 @@ Jim_Obj *valueObj; Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE); Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE); - rc = Jim_SetDictKeysVector(interp, argv[0], &nameObj, 1, valueObj); + rc = Jim_SetDictKeysVector(interp, argv[0], &nameObj, 1, valueObj, JIM_ERRMSG); } return rc; } static const jim_subcmd_type array_command_table[] = { - { .cmd = "exists", - .args = "arrayName", - .function = array_cmd_exists, - .minargs = 1, - .maxargs = 1, - .description = "Does array exist?" + { "exists", + "arrayName", + array_cmd_exists, + 1, + 1, + + }, + { "get", + "arrayName ?pattern?", + array_cmd_get, + 1, + 2, + + }, + { "names", + "arrayName ?pattern?", + array_cmd_names, + 1, + 2, + + }, + { "set", + "arrayName list", + array_cmd_set, + 2, + 2, + }, - { .cmd = "get", - .args = "arrayName ?pattern?", - .function = array_cmd_get, - .minargs = 1, - .maxargs = 2, - .description = "Array contents as name value list" + { "size", + "arrayName", + array_cmd_size, + 1, + 1, + }, - { .cmd = "names", - .args = "arrayName ?pattern?", - .function = array_cmd_names, - .minargs = 1, - .maxargs = 2, - .description = "Array keys as a list" - }, - { .cmd = "set", - .args = "arrayName list", - .function = array_cmd_set, - .minargs = 2, - .maxargs = 2, - .description = "Set array from list" + { "unset", + "arrayName ?pattern?", + array_cmd_unset, + 1, + 2, + }, - { .cmd = "size", - .args = "arrayName", - .function = array_cmd_size, - .minargs = 1, - .maxargs = 1, - .description = "Number of elements in array" - }, - { .cmd = "unset", - .args = "arrayName ?pattern?", - .function = array_cmd_unset, - .minargs = 1, - .maxargs = 2, - .description = "Unset elements of an array" - }, - { .cmd = 0, + { NULL } }; int Jim_arrayInit(Jim_Interp *interp) { @@ -6389,53 +5527,11 @@ Jim_stdlibInit(interp); Jim_tclcompatInit(interp); return JIM_OK; } -/* Jim - A small embeddable Tcl interpreter - * - * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org> - * Copyright 2005 Clemens Hintze <c.hintze@gmx.net> - * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net> - * Copyright 2008,2009 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com - * Copyright 2008 Andrew Lunn <andrew@lunn.ch> - * Copyright 2008 Duane Ellis <openocd@duaneellis.com> - * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de> - * Copyright 2008 Steve Bennett <steveb@workware.net.au> - * Copyright 2009 Nico Coesel <ncoesel@dealogic.nl> - * Copyright 2009 Zachary T Welch zw@superlucidity.net - * Copyright 2009 David Brownell - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of the Jim Tcl Project. - **/ -#define JIM_OPTIMIZATION /* comment to avoid optimizations and reduce size */ +#define JIM_OPTIMIZATION #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -6445,28 +5541,28 @@ #include <assert.h> #include <errno.h> #include <time.h> #include <setjmp.h> -#include <unistd.h> + +#ifdef HAVE_SYS_TIME_H #include <sys/time.h> - - +#endif #ifdef HAVE_BACKTRACE #include <execinfo.h> #endif #ifdef HAVE_CRT_EXTERNS_H #include <crt_externs.h> #endif -/* For INFINITY, even if math functions are not enabled */ + #include <math.h> -/* We may decide to switch to using $[...] after all, so leave it as an option */ -/*#define EXPRSUGAR_BRACKET*/ -/* For the no-autoconf case */ + + + #ifndef TCL_LIBRARY #define TCL_LIBRARY "." #endif #ifndef TCL_PLATFORM_OS #define TCL_PLATFORM_OS "unknown" @@ -6476,67 +5572,59 @@ #endif #ifndef TCL_PLATFORM_PATH_SEPARATOR #define TCL_PLATFORM_PATH_SEPARATOR ":" #endif -/*#define DEBUG_SHOW_SCRIPT*/ -/*#define DEBUG_SHOW_SCRIPT_TOKENS*/ -/*#define DEBUG_SHOW_SUBST*/ -/*#define DEBUG_SHOW_EXPR*/ -/*#define DEBUG_SHOW_EXPR_TOKENS*/ -/*#define JIM_DEBUG_GC*/ + + + + + + #ifdef JIM_MAINTAINER #define JIM_DEBUG_COMMAND #define JIM_DEBUG_PANIC #endif const char *jim_tt_name(int type); #ifdef JIM_DEBUG_PANIC -static void JimPanicDump(int panic_condition, Jim_Interp *interp, const char *fmt, ...); +static void JimPanicDump(int panic_condition, const char *fmt, ...); #define JimPanic(X) JimPanicDump X #else #define JimPanic(X) #endif -/* ----------------------------------------------------------------------------- - * Global variables - * ---------------------------------------------------------------------------*/ -/* A shared empty string for the objects string representation. - * Jim_InvalidateStringRep knows about it and doesn't try to free it. */ static char JimEmptyStringRep[] = ""; -/* ----------------------------------------------------------------------------- - * Required prototypes of not exported functions - * ---------------------------------------------------------------------------*/ static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf); static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags); static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr, int flags); static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr); static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr); static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, const char *prefix, const char *const *tablePtr, const char *name); static void JimDeleteLocalProcs(Jim_Interp *interp); -static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, const char *filename, int linenr, +static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, Jim_Obj *fileNameObj, int linenr, int argc, Jim_Obj *const *argv); static int JimEvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv, - const char *filename, int linenr); + Jim_Obj *fileNameObj, int linenr); static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr); static int JimSign(jim_wide w); static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr); static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen); static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len); static const Jim_HashTableType JimVariablesHashTableType; -/* Fast access to the int (wide) value of an object which is known to be of int type */ + #define JimWideValue(objPtr) (objPtr)->internalRep.wideValue -#define JimObjTypeName(O) (objPtr->typePtr ? objPtr->typePtr->name : "none") +#define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none") static int utf8_tounicode_case(const char *s, int *uc, int upper) { int l = utf8_tounicode(s, uc); if (upper) { @@ -6543,30 +5631,14 @@ *uc = utf8_upper(*uc); } return l; } -/* These can be used in addition to JIM_CASESENS/JIM_NOCASE */ + #define JIM_CHARSET_SCAN 2 #define JIM_CHARSET_GLOB 0 -/** - * pattern points to a string like "[^a-z\ub5]" - * - * The pattern may contain trailing chars, which are ignored. - * - * The pattern is matched against unicode char 'c'. - * - * If (flags & JIM_NOCASE), case is ignored when matching. - * If (flags & JIM_CHARSET_SCAN), the considers ^ and ] special at the start - * of the charset, per scan, rather than glob/string match. - * - * If the unicode char 'c' matches that set, returns a pointer to the ']' character, - * or the null character if the ']' is missing. - * - * Returns NULL on no match. - */ static const char *JimCharsetMatch(const char *pattern, int c, int flags) { int not = 0; int pchar; int match = 0; @@ -6581,34 +5653,34 @@ if (*pattern == '^') { not++; pattern++; } - /* Special case. If the first char is ']', it is part of the set */ + if (*pattern == ']') { goto first; } } while (*pattern && *pattern != ']') { - /* Exact match */ + if (pattern[0] == '\\') { first: pattern += utf8_tounicode_case(pattern, &pchar, nocase); } else { - /* Is this a range? a-z */ + int start; int end; pattern += utf8_tounicode_case(pattern, &start, nocase); if (pattern[0] == '-' && pattern[1]) { - /* skip '-' */ + pattern += utf8_tounicode(pattern, &pchar); pattern += utf8_tounicode_case(pattern, &end, nocase); - /* Handle reversed range too */ + if ((c >= start && c <= end) || (c >= end && c <= start)) { match = 1; } continue; } @@ -6624,15 +5696,12 @@ } return match ? pattern : NULL; } -/* Glob-style pattern matching. */ -/* Note: string *must* be valid UTF-8 sequences - * slen is a char length, not byte counts. - */ + static int GlobMatch(const char *pattern, const char *string, int nocase) { int c; int pchar; while (*pattern) { @@ -6641,19 +5710,19 @@ while (pattern[1] == '*') { pattern++; } pattern++; if (!pattern[0]) { - return 1; /* match */ + return 1; } while (*string) { - /* Recursive call - Does the remaining pattern match anywhere? */ + if (GlobMatch(pattern, string, nocase)) - return 1; /* match */ + return 1; string += utf8_tounicode(string, &c); } - return 0; /* no match */ + return 0; case '?': string += utf8_tounicode(string, &c); break; @@ -6662,20 +5731,20 @@ pattern = JimCharsetMatch(pattern + 1, c, nocase ? JIM_NOCASE : 0); if (!pattern) { return 0; } if (!*pattern) { - /* Ran out of pattern (no ']') */ + continue; } break; } case '\\': if (pattern[1]) { pattern++; } - /* fall through */ + default: string += utf8_tounicode_case(string, &c, nocase); utf8_tounicode_case(pattern, &pchar, nocase); if (pchar != c) { return 0; @@ -6699,15 +5768,10 @@ static int JimStringMatch(Jim_Interp *interp, Jim_Obj *patternObj, const char *string, int nocase) { return GlobMatch(Jim_String(patternObj), string, nocase); } -/** - * string comparison works on binary data. - * - * Note that the lengths are byte lengths, not char lengths. - */ static int JimStringCompare(const char *s1, int l1, const char *s2, int l2) { if (l1 < l2) { return memcmp(s1, s2, l1) <= 0 ? -1 : 1; } @@ -6717,16 +5781,10 @@ else { return JimSign(memcmp(s1, s2, l1)); } } -/** - * No-case version. - * - * If maxchars is -1, compares to end of string. - * Otherwise compares at most 'maxchars' characters. - */ static int JimStringCompareNoCase(const char *s1, const char *s2, int maxchars) { while (*s1 && *s2 && maxchars) { int c1, c2; s1 += utf8_tounicode_case(s1, &c1, 1); @@ -6737,23 +5795,20 @@ maxchars--; } if (!maxchars) { return 0; } - /* One string or both terminated */ + if (*s1) { return 1; } if (*s2) { return -1; } return 0; } -/* Search 's1' inside 's2', starting to search from char 'index' of 's2'. - * The index of the first occurrence of s1 in s2 is returned. - * If s1 is not found inside s2, -1 is returned. */ static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx) { int i; int l1bytelen; @@ -6774,33 +5829,27 @@ s2 += utf8_tounicode(s2, &c); } return -1; } -/** - * Note: Lengths and return value are in bytes, not chars. - */ static int JimStringLast(const char *s1, int l1, const char *s2, int l2) { const char *p; if (!l1 || !l2 || l1 > l2) return -1; - /* Now search for the needle */ + for (p = s2 + l2 - 1; p != s2 - 1; p--) { if (*p == *s1 && memcmp(s1, p, l1) == 0) { return p - s2; } } return -1; } #ifdef JIM_UTF8 -/** - * Note: Lengths and return value are in chars. - */ static int JimStringLastUtf8(const char *s1, int l1, const char *s2, int l2) { int n = JimStringLast(s1, utf8_index(s1, l1), s2, utf8_index(s2, l2)); if (n > 0) { n = utf8_strlen(s2, n); @@ -6814,17 +5863,10 @@ const char *fmt = "%" JIM_WIDE_MODIFIER; return sprintf(buf, fmt, wideValue); } -/** - * After an strtol()/strtod()-like conversion, - * check whether something was converted and that - * the only thing left is white space. - * - * Returns JIM_OK or JIM_ERR. - */ static int JimCheckConversion(const char *str, const char *endptr) { if (str[0] == '\0' || str == endptr) { return JIM_ERR; } @@ -6854,20 +5896,18 @@ int len; char *buf0 = buf; len = sprintf(buf, "%.12g", doubleValue); - /* Add a final ".0" if it's a number. But not - * for NaN or InF */ while (*buf) { if (*buf == '.' || isalpha(UCHAR(*buf))) { - /* inf -> Inf, nan -> Nan */ + if (*buf == 'i' || *buf == 'n') { *buf = toupper(UCHAR(*buf)); } if (*buf == 'I') { - /* Infinity -> Inf */ + buf[3] = '\0'; len = buf - buf0 + 3; } return len; } @@ -6883,11 +5923,11 @@ int Jim_StringToDouble(const char *str, double *doublePtr) { char *endptr; - /* Callers can check for underflow via ERANGE */ + errno = 0; *doublePtr = strtod(str, &endptr); return JimCheckConversion(str, endptr); @@ -6903,29 +5943,21 @@ res *= b; } return res; } -/* ----------------------------------------------------------------------------- - * Special functions - * ---------------------------------------------------------------------------*/ #ifdef JIM_DEBUG_PANIC -/* Note that 'interp' may be NULL if not available in the - * context of the panic. It's only useful to get the error - * file descriptor, it will default to stderr otherwise. */ -void JimPanicDump(int condition, Jim_Interp *interp, const char *fmt, ...) +void JimPanicDump(int condition, const char *fmt, ...) { va_list ap; if (!condition) { return; } va_start(ap, fmt); - /* - * Send it here first.. Assuming STDIO still works - */ + fprintf(stderr, JIM_NL "JIM INTERPRETER PANIC: "); vfprintf(stderr, fmt, ap); fprintf(stderr, JIM_NL JIM_NL); va_end(ap); @@ -6946,13 +5978,10 @@ abort(); } #endif -/* ----------------------------------------------------------------------------- - * Memory allocation - * ---------------------------------------------------------------------------*/ void *Jim_Alloc(int size) { return malloc(size); } @@ -6975,39 +6004,33 @@ char *Jim_StrDupLen(const char *s, int l) { char *copy = Jim_Alloc(l + 1); memcpy(copy, s, l + 1); - copy[l] = 0; /* Just to be sure, original could be substring */ + copy[l] = 0; return copy; } -/* ----------------------------------------------------------------------------- - * Time related functions - * ---------------------------------------------------------------------------*/ -/* Returns microseconds of CPU used since start. */ + static jim_wide JimClock(void) { struct timeval tv; gettimeofday(&tv, NULL); return (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec; } -/* ----------------------------------------------------------------------------- - * Hash Tables - * ---------------------------------------------------------------------------*/ -/* -------------------------- private prototypes ---------------------------- */ + static int JimExpandHashTableIfNeeded(Jim_HashTable *ht); static unsigned int JimHashTableNextPower(unsigned int size); static int JimInsertHashEntry(Jim_HashTable *ht, const void *key); -/* -------------------------- hash functions -------------------------------- */ -/* Thomas Wang's 32 bit Mix Function */ + + unsigned int Jim_IntHashFunction(unsigned int key) { key += ~(key << 15); key ^= (key >> 10); key += (key << 3); @@ -7015,161 +6038,146 @@ key += ~(key << 11); key ^= (key >> 16); return key; } -/* Generic hash function (we are using to multiply by 9 and add the byte - * as Tcl) */ unsigned int Jim_GenHashFunction(const unsigned char *buf, int len) { unsigned int h = 0; while (len--) h += (h << 3) + *buf++; return h; } -/* ----------------------------- API implementation ------------------------- */ -/* reset a hashtable already initialized with ht_init(). - * NOTE: This function should only called by ht_destroy(). */ + static void JimResetHashTable(Jim_HashTable *ht) { ht->table = NULL; ht->size = 0; ht->sizemask = 0; ht->used = 0; ht->collisions = 0; } -/* Initialize the hash table */ + int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr) { JimResetHashTable(ht); ht->type = type; ht->privdata = privDataPtr; return JIM_OK; } -/* Resize the table to the minimal size that contains all the elements, - * but with the invariant of a USER/BUCKETS ration near to <= 1 */ int Jim_ResizeHashTable(Jim_HashTable *ht) { int minimal = ht->used; if (minimal < JIM_HT_INITIAL_SIZE) minimal = JIM_HT_INITIAL_SIZE; return Jim_ExpandHashTable(ht, minimal); } -/* Expand or create the hashtable */ + int Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size) { - Jim_HashTable n; /* the new hashtable */ + Jim_HashTable n; unsigned int realsize = JimHashTableNextPower(size), i; - /* the size is invalid if it is smaller than the number of - * elements already inside the hashtable */ if (ht->used >= size) return JIM_ERR; Jim_InitHashTable(&n, ht->type, ht->privdata); n.size = realsize; n.sizemask = realsize - 1; n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *)); - /* Initialize all the pointers to NULL */ + memset(n.table, 0, realsize * sizeof(Jim_HashEntry *)); - /* Copy all the elements from the old to the new table: - * note that if the old hash table is empty ht->size is zero, - * so Jim_ExpandHashTable just creates an hash table. */ n.used = ht->used; - for (i = 0; i < ht->size && ht->used > 0; i++) { + for (i = 0; ht->used > 0; i++) { Jim_HashEntry *he, *nextHe; if (ht->table[i] == NULL) continue; - /* For each hash entry on this slot... */ + he = ht->table[i]; while (he) { unsigned int h; nextHe = he->next; - /* Get the new element index */ + h = Jim_HashKey(ht, he->key) & n.sizemask; he->next = n.table[h]; n.table[h] = he; ht->used--; - /* Pass to the next element */ + he = nextHe; } } assert(ht->used == 0); Jim_Free(ht->table); - /* Remap the new hashtable in the old */ + *ht = n; return JIM_OK; } -/* Add an element to the target hash table */ + int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val) { int idx; Jim_HashEntry *entry; - /* Get the index of the new element, or -1 if - * the element already exists. */ if ((idx = JimInsertHashEntry(ht, key)) == -1) return JIM_ERR; - /* Allocates the memory and stores key */ + entry = Jim_Alloc(sizeof(*entry)); entry->next = ht->table[idx]; ht->table[idx] = entry; - /* Set the hash entry fields. */ + Jim_SetHashKey(ht, entry, key); Jim_SetHashVal(ht, entry, val); ht->used++; return JIM_OK; } -/* Add an element, discarding the old if the key already exists */ + int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val) { Jim_HashEntry *entry; - /* Try to add the element. If the key - * does not exists Jim_AddHashEntry will suceed. */ if (Jim_AddHashEntry(ht, key, val) == JIM_OK) return JIM_OK; - /* It already exists, get the entry */ + entry = Jim_FindHashEntry(ht, key); - /* Free the old value and set the new one */ + Jim_FreeEntryVal(ht, entry); Jim_SetHashVal(ht, entry, val); return JIM_OK; } -/* Search and remove an element */ + int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key) { unsigned int h; Jim_HashEntry *he, *prevHe; - if (ht->size == 0) + if (ht->used == 0) return JIM_ERR; h = Jim_HashKey(ht, key) & ht->sizemask; he = ht->table[h]; prevHe = NULL; while (he) { if (Jim_CompareHashKeys(ht, key, he->key)) { - /* Unlink the element from the list */ + if (prevHe) prevHe->next = he->next; else ht->table[h] = he->next; Jim_FreeEntryKey(ht, he); @@ -7179,20 +6187,20 @@ return JIM_OK; } prevHe = he; he = he->next; } - return JIM_ERR; /* not found */ + return JIM_ERR; } -/* Destroy an entire hash table */ + int Jim_FreeHashTable(Jim_HashTable *ht) { unsigned int i; - /* Free all the elements */ - for (i = 0; i < ht->size && ht->used > 0; i++) { + + for (i = 0; ht->used > 0; i++) { Jim_HashEntry *he, *nextHe; if ((he = ht->table[i]) == NULL) continue; while (he) { @@ -7202,23 +6210,23 @@ Jim_Free(he); ht->used--; he = nextHe; } } - /* Free the table and the allocated cache structure */ + Jim_Free(ht->table); - /* Re-initialize the table */ + JimResetHashTable(ht); - return JIM_OK; /* never fails */ + return JIM_OK; } Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key) { Jim_HashEntry *he; unsigned int h; - if (ht->size == 0) + if (ht->used == 0) return NULL; h = Jim_HashKey(ht, key) & ht->sizemask; he = ht->table[h]; while (he) { if (Jim_CompareHashKeys(ht, key, he->key)) @@ -7250,34 +6258,30 @@ } else { iter->entry = iter->nextEntry; } if (iter->entry) { - /* We need to save the 'next' here, the iterator user - * may delete the entry we are returning. */ iter->nextEntry = iter->entry->next; return iter->entry; } } return NULL; } -/* ------------------------- private functions ------------------------------ */ -/* Expand the hash table if needed */ + + static int JimExpandHashTableIfNeeded(Jim_HashTable *ht) { - /* If the hash table is empty expand it to the intial size, - * if the table is "full" dobule its size. */ if (ht->size == 0) return Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE); if (ht->size == ht->used) return Jim_ExpandHashTable(ht, ht->size * 2); return JIM_OK; } -/* Our hash table capability is a power of two */ + static unsigned int JimHashTableNextPower(unsigned int size) { unsigned int i = JIM_HT_INITIAL_SIZE; if (size >= 2147483648U) @@ -7287,34 +6291,31 @@ return i; i *= 2; } } -/* Returns the index of a free slot that can be populated with - * an hash entry for the given 'key'. - * If the key already exists, -1 is returned. */ static int JimInsertHashEntry(Jim_HashTable *ht, const void *key) { unsigned int h; Jim_HashEntry *he; - /* Expand the hashtable if needed */ + if (JimExpandHashTableIfNeeded(ht) == JIM_ERR) return -1; - /* Compute the key hash value */ + h = Jim_HashKey(ht, key) & ht->sizemask; - /* Search if this slot does not already contain the given key */ + he = ht->table[h]; while (he) { if (Jim_CompareHashKeys(ht, key, he->key)) return -1; he = he->next; } return h; } -/* ----------------------- StringCopy Hash Table Type ------------------------*/ + static unsigned int JimStringCopyHTHashFunction(const void *key) { return Jim_GenHashFunction(key, strlen(key)); } @@ -7352,51 +6353,47 @@ static void JimStringCopyHTKeyDestructor(void *privdata, const void *key) { JIM_NOTUSED(privdata); - Jim_Free((void *)key); /* ATTENTION: const cast */ + Jim_Free((void *)key); } static void JimStringKeyValCopyHTValDestructor(void *privdata, void *val) { JIM_NOTUSED(privdata); - Jim_Free((void *)val); /* ATTENTION: const cast */ + Jim_Free((void *)val); } #if 0 static Jim_HashTableType JimStringCopyHashTableType = { - JimStringCopyHTHashFunction, /* hash function */ - JimStringCopyHTKeyDup, /* key dup */ - NULL, /* val dup */ - JimStringCopyHTKeyCompare, /* key compare */ - JimStringCopyHTKeyDestructor, /* key destructor */ - NULL /* val destructor */ + JimStringCopyHTHashFunction, + JimStringCopyHTKeyDup, + NULL, + JimStringCopyHTKeyCompare, + JimStringCopyHTKeyDestructor, + NULL }; #endif -/* This is like StringCopy but does not auto-duplicate the key. - * It's used for intepreter's shared strings. */ static const Jim_HashTableType JimSharedStringsHashTableType = { - JimStringCopyHTHashFunction, /* hash function */ - NULL, /* key dup */ - NULL, /* val dup */ - JimStringCopyHTKeyCompare, /* key compare */ - JimStringCopyHTKeyDestructor, /* key destructor */ - NULL /* val destructor */ + JimStringCopyHTHashFunction, + NULL, + NULL, + JimStringCopyHTKeyCompare, + JimStringCopyHTKeyDestructor, + NULL }; -/* This is like StringCopy but also automatically handle dynamic - * allocated C strings as values. */ static const Jim_HashTableType JimStringKeyValCopyHashTableType = { - JimStringCopyHTHashFunction, /* hash function */ - JimStringCopyHTKeyDup, /* key dup */ - JimStringKeyValCopyHTValDup, /* val dup */ - JimStringCopyHTKeyCompare, /* key compare */ - JimStringCopyHTKeyDestructor, /* key destructor */ - JimStringKeyValCopyHTValDestructor, /* val destructor */ + JimStringCopyHTHashFunction, + JimStringCopyHTKeyDup, + JimStringKeyValCopyHTValDup, + JimStringCopyHTKeyCompare, + JimStringCopyHTKeyDestructor, + JimStringKeyValCopyHTValDestructor, }; typedef struct AssocDataValue { Jim_InterpDeleteProc *delProc; @@ -7411,22 +6408,18 @@ assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data); Jim_Free(data); } static const Jim_HashTableType JimAssocDataHashTableType = { - JimStringCopyHTHashFunction, /* hash function */ - JimStringCopyHTKeyDup, /* key dup */ - NULL, /* val dup */ - JimStringCopyHTKeyCompare, /* key compare */ - JimStringCopyHTKeyDestructor, /* key destructor */ - JimAssocDataHashTableValueDestructor /* val destructor */ + JimStringCopyHTHashFunction, + JimStringCopyHTKeyDup, + NULL, + JimStringCopyHTKeyCompare, + JimStringCopyHTKeyDestructor, + JimAssocDataHashTableValueDestructor }; -/* ----------------------------------------------------------------------------- - * Stack - This is a simple generic stack implementation. It is used for - * example in the 'expr' expression compiler. - * ---------------------------------------------------------------------------*/ void Jim_InitStack(Jim_Stack *stack) { stack->len = 0; stack->maxlen = 0; stack->vector = NULL; @@ -7475,71 +6468,64 @@ for (i = 0; i < stack->len; i++) freeFunc(stack->vector[i]); } -/* ----------------------------------------------------------------------------- - * Parser - * ---------------------------------------------------------------------------*/ -/* Token types */ -#define JIM_TT_NONE 0 /* No token returned */ -#define JIM_TT_STR 1 /* simple string */ -#define JIM_TT_ESC 2 /* string that needs escape chars conversion */ -#define JIM_TT_VAR 3 /* var substitution */ -#define JIM_TT_DICTSUGAR 4 /* Syntax sugar for [dict get], $foo(bar) */ -#define JIM_TT_CMD 5 /* command substitution */ -/* Note: Keep these three together for TOKEN_IS_SEP() */ -#define JIM_TT_SEP 6 /* word separator. arg is # of tokens. -ve if {*} */ -#define JIM_TT_EOL 7 /* line separator */ -#define JIM_TT_EOF 8 /* end of script */ -#define JIM_TT_LINE 9 /* special 'start-of-line' token. arg is # of arguments to the command. -ve if {*} */ -#define JIM_TT_WORD 10 /* special 'start-of-word' token. arg is # of tokens to combine. -ve if {*} */ +#define JIM_TT_NONE 0 +#define JIM_TT_STR 1 +#define JIM_TT_ESC 2 +#define JIM_TT_VAR 3 +#define JIM_TT_DICTSUGAR 4 +#define JIM_TT_CMD 5 -/* Additional token types needed for expressions */ +#define JIM_TT_SEP 6 +#define JIM_TT_EOL 7 +#define JIM_TT_EOF 8 + +#define JIM_TT_LINE 9 +#define JIM_TT_WORD 10 + + #define JIM_TT_SUBEXPR_START 11 #define JIM_TT_SUBEXPR_END 12 -#define JIM_TT_EXPR_INT 13 -#define JIM_TT_EXPR_DOUBLE 14 +#define JIM_TT_SUBEXPR_COMMA 13 +#define JIM_TT_EXPR_INT 14 +#define JIM_TT_EXPR_DOUBLE 15 -#define JIM_TT_EXPRSUGAR 15 /* $(expression) */ +#define JIM_TT_EXPRSUGAR 16 -/* Operator token types start here */ + #define JIM_TT_EXPR_OP 20 #define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF) -/* Parser states */ -#define JIM_PS_DEF 0 /* Default state */ -#define JIM_PS_QUOTE 1 /* Inside "" */ -#define JIM_PS_DICTSUGAR 2 /* Tokenising abc(def) into 4 separate tokens */ -/* Parser context structure. The same context is used both to parse - * Tcl scripts and lists. */ +#define JIM_PS_DEF 0 +#define JIM_PS_QUOTE 1 +#define JIM_PS_DICTSUGAR 2 + struct JimParserCtx { - const char *p; /* Pointer to the point of the program we are parsing */ - int len; /* Remaining length */ - int linenr; /* Current line number */ + const char *p; + int len; + int linenr; const char *tstart; - const char *tend; /* Returned token is at tstart-tend in 'prg'. */ - int tline; /* Line number of the returned token */ - int tt; /* Token type */ - int eof; /* Non zero if EOF condition is true. */ - int state; /* Parser state */ - int comment; /* Non zero if the next chars may be a comment. */ - char missing; /* At end of parse, ' ' if complete, '{' if braces incomplete, '"' if quotes incomplete */ - int missingline; /* Line number starting the missing token */ + const char *tend; + int tline; + int tt; + int eof; + int state; + int comment; + char missing; + int missingline; }; -/** - * Results of missing quotes, braces, etc. from parsing. - */ struct JimParseResult { - char missing; /* From JimParserCtx.missing */ - int line; /* From JimParserCtx.missingline */ + char missing; + int line; }; static int JimParseScript(struct JimParserCtx *pc); static int JimParseSep(struct JimParserCtx *pc); static int JimParseEol(struct JimParserCtx *pc); @@ -7552,13 +6538,10 @@ static void JimParseSubCmd(struct JimParserCtx *pc); static int JimParseSubQuote(struct JimParserCtx *pc); static void JimParseSubCmd(struct JimParserCtx *pc); static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc); -/* Initialize a parser context. - * 'prg' is a pointer to the program text, linenr is the line - * number of the first line contained in the program. */ static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr) { pc->p = prg; pc->len = len; pc->tstart = NULL; @@ -7573,11 +6556,11 @@ pc->missingline = linenr; } static int JimParseScript(struct JimParserCtx *pc) { - while (1) { /* the while is used to reiterate with continue if needed */ + while (1) { if (!pc->len) { pc->tstart = pc->p; pc->tend = pc->p - 1; pc->tline = pc->linenr; pc->tt = JIM_TT_EOL; @@ -7677,43 +6660,16 @@ pc->tend = pc->p - 1; pc->tt = JIM_TT_EOL; return JIM_OK; } -/* -** Here are the rules for parsing: -** {braced expression} -** - Count open and closing braces -** - Backslash escapes meaning of braces -** -** "quoted expression" -** - First double quote at start of word terminates the expression -** - Backslash escapes quote and bracket -** - [commands brackets] are counted/nested -** - command rules apply within [brackets], not quoting rules (i.e. quotes have their own rules) -** -** [command expression] -** - Count open and closing brackets -** - Backslash escapes quote, bracket and brace -** - [commands brackets] are counted/nested -** - "quoted expressions" are parsed according to quoting rules -** - {braced expressions} are parsed according to brace rules -** -** For everything, backslash escapes the next char, newline increments current line -*/ -/** - * Parses a braced expression starting at pc->p. - * - * Positions the parser at the end of the braced expression, - * sets pc->tend and possibly pc->missing. - */ static void JimParseSubBrace(struct JimParserCtx *pc) { int level = 1; - /* Skip the brace */ + pc->p++; pc->len--; while (pc->len) { switch (*pc->p) { case '\\': @@ -7748,26 +6704,16 @@ pc->missing = '{'; pc->missingline = pc->tline; pc->tend = pc->p - 1; } -/** - * Parses a quoted expression starting at pc->p. - * - * Positions the parser at the end of the quoted expression, - * sets pc->tend and possibly pc->missing. - * - * Returns the type of the token of the string, - * either JIM_TT_ESC (if it contains values which need to be [subst]ed) - * or JIM_TT_STR. - */ static int JimParseSubQuote(struct JimParserCtx *pc) { int tt = JIM_TT_STR; int line = pc->tline; - /* Skip the quote */ + pc->p++; pc->len--; while (pc->len) { switch (*pc->p) { case '\\': @@ -7806,23 +6752,17 @@ pc->missingline = line; pc->tend = pc->p - 1; return tt; } -/** - * Parses a [command] expression starting at pc->p. - * - * Positions the parser at the end of the command expression, - * sets pc->tend and possibly pc->missing. - */ static void JimParseSubCmd(struct JimParserCtx *pc) { int level = 1; int startofword = 1; int line = pc->tline; - /* Skip the bracket */ + pc->p++; pc->len--; while (pc->len) { switch (*pc->p) { case '\\': @@ -7898,17 +6838,17 @@ return JIM_OK; } static int JimParseVar(struct JimParserCtx *pc) { - /* skip the $ */ + pc->p++; pc->len--; #ifdef EXPRSUGAR_BRACKET if (*pc->p == '[') { - /* Parse $[...] expr shorthand syntax */ + JimParseCmd(pc); pc->tt = JIM_TT_EXPRSUGAR; return JIM_OK; } #endif @@ -7934,11 +6874,11 @@ pc->len--; } } else { while (1) { - /* Skip double colon, but not single colon! */ + if (pc->p[0] == ':' && pc->p[1] == ':') { pc->p += 2; pc->len -= 2; continue; } @@ -7947,11 +6887,11 @@ pc->len--; continue; } break; } - /* Parse [dict get] syntax sugar. */ + if (*pc->p == '(') { int count = 1; const char *paren = NULL; pc->tt = JIM_TT_DICTSUGAR; @@ -7974,11 +6914,11 @@ if (count == 0) { pc->p++; pc->len--; } else if (paren) { - /* Did not find a matching paren. Back up */ + paren++; pc->len += (pc->p - paren); pc->p = paren; } #ifndef EXPRSUGAR_BRACKET @@ -7987,14 +6927,10 @@ } #endif } pc->tend = pc->p - 1; } - /* Check if we parsed just the '$' character. - * That's not a variable so an error is returned - * to tell the state machine to consider this '$' just - * a string. */ if (pc->tstart == pc->p) { pc->p--; pc->len++; return JIM_ERR; } @@ -8010,11 +6946,11 @@ } else if (newword && *pc->p == '"') { pc->state = JIM_PS_QUOTE; pc->p++; pc->len--; - /* In case the end quote is missing */ + pc->missingline = pc->tline; } pc->tstart = pc->p; pc->tline = pc->linenr; while (1) { @@ -8040,19 +6976,19 @@ pc->p++; pc->len--; } break; case '(': - /* If the following token is not '$' just keep going */ + if (pc->len > 1 && pc->p[1] != '$') { break; } case ')': - /* Only need a separate ')' token if the previous was a var */ + if (*pc->p == '(' || pc->tt == JIM_TT_VAR) { if (pc->p == pc->tstart) { - /* At the start of the token, so just return this char */ + pc->p++; pc->len--; } pc->tend = pc->p - 1; pc->tt = JIM_TT_ESC; @@ -8091,11 +7027,11 @@ break; } pc->p++; pc->len--; } - return JIM_OK; /* unreached */ + return JIM_OK; } static int JimParseComment(struct JimParserCtx *pc) { while (*pc->p) { @@ -8111,11 +7047,11 @@ pc->len--; } return JIM_OK; } -/* xdigitval and odigitval are helper functions for JimEscape() */ + static int xdigitval(int c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') @@ -8130,17 +7066,10 @@ if (c >= '0' && c <= '7') return c - '0'; return -1; } -/* Perform Tcl escape substitution of 's', storing the result - * string into 'dest'. The escaped string is guaranteed to - * be the same length or shorted than the source string. - * Slen is the length of the string at 's', if it's -1 the string - * length will be calculated by the function. - * - * The function returns the length of the resulting string. */ static int JimEscape(char *dest, const char *s, int slen) { char *p = dest; int i, len; @@ -8175,15 +7104,10 @@ *p++ = 0x9; i++; break; case 'u': case 'x': - /* A unicode or hex sequence. - * \u Expect 1-4 hex chars and convert to utf-8. - * \x Expect 1-2 hex chars and convert to hex. - * An invalid sequence means simply the escaped char. - */ { int val = 0; int k; i++; @@ -8194,21 +7118,21 @@ break; } val = (val << 4) | c; } if (k) { - /* Got a valid sequence, so convert */ + if (s[i] == 'u') { p += utf8_fromunicode(p, val); } else { *p++ = val; } i += k; break; } - /* Not a valid codepoint, just an escaped char */ + *p++ = s[i]; } break; case 'v': *p++ = 0xb; @@ -8217,11 +7141,11 @@ case '\0': *p++ = '\\'; i++; break; case '\n': - /* Replace all spaces and tabs after backslash newline with a single space*/ + *p++ = ' '; do { i++; } while (s[i + 1] == ' ' || s[i + 1] == '\t'); break; @@ -8231,11 +7155,11 @@ case '3': case '4': case '5': case '6': case '7': - /* octal escape */ + { int val = 0; int c = odigitval(s[i + 1]); val = c; @@ -8271,30 +7195,10 @@ len = p - dest; *p = '\0'; return len; } -/* Returns a dynamically allocated copy of the current token in the - * parser context. The function performs conversion of escapes if - * the token is of type JIM_TT_ESC. - * - * Note that after the conversion, tokens that are grouped with - * braces in the source code, are always recognizable from the - * identical string obtained in a different way from the type. - * - * For example the string: - * - * {*}$a - * - * will return as first token "*", of type JIM_TT_STR - * - * While the string: - * - * *$a - * - * will return as first token "*", of type JIM_TT_ESC - */ static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc) { const char *start, *end; char *token; int len; @@ -8308,34 +7212,23 @@ } else { len = (end - start) + 1; token = Jim_Alloc(len + 1); if (pc->tt != JIM_TT_ESC) { - /* No escape conversion needed? Just copy it. */ + memcpy(token, start, len); token[len] = '\0'; } else { - /* Else convert the escape chars. */ + len = JimEscape(token, start, len); } } return Jim_NewStringObjNoAlloc(interp, token, len); } -/* Parses the given string to determine if it represents a complete script. - * - * This is useful for interactive shells implementation, for [info complete]. - * - * If 'stateCharPtr' != NULL, the function stores ' ' on complete script, - * '{' on scripts incomplete missing one or more '}' to be balanced. - * '[' on scripts incomplete missing one or more ']' to be balanced. - * '"' on scripts incomplete missing a '"' char. - * - * If the script is complete, 1 is returned, otherwise 0. - */ int Jim_ScriptIsComplete(const char *s, int len, char *stateCharPtr) { struct JimParserCtx parser; JimParserInit(&parser, s, len, 1); @@ -8346,13 +7239,10 @@ *stateCharPtr = parser.missing; } return parser.missing == ' '; } -/* ----------------------------------------------------------------------------- - * Tcl Lists parsing - * ---------------------------------------------------------------------------*/ static int JimParseListSep(struct JimParserCtx *pc); static int JimParseListStr(struct JimParserCtx *pc); static int JimParseListQuote(struct JimParserCtx *pc); static int JimParseList(struct JimParserCtx *pc) @@ -8412,11 +7302,11 @@ while (pc->len) { switch (*pc->p) { case '\\': pc->tt = JIM_TT_ESC; if (--pc->len == 0) { - /* Trailing backslash */ + pc->tend = pc->p; return JIM_OK; } pc->p++; break; @@ -8445,11 +7335,11 @@ while (pc->len) { switch (*pc->p) { case '\\': if (--pc->len == 0) { - /* Trailing backslash */ + pc->tend = pc->p; return JIM_OK; } pc->tt = JIM_TT_ESC; pc->p++; @@ -8466,81 +7356,69 @@ } pc->tend = pc->p - 1; return JIM_OK; } -/* ----------------------------------------------------------------------------- - * Jim_Obj related functions - * ---------------------------------------------------------------------------*/ -/* Return a new initialized object. */ + Jim_Obj *Jim_NewObj(Jim_Interp *interp) { Jim_Obj *objPtr; - /* -- Check if there are objects in the free list -- */ + if (interp->freeList != NULL) { - /* -- Unlink the object from the free list -- */ + objPtr = interp->freeList; interp->freeList = objPtr->nextObjPtr; } else { - /* -- No ready to use objects: allocate a new one -- */ + objPtr = Jim_Alloc(sizeof(*objPtr)); } - /* Object is returned with refCount of 0. Every - * kind of GC implemented should take care to don't try - * to scan objects with refCount == 0. */ objPtr->refCount = 0; - /* All the other fields are left not initialized to save time. - * The caller will probably want to set them to the right - * value anyway. */ - /* -- Put the object into the live list -- */ + objPtr->prevObjPtr = NULL; objPtr->nextObjPtr = interp->liveList; if (interp->liveList) interp->liveList->prevObjPtr = objPtr; interp->liveList = objPtr; return objPtr; } -/* Free an object. Actually objects are never freed, but - * just moved to the free objects list, where they will be - * reused by Jim_NewObj(). */ void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr) { - /* Check if the object was already freed, panic. */ - JimPanic((objPtr->refCount != 0, interp, "!!!Object %p freed with bad refcount %d, type=%s", objPtr, + + JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr, objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>")); - /* Free the internal representation */ + Jim_FreeIntRep(interp, objPtr); - /* Free the string representation */ + if (objPtr->bytes != NULL) { if (objPtr->bytes != JimEmptyStringRep) Jim_Free(objPtr->bytes); } - /* Unlink the object from the live objects list */ + if (objPtr->prevObjPtr) objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr; if (objPtr->nextObjPtr) objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr; if (interp->liveList == objPtr) interp->liveList = objPtr->nextObjPtr; - /* Link the object into the free objects list */ + objPtr->prevObjPtr = NULL; objPtr->nextObjPtr = interp->freeList; if (interp->freeList) interp->freeList->prevObjPtr = objPtr; interp->freeList = objPtr; objPtr->refCount = -1; } -/* Invalidate the string representation of an object. */ + void Jim_InvalidateStringRep(Jim_Obj *objPtr) { if (objPtr->bytes != NULL) { if (objPtr->bytes != JimEmptyStringRep) Jim_Free(objPtr->bytes); @@ -8549,12 +7427,10 @@ } #define Jim_SetStringRep(o, b, l) \ do { (o)->bytes = b; (o)->length = l; } while (0) -/* Set the initial string representation for an object. - * Does not try to free an old one. */ void Jim_InitStringRep(Jim_Obj *objPtr, const char *bytes, int length) { if (length == 0) { objPtr->bytes = JimEmptyStringRep; objPtr->length = 0; @@ -8565,54 +7441,51 @@ memcpy(objPtr->bytes, bytes, length); objPtr->bytes[length] = '\0'; } } -/* Duplicate an object. The returned object has refcount = 0. */ + Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr) { Jim_Obj *dupPtr; dupPtr = Jim_NewObj(interp); if (objPtr->bytes == NULL) { - /* Object does not have a valid string representation. */ + dupPtr->bytes = NULL; } else { Jim_InitStringRep(dupPtr, objPtr->bytes, objPtr->length); } - /* By default, the new object has the same type as the old object */ + dupPtr->typePtr = objPtr->typePtr; if (objPtr->typePtr != NULL) { if (objPtr->typePtr->dupIntRepProc == NULL) { dupPtr->internalRep = objPtr->internalRep; } else { - /* The dup proc may set a different type, e.g. NULL */ + objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr); } } return dupPtr; } -/* Return the string representation for objPtr. If the object - * string representation is invalid, calls the method to create - * a new one starting from the internal representation of the object. */ const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr) { if (objPtr->bytes == NULL) { - /* Invalid string repr. Generate it. */ - JimPanic((objPtr->typePtr->updateStringProc == NULL, NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); + + JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name)); objPtr->typePtr->updateStringProc(objPtr); } if (lenPtr) *lenPtr = objPtr->length; return objPtr->bytes; } -/* Just returns the length of the object's string rep */ + int Jim_Length(Jim_Obj *objPtr) { int len; Jim_GetString(objPtr, &len); @@ -8641,13 +7514,10 @@ NULL, NULL, JIM_TYPE_NONE, }; -/* ----------------------------------------------------------------------------- - * String Object - * ---------------------------------------------------------------------------*/ static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); static const Jim_ObjType stringObjType = { "string", @@ -8659,39 +7529,29 @@ static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) { JIM_NOTUSED(interp); - /* This is a bit subtle: the only caller of this function - * should be Jim_DuplicateObj(), that will copy the - * string representaion. After the copy, the duplicated - * object will not have more room in teh buffer than - * srcPtr->length bytes. So we just set it to length. */ dupPtr->internalRep.strValue.maxLength = srcPtr->length; dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength; } static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr) { - /* Get a fresh string representation. */ + (void)Jim_String(objPtr); - /* Free any other internal representation. */ + Jim_FreeIntRep(interp, objPtr); - /* Set it as string, i.e. just set the maxLength field. */ + objPtr->typePtr = &stringObjType; objPtr->internalRep.strValue.maxLength = objPtr->length; - /* Don't know the utf-8 length yet */ + objPtr->internalRep.strValue.charLength = -1; return JIM_OK; } -/** - * Returns the length of the object string in chars, not bytes. - * - * These may be different for a utf-8 string. - */ int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr) { #ifdef JIM_UTF8 if (objPtr->typePtr != &stringObjType) SetStringFromAny(interp, objPtr); @@ -8703,19 +7563,19 @@ #else return Jim_Length(objPtr); #endif } -/* len is in bytes -- see also Jim_NewStringObjUtf8() */ + Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len) { Jim_Obj *objPtr = Jim_NewObj(interp); - /* Need to find out how many bytes the string requires */ + if (len == -1) len = strlen(s); - /* Alloc/Set the string rep. */ + if (len == 0) { objPtr->bytes = JimEmptyStringRep; objPtr->length = 0; } else { @@ -8723,25 +7583,25 @@ objPtr->length = len; memcpy(objPtr->bytes, s, len); objPtr->bytes[len] = '\0'; } - /* No typePtr field for the vanilla string object. */ + objPtr->typePtr = NULL; return objPtr; } -/* charlen is in characters -- see also Jim_NewStringObj() */ + Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen) { #ifdef JIM_UTF8 - /* Need to find out how many bytes the string requires */ + int bytelen = utf8_index(s, charlen); Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen); - /* Remember the utf8 length, so set the type */ + objPtr->typePtr = &stringObjType; objPtr->internalRep.strValue.maxLength = bytelen; objPtr->internalRep.strValue.charLength = charlen; return objPtr; @@ -8748,12 +7608,10 @@ #else return Jim_NewStringObj(interp, s, charlen); #endif } -/* This version does not try to duplicate the 's' pointer, but - * use it directly. */ Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len) { Jim_Obj *objPtr = Jim_NewObj(interp); if (len == -1) @@ -8761,12 +7619,10 @@ Jim_SetStringRep(objPtr, s, len); objPtr->typePtr = NULL; return objPtr; } -/* Low-level string append. Use it only against objects - * of type "string". */ static void StringAppendString(Jim_Obj *objPtr, const char *str, int len) { int needlen; if (len == -1) @@ -8773,11 +7629,11 @@ len = strlen(str); needlen = objPtr->length + len; if (objPtr->internalRep.strValue.maxLength < needlen || objPtr->internalRep.strValue.maxLength == 0) { needlen *= 2; - /* Inefficient to malloc() for less than 8 bytes */ + if (needlen < 7) { needlen = 7; } if (objPtr->bytes == JimEmptyStringRep) { objPtr->bytes = Jim_Alloc(needlen + 1); @@ -8788,20 +7644,20 @@ objPtr->internalRep.strValue.maxLength = needlen; } memcpy(objPtr->bytes + objPtr->length, str, len); objPtr->bytes[objPtr->length + len] = '\0'; if (objPtr->internalRep.strValue.charLength >= 0) { - /* Update the utf-8 char length */ + objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len); } objPtr->length += len; } -/* Higher level API to append strings to objects. */ + void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len) { - JimPanic((Jim_IsShared(objPtr), interp, "Jim_AppendString called with shared object")); + JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object")); if (objPtr->typePtr != &stringObjType) SetStringFromAny(interp, objPtr); StringAppendString(objPtr, str, len); } @@ -8862,28 +7718,17 @@ return JimStringCompareNoCase(s1, s2, -1); } return JimStringCompare(s1, l1, s2, l2); } -/* Convert a range, as returned by Jim_GetRange(), into - * an absolute index into an object of the specified length. - * This function may return negative values, or values - * bigger or equal to the length of the list if the index - * is out of range. */ static int JimRelToAbsIndex(int len, int idx) { if (idx < 0) return len + idx; return idx; } -/* Convert a pair of index as normalize by JimRelToAbsIndex(), - * into a range stored in *firstPtr, *lastPtr, *rangeLenPtr, suitable - * for implementation of commands like [string range] and [lrange]. - * - * The resulting range is guaranteed to address valid elements of - * the structure. */ static void JimRelToAbsRange(int len, int first, int last, int *firstPtr, int *lastPtr, int *rangeLenPtr) { int rangeLen; @@ -8951,11 +7796,11 @@ JimRelToAbsRange(len, first, last, &first, &last, &rangeLen); if (first == 0 && rangeLen == len) { return strObjPtr; } if (len == bytelen) { - /* ASCII optimisation */ + return Jim_NewStringObj(interp, str + first, rangeLen); } return Jim_NewStringObjUtf8(interp, str + utf8_index(str, first), rangeLen); #else return Jim_StringByteRangeObj(interp, strObjPtr, firstObjPtr, lastObjPtr); @@ -9004,14 +7849,10 @@ } *p = 0; return Jim_NewStringObjNoAlloc(interp, buf, len); } -/* Similar to memchr() except searches a UTF-8 string 'str' of byte length 'len' - * for unicode character 'c'. - * Returns the position if found or NULL if not - */ static const char *utf8_memchr(const char *str, int len, int c) { #ifdef JIM_UTF8 while (len) { int sc; @@ -9026,40 +7867,26 @@ #else return memchr(str, c, len); #endif } -/** - * Searches for the first non-trim char in string (str, len) - * - * If none is found, returns just past the last char. - * - * Lengths are in bytes. - */ static const char *JimFindTrimLeft(const char *str, int len, const char *trimchars, int trimlen) { while (len) { int c; int n = utf8_tounicode(str, &c); if (utf8_memchr(trimchars, trimlen, c) == NULL) { - /* Not a trim char, so stop */ + break; } str += n; len -= n; } return str; } -/** - * Searches backwards for a non-trim char in string (str, len). - * - * Returns a pointer to just after the non-trim char, or NULL if not found. - * - * Lengths are in bytes. - */ static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen) { str += len; while (len) { @@ -9078,11 +7905,11 @@ return NULL; } static const char default_trim_chars[] = " \t\n\r"; -/* sizeof() here includes the null byte */ + static int default_trim_chars_len = sizeof(default_trim_chars); static Jim_Obj *JimStringTrimLeft(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr) { int len; @@ -9115,15 +7942,15 @@ } if (strObjPtr->typePtr != &stringObjType) { SetStringFromAny(interp, strObjPtr); } - Jim_GetString(strObjPtr, &len); + len = Jim_Length(strObjPtr); nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen); if (nontrim == NULL) { - /* All trim, so return a zero-length string */ + return Jim_NewEmptyStringObj(interp); } if (nontrim == strObjPtr->bytes + len) { return strObjPtr; } @@ -9130,28 +7957,28 @@ if (Jim_IsShared(strObjPtr)) { strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes)); } else { - /* Can modify this string in place */ + strObjPtr->bytes[nontrim - strObjPtr->bytes] = 0; strObjPtr->length = (nontrim - strObjPtr->bytes); } return strObjPtr; } static Jim_Obj *JimStringTrim(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr) { - /* First trim left. */ + Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr); - /* Now trim right */ + strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr); if (objPtr != strObjPtr) { - /* Note that we don't want this object to be leaked */ + Jim_IncrRefCount(objPtr); Jim_DecrRefCount(interp, objPtr); } return strObjPtr; @@ -9226,38 +8053,20 @@ } Jim_SetResultInt(interp, 1); return JIM_OK; } -/* ----------------------------------------------------------------------------- - * Compared String Object - * ---------------------------------------------------------------------------*/ -/* This is strange object that allows to compare a C literal string - * with a Jim object in very short time if the same comparison is done - * multiple times. For example every time the [if] command is executed, - * Jim has to check if a given argument is "else". This comparions if - * the code has no errors are true most of the times, so we can cache - * inside the object the pointer of the string of the last matching - * comparison. Because most C compilers perform literal sharing, - * so that: char *x = "foo", char *y = "foo", will lead to x == y, - * this works pretty well even if comparisons are at different places - * inside the C code. */ static const Jim_ObjType comparedStringObjType = { "compared-string", NULL, NULL, NULL, JIM_TYPE_REFERENCES, }; -/* The only way this object is exposed to the API is via the following - * function. Returns true if the string and the object string repr. - * are the same, otherwise zero is returned. - * - * Note: this isn't binary safe, but it hardly needs to be.*/ int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str) { if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) return 1; else { @@ -9267,11 +8076,11 @@ return 0; if (objPtr->typePtr != &comparedStringObjType) { Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &comparedStringObjType; } - objPtr->internalRep.ptr = (char *)str; /*ATTENTION: const cast */ + objPtr->internalRep.ptr = (char *)str; return 1; } } static int qsortCompareStringPointers(const void *a, const void *b) @@ -9281,31 +8090,10 @@ return strcmp(*sa, *sb); } -/* ----------------------------------------------------------------------------- - * Source Object - * - * This object is just a string from the language point of view, but - * in the internal representation it contains the filename and line number - * where this given token was read. This information is used by - * Jim_EvalObj() if the object passed happens to be of type "source". - * - * This allows to propagate the information about line numbers and file - * names and give error messages with absolute line numbers. - * - * Note that this object uses shared strings for filenames, and the - * pointer to the filename together with the line number is taken into - * the space for the "inline" internal representation of the Jim_Object, - * so there is almost memory zero-overhead. - * - * Also the object will be converted to something else if the given - * token it represents in the source file is not something to be - * evaluated (not a script), and will be specialized in some other way, - * so the time overhead is also null. - * ---------------------------------------------------------------------------*/ static void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); static void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static const Jim_ObjType sourceObjType = { @@ -9316,36 +8104,30 @@ JIM_TYPE_REFERENCES, }; void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) { - Jim_ReleaseSharedString(interp, objPtr->internalRep.sourceValue.fileName); + Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj); } void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) { - dupPtr->internalRep.sourceValue.fileName = - Jim_GetSharedString(interp, srcPtr->internalRep.sourceValue.fileName); - dupPtr->internalRep.sourceValue.lineNumber = dupPtr->internalRep.sourceValue.lineNumber; - dupPtr->typePtr = &sourceObjType; + dupPtr->internalRep = srcPtr->internalRep; + Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj); } static void JimSetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, - const char *fileName, int lineNumber) + Jim_Obj *fileNameObj, int lineNumber) { - if (fileName) { - JimPanic((Jim_IsShared(objPtr), interp, "JimSetSourceInfo called with shared object")); - JimPanic((objPtr->typePtr != NULL, interp, "JimSetSourceInfo called with typePtr != NULL")); - objPtr->internalRep.sourceValue.fileName = Jim_GetSharedString(interp, fileName); - objPtr->internalRep.sourceValue.lineNumber = lineNumber; - objPtr->typePtr = &sourceObjType; - } + JimPanic((Jim_IsShared(objPtr), "JimSetSourceInfo called with shared object")); + JimPanic((objPtr->typePtr != NULL, "JimSetSourceInfo called with typePtr != NULL")); + Jim_IncrRefCount(fileNameObj); + objPtr->internalRep.sourceValue.fileNameObj = fileNameObj; + objPtr->internalRep.sourceValue.lineNumber = lineNumber; + objPtr->typePtr = &sourceObjType; } -/* ----------------------------------------------------------------------------- - * Script Object - * ---------------------------------------------------------------------------*/ static const Jim_ObjType scriptLineObjType = { "scriptline", NULL, NULL, @@ -9383,98 +8165,26 @@ DupScriptInternalRep, NULL, JIM_TYPE_REFERENCES, }; -/* The ScriptToken structure represents every token into a scriptObj. - * Every token contains an associated Jim_Obj that can be specialized - * by commands operating on it. */ typedef struct ScriptToken { int type; Jim_Obj *objPtr; } ScriptToken; -/* This is the script object internal representation. An array of - * ScriptToken structures, including a pre-computed representation of the - * command length and arguments. - * - * For example the script: - * - * puts hello - * set $i $x$y [foo]BAR - * - * will produce a ScriptObj with the following Tokens: - * - * LIN 2 - * ESC puts - * ESC hello - * LIN 4 - * ESC set - * VAR i - * WRD 2 - * VAR x - * VAR y - * WRD 2 - * CMD foo - * ESC BAR - * - * "puts hello" has two args (LIN 2), composed of single tokens. - * (Note that the WRD token is omitted for the common case of a single token.) - * - * "set $i $x$y [foo]BAR" has four (LIN 4) args, the first word - * has 1 token (ESC SET), and the last has two tokens (WRD 2 CMD foo ESC BAR) - * - * The precomputation of the command structure makes Jim_Eval() faster, - * and simpler because there aren't dynamic lengths / allocations. - * - * -- {expand}/{*} handling -- - * - * Expand is handled in a special way. - * - * If a "word" begins with {*}, the word token count is -ve. - * - * For example the command: - * - * list {*}{a b} - * - * Will produce the following cmdstruct array: - * - * LIN 2 - * ESC list - * WRD -1 - * STR a b - * - * Note that the 'LIN' token also contains the source information for the - * first word of the line for error reporting purposes - * - * -- the substFlags field of the structure -- - * - * The scriptObj structure is used to represent both "script" objects - * and "subst" objects. In the second case, the there are no LIN and WRD - * tokens. Instead SEP and EOL tokens are added as-is. - * In addition, the field 'substFlags' is used to represent the flags used to turn - * the string into the internal representation used to perform the - * substitution. If this flags are not what the application requires - * the scriptObj is created again. For example the script: - * - * subst -nocommands $string - * subst -novariables $string - * - * Will recreate the internal representation of the $string object - * two times. - */ typedef struct ScriptObj { - int len; /* Length as number of tokens. */ - ScriptToken *token; /* Tokens array. */ - int substFlags; /* flags used for the compilation of "subst" objects */ + int len; + ScriptToken *token; + int substFlags; int inUse; /* Used to share a ScriptObj. Currently only used by Jim_EvalObj() as protection against shimmering of the currently evaluated object. */ - const char *fileName; - int line; /* Line number of the first line */ + Jim_Obj *fileNameObj; + int line; } ScriptObj; void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) { int i; @@ -9485,47 +8195,38 @@ return; for (i = 0; i < script->len; i++) { Jim_DecrRefCount(interp, script->token[i].objPtr); } Jim_Free(script->token); - if (script->fileName) { - Jim_ReleaseSharedString(interp, script->fileName); - } + Jim_DecrRefCount(interp, script->fileNameObj); Jim_Free(script); } void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) { JIM_NOTUSED(interp); JIM_NOTUSED(srcPtr); - /* Just returns an simple string. */ + dupPtr->typePtr = NULL; } -/* A simple parser token. - * All the simple tokens for the script point into the same script string rep. - */ typedef struct { - const char *token; /* Pointer to the start of the token */ - int len; /* Length of this token */ - int type; /* Token type */ - int line; /* Line number */ + const char *token; + int len; + int type; + int line; } ParseToken; -/* A list of parsed tokens representing a script. - * Tokens are added to this list as the script is parsed. - * It grows as needed. - */ typedef struct { - /* Start with a statically allocated list of tokens which will be expanded with realloc if needed */ - ParseToken *list; /* Array of tokens */ - int size; /* Current size of the list */ - int count; /* Number of entries used */ - ParseToken static_list[20]; /* Small initial token space to avoid allocation */ + + ParseToken *list; + int size; + int count; + ParseToken static_list[20]; } ParseTokenList; static void ScriptTokenListInit(ParseTokenList *tokenlist) { tokenlist->list = tokenlist->static_list; @@ -9538,29 +8239,24 @@ if (tokenlist->list != tokenlist->static_list) { Jim_Free(tokenlist->list); } } -/** - * Adds the new token to the tokenlist. - * The token has the given length, type and line number. - * The token list is resized as necessary. - */ static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type, int line) { ParseToken *t; if (tokenlist->count == tokenlist->size) { - /* Resize the list */ + tokenlist->size *= 2; if (tokenlist->list != tokenlist->static_list) { tokenlist->list = Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list)); } else { - /* The list needs to become allocated */ + tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list)); memcpy(tokenlist->list, tokenlist->static_list, tokenlist->count * sizeof(*tokenlist->list)); } } @@ -9569,79 +8265,58 @@ t->len = len; t->type = type; t->line = line; } -/* Counts the number of adjoining non-separator. - * - * Returns -ve if the first token is the expansion - * operator (in which case the count doesn't include - * that token). - */ static int JimCountWordTokens(ParseToken *t) { int expand = 1; int count = 0; - /* Is the first word {*} or {expand}? */ + if (t->type == JIM_TT_STR && !TOKEN_IS_SEP(t[1].type)) { if ((t->len == 1 && *t->token == '*') || (t->len == 6 && strncmp(t->token, "expand", 6) == 0)) { - /* Create an expand token */ + expand = -1; t++; } } - /* Now count non-separator words */ + while (!TOKEN_IS_SEP(t->type)) { t++; count++; } return count * expand; } -/** - * Create a script/subst object from the given token. - */ static Jim_Obj *JimMakeScriptObj(Jim_Interp *interp, const ParseToken *t) { Jim_Obj *objPtr; if (t->type == JIM_TT_ESC && memchr(t->token, '\\', t->len) != NULL) { - /* Convert the backlash escapes . */ + int len = t->len; char *str = Jim_Alloc(len + 1); len = JimEscape(str, t->token, len); objPtr = Jim_NewStringObjNoAlloc(interp, str, len); } else { - /* REVIST: Strictly, JIM_TT_STR should replace <backslash><newline><whitespace> - * with a single space. This is currently not done. - */ objPtr = Jim_NewStringObj(interp, t->token, t->len); } return objPtr; } -/** - * Takes a tokenlist and creates the allocated list of script tokens - * in script->token, of length script->len. - * - * Unnecessary tokens are discarded, and LINE and WORD tokens are inserted - * as required. - * - * Also sets script->line to the line number of the first token - */ static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, ParseTokenList *tokenlist) { int i; struct ScriptToken *token; - /* Number of tokens so far for the current command */ + int lineargs = 0; - /* This is the first token for the current command */ + ScriptToken *linefirst; int count; int linenr; #ifdef DEBUG_SHOW_SCRIPT_TOKENS @@ -9650,11 +8325,11 @@ printf("[%2d]@%d %s '%.*s'\n", i, tokenlist->list[i].line, jim_tt_name(tokenlist->list[i].type), tokenlist->list[i].len, tokenlist->list[i].token); } #endif - /* May need up to one extra script token for each EOL in the worst case */ + count = tokenlist->count; for (i = 0; i < tokenlist->count; i++) { if (tokenlist->list[i].type == JIM_TT_EOL) { count++; } @@ -9661,70 +8336,67 @@ } linenr = script->line = tokenlist->list[0].line; token = script->token = Jim_Alloc(sizeof(ScriptToken) * count); - /* This is the first token for the current command */ + linefirst = token++; for (i = 0; i < tokenlist->count; ) { - /* Look ahead to find out how many tokens make up the next word */ + int wordtokens; - /* Skip any leading separators */ + while (tokenlist->list[i].type == JIM_TT_SEP) { i++; } wordtokens = JimCountWordTokens(tokenlist->list + i); if (wordtokens == 0) { - /* None, so at end of line */ + if (lineargs) { linefirst->type = JIM_TT_LINE; linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr); Jim_IncrRefCount(linefirst->objPtr); - /* Reset for new line */ + lineargs = 0; linefirst = token++; } i++; continue; } else if (wordtokens != 1) { - /* More than 1, or {expand}, so insert a WORD token */ + token->type = JIM_TT_WORD; token->objPtr = Jim_NewIntObj(interp, wordtokens); Jim_IncrRefCount(token->objPtr); token++; if (wordtokens < 0) { - /* Skip the expand token */ + i++; wordtokens = -wordtokens - 1; lineargs--; } } if (lineargs == 0) { - /* First real token on the line, so record the line number */ + linenr = tokenlist->list[i].line; } lineargs++; - /* Add each non-separator word token to the line */ + while (wordtokens--) { const ParseToken *t = &tokenlist->list[i++]; token->type = t->type; token->objPtr = JimMakeScriptObj(interp, t); Jim_IncrRefCount(token->objPtr); - /* Every object is initially a string, but the - * internal type may be specialized during execution of the - * script. */ - JimSetSourceInfo(interp, token->objPtr, script->fileName, t->line); + JimSetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line); token++; } } if (lineargs == 0) { @@ -9734,22 +8406,19 @@ script->len = token - script->token; assert(script->len < count); #ifdef DEBUG_SHOW_SCRIPT - printf("==== Script (%s) ====\n", script->fileName); + printf("==== Script (%s) ====\n", Jim_String(script->fileNameObj)); for (i = 0; i < script->len; i++) { const ScriptToken *t = &script->token[i]; printf("[%2d] %s %s\n", i, jim_tt_name(t->type), Jim_String(t->objPtr)); } #endif } -/** - * Similar to ScriptObjAddTokens(), but for subst objects. - */ static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script, ParseTokenList *tokenlist) { int i; struct ScriptToken *token; @@ -9757,38 +8426,35 @@ token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count); for (i = 0; i < tokenlist->count; i++) { const ParseToken *t = &tokenlist->list[i]; - /* Create a token for 't' */ + token->type = t->type; token->objPtr = JimMakeScriptObj(interp, t); Jim_IncrRefCount(token->objPtr); token++; } script->len = i; } -/* This method takes the string representation of an object - * as a Tcl script, and generates the pre-parsed internal representation - * of the script. */ static int SetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, struct JimParseResult *result) { int scriptTextLen; const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); struct JimParserCtx parser; struct ScriptObj *script; ParseTokenList tokenlist; int line = 1; - /* Try to get information about filename / line number */ + if (objPtr->typePtr == &sourceObjType) { line = objPtr->internalRep.sourceValue.lineNumber; } - /* Initially parse the script into tokens (in tokenlist) */ + ScriptTokenListInit(&tokenlist); JimParserInit(&parser, scriptText, scriptTextLen, line); while (!parser.eof) { JimParseScript(&parser); @@ -9800,32 +8466,32 @@ result->missing = parser.missing; result->line = parser.missingline; return JIM_ERR; } - /* Add a final EOF token */ + ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0); - /* Create the "real" script tokens from the initial token list */ + script = Jim_Alloc(sizeof(*script)); memset(script, 0, sizeof(*script)); script->inUse = 1; script->line = line; if (objPtr->typePtr == &sourceObjType) { - script->fileName = Jim_GetSharedString(interp, objPtr->internalRep.sourceValue.fileName); + script->fileNameObj = objPtr->internalRep.sourceValue.fileNameObj; } + else { + script->fileNameObj = interp->emptyObj; + } + Jim_IncrRefCount(script->fileNameObj); ScriptObjAddTokens(interp, script, &tokenlist); - /* No longer need the token list */ + ScriptTokenListFree(&tokenlist); - if (!script->fileName) { - script->fileName = Jim_GetSharedString(interp, ""); - } - - /* Free the old internal rep and set the new one. */ + Jim_FreeIntRep(interp, objPtr); Jim_SetIntRepPtr(objPtr, script); objPtr->typePtr = &scriptObjType; return JIM_OK; @@ -9839,13 +8505,10 @@ SetScriptFromAny(interp, objPtr, NULL); } return (ScriptObj *) Jim_GetIntRepPtr(objPtr); } -/* ----------------------------------------------------------------------------- - * Commands - * ---------------------------------------------------------------------------*/ static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr) { cmdPtr->inUse++; } @@ -9858,67 +8521,61 @@ if (cmdPtr->u.proc.staticVars) { Jim_FreeHashTable(cmdPtr->u.proc.staticVars); Jim_Free(cmdPtr->u.proc.staticVars); } if (cmdPtr->u.proc.prevCmd) { - /* Delete any pushed command too */ + JimDecrCmdRefCount(interp, cmdPtr->u.proc.prevCmd); } } else { - /* native (C) */ + if (cmdPtr->u.native.delProc) { cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData); } } Jim_Free(cmdPtr); } } -/* Commands HashTable Type. - * - * Keys are dynamic allocated strings, Values are Jim_Cmd structures. */ static void JimCommandsHT_ValDestructor(void *interp, void *val) { JimDecrCmdRefCount(interp, val); } static const Jim_HashTableType JimCommandsHashTableType = { - JimStringCopyHTHashFunction, /* hash function */ - JimStringCopyHTKeyDup, /* key dup */ - NULL, /* val dup */ - JimStringCopyHTKeyCompare, /* key compare */ - JimStringCopyHTKeyDestructor, /* key destructor */ - JimCommandsHT_ValDestructor /* val destructor */ + JimStringCopyHTHashFunction, + JimStringCopyHTKeyDup, + NULL, + JimStringCopyHTKeyCompare, + JimStringCopyHTKeyDestructor, + JimCommandsHT_ValDestructor }; -/* ------------------------- Commands related functions --------------------- */ + int Jim_CreateCommand(Jim_Interp *interp, const char *cmdName, Jim_CmdProc cmdProc, void *privData, Jim_DelCmdProc delProc) { Jim_Cmd *cmdPtr; if (Jim_DeleteHashEntry(&interp->commands, cmdName) != JIM_ERR) { - /* Command existed so incr proc epoch */ + Jim_InterpIncrProcEpoch(interp); } cmdPtr = Jim_Alloc(sizeof(*cmdPtr)); - /* Store the new details for this proc */ + memset(cmdPtr, 0, sizeof(*cmdPtr)); cmdPtr->inUse = 1; cmdPtr->u.native.delProc = delProc; cmdPtr->u.native.cmdProc = cmdProc; cmdPtr->u.native.privData = privData; Jim_AddHashEntry(&interp->commands, cmdName, cmdPtr); - /* There is no need to increment the 'proc epoch' because - * creation of a new procedure can never affect existing - * cached commands. We don't do negative caching. */ return JIM_OK; } static int JimCreateProcedure(Jim_Interp *interp, Jim_Obj *cmdName, Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr) @@ -9932,11 +8589,11 @@ return JIM_ERR; } argListLen = Jim_ListLength(interp, argListObjPtr); - /* Allocate space for both the command pointer and the arg list */ + cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen); memset(cmdPtr, 0, sizeof(*cmdPtr)); cmdPtr->inUse = 1; cmdPtr->isproc = 1; cmdPtr->u.proc.argListObjPtr = argListObjPtr; @@ -9945,11 +8602,11 @@ cmdPtr->u.proc.argsPos = -1; cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1); Jim_IncrRefCount(argListObjPtr); Jim_IncrRefCount(bodyObjPtr); - /* Create the statics hash table. */ + if (staticsListObjPtr) { int len, i; len = Jim_ListLength(interp, staticsListObjPtr); if (len != 0) { @@ -9959,15 +8616,13 @@ Jim_Obj *objPtr = 0, *initObjPtr = 0, *nameObjPtr = 0; Jim_Var *varPtr; int subLen; Jim_ListIndex(interp, staticsListObjPtr, i, &objPtr, JIM_NONE); - /* Check if it's composed of two elements. */ + subLen = Jim_ListLength(interp, objPtr); if (subLen == 1 || subLen == 2) { - /* Try to get the variable value from the current - * environment. */ Jim_ListIndex(interp, objPtr, 0, &nameObjPtr, JIM_NONE); if (subLen == 1) { initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE); if (initObjPtr == NULL) { Jim_SetResultFormatted(interp, @@ -10003,20 +8658,20 @@ } } } } - /* Parse the args out into arglist, validating as we go */ - /* Examine the argument list for default parameters and 'args' */ + + for (i = 0; i < argListLen; i++) { Jim_Obj *argPtr; Jim_Obj *nameObjPtr; Jim_Obj *defaultObjPtr; int len; int n = 1; - /* Examine a parameter */ + Jim_ListIndex(interp, argListObjPtr, i, &argPtr, JIM_NONE); len = Jim_ListLength(interp, argPtr); if (len == 0) { Jim_SetResultString(interp, "procedure has argument with no name", -1); goto err; @@ -10025,16 +8680,16 @@ Jim_SetResultString(interp, "procedure has argument with too many fields", -1); goto err; } if (len == 2) { - /* Optional parameter */ + Jim_ListIndex(interp, argPtr, 0, &nameObjPtr, JIM_NONE); Jim_ListIndex(interp, argPtr, 1, &defaultObjPtr, JIM_NONE); } else { - /* Required parameter */ + nameObjPtr = argPtr; defaultObjPtr = NULL; } @@ -10056,46 +8711,33 @@ cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr; cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr; } - /* Add the new command */ + - /* It may already exist, so we try to delete the old one. - * Note that reference count means that it won't be deleted yet if - * it exists in the call stack. - * - * BUT, if 'local' is in force, instead of deleting the existing - * proc, we stash a reference to the old proc here. - */ he = Jim_FindHashEntry(&interp->commands, Jim_String(cmdName)); if (he) { - /* There was an old procedure with the same name, this requires - * a 'proc epoch' update. */ - /* If a procedure with the same name didn't existed there is no need - * to increment the 'proc epoch' because creation of a new procedure - * can never affect existing cached commands. We don't do - * negative caching. */ Jim_InterpIncrProcEpoch(interp); } if (he && interp->local) { - /* Just push this proc over the top of the previous one */ + cmdPtr->u.proc.prevCmd = he->u.val; he->u.val = cmdPtr; } else { if (he) { - /* Replace the existing proc */ + Jim_DeleteHashEntry(&interp->commands, Jim_String(cmdName)); } Jim_AddHashEntry(&interp->commands, Jim_String(cmdName), cmdPtr); } - /* Unlike Tcl, set the name of the proc as the result */ + Jim_SetResult(interp, cmdName); return JIM_OK; err: if (cmdPtr->u.proc.staticVars) { @@ -10118,42 +8760,39 @@ int Jim_RenameCommand(Jim_Interp *interp, const char *oldName, const char *newName) { Jim_HashEntry *he; - /* Does it exist? */ + he = Jim_FindHashEntry(&interp->commands, oldName); if (he == NULL) { Jim_SetResultFormatted(interp, "can't %s \"%s\": command doesn't exist", newName[0] ? "rename" : "delete", oldName); return JIM_ERR; } - if (newName[0] == '\0') /* Delete! */ + if (newName[0] == '\0') return Jim_DeleteCommand(interp, oldName); - /* rename */ + if (Jim_FindHashEntry(&interp->commands, newName)) { Jim_SetResultFormatted(interp, "can't rename to \"%s\": command already exists", newName); return JIM_ERR; } - /* Add the new name first */ + JimIncrCmdRefCount(he->u.val); Jim_AddHashEntry(&interp->commands, newName, he->u.val); - /* Now remove the old name */ + Jim_DeleteHashEntry(&interp->commands, oldName); - /* Increment the epoch */ + Jim_InterpIncrProcEpoch(interp); return JIM_OK; } -/* ----------------------------------------------------------------------------- - * Command object - * ---------------------------------------------------------------------------*/ static int SetCommandFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); static const Jim_ObjType commandObjType = { "command", @@ -10166,33 +8805,25 @@ int SetCommandFromAny(Jim_Interp *interp, Jim_Obj *objPtr) { Jim_HashEntry *he; const char *cmdName; - /* Get the string representation */ + cmdName = Jim_String(objPtr); - /* Lookup this name into the commands hash table */ + he = Jim_FindHashEntry(&interp->commands, cmdName); if (he == NULL) return JIM_ERR; - /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &commandObjType; objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch; objPtr->internalRep.cmdValue.cmdPtr = (void *)he->u.val; return JIM_OK; } -/* This function returns the command structure for the command name - * stored in objPtr. It tries to specialize the objPtr to contain - * a cached info instead to perform the lookup into the hash table - * every time. The information cached may not be uptodate, in such - * a case the lookup is performed and the cache updated. - * - * Respects the 'upcall' setting - */ Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags) { Jim_Cmd *cmd; if ((objPtr->typePtr != &commandObjType || @@ -10208,39 +8839,30 @@ cmd = cmd->u.proc.prevCmd; } return cmd; } -/* ----------------------------------------------------------------------------- - * Variables - * ---------------------------------------------------------------------------*/ -/* Variables HashTable Type. - * - * Keys are dynamic allocated strings, Values are Jim_Var structures. */ static void JimVariablesHTValDestructor(void *interp, void *val) { Jim_Var *varPtr = (void *)val; Jim_DecrRefCount(interp, varPtr->objPtr); Jim_Free(val); } static const Jim_HashTableType JimVariablesHashTableType = { - JimStringCopyHTHashFunction, /* hash function */ - JimStringCopyHTKeyDup, /* key dup */ - NULL, /* val dup */ - JimStringCopyHTKeyCompare, /* key compare */ - JimStringCopyHTKeyDestructor, /* key destructor */ - JimVariablesHTValDestructor /* val destructor */ + JimStringCopyHTHashFunction, + JimStringCopyHTKeyDup, + NULL, + JimStringCopyHTKeyCompare, + JimStringCopyHTKeyDestructor, + JimVariablesHTValDestructor }; -/* ----------------------------------------------------------------------------- - * Variable object - * ---------------------------------------------------------------------------*/ -#define JIM_DICT_SUGAR 100 /* Only returned by SetVariableFromAny() */ +#define JIM_DICT_SUGAR 100 static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); static const Jim_ObjType variableObjType = { "variable", @@ -10248,28 +8870,20 @@ NULL, NULL, JIM_TYPE_REFERENCES, }; -/* Return true if the string "str" looks like syntax sugar for [dict]. I.e. - * is in the form "varname(key)". */ static int JimNameIsDictSugar(const char *str, int len) { if (len && str[len - 1] == ')' && strchr(str, '(') != NULL) return 1; return 0; } -/** - * Check that the name does not contain embedded nulls. - * - * Variable and procedure names are maniplated as null terminated strings, so - * don't allow names with embedded nulls. - */ static int JimValidName(Jim_Interp *interp, const char *type, Jim_Obj *nameObjPtr) { - /* Variable names and proc names can't contain embedded nulls */ + if (nameObjPtr->typePtr != &variableObjType) { int len; const char *str = Jim_GetString(nameObjPtr, &len); if (memchr(str, '\0', len)) { Jim_SetResultFormatted(interp, "%s name contains embedded null", type); @@ -10277,26 +8891,21 @@ } } return JIM_OK; } -/* This method should be called only by the variable API. - * It returns JIM_OK on success (variable already exists), - * JIM_ERR if it does not exists, JIM_DICT_SUGAR if it's not - * a variable name, but syntax glue for [dict] i.e. the last - * character is ')' */ static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) { Jim_HashEntry *he; const char *varName; int len; Jim_CallFrame *framePtr = interp->framePtr; - /* Check if the object is already an uptodate variable */ + if (objPtr->typePtr == &variableObjType && objPtr->internalRep.varValue.callFrameId == framePtr->id) { - return JIM_OK; /* nothing to do */ + return JIM_OK; } if (objPtr->typePtr == &dictSubstObjType) { return JIM_DICT_SUGAR; } @@ -10303,14 +8912,14 @@ if (JimValidName(interp, "variable", objPtr) != JIM_OK) { return JIM_ERR; } - /* Get the string representation */ + varName = Jim_GetString(objPtr, &len); - /* Make sure it's not syntax glue to get/set dict. */ + if (JimNameIsDictSugar(varName, len)) { return JIM_DICT_SUGAR; } if (varName[0] == ':' && varName[1] == ':') { @@ -10319,37 +8928,32 @@ if (he == NULL) { return JIM_ERR; } } else { - /* Lookup this name into the variables hash table */ + he = Jim_FindHashEntry(&framePtr->vars, varName); if (he == NULL) { - /* Try with static vars. */ + if (framePtr->staticVars == NULL) return JIM_ERR; if (!(he = Jim_FindHashEntry(framePtr->staticVars, varName))) return JIM_ERR; } } - /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &variableObjType; objPtr->internalRep.varValue.callFrameId = framePtr->id; objPtr->internalRep.varValue.varPtr = (void *)he->u.val; return JIM_OK; } -/* -------------------- Variables related functions ------------------------- */ + static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr); static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags); -/* For now that's dummy. Variables lookup should be optimized - * in many ways, with caching of lookups, and possibly with - * a table of pre-allocated vars in every CallFrame for local vars. - * All the caching should also have an 'epoch' mechanism similar - * to the one used by Tcl for procedures lookup caching. */ int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr) { const char *name; Jim_Var *var; @@ -10356,35 +8960,35 @@ int err; if ((err = SetVariableFromAny(interp, nameObjPtr)) != JIM_OK) { Jim_CallFrame *framePtr = interp->framePtr; - /* Check for [dict] syntax sugar. */ + if (err == JIM_DICT_SUGAR) return JimDictSugarSet(interp, nameObjPtr, valObjPtr); if (JimValidName(interp, "variable", nameObjPtr) != JIM_OK) { return JIM_ERR; } - /* New variable to create */ + name = Jim_String(nameObjPtr); var = Jim_Alloc(sizeof(*var)); var->objPtr = valObjPtr; Jim_IncrRefCount(valObjPtr); var->linkFramePtr = NULL; - /* Insert the new variable */ + if (name[0] == ':' && name[1] == ':') { - /* Into the top level frame */ + framePtr = interp->topFramePtr; Jim_AddHashEntry(&framePtr->vars, name + 2, var); } else { Jim_AddHashEntry(&framePtr->vars, name, var); } - /* Make the object int rep a variable */ + Jim_FreeIntRep(interp, nameObjPtr); nameObjPtr->typePtr = &variableObjType; nameObjPtr->internalRep.varValue.callFrameId = framePtr->id; nameObjPtr->internalRep.varValue.varPtr = var; } @@ -10393,11 +8997,11 @@ if (var->linkFramePtr == NULL) { Jim_IncrRefCount(valObjPtr); Jim_DecrRefCount(interp, var->objPtr); var->objPtr = valObjPtr; } - else { /* Else handle the link */ + else { Jim_CallFrame *savedCallFrame; savedCallFrame = interp->framePtr; interp->framePtr = var->linkFramePtr; err = Jim_SetVariable(interp, var->objPtr, valObjPtr); @@ -10455,38 +9059,38 @@ int len; varName = Jim_GetString(nameObjPtr, &len); if (varName[0] == ':' && varName[1] == ':') { - /* Linking a global var does nothing */ + return JIM_OK; } if (JimNameIsDictSugar(varName, len)) { Jim_SetResultString(interp, "Dict key syntax invalid as link source", -1); return JIM_ERR; } - /* Check for an existing variable or link */ + if (SetVariableFromAny(interp, nameObjPtr) == JIM_OK) { Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr; if (varPtr->linkFramePtr == NULL) { Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr); return JIM_ERR; } - /* It exists, but is a link, so delete the link */ + varPtr->linkFramePtr = NULL; } - /* Check for cycles. */ + if (interp->framePtr == targetCallFrame) { Jim_Obj *objPtr = targetNameObjPtr; Jim_Var *varPtr; - /* Cycles are only possible with 'uplevel 0' */ + while (1) { if (Jim_StringEqObj(objPtr, nameObjPtr)) { Jim_SetResultString(interp, "can't upvar from variable to itself", -1); return JIM_ERR; } @@ -10497,27 +9101,17 @@ break; objPtr = varPtr->objPtr; } } - /* Perform the binding */ + Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr); - /* We are now sure 'nameObjPtr' type is variableObjType */ + nameObjPtr->internalRep.varValue.varPtr->linkFramePtr = targetCallFrame; return JIM_OK; } -/* Return the Jim_Obj pointer associated with a variable name, - * or NULL if the variable was not found in the current context. - * The same optimization discussed in the comment to the - * 'SetVariable' function should apply here. - * - * If JIM_UNSHARED is set and the variable is an array element (dict sugar) - * in a dictionary which is shared, the array variable value is duplicated first. - * This allows the array element to be updated (e.g. append, lappend) without - * affecting other references to the dictionary. - */ Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags) { switch (SetVariableFromAny(interp, nameObjPtr)) { case JIM_OK:{ Jim_Var *varPtr = nameObjPtr->internalRep.varValue.varPtr; @@ -10526,26 +9120,26 @@ return varPtr->objPtr; } else { Jim_Obj *objPtr; - /* The variable is a link? Resolve it. */ + Jim_CallFrame *savedCallFrame = interp->framePtr; interp->framePtr = varPtr->linkFramePtr; objPtr = Jim_GetVariable(interp, varPtr->objPtr, flags); interp->framePtr = savedCallFrame; if (objPtr) { return objPtr; } - /* Error, so fall through to the error message */ + } } break; case JIM_DICT_SUGAR: - /* [dict] syntax sugar. */ + return JimDictSugarGet(interp, nameObjPtr, flags); } if (flags & JIM_ERRMSG) { Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr); } @@ -10587,28 +9181,25 @@ interp->framePtr = savedFramePtr; return objPtr; } -/* Unset a variable. - * Note: On success unset invalidates all the variable objects created - * in the current call frame incrementing. */ int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags) { const char *name; Jim_Var *varPtr; int retval; retval = SetVariableFromAny(interp, nameObjPtr); if (retval == JIM_DICT_SUGAR) { - /* [dict] syntax sugar. */ + return JimDictSugarSet(interp, nameObjPtr, NULL); } else if (retval == JIM_OK) { varPtr = nameObjPtr->internalRep.varValue.varPtr; - /* If it's a link call UnsetVariable recursively */ + if (varPtr->linkFramePtr) { Jim_CallFrame *savedCallFrame; savedCallFrame = interp->framePtr; interp->framePtr = varPtr->linkFramePtr; @@ -10623,11 +9214,11 @@ framePtr = interp->topFramePtr; name += 2; } retval = Jim_DeleteHashEntry(&framePtr->vars, name); if (retval == JIM_OK) { - /* Change the callframe id, invalidating var lookup caching */ + JimChangeCallFrameId(interp, framePtr); } } } if (retval != JIM_OK && (flags & JIM_ERRMSG)) { @@ -10634,19 +9225,12 @@ Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr); } return retval; } -/* ---------- Dict syntax sugar (similar to array Tcl syntax) -------------- */ -/* Given a variable name for [dict] operation syntax sugar, - * this function returns two objects, the first with the name - * of the variable to set, and the second with the rispective key. - * For example "foo(bar)" will return objects with string repr. of - * "foo" and "bar". - * - * The returned objects have refcount = 1. The function can't fail. */ + static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr) { const char *str, *p; int len, keyLen; @@ -10653,66 +9237,58 @@ Jim_Obj *varObjPtr, *keyObjPtr; str = Jim_GetString(objPtr, &len); p = strchr(str, '('); - JimPanic((p == NULL, interp, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str)); + JimPanic((p == NULL, "JimDictSugarParseVarKey() called for non-dict-sugar (%s)", str)); varObjPtr = Jim_NewStringObj(interp, str, p - str); p++; keyLen = (str + len) - p; if (str[len - 1] == ')') { keyLen--; } - /* Create the objects with the variable name and key. */ + keyObjPtr = Jim_NewStringObj(interp, p, keyLen); Jim_IncrRefCount(varObjPtr); Jim_IncrRefCount(keyObjPtr); *varPtrPtr = varObjPtr; *keyPtrPtr = keyObjPtr; } -/* Helper of Jim_SetVariable() to deal with dict-syntax variable names. - * Also used by Jim_UnsetVariable() with valObjPtr = NULL. */ static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *valObjPtr) { int err; SetDictSubstFromAny(interp, objPtr); err = Jim_SetDictKeysVector(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, - &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr); + &objPtr->internalRep.dictSubstValue.indexObjPtr, 1, valObjPtr, JIM_ERRMSG); if (err == JIM_OK) { - /* Don't keep an extra ref to the result */ + Jim_SetEmptyResult(interp); } else { if (!valObjPtr) { - /* Better error message for unset a(2) where a exists but a(2) doesn't */ + if (Jim_GetVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, JIM_NONE)) { Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such element in array", objPtr); return err; } } - /* Make the error more informative and Tcl-compatible */ + Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array", (valObjPtr ? "set" : "unset"), objPtr); } return err; } -/** - * Expands the array variable (dict sugar) and returns the result, or NULL on error. - * - * If JIM_UNSHARED is set and the dictionary is shared, it will be duplicated - * and stored back to the variable before expansion. - */ static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr, Jim_Obj *keyObjPtr, int flags) { Jim_Obj *dictObjPtr; Jim_Obj *resObjPtr = NULL; @@ -10736,31 +9312,31 @@ } } else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) { dictObjPtr = Jim_DuplicateObj(interp, dictObjPtr); if (Jim_SetVariable(interp, varObjPtr, dictObjPtr) != JIM_OK) { - /* This can probably never happen */ - JimPanic((1, interp, "SetVariable failed for JIM_UNSHARED")); + + JimPanic((1, "SetVariable failed for JIM_UNSHARED")); } - /* We know that the key exists. Get the result in the now-unshared dictionary */ + Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE); } return resObjPtr; } -/* Helper of Jim_GetVariable() to deal with dict-syntax variable names */ + static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *objPtr, int flags) { SetDictSubstFromAny(interp, objPtr); return JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr, objPtr->internalRep.dictSubstValue.indexObjPtr, flags); } -/* --------- $var(INDEX) substitution, using a specialized object ----------- */ + void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr) { Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr); Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr); @@ -10774,18 +9350,18 @@ srcPtr->internalRep.dictSubstValue.varNameObjPtr; dupPtr->internalRep.dictSubstValue.indexObjPtr = srcPtr->internalRep.dictSubstValue.indexObjPtr; dupPtr->typePtr = &dictSubstObjType; } -/* Note: The object *must* be in dict-sugar format */ + static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr) { if (objPtr->typePtr != &dictSubstObjType) { Jim_Obj *varObjPtr, *keyObjPtr; if (objPtr->typePtr == &interpolatedObjType) { - /* An interpolated object in dict-sugar form */ + const ScriptToken *token = objPtr->internalRep.twoPtrValue.ptr1; varObjPtr = token[0].objPtr; keyObjPtr = objPtr->internalRep.twoPtrValue.ptr2; @@ -10802,16 +9378,10 @@ objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr; objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr; } } -/* This function is used to expand [dict get] sugar in the form - * of $var(INDEX). The function is mainly used by Jim_EvalObj() - * to deal with tokens of type JIM_TT_DICTSUGAR. objPtr points to an - * object that is *guaranteed* to be in the form VARNAME(INDEX). - * The 'index' part is [subst]ituted, and is used to lookup a key inside - * the [dict]ionary contained in variable VARNAME. */ static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr) { Jim_Obj *resObjPtr = NULL; Jim_Obj *substKeyObjPtr = NULL; @@ -10834,20 +9404,17 @@ static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr) { Jim_Obj *resultObjPtr; if (Jim_EvalExpression(interp, objPtr, &resultObjPtr) == JIM_OK) { - /* Note that the result has a ref count of 1, but we need a ref count of 0 */ + resultObjPtr->refCount--; return resultObjPtr; } return NULL; } -/* ----------------------------------------------------------------------------- - * CallFrame - * ---------------------------------------------------------------------------*/ static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent) { Jim_CallFrame *cf; @@ -10872,18 +9439,18 @@ if (cf->vars.table == NULL) Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp); return cf; } -/* Used to invalidate every caching related to callframe stability. */ + static void JimChangeCallFrameId(Jim_Interp *interp, Jim_CallFrame *cf) { cf->id = interp->callFrameEpoch++; } -#define JIM_FCF_NONE 0 /* no flags */ -#define JIM_FCF_NOHT 1 /* don't free the hash table */ +#define JIM_FCF_NONE 0 +#define JIM_FCF_NOHT 1 static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int flags) { if (cf->procArgsObjPtr) Jim_DecrRefCount(interp, cf->procArgsObjPtr); if (cf->procBodyObjPtr) @@ -10900,11 +9467,11 @@ Jim_HashEntry *nextEntry = he->next; Jim_Var *varPtr = (void *)he->u.val; Jim_DecrRefCount(interp, varPtr->objPtr); Jim_Free(he->u.val); - Jim_Free((void *)he->key); /* ATTENTION: const cast */ + Jim_Free((void *)he->key); Jim_Free(he); table[i] = NULL; he = nextEntry; } } @@ -10912,20 +9479,12 @@ } cf->nextFramePtr = interp->freeFramesList; interp->freeFramesList = cf; } -/* ----------------------------------------------------------------------------- - * References - * ---------------------------------------------------------------------------*/ #ifdef JIM_REFERENCES -/* References HashTable Type. - * - * Keys are jim_wide integers, dynamically allocated for now but in the - * future it's worth to cache this 8 bytes objects. Values are poitners - * to Jim_References. */ static void JimReferencesHTValDestructor(void *interp, void *val) { Jim_Reference *refPtr = (void *)val; Jim_DecrRefCount(interp, refPtr->objPtr); @@ -10935,11 +9494,11 @@ Jim_Free(val); } static unsigned int JimReferencesHTHashFunction(const void *key) { - /* Only the least significant bits are used. */ + const jim_wide *widePtr = key; unsigned int intValue = (unsigned int)*widePtr; return Jim_IntHashFunction(intValue); } @@ -10967,28 +9526,19 @@ Jim_Free((void *)key); } static const Jim_HashTableType JimReferencesHashTableType = { - JimReferencesHTHashFunction, /* hash function */ - JimReferencesHTKeyDup, /* key dup */ - NULL, /* val dup */ - JimReferencesHTKeyCompare, /* key compare */ - JimReferencesHTKeyDestructor, /* key destructor */ - JimReferencesHTValDestructor /* val destructor */ + JimReferencesHTHashFunction, + JimReferencesHTKeyDup, + NULL, + JimReferencesHTKeyCompare, + JimReferencesHTKeyDestructor, + JimReferencesHTValDestructor }; -/* ----------------------------------------------------------------------------- - * Reference object type and References API - * ---------------------------------------------------------------------------*/ -/* The string representation of references has two features in order - * to make the GC faster. The first is that every reference starts - * with a non common character '<', in order to make the string matching - * faster. The second is that the reference string rep is 42 characters - * in length, this allows to avoid to check every object with a string - * repr < 42, and usually there aren't many of these objects. */ #define JIM_REFERENCE_SPACE (35+JIM_REFERENCE_TAGLEN) static int JimFormatReference(char *buf, Jim_Reference *refPtr, jim_wide id) { @@ -11019,12 +9569,10 @@ objPtr->bytes = Jim_Alloc(len + 1); memcpy(objPtr->bytes, buf, len + 1); objPtr->length = len; } -/* returns true if 'c' is a valid reference tag character. - * i.e. inside the range [_a-zA-Z0-9] */ static int isrefchar(int c) { return (c == '_' || isalnum(c)); } @@ -11035,48 +9583,48 @@ const char *str, *start, *end; char refId[21]; Jim_Reference *refPtr; Jim_HashEntry *he; - /* Get the string representation */ + str = Jim_GetString(objPtr, &len); - /* Check if it looks like a reference */ + if (len < JIM_REFERENCE_SPACE) goto badformat; - /* Trim spaces */ + start = str; end = str + len - 1; while (*start == ' ') start++; while (*end == ' ' && end > start) end--; if (end - start + 1 != JIM_REFERENCE_SPACE) goto badformat; - /* <reference.<1234567>.%020> */ + if (memcmp(start, "<reference.<", 12) != 0) goto badformat; if (start[12 + JIM_REFERENCE_TAGLEN] != '>' || end[0] != '>') goto badformat; - /* The tag can't contain chars other than a-zA-Z0-9 + '_'. */ + for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) { if (!isrefchar(start[12 + i])) goto badformat; } - /* Extract info from the reference. */ + memcpy(refId, start + 14 + JIM_REFERENCE_TAGLEN, 20); refId[20] = '\0'; - /* Try to convert the ID into a jim_wide */ + if (Jim_StringToWide(refId, &wideValue, 10) != JIM_OK) goto badformat; - /* Check if the reference really exists! */ + he = Jim_FindHashEntry(&interp->references, &wideValue); if (he == NULL) { Jim_SetResultFormatted(interp, "invalid reference id \"%#s\"", objPtr); return JIM_ERR; } refPtr = he->u.val; - /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &referenceObjType; objPtr->internalRep.refValue.id = wideValue; objPtr->internalRep.refValue.refPtr = refPtr; return JIM_OK; @@ -11084,22 +9632,19 @@ badformat: Jim_SetResultFormatted(interp, "expected reference but got \"%#s\"", objPtr); return JIM_ERR; } -/* Returns a new reference pointing to objPtr, having cmdNamePtr - * as finalizer command (or NULL if there is no finalizer). - * The returned reference object has refcount = 0. */ Jim_Obj *Jim_NewReference(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr) { struct Jim_Reference *refPtr; jim_wide wideValue = interp->referenceNextId; Jim_Obj *refObjPtr; const char *tag; int tagLen, i; - /* Perform the Garbage Collection if needed. */ + Jim_CollectIfNeeded(interp); refPtr = Jim_Alloc(sizeof(*refPtr)); refPtr->objPtr = objPtr; Jim_IncrRefCount(objPtr); @@ -11111,12 +9656,10 @@ refObjPtr->typePtr = &referenceObjType; refObjPtr->bytes = NULL; refObjPtr->internalRep.refValue.id = interp->referenceNextId; refObjPtr->internalRep.refValue.refPtr = refPtr; interp->referenceNextId++; - /* Set the tag. Trimmed at JIM_REFERENCE_TAGLEN. Everything - * that does not pass the 'isrefchar' test is replaced with '_' */ tag = Jim_GetString(tagPtr, &tagLen); if (tagLen > JIM_REFERENCE_TAGLEN) tagLen = JIM_REFERENCE_TAGLEN; for (i = 0; i < JIM_REFERENCE_TAGLEN; i++) { if (i < tagLen && isrefchar(tag[i])) @@ -11156,156 +9699,25 @@ return JIM_ERR; *cmdNamePtrPtr = refPtr->finalizerCmdNamePtr; return JIM_OK; } -/* ----------------------------------------------------------------------------- - * References Garbage Collection - * ---------------------------------------------------------------------------*/ -/* This the hash table type for the "MARK" phase of the GC */ + static const Jim_HashTableType JimRefMarkHashTableType = { - JimReferencesHTHashFunction, /* hash function */ - JimReferencesHTKeyDup, /* key dup */ - NULL, /* val dup */ - JimReferencesHTKeyCompare, /* key compare */ - JimReferencesHTKeyDestructor, /* key destructor */ - NULL /* val destructor */ + JimReferencesHTHashFunction, + JimReferencesHTKeyDup, + NULL, + JimReferencesHTKeyCompare, + JimReferencesHTKeyDestructor, + NULL }; -/* Performs the garbage collection. */ + int Jim_Collect(Jim_Interp *interp) { - Jim_HashTable marks; - Jim_HashTableIterator *htiter; - Jim_HashEntry *he; - Jim_Obj *objPtr; int collected = 0; - - /* Avoid recursive calls */ - if (interp->lastCollectId == -1) { - /* Jim_Collect() already running. Return just now. */ - return 0; - } - interp->lastCollectId = -1; - - /* Mark all the references found into the 'mark' hash table. - * The references are searched in every live object that - * is of a type that can contain references. */ - Jim_InitHashTable(&marks, &JimRefMarkHashTableType, NULL); - objPtr = interp->liveList; - while (objPtr) { - if (objPtr->typePtr == NULL || objPtr->typePtr->flags & JIM_TYPE_REFERENCES) { - const char *str, *p; - int len; - - /* If the object is of type reference, to get the - * Id is simple... */ - if (objPtr->typePtr == &referenceObjType) { - Jim_AddHashEntry(&marks, &objPtr->internalRep.refValue.id, NULL); -#ifdef JIM_DEBUG_GC - printf("MARK (reference): %d refcount: %d" JIM_NL, - (int)objPtr->internalRep.refValue.id, objPtr->refCount); -#endif - objPtr = objPtr->nextObjPtr; - continue; - } - /* Get the string repr of the object we want - * to scan for references. */ - p = str = Jim_GetString(objPtr, &len); - /* Skip objects too little to contain references. */ - if (len < JIM_REFERENCE_SPACE) { - objPtr = objPtr->nextObjPtr; - continue; - } - /* Extract references from the object string repr. */ - while (1) { - int i; - jim_wide id; - char buf[21]; - - if ((p = strstr(p, "<reference.<")) == NULL) - break; - /* Check if it's a valid reference. */ - if (len - (p - str) < JIM_REFERENCE_SPACE) - break; - if (p[41] != '>' || p[19] != '>' || p[20] != '.') - break; - for (i = 21; i <= 40; i++) - if (!isdigit(UCHAR(p[i]))) - break; - /* Get the ID */ - memcpy(buf, p + 21, 20); - buf[20] = '\0'; - Jim_StringToWide(buf, &id, 10); - - /* Ok, a reference for the given ID - * was found. Mark it. */ - Jim_AddHashEntry(&marks, &id, NULL); -#ifdef JIM_DEBUG_GC - printf("MARK: %d" JIM_NL, (int)id); -#endif - p += JIM_REFERENCE_SPACE; - } - } - objPtr = objPtr->nextObjPtr; - } - - /* Run the references hash table to destroy every reference that - * is not referenced outside (not present in the mark HT). */ - htiter = Jim_GetHashTableIterator(&interp->references); - while ((he = Jim_NextHashEntry(htiter)) != NULL) { - const jim_wide *refId; - Jim_Reference *refPtr; - - refId = he->key; - /* Check if in the mark phase we encountered - * this reference. */ - if (Jim_FindHashEntry(&marks, refId) == NULL) { -#ifdef JIM_DEBUG_GC - printf("COLLECTING %d" JIM_NL, (int)*refId); -#endif - collected++; - /* Drop the reference, but call the - * finalizer first if registered. */ - refPtr = he->u.val; - if (refPtr->finalizerCmdNamePtr) { - char *refstr = Jim_Alloc(JIM_REFERENCE_SPACE + 1); - Jim_Obj *objv[3], *oldResult; - - JimFormatReference(refstr, refPtr, *refId); - - objv[0] = refPtr->finalizerCmdNamePtr; - objv[1] = Jim_NewStringObjNoAlloc(interp, refstr, 32); - objv[2] = refPtr->objPtr; - Jim_IncrRefCount(objv[0]); - Jim_IncrRefCount(objv[1]); - Jim_IncrRefCount(objv[2]); - - /* Drop the reference itself */ - Jim_DeleteHashEntry(&interp->references, refId); - - /* Call the finalizer. Errors ignored. */ - oldResult = interp->result; - Jim_IncrRefCount(oldResult); - Jim_EvalObjVector(interp, 3, objv); - Jim_SetResult(interp, oldResult); - Jim_DecrRefCount(interp, oldResult); - - Jim_DecrRefCount(interp, objv[0]); - Jim_DecrRefCount(interp, objv[1]); - Jim_DecrRefCount(interp, objv[2]); - } - else { - Jim_DeleteHashEntry(&interp->references, refId); - } - } - } - Jim_FreeHashTableIterator(htiter); - Jim_FreeHashTable(&marks); - interp->lastCollectId = interp->referenceNextId; - interp->lastCollectTime = time(NULL); return collected; } #define JIM_COLLECT_ID_PERIOD 5000 #define JIM_COLLECT_TIME_PERIOD 300 @@ -11333,53 +9745,47 @@ } uval = {0x0102}; return uval.c[0] == 1; } -/* ----------------------------------------------------------------------------- - * Interpreter related functions - * ---------------------------------------------------------------------------*/ Jim_Interp *Jim_CreateInterp(void) { Jim_Interp *i = Jim_Alloc(sizeof(*i)); memset(i, 0, sizeof(*i)); - i->errorFileName = Jim_StrDup(""); i->maxNestingDepth = JIM_MAX_NESTING_DEPTH; i->lastCollectTime = time(NULL); - /* Note that we can create objects only after the - * interpreter liveList and freeList pointers are - * initialized to NULL. */ Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i); #ifdef JIM_REFERENCES Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i); #endif - Jim_InitHashTable(&i->sharedStrings, &JimSharedStringsHashTableType, NULL); Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i); Jim_InitHashTable(&i->packages, &JimStringKeyValCopyHashTableType, NULL); i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL); i->emptyObj = Jim_NewEmptyStringObj(i); i->trueObj = Jim_NewIntObj(i, 1); i->falseObj = Jim_NewIntObj(i, 0); + i->errorFileNameObj = i->emptyObj; i->result = i->emptyObj; i->stackTrace = Jim_NewListObj(i, NULL, 0); i->unknown = Jim_NewStringObj(i, "unknown", -1); i->errorProc = i->emptyObj; i->currentScriptObj = Jim_NewEmptyStringObj(i); Jim_IncrRefCount(i->emptyObj); + Jim_IncrRefCount(i->errorFileNameObj); Jim_IncrRefCount(i->result); Jim_IncrRefCount(i->stackTrace); Jim_IncrRefCount(i->unknown); Jim_IncrRefCount(i->currentScriptObj); Jim_IncrRefCount(i->errorProc); Jim_IncrRefCount(i->trueObj); Jim_IncrRefCount(i->falseObj); - /* Initialize key variables every interpreter should contain */ + Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY); Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0"); Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS); Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM); @@ -11402,11 +9808,11 @@ Jim_DecrRefCount(i, i->falseObj); Jim_DecrRefCount(i, i->result); Jim_DecrRefCount(i, i->stackTrace); Jim_DecrRefCount(i, i->errorProc); Jim_DecrRefCount(i, i->unknown); - Jim_Free((void *)i->errorFileName); + Jim_DecrRefCount(i, i->errorFileNameObj); Jim_DecrRefCount(i, i->currentScriptObj); Jim_FreeHashTable(&i->commands); #ifdef JIM_REFERENCES Jim_FreeHashTable(&i->references); #endif @@ -11413,18 +9819,16 @@ Jim_FreeHashTable(&i->packages); Jim_Free(i->prngState); Jim_FreeHashTable(&i->assocData); JimDeleteLocalProcs(i); - /* Free the call frames list */ + while (cf) { prevcf = cf->parentCallFrame; JimFreeCallFrame(i, cf, JIM_FCF_NONE); cf = prevcf; } - /* Check that the live object list is empty, otherwise - * there is a memory leak. */ if (i->liveList != NULL) { objPtr = i->liveList; printf(JIM_NL "-------------------------------------" JIM_NL); printf("Objects still in the free list:" JIM_NL); @@ -11433,26 +9837,26 @@ printf("%p (%d) %-10s: '%.20s'" JIM_NL, (void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)"); if (objPtr->typePtr == &sourceObjType) { printf("FILE %s LINE %d" JIM_NL, - objPtr->internalRep.sourceValue.fileName, + Jim_String(objPtr->internalRep.sourceValue.fileNameObj), objPtr->internalRep.sourceValue.lineNumber); } objPtr = objPtr->nextObjPtr; } printf("-------------------------------------" JIM_NL JIM_NL); - JimPanic((1, i, "Live list non empty freeing the interpreter! Leak?")); + JimPanic((1, "Live list non empty freeing the interpreter! Leak?")); } - /* Free all the freed objects. */ + objPtr = i->freeList; while (objPtr) { nextObjPtr = objPtr->nextObjPtr; Jim_Free(objPtr); objPtr = nextObjPtr; } - /* Free cached CallFrame structures */ + cf = i->freeFramesList; while (cf) { nextcf = cf->nextFramePtr; if (cf->vars.table != NULL) Jim_Free(cf->vars.table); @@ -11461,29 +9865,14 @@ } #ifdef jim_ext_load Jim_FreeLoadHandles(i); #endif - /* Free the sharedString hash table. Make sure to free it - * after every other Jim_Object was freed. */ - Jim_FreeHashTable(&i->sharedStrings); - /* Free the interpreter structure. */ + Jim_Free(i); } -/* Returns the call frame relative to the level represented by - * levelObjPtr. If levelObjPtr == NULL, the * level is assumed to be '1'. - * - * This function accepts the 'level' argument in the form - * of the commands [uplevel] and [upvar]. - * - * For a function accepting a relative integer as level suitable - * for implementation of [info level ?level?] check the - * JimGetCallFrameByInteger() function. - * - * Returns NULL on error. - */ Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr) { long level; const char *str; Jim_CallFrame *framePtr; @@ -11501,25 +9890,25 @@ else { if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) { level = -1; } else { - /* Convert from a relative to an absolute level */ + level = interp->framePtr->level - level; } } } else { - str = "1"; /* Needed to format the error message. */ + str = "1"; level = interp->framePtr->level - 1; } if (level == 0) { return interp->topFramePtr; } if (level > 0) { - /* Lookup */ + for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parentCallFrame) { if (framePtr->level == level) { return framePtr; } } @@ -11527,29 +9916,26 @@ Jim_SetResultFormatted(interp, "bad level \"%s\"", str); return NULL; } -/* Similar to Jim_GetCallFrameByLevel() but the level is specified - * as a relative integer like in the [info level ?level?] command. - **/ static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, Jim_Obj *levelObjPtr) { long level; Jim_CallFrame *framePtr; if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) { if (level <= 0) { - /* Convert from a relative to an absolute level */ + level = interp->framePtr->level + level; } if (level == 0) { return interp->topFramePtr; } - /* Lookup */ + for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parentCallFrame) { if (framePtr->level == level) { return framePtr; } } @@ -11557,21 +9943,10 @@ Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr); return NULL; } -static void JimSetErrorFileName(Jim_Interp *interp, const char *filename) -{ - Jim_Free((void *)interp->errorFileName); - interp->errorFileName = Jim_StrDup(filename); -} - -static void JimSetErrorLineNumber(Jim_Interp *interp, int linenr) -{ - interp->errorLine = linenr; -} - static void JimResetStackTrace(Jim_Interp *interp) { Jim_DecrRefCount(interp, interp->stackTrace); interp->stackTrace = Jim_NewListObj(interp, NULL, 0); Jim_IncrRefCount(interp->stackTrace); @@ -11579,81 +9954,69 @@ static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj) { int len; - /* Increment reference first in case these are the same object */ + Jim_IncrRefCount(stackTraceObj); Jim_DecrRefCount(interp, interp->stackTrace); interp->stackTrace = stackTraceObj; interp->errorFlag = 1; - /* This is a bit ugly. - * If the filename of the last entry of the stack trace is empty, - * the next stack level should be added. - */ len = Jim_ListLength(interp, interp->stackTrace); if (len >= 3) { Jim_Obj *filenameObj; Jim_ListIndex(interp, interp->stackTrace, len - 2, &filenameObj, JIM_NONE); Jim_GetString(filenameObj, &len); - if (len == 0) { + if (!Jim_Length(filenameObj)) { interp->addStackTrace = 1; } } } -/* Returns 1 if the stack trace information was used or 0 if not */ + static void JimAppendStackTrace(Jim_Interp *interp, const char *procname, - const char *filename, int linenr) + Jim_Obj *fileNameObj, int linenr) { if (strcmp(procname, "unknown") == 0) { procname = ""; } - if (!*procname && !*filename) { - /* No useful info here */ + if (!*procname && !Jim_Length(fileNameObj)) { + return; } if (Jim_IsShared(interp->stackTrace)) { Jim_DecrRefCount(interp, interp->stackTrace); interp->stackTrace = Jim_DuplicateObj(interp, interp->stackTrace); Jim_IncrRefCount(interp->stackTrace); } - /* If we have no procname but the previous element did, merge with that frame */ - if (!*procname && *filename) { - /* Just a filename. Check the previous entry */ + + if (!*procname && Jim_Length(fileNameObj)) { + int len = Jim_ListLength(interp, interp->stackTrace); if (len >= 3) { - Jim_Obj *procnameObj; - Jim_Obj *filenameObj; - - if (Jim_ListIndex(interp, interp->stackTrace, len - 3, &procnameObj, JIM_NONE) == JIM_OK - && Jim_ListIndex(interp, interp->stackTrace, len - 2, &filenameObj, - JIM_NONE) == JIM_OK) { - - const char *prev_procname = Jim_String(procnameObj); - const char *prev_filename = Jim_String(filenameObj); - - if (*prev_procname && !*prev_filename) { - ListSetIndex(interp, interp->stackTrace, len - 2, Jim_NewStringObj(interp, - filename, -1), 0); - ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), - 0); + Jim_Obj *objPtr; + if (Jim_ListIndex(interp, interp->stackTrace, len - 3, &objPtr, JIM_NONE) == JIM_OK && Jim_Length(objPtr)) { + + if (Jim_ListIndex(interp, interp->stackTrace, len - 2, &objPtr, JIM_NONE) == JIM_OK && !Jim_Length(objPtr)) { + + ListSetIndex(interp, interp->stackTrace, len - 2, fileNameObj, 0); + ListSetIndex(interp, interp->stackTrace, len - 1, Jim_NewIntObj(interp, linenr), 0); return; } } } } Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewStringObj(interp, procname, -1)); - Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewStringObj(interp, filename, -1)); + Jim_ListAppendElement(interp, interp->stackTrace, fileNameObj); Jim_ListAppendElement(interp, interp->stackTrace, Jim_NewIntObj(interp, linenr)); } int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc, void *data) @@ -11685,57 +10048,10 @@ int Jim_GetExitCode(Jim_Interp *interp) { return interp->exitCode; } -/* ----------------------------------------------------------------------------- - * Shared strings. - * Every interpreter has an hash table where to put shared dynamically - * allocate strings that are likely to be used a lot of times. - * For example, in the 'source' object type, there is a pointer to - * the filename associated with that object. Every script has a lot - * of this objects with the identical file name, so it is wise to share - * this info. - * - * The API is trivial: Jim_GetSharedString(interp, "foobar") - * returns the pointer to the shared string. Every time a reference - * to the string is no longer used, the user should call - * Jim_ReleaseSharedString(interp, stringPointer). Once no one is using - * a given string, it is removed from the hash table. - * ---------------------------------------------------------------------------*/ -const char *Jim_GetSharedString(Jim_Interp *interp, const char *str) -{ - Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str); - - if (he == NULL) { - char *strCopy = Jim_StrDup(str); - - Jim_AddHashEntry(&interp->sharedStrings, strCopy, NULL); - he = Jim_FindHashEntry(&interp->sharedStrings, strCopy); - he->u.intval = 1; - return strCopy; - } - else { - he->u.intval++; - return he->key; - } -} - -void Jim_ReleaseSharedString(Jim_Interp *interp, const char *str) -{ - Jim_HashEntry *he = Jim_FindHashEntry(&interp->sharedStrings, str); - - JimPanic((he == NULL, interp, "Jim_ReleaseSharedString called with " "unknown shared string '%s'", str)); - - if (--he->u.intval == 0) { - Jim_DeleteHashEntry(&interp->sharedStrings, str); - } -} - -/* ----------------------------------------------------------------------------- - * Integer object - * ---------------------------------------------------------------------------*/ #define JIM_INTEGER_SPACE 24 static void UpdateStringOfInt(struct Jim_Obj *objPtr); static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags); @@ -11745,15 +10061,10 @@ NULL, UpdateStringOfInt, JIM_TYPE_NONE, }; -/* A coerced double is closer to an int than a double. - * It is an int value temporarily masquerading as a double value. - * i.e. it has the same string value as an int and Jim_GetWide() - * succeeds, but also Jim_GetDouble() returns the value directly. - */ static const Jim_ObjType coercedDoubleObjType = { "coerced-double", NULL, NULL, UpdateStringOfInt, @@ -11776,18 +10087,18 @@ { jim_wide wideValue; const char *str; if (objPtr->typePtr == &coercedDoubleObjType) { - /* Simple switcheroo */ + objPtr->typePtr = &intObjType; return JIM_OK; } - /* Get the string representation */ + str = Jim_String(objPtr); - /* Try to convert into a jim_wide */ + if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) { if (flags & JIM_ERRMSG) { Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr); } return JIM_ERR; @@ -11794,11 +10105,11 @@ } if ((wideValue == JIM_WIDE_MIN || wideValue == JIM_WIDE_MAX) && errno == ERANGE) { Jim_SetResultString(interp, "Integer value too big to be represented", -1); return JIM_ERR; } - /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &intObjType; objPtr->internalRep.wideValue = wideValue; return JIM_OK; } @@ -11816,11 +10127,11 @@ return JIM_ERR; *widePtr = JimWideValue(objPtr); return JIM_OK; } -/* Get a wide but does not set an error if the format is bad. */ + static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr) { if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR) return JIM_ERR; *widePtr = JimWideValue(objPtr); @@ -11849,13 +10160,10 @@ objPtr->bytes = NULL; objPtr->internalRep.wideValue = wideValue; return objPtr; } -/* ----------------------------------------------------------------------------- - * Double object - * ---------------------------------------------------------------------------*/ #define JIM_DOUBLE_SPACE 30 static void UpdateStringOfDouble(struct Jim_Obj *objPtr); static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr); @@ -11882,44 +10190,41 @@ { double doubleValue; jim_wide wideValue; const char *str; - /* Preserve the string representation. - * Needed so we can convert back to int without loss - */ str = Jim_String(objPtr); #ifdef HAVE_LONG_LONG - /* Assume a 53 bit mantissa */ + #define MIN_INT_IN_DOUBLE -(1LL << 53) #define MAX_INT_IN_DOUBLE -(MIN_INT_IN_DOUBLE + 1) if (objPtr->typePtr == &intObjType && JimWideValue(objPtr) >= MIN_INT_IN_DOUBLE && JimWideValue(objPtr) <= MAX_INT_IN_DOUBLE) { - /* Direct conversion to coerced double */ + objPtr->typePtr = &coercedDoubleObjType; return JIM_OK; } else #endif if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) { - /* Managed to convert to an int, so we can use this as a cooerced double */ + Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &coercedDoubleObjType; objPtr->internalRep.wideValue = wideValue; return JIM_OK; } else { - /* Try to convert into a double */ + if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) { Jim_SetResultFormatted(interp, "expected number but got \"%#s\"", objPtr); return JIM_ERR; } - /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); } objPtr->typePtr = &doubleObjType; objPtr->internalRep.doubleValue = doubleValue; return JIM_OK; @@ -11952,23 +10257,17 @@ objPtr->bytes = NULL; objPtr->internalRep.doubleValue = doubleValue; return objPtr; } -/* ----------------------------------------------------------------------------- - * List object - * ---------------------------------------------------------------------------*/ +static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec); static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr); static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static void UpdateStringOfList(struct Jim_Obj *objPtr); static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); -/* Note that while the elements of the list may contain references, - * the list object itself can't. This basically means that the - * list object string representation as a whole can't contain references - * that are not presents in the single elements. */ static const Jim_ObjType listObjType = { "list", FreeListInternalRep, DupListInternalRep, UpdateStringOfList, @@ -12001,21 +10300,18 @@ Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]); } dupPtr->typePtr = &listObjType; } -/* The following function checks if a given string can be encoded - * into a list element without any kind of quoting, surrounded by braces, - * or using escapes to quote. */ #define JIM_ELESTR_SIMPLE 0 #define JIM_ELESTR_BRACE 1 #define JIM_ELESTR_QUOTE 2 static int ListElementQuotingType(const char *s, int len) { int i, level, blevel, trySimple = 1; - /* Try with the SIMPLE case */ + if (len == 0) return JIM_ELESTR_BRACE; if (s[0] == '#') return JIM_ELESTR_BRACE; if (s[0] == '"' || s[0] == '{') { @@ -12043,11 +10339,11 @@ } } return JIM_ELESTR_SIMPLE; testbrace: - /* Test if it's possible to do with braces */ + if (s[len - 1] == '\\') return JIM_ELESTR_QUOTE; level = 0; blevel = 0; for (i = 0; i < len; i++) { @@ -12102,12 +10398,10 @@ return JIM_ELESTR_SIMPLE; } return JIM_ELESTR_QUOTE; } -/* Returns the malloc-ed representation of a string - * using backslash to quote special chars. */ static char *BackslashQuoteString(const char *s, int len, int *qlenPtr) { char *q = Jim_Alloc(len * 2 + 1), *p; p = q; @@ -12166,11 +10460,11 @@ const char *strRep; char *p; int *quotingType; Jim_Obj **ele = objPtr->internalRep.listValue.ele; - /* (Over) Estimate the space needed. */ + quotingType = Jim_Alloc(sizeof(int) * objPtr->internalRep.listValue.len + 1); bufLen = 0; for (i = 0; i < objPtr->internalRep.listValue.len; i++) { int len; @@ -12185,15 +10479,15 @@ break; case JIM_ELESTR_QUOTE: bufLen += len * 2; break; } - bufLen++; /* elements separator. */ + bufLen++; } bufLen++; - /* Generate the string rep. */ + p = objPtr->bytes = Jim_Alloc(bufLen + 1); realLength = 0; for (i = 0; i < objPtr->internalRep.listValue.len; i++) { int len, qlen; char *q; @@ -12219,95 +10513,91 @@ Jim_Free(q); p += qlen; realLength += qlen; break; } - /* Add a separating space */ + if (i + 1 != objPtr->internalRep.listValue.len) { *p++ = ' '; realLength++; } } - *p = '\0'; /* nul term. */ + *p = '\0'; objPtr->length = realLength; Jim_Free(quotingType); } -int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) +static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) { struct JimParserCtx parser; const char *str; int strLen; - const char *filename = NULL; - int linenr = 1; + Jim_Obj *fileNameObj; + int linenr; - /* Try to preserve information about filename / line number */ + if (objPtr->typePtr == &sourceObjType) { - filename = Jim_GetSharedString(interp, objPtr->internalRep.sourceValue.fileName); + fileNameObj = objPtr->internalRep.sourceValue.fileNameObj; linenr = objPtr->internalRep.sourceValue.lineNumber; } + else { + fileNameObj = interp->emptyObj; + linenr = 1; + } + Jim_IncrRefCount(fileNameObj); - /* Get the string representation */ + str = Jim_GetString(objPtr, &strLen); - /* Free the old internal repr just now and initialize the - * new one just now. The string->list conversion can't fail. */ Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &listObjType; objPtr->internalRep.listValue.len = 0; objPtr->internalRep.listValue.maxLen = 0; objPtr->internalRep.listValue.ele = NULL; - /* Convert into a list */ + JimParserInit(&parser, str, strLen, linenr); while (!parser.eof) { Jim_Obj *elementPtr; JimParseList(&parser); if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC) continue; elementPtr = JimParserGetTokenObj(interp, &parser); - JimSetSourceInfo(interp, elementPtr, filename, parser.tline); + JimSetSourceInfo(interp, elementPtr, fileNameObj, parser.tline); ListAppendElement(objPtr, elementPtr); } - if (filename) { - Jim_ReleaseSharedString(interp, filename); - } + Jim_DecrRefCount(interp, fileNameObj); return JIM_OK; } Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len) { Jim_Obj *objPtr; - int i; objPtr = Jim_NewObj(interp); objPtr->typePtr = &listObjType; objPtr->bytes = NULL; objPtr->internalRep.listValue.ele = NULL; objPtr->internalRep.listValue.len = 0; objPtr->internalRep.listValue.maxLen = 0; - for (i = 0; i < len; i++) { - ListAppendElement(objPtr, elements[i]); + + if (len) { + ListInsertElements(objPtr, 0, len, elements); } + return objPtr; } -/* Return a vector of Jim_Obj with the elements of a Jim list, and the - * length of the vector. Note that the user of this function should make - * sure that the list object can't shimmer while the vector returned - * is in use, this vector is the one stored inside the internal representation - * of the list object. This function is not exported, extensions should - * always access to the List object elements using Jim_ListIndex(). */ static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen, Jim_Obj ***listVec) { *listLen = Jim_ListLength(interp, listObj); *listVec = listObj->internalRep.listValue.ele; } -/* Sorting uses ints, but commands may return wide */ + static int JimSign(jim_wide w) { if (w == 0) { return 0; } @@ -12315,11 +10605,11 @@ return -1; } return 1; } -/* ListSortElements type values */ + struct lsort_info { jmp_buf jmpbuf; Jim_Obj *command; Jim_Interp *interp; enum { @@ -12345,11 +10635,11 @@ longjmp(sort_info->jmpbuf, JIM_ERR); } return sort_info->subfn(&lObj, &rObj); } -/* Sort the internal rep of a list. */ + static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj) { return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order; } @@ -12375,11 +10665,11 @@ Jim_Obj *compare_script; int rc; jim_wide ret = 0; - /* This must be a valid list */ + compare_script = Jim_DuplicateObj(sort_info->interp, sort_info->command); Jim_ListAppendElement(sort_info->interp, compare_script, *lhsObj); Jim_ListAppendElement(sort_info->interp, compare_script, *rhsObj); rc = Jim_EvalObj(sort_info->interp, compare_script); @@ -12389,11 +10679,11 @@ } return JimSign(ret) * sort_info->order; } -/* Sort a list *in place*. MUST be called with non-shared objects. */ + static int ListSortElements(Jim_Interp *interp, Jim_Obj *listObjPtr, struct lsort_info *info) { struct lsort_info *prev_info; typedef int (qsort_comparator) (const void *, const void *); @@ -12400,15 +10690,15 @@ int (*fn) (Jim_Obj **, Jim_Obj **); Jim_Obj **vector; int len; int rc; - JimPanic((Jim_IsShared(listObjPtr), interp, "Jim_ListSortElements called with shared object")); + JimPanic((Jim_IsShared(listObjPtr), "Jim_ListSortElements called with shared object")); if (!Jim_IsList(listObjPtr)) SetListFromAny(interp, listObjPtr); - /* Allow lsort to be called reentrantly */ + prev_info = sort_info; sort_info = info; vector = listObjPtr->internalRep.listValue.ele; len = listObjPtr->internalRep.listValue.len; @@ -12424,16 +10714,16 @@ break; case JIM_LSORT_COMMAND: fn = ListSortCommand; break; default: - fn = NULL; /* avoid warning */ - JimPanic((1, interp, "ListSort called with invalid sort type")); + fn = NULL; + JimPanic((1, "ListSort called with invalid sort type")); } if (info->indexed) { - /* Need to interpose a "list index" function */ + info->subfn = fn; fn = ListSortIndexHelper; } if ((rc = setjmp(info->jmpbuf)) == 0) { @@ -12443,30 +10733,25 @@ sort_info = prev_info; return rc; } -/* This is the low-level function to insert elements into a list. - * The higher-level Jim_ListInsertElements() performs shared object - * check and invalidate the string repr. This version is used - * in the internals of the List Object and is not exported. - * - * NOTE: this function can be called only against objects - * with internal type of List. */ static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec) { int currentLen = listPtr->internalRep.listValue.len; int requiredLen = currentLen + elemc; int i; Jim_Obj **point; if (requiredLen > listPtr->internalRep.listValue.maxLen) { - int maxLen = requiredLen * 2; + listPtr->internalRep.listValue.maxLen = requiredLen * 2; - listPtr->internalRep.listValue.ele = - Jim_Realloc(listPtr->internalRep.listValue.ele, sizeof(Jim_Obj *) * maxLen); - listPtr->internalRep.listValue.maxLen = maxLen; + listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele, + sizeof(Jim_Obj *) * listPtr->internalRep.listValue.maxLen); + } + if (idx < 0) { + idx = currentLen; } point = listPtr->internalRep.listValue.ele + idx; memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *)); for (i = 0; i < elemc; ++i) { point[i] = elemVec[i]; @@ -12473,40 +10758,33 @@ Jim_IncrRefCount(point[i]); } listPtr->internalRep.listValue.len += elemc; } -/* Convenience call to ListInsertElements() to append a single element. - */ static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr) { - ListInsertElements(listPtr, listPtr->internalRep.listValue.len, 1, &objPtr); + ListInsertElements(listPtr, -1, 1, &objPtr); } - -/* Appends every element of appendListPtr into listPtr. - * Both have to be of the list type. - * Convenience call to ListInsertElements() - */ static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr) { - ListInsertElements(listPtr, listPtr->internalRep.listValue.len, + ListInsertElements(listPtr, -1, appendListPtr->internalRep.listValue.len, appendListPtr->internalRep.listValue.ele); } void Jim_ListAppendElement(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr) { - JimPanic((Jim_IsShared(listPtr), interp, "Jim_ListAppendElement called with shared object")); + JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendElement called with shared object")); if (!Jim_IsList(listPtr)) SetListFromAny(interp, listPtr); Jim_InvalidateStringRep(listPtr); ListAppendElement(listPtr, objPtr); } void Jim_ListAppendList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr) { - JimPanic((Jim_IsShared(listPtr), interp, "Jim_ListAppendList called with shared object")); + JimPanic((Jim_IsShared(listPtr), "Jim_ListAppendList called with shared object")); if (!Jim_IsList(listPtr)) SetListFromAny(interp, listPtr); Jim_InvalidateStringRep(listPtr); ListAppendList(listPtr, appendListPtr); } @@ -12519,11 +10797,11 @@ } void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx, int objc, Jim_Obj *const *objVec) { - JimPanic((Jim_IsShared(listPtr), interp, "Jim_ListInsertElement called with shared object")); + JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object")); if (!Jim_IsList(listPtr)) SetListFromAny(interp, listPtr); if (idx >= 0 && idx > listPtr->internalRep.listValue.len) idx = listPtr->internalRep.listValue.len; else if (idx < 0) @@ -12568,13 +10846,10 @@ listPtr->internalRep.listValue.ele[idx] = newObjPtr; Jim_IncrRefCount(newObjPtr); return JIM_OK; } -/* Modify the list stored into the variable named 'varNamePtr' - * setting the element specified by the 'indexc' indexes objects in 'indexv', - * with the new element 'newObjptr'. */ int Jim_SetListIndex(Jim_Interp *interp, Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr) { Jim_Obj *varObjPtr, *objPtr, *listObjPtr; int shared, i, idx; @@ -12616,51 +10891,48 @@ Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv) { int i; - /* If all the objects in objv are lists, - * it's possible to return a list as result, that's the - * concatenation of all the lists. */ for (i = 0; i < objc; i++) { if (!Jim_IsList(objv[i])) break; } if (i == objc) { Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0); for (i = 0; i < objc; i++) - Jim_ListAppendList(interp, objPtr, objv[i]); + ListAppendList(objPtr, objv[i]); return objPtr; } else { - /* Else... we have to glue strings together */ + int len = 0, objLen; char *bytes, *p; - /* Compute the length */ + for (i = 0; i < objc; i++) { Jim_GetString(objv[i], &objLen); len += objLen; } if (objc) len += objc - 1; - /* Create the string rep, and a string object holding it. */ + p = bytes = Jim_Alloc(len + 1); for (i = 0; i < objc; i++) { const char *s = Jim_GetString(objv[i], &objLen); - /* Remove leading space */ + while (objLen && (*s == ' ' || *s == '\t' || *s == '\n')) { s++; objLen--; len--; } - /* And trailing space */ + while (objLen && (s[objLen - 1] == ' ' || s[objLen - 1] == '\n' || s[objLen - 1] == '\t')) { - /* Handle trailing backslash-space case */ + if (objLen > 1 && s[objLen - 2] == '\\') { break; } objLen--; len--; @@ -12669,63 +10941,51 @@ p += objLen; if (objLen && i + 1 != objc) { *p++ = ' '; } else if (i + 1 != objc) { - /* Drop the space calcuated for this - * element that is instead null. */ len--; } } *p = '\0'; return Jim_NewStringObjNoAlloc(interp, bytes, len); } } -/* Returns a list composed of the elements in the specified range. - * first and start are directly accepted as Jim_Objects and - * processed for the end?-index? case. */ Jim_Obj *Jim_ListRange(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr) { int first, last; int len, rangeLen; if (Jim_GetIndex(interp, firstObjPtr, &first) != JIM_OK || Jim_GetIndex(interp, lastObjPtr, &last) != JIM_OK) return NULL; - len = Jim_ListLength(interp, listObjPtr); /* will convert into list */ + len = Jim_ListLength(interp, listObjPtr); first = JimRelToAbsIndex(len, first); last = JimRelToAbsIndex(len, last); JimRelToAbsRange(len, first, last, &first, &last, &rangeLen); if (first == 0 && last == len) { return listObjPtr; } return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen); } -/* ----------------------------------------------------------------------------- - * Dict object - * ---------------------------------------------------------------------------*/ static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static void UpdateStringOfDict(struct Jim_Obj *objPtr); static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); -/* Dict HashTable Type. - * - * Keys and Values are Jim objects. */ static unsigned int JimObjectHTHashFunction(const void *key) { const char *str; Jim_Obj *objPtr = (Jim_Obj *)key; - int len, h; + int len; str = Jim_GetString(objPtr, &len); - h = Jim_GenHashFunction((unsigned char *)str, len); - return h; + return Jim_GenHashFunction((unsigned char *)str, len); } static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2) { JIM_NOTUSED(privdata); @@ -12739,23 +10999,19 @@ Jim_DecrRefCount(interp, objPtr); } static const Jim_HashTableType JimDictHashTableType = { - JimObjectHTHashFunction, /* hash function */ - NULL, /* key dup */ - NULL, /* val dup */ - JimObjectHTKeyCompare, /* key compare */ - (void (*)(void *, const void *)) /* ATTENTION: const cast */ - JimObjectHTKeyValDestructor, /* key destructor */ - JimObjectHTKeyValDestructor /* val destructor */ + JimObjectHTHashFunction, + NULL, + NULL, + JimObjectHTKeyCompare, + (void (*)(void *, const void *)) + JimObjectHTKeyValDestructor, + JimObjectHTKeyValDestructor }; -/* Note that while the elements of the dict may contain references, - * the list object itself can't. This basically means that the - * dict object string representation as a whole can't contain references - * that are not presents in the single elements. */ static const Jim_ObjType dictObjType = { "dict", FreeDictInternalRep, DupDictInternalRep, UpdateStringOfDict, @@ -12774,23 +11030,23 @@ { Jim_HashTable *ht, *dupHt; Jim_HashTableIterator *htiter; Jim_HashEntry *he; - /* Create a new hash table */ + ht = srcPtr->internalRep.ptr; dupHt = Jim_Alloc(sizeof(*dupHt)); Jim_InitHashTable(dupHt, &JimDictHashTableType, interp); if (ht->size != 0) Jim_ExpandHashTable(dupHt, ht->size); - /* Copy every element from the source to the dup hash table */ + htiter = Jim_GetHashTableIterator(ht); while ((he = Jim_NextHashEntry(htiter)) != NULL) { const Jim_Obj *keyObjPtr = he->key; Jim_Obj *valObjPtr = he->u.val; - Jim_IncrRefCount((Jim_Obj *)keyObjPtr); /* ATTENTION: const cast */ + Jim_IncrRefCount((Jim_Obj *)keyObjPtr); Jim_IncrRefCount(valObjPtr); Jim_AddHashEntry(dupHt, keyObjPtr, valObjPtr); } Jim_FreeHashTableIterator(htiter); @@ -12807,22 +11063,22 @@ Jim_HashTable *ht; Jim_HashTableIterator *htiter; Jim_HashEntry *he; Jim_Obj **objv; - /* Trun the hash table into a flat vector of Jim_Objects. */ + ht = objPtr->internalRep.ptr; objc = ht->used * 2; objv = Jim_Alloc(objc * sizeof(Jim_Obj *)); htiter = Jim_GetHashTableIterator(ht); i = 0; while ((he = Jim_NextHashEntry(htiter)) != NULL) { - objv[i++] = (Jim_Obj *)he->key; /* ATTENTION: const cast */ + objv[i++] = (Jim_Obj *)he->key; objv[i++] = he->u.val; } Jim_FreeHashTableIterator(htiter); - /* (Over) Estimate the space needed. */ + quotingType = Jim_Alloc(sizeof(int) * objc); bufLen = 0; for (i = 0; i < objc; i++) { int len; @@ -12837,15 +11093,15 @@ break; case JIM_ELESTR_QUOTE: bufLen += len * 2; break; } - bufLen++; /* elements separator. */ + bufLen++; } bufLen++; - /* Generate the string rep. */ + p = objPtr->bytes = Jim_Alloc(bufLen + 1); realLength = 0; for (i = 0; i < objc; i++) { int len, qlen; char *q; @@ -12871,40 +11127,37 @@ Jim_Free(q); p += qlen; realLength += qlen; break; } - /* Add a separating space */ + if (i + 1 != objc) { *p++ = ' '; realLength++; } } - *p = '\0'; /* nul term. */ + *p = '\0'; objPtr->length = realLength; Jim_Free(quotingType); Jim_Free(objv); } static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) { int listlen; - /* Get the string representation. Do this first so we don't - * change order in case of fast conversion to dict. - */ Jim_String(objPtr); - /* For simplicity, convert a non-list object to a list and then to a dict */ + listlen = Jim_ListLength(interp, objPtr); if (listlen % 2) { Jim_SetResultString(interp, "invalid dictionary value: must be a list with an even number of elements", -1); return JIM_ERR; } else { - /* Now it is easy to convert to a dict from a list, and it can't fail */ + Jim_HashTable *ht; int i; ht = Jim_Alloc(sizeof(*ht)); Jim_InitHashTable(ht, &JimDictHashTableType, interp); @@ -12922,11 +11175,11 @@ if (Jim_AddHashEntry(ht, keyObjPtr, valObjPtr) != JIM_OK) { Jim_HashEntry *he; he = Jim_FindHashEntry(ht, keyObjPtr); Jim_DecrRefCount(interp, keyObjPtr); - /* ATTENTION: const cast */ + Jim_DecrRefCount(interp, (Jim_Obj *)he->u.val); he->u.val = valObjPtr; } } @@ -12936,47 +11189,39 @@ return JIM_OK; } } -/* Dict object API */ -/* Add an element to a dict. objPtr must be of the "dict" type. - * The higer-level exported function is Jim_DictAddElement(). - * If an element with the specified key already exists, the value - * associated is replaced with the new one. - * - * if valueObjPtr == NULL, the key is instead removed if it exists. */ + static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr) { Jim_HashTable *ht = objPtr->internalRep.ptr; - if (valueObjPtr == NULL) { /* unset */ + if (valueObjPtr == NULL) { return Jim_DeleteHashEntry(ht, keyObjPtr); } Jim_IncrRefCount(keyObjPtr); Jim_IncrRefCount(valueObjPtr); if (Jim_AddHashEntry(ht, keyObjPtr, valueObjPtr) != JIM_OK) { Jim_HashEntry *he = Jim_FindHashEntry(ht, keyObjPtr); Jim_DecrRefCount(interp, keyObjPtr); - /* ATTENTION: const cast */ + Jim_DecrRefCount(interp, (Jim_Obj *)he->u.val); he->u.val = valueObjPtr; } return JIM_OK; } -/* Add an element, higher-level interface for DictAddElement(). - * If valueObjPtr == NULL, the key is removed if it exists. */ int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr) { int retcode; - JimPanic((Jim_IsShared(objPtr), interp, "Jim_DictAddElement called with shared object")); + JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object")); if (objPtr->typePtr != &dictObjType) { if (SetDictFromAny(interp, objPtr) != JIM_OK) return JIM_ERR; } retcode = DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr); @@ -12987,11 +11232,11 @@ Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len) { Jim_Obj *objPtr; int i; - JimPanic((len % 2, interp, "Jim_NewDictObj() 'len' argument must be even")); + JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even")); objPtr = Jim_NewObj(interp); objPtr->typePtr = &dictObjType; objPtr->bytes = NULL; objPtr->internalRep.ptr = Jim_Alloc(sizeof(Jim_HashTable)); @@ -12999,13 +11244,10 @@ for (i = 0; i < len; i += 2) DictAddElement(interp, objPtr, elements[i], elements[i + 1]); return objPtr; } -/* Return the value associated to the specified dict key - * Note: Returns JIM_OK if OK, JIM_ERR if entry not found or -1 if can't create dict value - */ int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags) { Jim_HashEntry *he; Jim_HashTable *ht; @@ -13023,11 +11265,11 @@ } *objPtrPtr = he->u.val; return JIM_OK; } -/* Return an allocated array of key/value pairs for the dictionary. Stores the length in *len */ + int Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len) { Jim_HashTable *ht; Jim_HashTableIterator *htiter; Jim_HashEntry *he; @@ -13038,26 +11280,26 @@ if (SetDictFromAny(interp, dictPtr) != JIM_OK) return JIM_ERR; } ht = dictPtr->internalRep.ptr; - /* Turn the hash table into a flat vector of Jim_Objects. */ + objv = Jim_Alloc((ht->used * 2) * sizeof(Jim_Obj *)); htiter = Jim_GetHashTableIterator(ht); i = 0; while ((he = Jim_NextHashEntry(htiter)) != NULL) { - objv[i++] = (Jim_Obj *)he->key; /* ATTENTION: const cast */ + objv[i++] = (Jim_Obj *)he->key; objv[i++] = he->u.val; } *len = i; Jim_FreeHashTableIterator(htiter); *objPtrPtr = objv; return JIM_OK; } -/* Return the value associated to the specified dict keys */ + int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags) { int i; @@ -13076,27 +11318,22 @@ } *objPtrPtr = dictPtr; return JIM_OK; } -/* Modify the dict stored into the variable named 'varNamePtr' - * setting the element specified by the 'keyc' keys objects in 'keyv', - * with the new value of the element 'newObjPtr'. - * - * If newObjPtr == NULL the operation is to remove the given key - * from the dictionary. */ int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr, - Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr) + Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr, int flags) { Jim_Obj *varObjPtr, *objPtr, *dictObjPtr; int shared, i; varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, newObjPtr == NULL ? JIM_ERRMSG : JIM_NONE); if (objPtr == NULL) { - if (newObjPtr == NULL) /* Cannot remove a key from non existing var */ + if (newObjPtr == NULL) { return JIM_ERR; + } varObjPtr = objPtr = Jim_NewDictObj(interp, NULL, 0); if (Jim_SetVariable(interp, varNamePtr, objPtr) != JIM_OK) { Jim_FreeNewObj(interp, varObjPtr); return JIM_ERR; } @@ -13104,57 +11341,53 @@ if ((shared = Jim_IsShared(objPtr))) varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr); for (i = 0; i < keyc - 1; i++) { dictObjPtr = objPtr; - /* Check if it's a valid dictionary */ + if (dictObjPtr->typePtr != &dictObjType) { - if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) + if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) { goto err; + } } - /* Check if the given key exists. */ + Jim_InvalidateStringRep(dictObjPtr); if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr, newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) { - /* This key exists at the current level. - * Make sure it's not shared!. */ if (Jim_IsShared(objPtr)) { objPtr = Jim_DuplicateObj(interp, objPtr); DictAddElement(interp, dictObjPtr, keyv[i], objPtr); } } else { - /* Key not found. If it's an [unset] operation - * this is an error. Only the last key may not - * exist. */ - if (newObjPtr == NULL) + if (newObjPtr == NULL) { goto err; - /* Otherwise set an empty dictionary - * as key's value. */ + } objPtr = Jim_NewDictObj(interp, NULL, 0); DictAddElement(interp, dictObjPtr, keyv[i], objPtr); } } + if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) { - goto err; + if (newObjPtr || (flags & JIM_ERRMSG)) { + goto err; + } } Jim_InvalidateStringRep(objPtr); Jim_InvalidateStringRep(varObjPtr); - if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) + if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) { goto err; + } Jim_SetResult(interp, varObjPtr); return JIM_OK; err: if (shared) { Jim_FreeNewObj(interp, varObjPtr); } return JIM_ERR; } -/* ----------------------------------------------------------------------------- - * Index object - * ---------------------------------------------------------------------------*/ static void UpdateStringOfIndex(struct Jim_Obj *objPtr); static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); static const Jim_ObjType indexObjType = { "index", @@ -13185,14 +11418,14 @@ { int idx, end = 0; const char *str; char *endptr; - /* Get the string representation */ + str = Jim_String(objPtr); - /* Try to convert into an index */ + if (strncmp(str, "end", 3) == 0) { end = 1; str += 3; idx = 0; } @@ -13203,21 +11436,21 @@ goto badindex; } str = endptr; } - /* Now str may include or +<num> or -<num> */ + if (*str == '+' || *str == '-') { int sign = (*str == '+' ? 1 : -1); idx += sign * strtol(++str, &endptr, 10); if (str == endptr || *endptr) { goto badindex; } str = endptr; } - /* The only thing left should be spaces */ + while (isspace(UCHAR(*str))) { str++; } if (*str) { goto badindex; @@ -13225,19 +11458,19 @@ if (end) { if (idx > 0) { idx = INT_MAX; } else { - /* end-1 is repesented as -2 */ + idx--; } } else if (idx < 0) { idx = -INT_MAX; } - /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &indexObjType; objPtr->internalRep.indexValue = idx; return JIM_OK; @@ -13247,11 +11480,11 @@ return JIM_ERR; } int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr) { - /* Avoid shimmering if the object is an integer. */ + if (objPtr->typePtr == &intObjType) { jim_wide val = JimWideValue(objPtr); if (!(val < LONG_MIN) && !(val > LONG_MAX)) { *indexPtr = (val < 0) ? -INT_MAX : (long)val;; @@ -13262,24 +11495,21 @@ return JIM_ERR; *indexPtr = objPtr->internalRep.indexValue; return JIM_OK; } -/* ----------------------------------------------------------------------------- - * Return Code Object. - * ---------------------------------------------------------------------------*/ -/* NOTE: These must be kept in the same order as JIM_OK, JIM_ERR, ... */ + static const char * const jimReturnCodes[] = { - [JIM_OK] = "ok", - [JIM_ERR] = "error", - [JIM_RETURN] = "return", - [JIM_BREAK] = "break", - [JIM_CONTINUE] = "continue", - [JIM_SIGNAL] = "signal", - [JIM_EXIT] = "exit", - [JIM_EVAL] = "eval", + "ok", + "error", + "return", + "break", + "continue", + "signal", + "exit", + "eval", NULL }; #define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes)) @@ -13291,13 +11521,10 @@ NULL, NULL, JIM_TYPE_NONE, }; -/* Converts a (standard) return code to a string. Returns "?" for - * non-standard return codes. - */ const char *Jim_ReturnCode(int code) { if (code < 0 || code >= (int)jimReturnCodesSize) { return "?"; } @@ -13309,18 +11536,18 @@ int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr) { int returnCode; jim_wide wideValue; - /* Try to convert into an integer */ + if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR) returnCode = (int)wideValue; else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) { Jim_SetResultFormatted(interp, "expected return code but got \"%#s\"", objPtr); return JIM_ERR; } - /* Free the old internal repr and set the new one. */ + Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = &returnCodeObjType; objPtr->internalRep.returnCode = returnCode; return JIM_OK; } @@ -13331,25 +11558,22 @@ return JIM_ERR; *intPtr = objPtr->internalRep.returnCode; return JIM_OK; } -/* ----------------------------------------------------------------------------- - * Expression Parsing - * ---------------------------------------------------------------------------*/ static int JimParseExprOperator(struct JimParserCtx *pc); static int JimParseExprNumber(struct JimParserCtx *pc); static int JimParseExprIrrational(struct JimParserCtx *pc); -/* Exrp's Stack machine operators opcodes. */ -/* Binary operators (numbers) */ + + enum { - /* Continues on from the JIM_TT_ space */ - /* Operations */ - JIM_EXPROP_MUL = JIM_TT_EXPR_OP, /* 15 */ + + + JIM_EXPROP_MUL = JIM_TT_EXPR_OP, JIM_EXPROP_DIV, JIM_EXPROP_MOD, JIM_EXPROP_SUB, JIM_EXPROP_ADD, JIM_EXPROP_LSHIFT, @@ -13360,61 +11584,60 @@ JIM_EXPROP_GT, JIM_EXPROP_LTE, JIM_EXPROP_GTE, JIM_EXPROP_NUMEQ, JIM_EXPROP_NUMNE, - JIM_EXPROP_BITAND, /* 30 */ + JIM_EXPROP_BITAND, JIM_EXPROP_BITXOR, JIM_EXPROP_BITOR, - /* Note must keep these together */ - JIM_EXPROP_LOGICAND, /* 33 */ + + JIM_EXPROP_LOGICAND, JIM_EXPROP_LOGICAND_LEFT, JIM_EXPROP_LOGICAND_RIGHT, - /* and these */ - JIM_EXPROP_LOGICOR, /* 36 */ + + JIM_EXPROP_LOGICOR, JIM_EXPROP_LOGICOR_LEFT, JIM_EXPROP_LOGICOR_RIGHT, - /* and these */ - /* Ternary operators */ - JIM_EXPROP_TERNARY, /* 39 */ + + + JIM_EXPROP_TERNARY, JIM_EXPROP_TERNARY_LEFT, JIM_EXPROP_TERNARY_RIGHT, - /* and these */ - JIM_EXPROP_COLON, /* 42 */ + + JIM_EXPROP_COLON, JIM_EXPROP_COLON_LEFT, JIM_EXPROP_COLON_RIGHT, - JIM_EXPROP_POW, /* 45 */ + JIM_EXPROP_POW, -/* Binary operators (strings) */ - JIM_EXPROP_STREQ, + + JIM_EXPROP_STREQ, JIM_EXPROP_STRNE, JIM_EXPROP_STRIN, JIM_EXPROP_STRNI, -/* Unary operators (numbers) */ - JIM_EXPROP_NOT, + + JIM_EXPROP_NOT, JIM_EXPROP_BITNOT, JIM_EXPROP_UNARYMINUS, JIM_EXPROP_UNARYPLUS, - /* Functions */ - JIM_EXPROP_FUNC_FIRST, + + JIM_EXPROP_FUNC_FIRST, JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST, JIM_EXPROP_FUNC_ABS, JIM_EXPROP_FUNC_DOUBLE, JIM_EXPROP_FUNC_ROUND, JIM_EXPROP_FUNC_RAND, JIM_EXPROP_FUNC_SRAND, -#ifdef JIM_MATH_FUNCTIONS - /* math functions from libm */ - JIM_EXPROP_FUNC_SIN, + + JIM_EXPROP_FUNC_SIN, JIM_EXPROP_FUNC_COS, JIM_EXPROP_FUNC_TAN, JIM_EXPROP_FUNC_ASIN, JIM_EXPROP_FUNC_ACOS, JIM_EXPROP_FUNC_ATAN, @@ -13425,11 +11648,11 @@ JIM_EXPROP_FUNC_FLOOR, JIM_EXPROP_FUNC_EXP, JIM_EXPROP_FUNC_LOG, JIM_EXPROP_FUNC_LOG10, JIM_EXPROP_FUNC_SQRT, -#endif + JIM_EXPROP_FUNC_POW, }; struct JimExprState { Jim_Obj **stack; @@ -13436,11 +11659,11 @@ int stacklen; int opcode; int skip; }; -/* Operators table */ + typedef struct Jim_ExprOperator { const char *name; int precedence; int arity; @@ -13575,11 +11798,11 @@ return rc; } static int JimExprOpNone(Jim_Interp *interp, struct JimExprState *e) { - JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND)); + JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()")); ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp))); return JIM_OK; } @@ -13649,11 +11872,11 @@ return rc; } #endif -/* A binary operation on two ints */ + static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e) { Jim_Obj *B = ExprPop(e); Jim_Obj *A = ExprPop(e); jim_wide wA, wB; @@ -13685,18 +11908,10 @@ wC = 0; Jim_SetResultString(interp, "Division by zero", -1); rc = JIM_ERR; } else { - /* - * From Tcl 8.x - * - * This code is tricky: C doesn't guarantee much - * about the quotient or remainder, but Tcl does. - * The remainder always has the same sign as the - * divisor and a smaller absolute value. - */ int negative = 0; if (wB < 0) { wB = -wB; wA = -wA; @@ -13711,16 +11926,16 @@ } } break; case JIM_EXPROP_ROTL: case JIM_EXPROP_ROTR:{ - /* uint32_t would be better. But not everyone has inttypes.h? */ + unsigned long uA = (unsigned long)wA; unsigned long uB = (unsigned long)wB; const unsigned int S = sizeof(unsigned long) * 8; - /* Shift left by the word size or more is undefined. */ + uB %= S; if (e->opcode == JIM_EXPROP_ROTR) { uB = S - uB; } @@ -13739,11 +11954,11 @@ return rc; } -/* A binary operation on two ints or two doubles (or two strings for some ops) */ + static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e) { int intresult = 0; int rc = JIM_OK; double dA, dB, dC = 0; @@ -13754,16 +11969,17 @@ if ((A->typePtr != &doubleObjType || A->bytes) && (B->typePtr != &doubleObjType || B->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) { - /* Both are ints */ + intresult = 1; switch (e->opcode) { case JIM_EXPROP_POW: + case JIM_EXPROP_FUNC_POW: wC = JimPowWide(wA, wB); break; case JIM_EXPROP_ADD: wC = wA + wB; break; @@ -13777,18 +11993,10 @@ if (wB == 0) { Jim_SetResultString(interp, "Division by zero", -1); rc = JIM_ERR; } else { - /* - * From Tcl 8.x - * - * This code is tricky: C doesn't guarantee much - * about the quotient or remainder, but Tcl does. - * The remainder always has the same sign as the - * divisor and a smaller absolute value. - */ if (wB < 0) { wB = -wB; wA = -wA; } wC = wA / wB; @@ -13820,10 +12028,11 @@ } } else if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) { switch (e->opcode) { case JIM_EXPROP_POW: + case JIM_EXPROP_FUNC_POW: #ifdef JIM_MATH_FUNCTIONS dC = pow(dA, dB); #else Jim_SetResultString(interp, "unsupported", -1); rc = JIM_ERR; @@ -13877,13 +12086,13 @@ default: abort(); } } else { - /* Handle the string case */ + - /* REVISIT: Could optimise the eq/ne case by checking lengths */ + int i = Jim_StringCompareObj(interp, A, B, 0); intresult = 1; switch (e->opcode) { @@ -14003,21 +12212,21 @@ Jim_Obj *A = ExprPop(e); int rc = JIM_OK; switch (ExprBool(interp, A)) { case 0: - /* false, so skip RHS opcodes with a 0 result */ + e->skip = JimWideValue(skip); ExprPush(e, Jim_NewIntObj(interp, 0)); break; case 1: - /* true so continue */ + break; case -1: - /* Invalid */ + rc = JIM_ERR; } Jim_DecrRefCount(interp, A); Jim_DecrRefCount(interp, skip); @@ -14030,21 +12239,21 @@ Jim_Obj *A = ExprPop(e); int rc = JIM_OK; switch (ExprBool(interp, A)) { case 0: - /* false, so do nothing */ + break; case 1: - /* true so skip RHS opcodes with a 1 result */ + e->skip = JimWideValue(skip); ExprPush(e, Jim_NewIntObj(interp, 1)); break; case -1: - /* Invalid */ + rc = JIM_ERR; break; } Jim_DecrRefCount(interp, A); Jim_DecrRefCount(interp, skip); @@ -14065,11 +12274,11 @@ case 1: ExprPush(e, Jim_NewIntObj(interp, 1)); break; case -1: - /* Invalid */ + rc = JIM_ERR; break; } Jim_DecrRefCount(interp, A); @@ -14080,27 +12289,27 @@ { Jim_Obj *skip = ExprPop(e); Jim_Obj *A = ExprPop(e); int rc = JIM_OK; - /* Repush A */ + ExprPush(e, A); switch (ExprBool(interp, A)) { case 0: - /* false, skip RHS opcodes */ + e->skip = JimWideValue(skip); - /* Push a dummy value */ + ExprPush(e, Jim_NewIntObj(interp, 0)); break; case 1: - /* true so do nothing */ + break; case -1: - /* Invalid */ + rc = JIM_ERR; break; } Jim_DecrRefCount(interp, A); Jim_DecrRefCount(interp, skip); @@ -14112,15 +12321,15 @@ { Jim_Obj *skip = ExprPop(e); Jim_Obj *B = ExprPop(e); Jim_Obj *A = ExprPop(e); - /* No need to check for A as non-boolean */ + if (ExprBool(interp, A)) { - /* true, so skip RHS opcodes */ + e->skip = JimWideValue(skip); - /* Repush B as the answer */ + ExprPush(e, B); } Jim_DecrRefCount(interp, skip); Jim_DecrRefCount(interp, A); @@ -14139,98 +12348,104 @@ LAZY_OP, LAZY_LEFT, LAZY_RIGHT }; -/* name - precedence - arity - opcode */ static const struct Jim_ExprOperator Jim_ExprOperators[] = { - [JIM_EXPROP_FUNC_INT] = {"int", 400, 1, JimExprOpNumUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_DOUBLE] = {"double", 400, 1, JimExprOpNumUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_ABS] = {"abs", 400, 1, JimExprOpNumUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_ROUND] = {"round", 400, 1, JimExprOpNumUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_RAND] = {"rand", 400, 0, JimExprOpNone, LAZY_NONE}, - [JIM_EXPROP_FUNC_SRAND] = {"srand", 400, 1, JimExprOpIntUnary, LAZY_NONE}, + {"*", 200, 2, JimExprOpBin, LAZY_NONE}, + {"/", 200, 2, JimExprOpBin, LAZY_NONE}, + {"%", 200, 2, JimExprOpIntBin, LAZY_NONE}, + + {"-", 100, 2, JimExprOpBin, LAZY_NONE}, + {"+", 100, 2, JimExprOpBin, LAZY_NONE}, + + {"<<", 90, 2, JimExprOpIntBin, LAZY_NONE}, + {">>", 90, 2, JimExprOpIntBin, LAZY_NONE}, + + {"<<<", 90, 2, JimExprOpIntBin, LAZY_NONE}, + {">>>", 90, 2, JimExprOpIntBin, LAZY_NONE}, + + {"<", 80, 2, JimExprOpBin, LAZY_NONE}, + {">", 80, 2, JimExprOpBin, LAZY_NONE}, + {"<=", 80, 2, JimExprOpBin, LAZY_NONE}, + {">=", 80, 2, JimExprOpBin, LAZY_NONE}, + + {"==", 70, 2, JimExprOpBin, LAZY_NONE}, + {"!=", 70, 2, JimExprOpBin, LAZY_NONE}, + + {"&", 50, 2, JimExprOpIntBin, LAZY_NONE}, + {"^", 49, 2, JimExprOpIntBin, LAZY_NONE}, + {"|", 48, 2, JimExprOpIntBin, LAZY_NONE}, + + {"&&", 10, 2, NULL, LAZY_OP}, + {NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT}, + {NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT}, + + {"||", 9, 2, NULL, LAZY_OP}, + {NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT}, + {NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT}, + + {"?", 5, 2, JimExprOpNull, LAZY_OP}, + {NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT}, + {NULL, 5, 2, JimExprOpNull, LAZY_RIGHT}, + + {":", 5, 2, JimExprOpNull, LAZY_OP}, + {NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT}, + {NULL, 5, 2, JimExprOpNull, LAZY_RIGHT}, + + {"**", 250, 2, JimExprOpBin, LAZY_NONE}, + + {"eq", 60, 2, JimExprOpStrBin, LAZY_NONE}, + {"ne", 60, 2, JimExprOpStrBin, LAZY_NONE}, + + {"in", 55, 2, JimExprOpStrBin, LAZY_NONE}, + {"ni", 55, 2, JimExprOpStrBin, LAZY_NONE}, + + {"!", 300, 1, JimExprOpNumUnary, LAZY_NONE}, + {"~", 300, 1, JimExprOpIntUnary, LAZY_NONE}, + {NULL, 300, 1, JimExprOpNumUnary, LAZY_NONE}, + {NULL, 300, 1, JimExprOpNumUnary, LAZY_NONE}, + + + + {"int", 400, 1, JimExprOpNumUnary, LAZY_NONE}, + {"abs", 400, 1, JimExprOpNumUnary, LAZY_NONE}, + {"double", 400, 1, JimExprOpNumUnary, LAZY_NONE}, + {"round", 400, 1, JimExprOpNumUnary, LAZY_NONE}, + {"rand", 400, 0, JimExprOpNone, LAZY_NONE}, + {"srand", 400, 1, JimExprOpIntUnary, LAZY_NONE}, #ifdef JIM_MATH_FUNCTIONS - [JIM_EXPROP_FUNC_SIN] = {"sin", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_COS] = {"cos", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_TAN] = {"tan", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_ASIN] = {"asin", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_ACOS] = {"acos", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_ATAN] = {"atan", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_SINH] = {"sinh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_COSH] = {"cosh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_TANH] = {"tanh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_CEIL] = {"ceil", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_FLOOR] = {"floor", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_EXP] = {"exp", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_LOG] = {"log", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_LOG10] = {"log10", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, - [JIM_EXPROP_FUNC_SQRT] = {"sqrt", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, -#endif - - [JIM_EXPROP_NOT] = {"!", 300, 1, JimExprOpNumUnary, LAZY_NONE}, - [JIM_EXPROP_BITNOT] = {"~", 300, 1, JimExprOpIntUnary, LAZY_NONE}, - [JIM_EXPROP_UNARYMINUS] = {NULL, 300, 1, JimExprOpNumUnary, LAZY_NONE}, - [JIM_EXPROP_UNARYPLUS] = {NULL, 300, 1, JimExprOpNumUnary, LAZY_NONE}, - - [JIM_EXPROP_POW] = {"**", 250, 2, JimExprOpBin, LAZY_NONE}, - - [JIM_EXPROP_MUL] = {"*", 200, 2, JimExprOpBin, LAZY_NONE}, - [JIM_EXPROP_DIV] = {"/", 200, 2, JimExprOpBin, LAZY_NONE}, - [JIM_EXPROP_MOD] = {"%", 200, 2, JimExprOpIntBin, LAZY_NONE}, - - [JIM_EXPROP_SUB] = {"-", 100, 2, JimExprOpBin, LAZY_NONE}, - [JIM_EXPROP_ADD] = {"+", 100, 2, JimExprOpBin, LAZY_NONE}, - - [JIM_EXPROP_ROTL] = {"<<<", 90, 2, JimExprOpIntBin, LAZY_NONE}, - [JIM_EXPROP_ROTR] = {">>>", 90, 2, JimExprOpIntBin, LAZY_NONE}, - [JIM_EXPROP_LSHIFT] = {"<<", 90, 2, JimExprOpIntBin, LAZY_NONE}, - [JIM_EXPROP_RSHIFT] = {">>", 90, 2, JimExprOpIntBin, LAZY_NONE}, - - [JIM_EXPROP_LT] = {"<", 80, 2, JimExprOpBin, LAZY_NONE}, - [JIM_EXPROP_GT] = {">", 80, 2, JimExprOpBin, LAZY_NONE}, - [JIM_EXPROP_LTE] = {"<=", 80, 2, JimExprOpBin, LAZY_NONE}, - [JIM_EXPROP_GTE] = {">=", 80, 2, JimExprOpBin, LAZY_NONE}, - - [JIM_EXPROP_NUMEQ] = {"==", 70, 2, JimExprOpBin, LAZY_NONE}, - [JIM_EXPROP_NUMNE] = {"!=", 70, 2, JimExprOpBin, LAZY_NONE}, - - [JIM_EXPROP_STREQ] = {"eq", 60, 2, JimExprOpStrBin, LAZY_NONE}, - [JIM_EXPROP_STRNE] = {"ne", 60, 2, JimExprOpStrBin, LAZY_NONE}, - - [JIM_EXPROP_STRIN] = {"in", 55, 2, JimExprOpStrBin, LAZY_NONE}, - [JIM_EXPROP_STRNI] = {"ni", 55, 2, JimExprOpStrBin, LAZY_NONE}, - - [JIM_EXPROP_BITAND] = {"&", 50, 2, JimExprOpIntBin, LAZY_NONE}, - [JIM_EXPROP_BITXOR] = {"^", 49, 2, JimExprOpIntBin, LAZY_NONE}, - [JIM_EXPROP_BITOR] = {"|", 48, 2, JimExprOpIntBin, LAZY_NONE}, - - [JIM_EXPROP_LOGICAND] = {"&&", 10, 2, NULL, LAZY_OP}, - [JIM_EXPROP_LOGICOR] = {"||", 9, 2, NULL, LAZY_OP}, - - [JIM_EXPROP_TERNARY] = {"?", 5, 2, JimExprOpNull, LAZY_OP}, - [JIM_EXPROP_COLON] = {":", 5, 2, JimExprOpNull, LAZY_OP}, - - /* private operators */ - [JIM_EXPROP_TERNARY_LEFT] = {NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT}, - [JIM_EXPROP_TERNARY_RIGHT] = {NULL, 5, 2, JimExprOpNull, LAZY_RIGHT}, - [JIM_EXPROP_COLON_LEFT] = {NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT}, - [JIM_EXPROP_COLON_RIGHT] = {NULL, 5, 2, JimExprOpNull, LAZY_RIGHT}, - [JIM_EXPROP_LOGICAND_LEFT] = {NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT}, - [JIM_EXPROP_LOGICAND_RIGHT] = {NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT}, - [JIM_EXPROP_LOGICOR_LEFT] = {NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT}, - [JIM_EXPROP_LOGICOR_RIGHT] = {NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT}, + {"sin", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"cos", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"tan", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"asin", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"acos", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"atan", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"sinh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"cosh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"tanh", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"ceil", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"floor", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"exp", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"log", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"log10", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"sqrt", 400, 1, JimExprOpDoubleUnary, LAZY_NONE}, + {"pow", 400, 2, JimExprOpBin, LAZY_NONE}, +#endif }; #define JIM_EXPR_OPERATORS_NUM \ (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator)) static int JimParseExpression(struct JimParserCtx *pc) { - /* Discard spaces and quoted newline */ + while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) { + if (*pc->p == '\n') { + pc->linenr++; + } pc->p++; pc->len--; } if (pc->len == 0) { @@ -14240,30 +12455,30 @@ pc->eof = 1; return JIM_OK; } switch (*(pc->p)) { case '(': - pc->tstart = pc->tend = pc->p; - pc->tline = pc->linenr; - pc->tt = JIM_TT_SUBEXPR_START; - pc->p++; - pc->len--; - break; + pc->tt = JIM_TT_SUBEXPR_START; + goto singlechar; case ')': + pc->tt = JIM_TT_SUBEXPR_END; + goto singlechar; + case ',': + pc->tt = JIM_TT_SUBEXPR_COMMA; +singlechar: pc->tstart = pc->tend = pc->p; pc->tline = pc->linenr; - pc->tt = JIM_TT_SUBEXPR_END; pc->p++; pc->len--; break; case '[': return JimParseCmd(pc); case '$': if (JimParseVar(pc) == JIM_ERR) return JimParseExprOperator(pc); else { - /* Don't allow expr sugar in expressions */ + if (pc->tt == JIM_TT_EXPRSUGAR) { return JIM_ERR; } return JIM_OK; } @@ -14302,11 +12517,11 @@ static int JimParseExprNumber(struct JimParserCtx *pc) { int allowdot = 1; int allowhex = 0; - /* Assume an integer for now */ + pc->tt = JIM_TT_EXPR_INT; pc->tstart = pc->p; pc->tline = pc->linenr; while (isdigit(UCHAR(*pc->p)) || (allowhex && isxdigit(UCHAR(*pc->p))) @@ -14358,12 +12573,12 @@ static int JimParseExprOperator(struct JimParserCtx *pc) { int i; int bestIdx = -1, bestLen = 0; - /* Try to get the longest match. */ - for (i = JIM_TT_EXPR_OP; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) { + + for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) { const char *opname; int oplen; opname = Jim_ExprOperators[i].name; if (opname == NULL) { @@ -14370,19 +12585,19 @@ continue; } oplen = strlen(opname); if (strncmp(opname, pc->p, oplen) == 0 && oplen > bestLen) { - bestIdx = i; + bestIdx = i + JIM_TT_EXPR_OP; bestLen = oplen; } } if (bestIdx == -1) { return JIM_ERR; } - /* Validate paretheses around function arguments */ + if (bestIdx >= JIM_EXPROP_FUNC_FIRST) { const char *p = pc->p + bestLen; int len = pc->len - bestLen; while (len && isspace(UCHAR(*p))) { @@ -14403,36 +12618,37 @@ return JIM_OK; } static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode) { - return &Jim_ExprOperators[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] = - { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", "INT", + { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT", "DBL", "$()" }; if (type < JIM_TT_EXPR_OP) { return tt_names[type]; } else { const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(type); static char buf[20]; - if (op && op->name) { + if (op->name) { return op->name; } sprintf(buf, "(%d)", type); return buf; } } -/* ----------------------------------------------------------------------------- - * Expression Object - * ---------------------------------------------------------------------------*/ static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); static const Jim_ObjType exprObjType = { @@ -14441,16 +12657,16 @@ DupExprInternalRep, NULL, JIM_TYPE_REFERENCES, }; -/* Expr bytecode structure */ + typedef struct ExprByteCode { - int len; /* Length as number of tokens. */ - ScriptToken *token; /* Tokens array. */ - int inUse; /* Used for sharing. */ + int len; + ScriptToken *token; + int inUse; } ExprByteCode; static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr) { int i; @@ -14478,82 +12694,52 @@ static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr) { JIM_NOTUSED(interp); JIM_NOTUSED(srcPtr); - /* Just returns an simple string. */ + dupPtr->typePtr = NULL; } -/* Check if an expr program looks correct. */ + static int ExprCheckCorrectness(ExprByteCode * expr) { int i; int stacklen = 0; int ternary = 0; - /* Try to check if there are stack underflows, - * and make sure at the end of the program there is - * a single result on the stack. */ for (i = 0; i < expr->len; i++) { ScriptToken *t = &expr->token[i]; const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type); - if (op) { - 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 -= 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--; } - /* All operations and operands add one to the stack */ + stacklen++; } if (stacklen != 1 || ternary != 0) { return JIM_ERR; } return JIM_OK; } -/* This procedure converts every occurrence of || and && opereators - * in lazy unary versions. - * - * a b || is converted into: - * - * a <offset> |L b |R - * - * a b && is converted into: - * - * a <offset> &L b &R - * - * "|L" checks if 'a' is true: - * 1) if it is true pushes 1 and skips <offset> instructions to reach - * the opcode just after |R. - * 2) if it is false does nothing. - * "|R" checks if 'b' is true: - * 1) if it is true pushes 1, otherwise pushes 0. - * - * "&L" checks if 'a' is true: - * 1) if it is true does nothing. - * 2) If it is false pushes 0 and skips <offset> instructions to reach - * the opcode just after &R - * "&R" checks if 'a' is true: - * if it is true pushes 1, otherwise pushes 0. - */ static int ExprAddLazyOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t) { int i; int leftindex, arity, offset; - /* Search for the end of the first operator */ + leftindex = expr->len - 1; arity = 1; while (arity) { ScriptToken *tt = &expr->token[leftindex]; @@ -14566,33 +12752,31 @@ return JIM_ERR; } } leftindex++; - /* Move them up */ + memmove(&expr->token[leftindex + 2], &expr->token[leftindex], sizeof(*expr->token) * (expr->len - leftindex)); expr->len += 2; offset = (expr->len - leftindex) - 1; - /* Now we rely on the fact the the left and right version have opcodes - * 1 and 2 after the main opcode respectively - */ 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); - /* Now add the 'R' operator */ + expr->token[expr->len].objPtr = interp->emptyObj; expr->token[expr->len].type = t->type + 2; expr->len++; - /* Do we need to adjust the skip count for any &L, |L, ?L or :L in the left operand? */ + for (i = leftindex - 1; i > 0; i--) { - if (JimExprOperatorInfoByOpcode(expr->token[i].type)->lazy == LAZY_LEFT) { + 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; } } } @@ -14616,16 +12800,10 @@ expr->len++; } return JIM_OK; } -/** - * Returns the index of the COLON_LEFT to the left of 'right_index' - * taking into account nesting. - * - * The expression *must* be well formed, thus a COLON_LEFT will always be found. - */ static int ExprTernaryGetColonLeftIndex(ExprByteCode *expr, int right_index) { int ternary_count = 1; right_index--; @@ -14641,20 +12819,14 @@ return right_index; } right_index--; } - /*notreached*/ + return -1; } -/** - * Find the left/right indices for the ternary expression to the left of 'right_index'. - * - * Returns 1 if found, and fills in *prev_right_index and *prev_left_index. - * Otherwise returns 0. - */ 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; @@ -14675,55 +12847,10 @@ i--; } return 0; } -/* -* ExprTernaryReorderExpression description -* ======================================== -* -* ?: is right-to-left associative which doesn't work with the stack-based -* expression engine. The fix is to reorder the bytecode. -* -* The expression: -* -* expr 1?2:0?3:4 -* -* Has initial bytecode: -* -* '1' '2' (40=TERNARY_LEFT) '2' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '0' (44=COLON_RIGHT) -* '2' (40=TERNARY_LEFT) '3' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '4' (44=COLON_RIGHT) -* -* The fix involves simulating this expression instead: -* -* expr 1?2:(0?3:4) -* -* With the following bytecode: -* -* '1' '2' (40=TERNARY_LEFT) '2' (41=TERNARY_RIGHT) '10' (43=COLON_LEFT) '0' '2' (40=TERNARY_LEFT) -* '3' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '4' (44=COLON_RIGHT) (44=COLON_RIGHT) -* -* i.e. The token COLON_RIGHT at index 8 is moved towards the end of the stack, all tokens above 8 -* are shifted down and the skip count of the token JIM_EXPROP_COLON_LEFT at index 5 is -* incremented by the amount tokens shifted down. The token JIM_EXPROP_COLON_RIGHT that is moved -* is identified as immediately preceeding a token JIM_EXPROP_TERNARY_LEFT -* -* ExprTernaryReorderExpression works thus as follows : -* - start from the end of the stack -* - while walking towards the beginning of the stack -* if token=JIM_EXPROP_COLON_RIGHT then -* find the associated token JIM_EXPROP_TERNARY_LEFT, which allows to -* find the associated token previous(JIM_EXPROP_COLON_RIGHT) -* find the associated token previous(JIM_EXPROP_LEFT_RIGHT) -* if all found then -* perform the rotation -* update the skip count of the token previous(JIM_EXPROP_LEFT_RIGHT) -* end if -* end if -* -* Note: care has to be taken for nested ternary constructs!!! -*/ static void ExprTernaryReorderExpression(Jim_Interp *interp, ExprByteCode *expr) { int i; for (i = expr->len - 1; i > 1; i--) { @@ -14734,77 +12861,53 @@ if (expr->token[i].type != JIM_EXPROP_COLON_RIGHT) { continue; } - /* COLON_RIGHT found: get the indexes needed to move the tokens in the stack (if any) */ + if (ExprTernaryGetMoveIndices(expr, i, &prev_right_index, &prev_left_index) == 0) { continue; } - /* - ** rotate tokens down - ** - ** +-> [i] : JIM_EXPROP_COLON_RIGHT - ** | | | - ** | V V - ** | [...] : ... - ** | | | - ** | V V - ** | [...] : ... - ** | | | - ** | V V - ** +- [prev_right_index] : JIM_EXPROP_COLON_RIGHT - */ tmp = expr->token[prev_right_index]; for (j = prev_right_index; j < i; j++) { expr->token[j] = expr->token[j + 1]; } expr->token[i] = tmp; - /* Increment the 'skip' count associated to the previous JIM_EXPROP_COLON_LEFT token - * - * This is 'colon left increment' = i - prev_right_index - * - * [prev_left_index] : JIM_EXPROP_LEFT_RIGHT - * [prev_left_index-1] : skip_count - * - */ JimWideValue(expr->token[prev_left_index-1].objPtr) += (i - prev_right_index); - /* Adjust for i-- in the loop */ + i++; } } -static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist) +static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *fileNameObj) { Jim_Stack stack; ExprByteCode *expr; int ok = 1; int i; int prevtt = JIM_TT_NONE; int have_ternary = 0; - /* -1 for EOL */ + int count = tokenlist->count - 1; expr = Jim_Alloc(sizeof(*expr)); expr->inUse = 1; expr->len = 0; Jim_InitStack(&stack); - /* Need extra bytecodes for lazy operators. - * Also check for the ternary operator - */ for (i = 0; i < tokenlist->count; i++) { ParseToken *t = &tokenlist->list[i]; + const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type); - if (JimExprOperatorInfoByOpcode(t->type)->lazy == LAZY_OP) { + if (op->lazy == LAZY_OP) { count += 2; - /* Ternary is a lazy op but also needs reordering */ + if (t->type == JIM_EXPROP_TERNARY) { have_ternary = 1; } } } @@ -14812,11 +12915,11 @@ expr->token = Jim_Alloc(sizeof(ScriptToken) * count); for (i = 0; i < tokenlist->count && ok; i++) { ParseToken *t = &tokenlist->list[i]; - /* Next token will be stored here */ + struct ScriptToken *token = &expr->token[expr->len]; if (t->type == JIM_TT_EOL) { break; } @@ -14828,10 +12931,14 @@ case JIM_TT_DICTSUGAR: case JIM_TT_EXPRSUGAR: case JIM_TT_CMD: token->objPtr = Jim_NewStringObj(interp, t->token, t->len); token->type = t->type; + if (t->type == JIM_TT_CMD) { + + JimSetSourceInfo(interp, token->objPtr, fileNameObj, t->line); + } expr->len++; break; case JIM_TT_EXPR_INT: token->objPtr = Jim_NewIntObj(interp, strtoull(t->token, NULL, 0)); @@ -14847,10 +12954,14 @@ case JIM_TT_SUBEXPR_START: Jim_StackPush(&stack, t); prevtt = JIM_TT_NONE; continue; + + case JIM_TT_SUBEXPR_COMMA: + + continue; case JIM_TT_SUBEXPR_END: ok = 0; while (Jim_StackLen(&stack)) { ParseToken *tt = Jim_StackPop(&stack); @@ -14870,15 +12981,15 @@ } break; default:{ - /* Must be an operator */ + const struct Jim_ExprOperator *op; ParseToken *tt; - /* Convert -/+ to unary minus or unary plus if necessary */ + if (prevtt == JIM_TT_NONE || prevtt >= JIM_TT_EXPR_OP) { if (t->type == JIM_EXPROP_SUB) { t->type = JIM_EXPROP_UNARYMINUS; } else if (t->type == JIM_EXPROP_ADD) { @@ -14886,16 +12997,16 @@ } } op = JimExprOperatorInfoByOpcode(t->type); - /* Now handle precedence */ + while ((tt = Jim_StackPeek(&stack)) != NULL) { const struct Jim_ExprOperator *tt_op = JimExprOperatorInfoByOpcode(tt->type); - /* Note that right-to-left associativity of ?: operator is handled later */ + if (op->arity != 1 && tt_op->precedence >= op->precedence) { if (ExprAddOperator(interp, expr, tt) != JIM_OK) { ok = 0; goto err; @@ -14911,11 +13022,11 @@ } } prevtt = t->type; } - /* Reduce any remaining subexpr */ + while (Jim_StackLen(&stack)) { ParseToken *tt = Jim_StackPop(&stack); if (tt->type == JIM_TT_SUBEXPR_START) { ok = 0; @@ -14931,11 +13042,11 @@ if (have_ternary) { ExprTernaryReorderExpression(interp, expr); } err: - /* Free the stack used for the compilation. */ + Jim_FreeStack(&stack); for (i = 0; i < expr->len; i++) { Jim_IncrRefCount(expr->token[i].objPtr); } @@ -14947,30 +13058,35 @@ return expr; } -/* This method takes the string representation of an expression - * and generates a program for the Expr's stack-based VM. */ -int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) +static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr) { int exprTextLen; const char *exprText; struct JimParserCtx parser; struct ExprByteCode *expr; ParseTokenList tokenlist; + int line; + Jim_Obj *fileNameObj; int rc = JIM_ERR; - int line = 1; - /* Try to get information about filename / line number */ + if (objPtr->typePtr == &sourceObjType) { + fileNameObj = objPtr->internalRep.sourceValue.fileNameObj; line = objPtr->internalRep.sourceValue.lineNumber; } + else { + fileNameObj = interp->emptyObj; + line = 1; + } + Jim_IncrRefCount(fileNameObj); exprText = Jim_GetString(objPtr, &exprTextLen); - /* Initially tokenise the expression into tokenlist */ + ScriptTokenListInit(&tokenlist); JimParserInit(&parser, exprText, exprTextLen, line); while (!parser.eof) { if (JimParseExpression(&parser) != JIM_OK) { @@ -14994,14 +13110,14 @@ tokenlist.list[i].len, tokenlist.list[i].token); } } #endif - /* Now create the expression bytecode from the tokenlist */ - expr = ExprCreateByteCode(interp, &tokenlist); + + expr = ExprCreateByteCode(interp, &tokenlist, fileNameObj); - /* No longer need the token list */ + ScriptTokenListFree(&tokenlist); if (!expr) { goto err; } @@ -15017,20 +13133,21 @@ printf("[%2d] %s '%s'\n", i, jim_tt_name(t->type), Jim_String(t->objPtr)); } } #endif - /* Check program correctness. */ + if (ExprCheckCorrectness(expr) != JIM_OK) { ExprFreeByteCode(interp, expr); goto invalidexpr; } rc = JIM_OK; err: - /* Free the old internal rep and set the new one. */ + + Jim_DecrRefCount(interp, fileNameObj); Jim_FreeIntRep(interp, objPtr); Jim_SetIntRepPtr(objPtr, expr); objPtr->typePtr = &exprObjType; return rc; } @@ -15043,25 +13160,10 @@ } } return (ExprByteCode *) Jim_GetIntRepPtr(objPtr); } -/* ----------------------------------------------------------------------------- - * Expressions evaluation. - * Jim uses a specialized stack-based virtual machine for expressions, - * that takes advantage of the fact that expr's operators - * can't be redefined. - * - * Jim_EvalExpression() uses the bytecode compiled by - * SetExprFromAny() method of the "expression" object. - * - * On success a Tcl Object containing the result of the evaluation - * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is - * returned. - * On error the function returns a retcode != to JIM_OK and set a suitable - * error on the interp. - * ---------------------------------------------------------------------------*/ #define JIM_EE_STATICSTACK_LEN 10 int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr) { ExprByteCode *expr; @@ -15070,31 +13172,17 @@ int retcode = JIM_OK; struct JimExprState e; expr = JimGetExpression(interp, exprObjPtr); if (!expr) { - return JIM_ERR; /* error in expression. */ + return JIM_ERR; } #ifdef JIM_OPTIMIZATION - /* Check for one of the following common expressions used by while/for - * - * CONST - * $a - * !$a - * $a < CONST, $a < $b - * $a <= CONST, $a <= $b - * $a > CONST, $a > $b - * $a >= CONST, $a >= $b - * $a != CONST, $a != $b - * $a == CONST, $a == $b - */ { Jim_Obj *objPtr; - /* STEP 1 -- Check if there are the conditions to run the specialized - * version of while */ switch (expr->len) { case 1: if (expr->token[0].type == JIM_TT_EXPR_INT) { *exprResultPtrPtr = expr->token[0].objPtr; @@ -15133,11 +13221,11 @@ case JIM_EXPROP_LTE: case JIM_EXPROP_GT: case JIM_EXPROP_GTE: case JIM_EXPROP_NUMEQ: case JIM_EXPROP_NUMNE:{ - /* optimise ok */ + jim_wide wideValueA; jim_wide wideValueB; objPtr = Jim_GetVariable(interp, expr->token[0].objPtr, JIM_NONE); if (objPtr && JimIsWide(objPtr) @@ -15171,11 +13259,11 @@ cmpRes = wideValueA == wideValueB; break; case JIM_EXPROP_NUMNE: cmpRes = wideValueA != wideValueB; break; - default: /*notreached */ + default: cmpRes = 0; } *exprResultPtrPtr = cmpRes ? interp->trueObj : interp->falseObj; Jim_IncrRefCount(*exprResultPtrPtr); @@ -15188,28 +13276,22 @@ break; } } #endif - /* In order to avoid that the internal repr gets freed due to - * shimmering of the exprObjPtr's object, we make the internal rep - * shared. */ expr->inUse++; - /* The stack-based expr VM itself */ + - /* Stack allocation. Expr programs have the feature that - * a program of length N can't require a stack longer than - * N. */ if (expr->len > JIM_EE_STATICSTACK_LEN) e.stack = Jim_Alloc(sizeof(Jim_Obj *) * expr->len); else e.stack = staticStack; e.stacklen = 0; - /* Execute every instruction */ + for (i = 0; i < expr->len && retcode == JIM_OK; i++) { Jim_Obj *objPtr; switch (expr->token[i].type) { case JIM_TT_EXPR_INT: @@ -15251,16 +13333,16 @@ ExprPush(&e, Jim_GetResult(interp)); } break; default:{ - /* Find and execute the operation */ + e.skip = 0; e.opcode = expr->token[i].type; retcode = JimExprOperatorInfoByOpcode(e.opcode)->funcop(interp, &e); - /* Skip some opcodes if necessary */ + i += e.skip; continue; } } } @@ -15307,61 +13389,34 @@ Jim_DecrRefCount(interp, exprResultPtr); return JIM_OK; } -/* ----------------------------------------------------------------------------- - * ScanFormat String Object - * ---------------------------------------------------------------------------*/ -/* This Jim_Obj will held a parsed representation of a format string passed to - * the Jim_ScanString command. For error diagnostics, the scanformat string has - * to be parsed in its entirely first and then, if correct, can be used for - * scanning. To avoid endless re-parsing, the parsed representation will be - * stored in an internal representation and re-used for performance reason. */ -/* A ScanFmtPartDescr will held the information of /one/ part of the whole - * scanformat string. This part will later be used to extract information - * out from the string to be parsed by Jim_ScanString */ typedef struct ScanFmtPartDescr { - char type; /* Type of conversion (e.g. c, d, f) */ - char modifier; /* Modify type (e.g. l - long, h - short */ - size_t width; /* Maximal width of input to be converted */ - int pos; /* -1 - no assign, 0 - natural pos, >0 - XPG3 pos */ - char *arg; /* Specification of a CHARSET conversion */ - char *prefix; /* Prefix to be scanned literally before conversion */ + char type; + char modifier; + size_t width; + int pos; + char *arg; + char *prefix; } ScanFmtPartDescr; -/* The ScanFmtStringObj will hold the internal representation of a scanformat - * string parsed and separated in part descriptions. Furthermore it contains - * the original string representation of the scanformat string to allow for - * fast update of the Jim_Obj's string representation part. - * - * As an add-on the internal object representation adds some scratch pad area - * for usage by Jim_ScanString to avoid endless allocating and freeing of - * memory for purpose of string scanning. - * - * The error member points to a static allocated string in case of a mal- - * formed scanformat string or it contains '0' (NULL) in case of a valid - * parse representation. - * - * The whole memory of the internal representation is allocated as a single - * area of memory that will be internally separated. So freeing and duplicating - * of such an object is cheap */ typedef struct ScanFmtStringObj { - jim_wide size; /* Size of internal repr in bytes */ - char *stringRep; /* Original string representation */ - size_t count; /* Number of ScanFmtPartDescr contained */ - size_t convCount; /* Number of conversions that will assign */ - size_t maxPos; /* Max position index if XPG3 is used */ - const char *error; /* Ptr to error text (NULL if no error */ - char *scratch; /* Some scratch pad used by Jim_ScanString */ - ScanFmtPartDescr descr[1]; /* The vector of partial descriptions */ + jim_wide size; + char *stringRep; + size_t count; + size_t convCount; + size_t maxPos; + const char *error; + char *scratch; + ScanFmtPartDescr descr[1]; } ScanFmtStringObj; static void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); static void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); @@ -15399,16 +13454,10 @@ objPtr->bytes = Jim_StrDup(bytes); objPtr->length = strlen(bytes); } -/* SetScanFmtFromAny will parse a given string and create the internal - * representation of the format specification. In case of an error - * the error data member of the internal representation will be set - * to an descriptive error text and the function will be left with - * JIM_ERR to indicate unsucessful parsing (aka. malformed scanformat - * specification */ static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr) { ScanFmtStringObj *fmtObj; char *buffer; @@ -15417,22 +13466,22 @@ int maxFmtLen = objPtr->length; const char *fmtEnd = fmt + maxFmtLen; int curr; Jim_FreeIntRep(interp, objPtr); - /* Count how many conversions could take place maximally */ + for (i = 0, maxCount = 0; i < maxFmtLen; ++i) if (fmt[i] == '%') ++maxCount; - /* Calculate an approximation of the memory necessary */ - approxSize = sizeof(ScanFmtStringObj) /* Size of the container */ - +(maxCount + 1) * sizeof(ScanFmtPartDescr) /* Size of all partials */ - +maxFmtLen * sizeof(char) + 3 + 1 /* Scratch + "%n" + '\0' */ - + maxFmtLen * sizeof(char) + 1 /* Original stringrep */ - + maxFmtLen * sizeof(char) /* Arg for CHARSETs */ - +(maxCount + 1) * sizeof(char) /* '\0' for every partial */ - +1; /* safety byte */ + + approxSize = sizeof(ScanFmtStringObj) + +(maxCount + 1) * sizeof(ScanFmtPartDescr) + +maxFmtLen * sizeof(char) + 3 + 1 + + maxFmtLen * sizeof(char) + 1 + + maxFmtLen * sizeof(char) + +(maxCount + 1) * sizeof(char) + +1; fmtObj = (ScanFmtStringObj *) Jim_Alloc(approxSize); memset(fmtObj, 0, approxSize); fmtObj->size = approxSize; fmtObj->maxPos = 0; fmtObj->scratch = (char *)&fmtObj->descr[maxCount + 1]; @@ -15444,12 +13493,12 @@ for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) { int width = 0, skip; ScanFmtPartDescr *descr = &fmtObj->descr[curr]; fmtObj->count++; - descr->width = 0; /* Assume width unspecified */ - /* Overread and store any "literal" prefix */ + descr->width = 0; + if (*fmt != '%' || fmt[1] == '%') { descr->type = 0; descr->prefix = &buffer[i]; for (; fmt < fmtEnd; ++fmt) { if (*fmt == '%') { @@ -15459,65 +13508,65 @@ } buffer[i++] = *fmt; } buffer[i++] = 0; } - /* Skip the conversion introducing '%' sign */ + ++fmt; - /* End reached due to non-conversion literal only? */ + if (fmt >= fmtEnd) goto done; - descr->pos = 0; /* Assume "natural" positioning */ + descr->pos = 0; if (*fmt == '*') { - descr->pos = -1; /* Okay, conversion will not be assigned */ + descr->pos = -1; ++fmt; } else - fmtObj->convCount++; /* Otherwise count as assign-conversion */ - /* Check if next token is a number (could be width or pos */ + fmtObj->convCount++; + if (sscanf(fmt, "%d%n", &width, &skip) == 1) { fmt += skip; - /* Was the number a XPG3 position specifier? */ + if (descr->pos != -1 && *fmt == '$') { int prev; ++fmt; descr->pos = width; width = 0; - /* Look if "natural" postioning and XPG3 one was mixed */ + if ((lastPos == 0 && descr->pos > 0) || (lastPos > 0 && descr->pos == 0)) { fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers"; return JIM_ERR; } - /* Look if this position was already used */ + for (prev = 0; prev < curr; ++prev) { if (fmtObj->descr[prev].pos == -1) continue; if (fmtObj->descr[prev].pos == descr->pos) { fmtObj->error = "variable is assigned by multiple \"%n$\" conversion specifiers"; return JIM_ERR; } } - /* Try to find a width after the XPG3 specifier */ + if (sscanf(fmt, "%d%n", &width, &skip) == 1) { descr->width = width; fmt += skip; } if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos) fmtObj->maxPos = descr->pos; } else { - /* Number was not a XPG3, so it has to be a width */ + descr->width = width; } } - /* If positioning mode was undetermined yet, fix this */ + if (lastPos == -1) lastPos = descr->pos; - /* Handle CHARSET conversion type ... */ + if (*fmt == '[') { int swapped = 1, beg = i, end, j; descr->type = '['; descr->arg = &buffer[i]; @@ -15532,11 +13581,11 @@ fmtObj->error = "unmatched [ in format string"; return JIM_ERR; } end = i; buffer[i++] = 0; - /* In case a range fence was given "backwards", swap it */ + while (swapped) { swapped = 0; for (j = beg + 1; j < end - 1; ++j) { if (buffer[j] == '-' && buffer[j - 1] > buffer[j + 1]) { char tmp = buffer[j - 1]; @@ -15547,11 +13596,11 @@ } } } } else { - /* Remember any valid modifier if given */ + if (strchr("hlL", *fmt) != 0) descr->modifier = tolower((int)*fmt++); descr->type = *fmt; if (strchr("efgcsndoxui", *fmt) == 0) { @@ -15571,23 +13620,19 @@ } done: return JIM_OK; } -/* Some accessor macros to allow lowlevel access to fields of internal repr */ + #define FormatGetCnvCount(_fo_) \ ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount #define FormatGetMaxPos(_fo_) \ ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos #define FormatGetError(_fo_) \ ((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error -/* JimScanAString is used to scan an unspecified string that ends with - * next WS, or a string that is specified via a charset. - * - */ static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str) { char *buffer = Jim_StrDup(str); char *p = buffer; @@ -15594,11 +13639,11 @@ while (*str) { int c; int n; if (!sdescr && isspace(UCHAR(*str))) - break; /* EOS via WS if unspecified */ + break; n = utf8_tounicode(str, &c); if (sdescr && !JimCharsetMatch(sdescr, c, JIM_CHARSET_SCAN)) break; while (n--) @@ -15606,15 +13651,10 @@ } *p = 0; return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer); } -/* ScanOneEntry will scan one entry out of the string passed as argument. - * It use the sscanf() function for this task. After extracting and - * converting of the value, the count of scanned characters will be - * returned of -1 in case of no conversion tool place and string was - * already scanned thru */ static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int strLen, ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr) { const char *tok; @@ -15622,103 +13662,91 @@ size_t scanned = 0; size_t anchor = pos; int i; Jim_Obj *tmpObj = NULL; - /* First pessimistically assume, we will not scan anything :-) */ + *valObjPtr = 0; if (descr->prefix) { - /* There was a prefix given before the conversion, skip it and adjust - * the string-to-be-parsed accordingly */ - /* XXX: Should be checking strLen, not str[pos] */ + for (i = 0; pos < strLen && descr->prefix[i]; ++i) { - /* If prefix require, skip WS */ + if (isspace(UCHAR(descr->prefix[i]))) while (pos < strLen && isspace(UCHAR(str[pos]))) ++pos; else if (descr->prefix[i] != str[pos]) - break; /* Prefix do not match here, leave the loop */ + break; else - ++pos; /* Prefix matched so far, next round */ + ++pos; } if (pos >= strLen) { - return -1; /* All of str consumed: EOF condition */ + return -1; } else if (descr->prefix[i] != 0) - return 0; /* Not whole prefix consumed, no conversion possible */ + return 0; } - /* For all but following conversion, skip leading WS */ + if (descr->type != 'c' && descr->type != '[' && descr->type != 'n') while (isspace(UCHAR(str[pos]))) ++pos; - /* Determine how much skipped/scanned so far */ + scanned = pos - anchor; - /* %c is a special, simple case. no width */ + if (descr->type == 'n') { - /* Return pseudo conversion means: how much scanned so far? */ + *valObjPtr = Jim_NewIntObj(interp, anchor + scanned); } else if (pos >= strLen) { - /* Cannot scan anything, as str is totally consumed */ + return -1; } else if (descr->type == 'c') { int c; scanned += utf8_tounicode(&str[pos], &c); *valObjPtr = Jim_NewIntObj(interp, c); return scanned; } else { - /* Processing of conversions follows ... */ + if (descr->width > 0) { - /* Do not try to scan as fas as possible but only the given width. - * To ensure this, we copy the part that should be scanned. */ size_t sLen = utf8_strlen(&str[pos], strLen - pos); size_t tLen = descr->width > sLen ? sLen : descr->width; tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen); tok = tmpObj->bytes; } else { - /* As no width was given, simply refer to the original string */ + tok = &str[pos]; } switch (descr->type) { case 'd': case 'o': case 'x': case 'u': case 'i':{ - char *endp; /* Position where the number finished */ + char *endp; jim_wide w; int base = descr->type == 'o' ? 8 : descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10; - /* Try to scan a number with the given base */ + w = strtoull(tok, &endp, base); if (endp == tok && base == 0) { - /* If scanning failed, and base was undetermined, simply - * put it to 10 and try once more. This should catch the - * case where %i begin to parse a number prefix (e.g. - * '0x' but no further digits follows. This will be - * handled as a ZERO followed by a char 'x' by Tcl */ w = strtoull(tok, &endp, 10); } if (endp != tok) { - /* There was some number sucessfully scanned! */ + *valObjPtr = Jim_NewIntObj(interp, w); - /* Adjust the number-of-chars scanned so far */ + scanned += endp - tok; } else { - /* Nothing was scanned. We have to determine if this - * happened due to e.g. prefix mismatch or input str - * exhausted */ scanned = *tok ? 0 : -1; } break; } case 's': @@ -15732,36 +13760,28 @@ case 'g':{ char *endp; double value = strtod(tok, &endp); if (endp != tok) { - /* There was some number sucessfully scanned! */ + *valObjPtr = Jim_NewDoubleObj(interp, value); - /* Adjust the number-of-chars scanned so far */ + scanned += endp - tok; } else { - /* Nothing was scanned. We have to determine if this - * happened due to e.g. prefix mismatch or input str - * exhausted */ scanned = *tok ? 0 : -1; } break; } } - /* If a substring was allocated (due to pre-defined width) do not - * forget to free it */ if (tmpObj) { Jim_FreeNewObj(interp, tmpObj); } } return scanned; } -/* Jim_ScanString is the workhorse of string scanning. It will scan a given - * string and returns all converted (and not ignored) values in a list back - * to the caller. If an error occured, a NULL pointer will be returned */ Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags) { size_t i, pos; int scanned = 1; @@ -15771,65 +13791,65 @@ Jim_Obj **resultVec = 0; int resultc; Jim_Obj *emptyStr = 0; ScanFmtStringObj *fmtObj; - /* This should never happen. The format object should already be of the correct type */ - JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, interp, "Jim_ScanString() for non-scan format")); + + JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format")); fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr; - /* Check if format specification was valid */ + if (fmtObj->error != 0) { if (flags & JIM_ERRMSG) Jim_SetResultString(interp, fmtObj->error, -1); return 0; } - /* Allocate a new "shared" empty string for all unassigned conversions */ + emptyStr = Jim_NewEmptyStringObj(interp); Jim_IncrRefCount(emptyStr); - /* Create a list and fill it with empty strings up to max specified XPG3 */ - resultList = Jim_NewListObj(interp, 0, 0); + + resultList = Jim_NewListObj(interp, NULL, 0); if (fmtObj->maxPos > 0) { for (i = 0; i < fmtObj->maxPos; ++i) Jim_ListAppendElement(interp, resultList, emptyStr); JimListGetElements(interp, resultList, &resultc, &resultVec); } - /* Now handle every partial format description */ + for (i = 0, pos = 0; i < fmtObj->count; ++i) { ScanFmtPartDescr *descr = &(fmtObj->descr[i]); Jim_Obj *value = 0; - /* Only last type may be "literal" w/o conversion - skip it! */ + if (descr->type == 0) continue; - /* As long as any conversion could be done, we will proceed */ + if (scanned > 0) scanned = ScanOneEntry(interp, str, pos, strLen, fmtObj, i, &value); - /* In case our first try results in EOF, we will leave */ + if (scanned == -1 && i == 0) goto eof; - /* Advance next pos-to-be-scanned for the amount scanned already */ + pos += scanned; - /* value == 0 means no conversion took place so take empty string */ + if (value == 0) value = Jim_NewEmptyStringObj(interp); - /* If value is a non-assignable one, skip it */ + if (descr->pos == -1) { Jim_FreeNewObj(interp, value); } else if (descr->pos == 0) - /* Otherwise append it to the result list if no XPG3 was given */ + Jim_ListAppendElement(interp, resultList, value); else if (resultVec[descr->pos - 1] == emptyStr) { - /* But due to given XPG3, put the value into the corr. slot */ + Jim_DecrRefCount(interp, resultVec[descr->pos - 1]); Jim_IncrRefCount(value); resultVec[descr->pos - 1] = value; } else { - /* Otherwise, the slot was already used - free obj and ERROR */ + Jim_FreeNewObj(interp, value); goto err; } } Jim_DecrRefCount(interp, emptyStr); @@ -15842,14 +13862,11 @@ Jim_DecrRefCount(interp, emptyStr); Jim_FreeNewObj(interp, resultList); return 0; } -/* ----------------------------------------------------------------------------- - * Pseudo Random Number Generation - * ---------------------------------------------------------------------------*/ -/* Initialize the sbox with the numbers from 0 to 255 */ + static void JimPrngInit(Jim_Interp *interp) { #define PRNG_SEED_SIZE 256 int i; unsigned int *seed; @@ -15863,22 +13880,22 @@ } JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed)); Jim_Free(seed); } -/* Generates N bytes of random data */ + static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len) { Jim_PrngState *prng; unsigned char *destByte = (unsigned char *)dest; unsigned int si, sj, x; - /* initialization, only needed the first time */ + if (interp->prngState == NULL) JimPrngInit(interp); prng = interp->prngState; - /* generates 'len' bytes of pseudo-random numbers */ + for (x = 0; x < len; x++) { prng->i = (prng->i + 1) & 0xff; si = prng->sbox[prng->i]; prng->j = (prng->j + si) & 0xff; sj = prng->sbox[prng->j]; @@ -15886,43 +13903,40 @@ prng->sbox[prng->j] = si; *destByte++ = prng->sbox[(si + sj) & 0xff]; } } -/* Re-seed the generator with user-provided bytes */ + static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen) { int i; Jim_PrngState *prng; - /* initialization, only needed the first time */ + if (interp->prngState == NULL) JimPrngInit(interp); prng = interp->prngState; - /* Set the sbox[i] with i */ + for (i = 0; i < 256; i++) prng->sbox[i] = i; - /* Now use the seed to perform a random permutation of the sbox */ + for (i = 0; i < seedLen; i++) { unsigned char t; t = prng->sbox[i & 0xFF]; prng->sbox[i & 0xFF] = prng->sbox[seed[i]]; prng->sbox[seed[i]] = t; } prng->i = prng->j = 0; - /* discard at least the first 256 bytes of stream. - * borrow the seed buffer for this - */ for (i = 0; i < 256; i += seedLen) { JimRandomBytes(interp, seed, seedLen); } } -/* [incr] */ + static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { jim_wide wideValue, increment = 1; Jim_Obj *intObjPtr; @@ -15934,11 +13948,11 @@ if (Jim_GetWide(interp, argv[2], &increment) != JIM_OK) return JIM_ERR; } intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); if (!intObjPtr) { - /* Set missing variable to 0 */ + wideValue = 0; } else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) { return JIM_ERR; } @@ -15948,191 +13962,159 @@ Jim_FreeNewObj(interp, intObjPtr); return JIM_ERR; } } else { - /* Can do it the quick way */ + Jim_InvalidateStringRep(intObjPtr); JimWideValue(intObjPtr) = wideValue + increment; - /* The following step is required in order to invalidate the - * string repr of "FOO" if the var name is on the form of "FOO(IDX)" */ if (argv[1]->typePtr != &variableObjType) { - /* Note that this can't fail since GetVariable already succeeded */ + Jim_SetVariable(interp, argv[1], intObjPtr); } } Jim_SetResult(interp, intObjPtr); return JIM_OK; } -/* ----------------------------------------------------------------------------- - * Eval - * ---------------------------------------------------------------------------*/ -#define JIM_EVAL_SARGV_LEN 8 /* static arguments vector length */ -#define JIM_EVAL_SINTV_LEN 8 /* static interpolation vector length */ +#define JIM_EVAL_SARGV_LEN 8 +#define JIM_EVAL_SINTV_LEN 8 -/* Handle calls to the [unknown] command */ -static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *filename, + +static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv, Jim_Obj *fileNameObj, int linenr) { Jim_Obj **v, *sv[JIM_EVAL_SARGV_LEN]; int retCode; - /* If JimUnknown() is recursively called too many times... - * done here - */ if (interp->unknown_called > 50) { return JIM_ERR; } - /* If the [unknown] command does not exists returns - * just now */ if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL) return JIM_ERR; - /* The object interp->unknown just contains - * the "unknown" string, it is used in order to - * avoid to lookup the unknown command every time - * but instread to cache the result. */ if (argc + 1 <= JIM_EVAL_SARGV_LEN) v = sv; else v = Jim_Alloc(sizeof(Jim_Obj *) * (argc + 1)); - /* Make a copy of the arguments vector, but shifted on - * the right of one position. The command name of the - * command will be instead the first argument of the - * [unknown] call. */ memcpy(v + 1, argv, sizeof(Jim_Obj *) * argc); v[0] = interp->unknown; - /* Call it */ + interp->unknown_called++; - retCode = JimEvalObjVector(interp, argc + 1, v, filename, linenr); + retCode = JimEvalObjVector(interp, argc + 1, v, fileNameObj, linenr); interp->unknown_called--; - /* Clean up */ + if (v != sv) Jim_Free(v); return retCode; } -/* Eval the object vector 'objv' composed of 'objc' elements. - * Every element is used as single argument. - * Jim_EvalObj() will call this function every time its object - * argument is of "list" type, with no string representation. - * - * This is possible because the string representation of a - * list object generated by the UpdateStringOfList is made - * in a way that ensures that every list element is a different - * command argument. */ static int JimEvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv, - const char *filename, int linenr) + Jim_Obj *fileNameObj, int linenr) { int i, retcode; Jim_Cmd *cmdPtr; - /* Incr refcount of arguments. */ + for (i = 0; i < objc; i++) Jim_IncrRefCount(objv[i]); - /* Command lookup */ + cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG); if (cmdPtr == NULL) { - retcode = JimUnknown(interp, objc, objv, filename, linenr); + retcode = JimUnknown(interp, objc, objv, fileNameObj, linenr); } else { - /* Call it -- Make sure result is an empty object. */ + JimIncrCmdRefCount(cmdPtr); Jim_SetEmptyResult(interp); if (cmdPtr->isproc) { - retcode = JimCallProcedure(interp, cmdPtr, filename, linenr, objc, objv); + retcode = JimCallProcedure(interp, cmdPtr, fileNameObj, linenr, objc, objv); } else { interp->cmdPrivData = cmdPtr->u.native.privData; retcode = cmdPtr->u.native.cmdProc(interp, objc, objv); } JimDecrCmdRefCount(interp, cmdPtr); } - /* Decr refcount of arguments and return the retcode */ + for (i = 0; i < objc; i++) Jim_DecrRefCount(interp, objv[i]); return retcode; } int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv) { - return JimEvalObjVector(interp, objc, objv, NULL, 0); + return JimEvalObjVector(interp, objc, objv, interp->emptyObj, 1); } -/** - * Invokes 'prefix' as a command with the objv array as arguments. - */ -int Jim_EvalObjPrefix(Jim_Interp *interp, const char *prefix, int objc, Jim_Obj *const *objv) +int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv) { int i; int ret; Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv)); - nargv[0] = Jim_NewStringObj(interp, prefix, -1); + nargv[0] = prefix; for (i = 0; i < objc; i++) { nargv[i + 1] = objv[i]; } ret = Jim_EvalObjVector(interp, objc + 1, nargv); Jim_Free(nargv); return ret; } -static void JimAddErrorToStack(Jim_Interp *interp, int retcode, const char *filename, int line) +static void JimAddErrorToStack(Jim_Interp *interp, int retcode, Jim_Obj *fileNameObj, int line) { int rc = retcode; if (rc == JIM_ERR && !interp->errorFlag) { - /* This is the first error, so save the file/line information and reset the stack */ + interp->errorFlag = 1; - JimSetErrorFileName(interp, filename); - JimSetErrorLineNumber(interp, line); + Jim_IncrRefCount(fileNameObj); + Jim_DecrRefCount(interp, interp->errorFileNameObj); + interp->errorFileNameObj = fileNameObj; + interp->errorLine = line; JimResetStackTrace(interp); - /* Always add a level where the error first occurs */ + interp->addStackTrace++; } - /* Now if this is an "interesting" level, add it to the stack trace */ + if (rc == JIM_ERR && interp->addStackTrace > 0) { - /* Add the stack info for the current level */ + - JimAppendStackTrace(interp, Jim_String(interp->errorProc), filename, line); + JimAppendStackTrace(interp, Jim_String(interp->errorProc), fileNameObj, line); - /* Note: if we didn't have a filename for this level, - * don't clear the addStackTrace flag - * so we can pick it up at the next level - */ - if (*filename) { + if (Jim_Length(fileNameObj)) { interp->addStackTrace = 0; } Jim_DecrRefCount(interp, interp->errorProc); interp->errorProc = interp->emptyObj; Jim_IncrRefCount(interp->errorProc); } else if (rc == JIM_RETURN && interp->returnCode == JIM_ERR) { - /* Propagate the addStackTrace value through 'return -code error' */ + } else { interp->addStackTrace = 0; } } -/* And delete any local procs */ + static void JimDeleteLocalProcs(Jim_Interp *interp) { if (interp->localProcs) { char *procname; while ((procname = Jim_StackPop(interp->localProcs)) != NULL) { - /* If there is a pushed command, find it */ + Jim_Cmd *prevCmd = NULL; Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, procname); if (he) { Jim_Cmd *cmd = (Jim_Cmd *)he->u.val; if (cmd->isproc && cmd->u.proc.prevCmd) { @@ -16139,15 +14121,15 @@ prevCmd = cmd->u.proc.prevCmd; cmd->u.proc.prevCmd = NULL; } } - /* Delete the local proc */ + Jim_DeleteCommand(interp, procname); if (prevCmd) { - /* And restore the pushed command */ + Jim_AddHashEntry(&interp->commands, procname, prevCmd); } Jim_Free(procname); } Jim_FreeStack(interp->localProcs); @@ -16179,21 +14161,21 @@ case JIM_OK: case JIM_RETURN: objPtr = interp->result; break; case JIM_BREAK: - /* Stop substituting */ + return JIM_BREAK; case JIM_CONTINUE: - /* just skip this one */ + return JIM_CONTINUE; default: return JIM_ERR; } break; default: - JimPanic((1, interp, + JimPanic((1, "default token type (%d) reached " "in Jim_SubstObj().", token->type)); objPtr = NULL; break; } if (objPtr) { @@ -16201,14 +14183,10 @@ return JIM_OK; } return JIM_ERR; } -/* Interpolate the given tokens into a unique Jim_Obj returned by reference - * via *objPtrPtr. This function is only called by Jim_EvalObj() and Jim_SubstObj() - * The returned object has refcount = 0. - */ static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags) { int totlen = 0, i; Jim_Obj **intv; Jim_Obj *sintv[JIM_EVAL_SINTV_LEN]; @@ -16218,32 +14196,30 @@ if (tokens <= JIM_EVAL_SINTV_LEN) intv = sintv; else intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens); - /* Compute every token forming the argument - * in the intv objects vector. */ for (i = 0; i < tokens; i++) { switch (JimSubstOneToken(interp, &token[i], &intv[i])) { case JIM_OK: case JIM_RETURN: break; case JIM_BREAK: if (flags & JIM_SUBST_FLAG) { - /* Stop here */ + tokens = i; continue; } - /* XXX: Should probably set an error about break outside loop */ - /* fall through to error */ + + case JIM_CONTINUE: if (flags & JIM_SUBST_FLAG) { intv[i] = NULL; continue; } - /* XXX: Ditto continue outside loop */ - /* fall through to error */ + + default: while (i--) { Jim_DecrRefCount(interp, intv[i]); } if (intv != sintv) { @@ -16254,23 +14230,21 @@ Jim_IncrRefCount(intv[i]); Jim_String(intv[i]); totlen += intv[i]->length; } - /* Fast path return for a single token */ + if (tokens == 1 && intv[0] && intv == sintv) { Jim_DecrRefCount(interp, intv[0]); return intv[0]; } - /* Concatenate every token in an unique - * object. */ objPtr = Jim_NewStringObjNoAlloc(interp, NULL, 0); if (tokens == 4 && token[0].type == JIM_TT_ESC && token[1].type == JIM_TT_ESC && token[2].type == JIM_TT_VAR) { - /* May be able to do fast interpolated object -> dictSubst */ + objPtr->typePtr = &interpolatedObjType; objPtr->internalRep.twoPtrValue.ptr1 = (void *)token; objPtr->internalRep.twoPtrValue.ptr2 = intv[2]; Jim_IncrRefCount(intv[2]); } @@ -16283,39 +14257,33 @@ s += intv[i]->length; Jim_DecrRefCount(interp, intv[i]); } } objPtr->bytes[totlen] = '\0'; - /* Free the intv vector if not static. */ + if (intv != sintv) { Jim_Free(intv); } return objPtr; } -/* If listPtr is a list, call JimEvalObjVector() with the given source info. - * Otherwise eval with Jim_EvalObj() - */ -int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr, const char *filename, int linenr) +static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *fileNameObj, int linenr) { - if (!Jim_IsList(listPtr)) { - return Jim_EvalObj(interp, listPtr); + int retcode = JIM_OK; + + JimPanic((!Jim_IsList(listPtr), "JimEvalObjList() called without list arg")); + + if (listPtr->internalRep.listValue.len) { + Jim_IncrRefCount(listPtr); + retcode = JimEvalObjVector(interp, + listPtr->internalRep.listValue.len, + listPtr->internalRep.listValue.ele, fileNameObj, linenr); + Jim_DecrRefCount(interp, listPtr); } - else { - int retcode = JIM_OK; - - if (listPtr->internalRep.listValue.len) { - Jim_IncrRefCount(listPtr); - retcode = JimEvalObjVector(interp, - listPtr->internalRep.listValue.len, - listPtr->internalRep.listValue.ele, filename, linenr); - Jim_DecrRefCount(interp, listPtr); - } - return retcode; - } + return retcode; } int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr) { int i; @@ -16325,29 +14293,20 @@ Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL; int linenr = 0; interp->errorFlag = 0; - /* If the object is of type "list", we can call - * a specialized version of Jim_EvalObj() */ - if (Jim_IsList(scriptObjPtr)) { - return Jim_EvalObjList(interp, scriptObjPtr, NULL, 0); + if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) { + return JimEvalObjList(interp, scriptObjPtr, interp->emptyObj, 1); } - Jim_IncrRefCount(scriptObjPtr); /* Make sure it's shared. */ + Jim_IncrRefCount(scriptObjPtr); script = Jim_GetScript(interp, scriptObjPtr); - /* Reset the interpreter result. This is useful to - * return the empty result in the case of empty program. */ Jim_SetEmptyResult(interp); #ifdef JIM_OPTIMIZATION - /* Check for one of the following common scripts used by for, while - * - * {} - * incr a - */ if (script->len == 0) { Jim_DecrRefCount(interp, scriptObjPtr); return JIM_OK; } if (script->len == 3 @@ -16366,47 +14325,31 @@ return JIM_OK; } } #endif - /* Now we have to make sure the internal repr will not be - * freed on shimmering. - * - * Think for example to this: - * - * set x {llength $x; ... some more code ...}; eval $x - * - * In order to preserve the internal rep, we increment the - * inUse field of the script internal rep structure. */ script->inUse++; token = script->token; argv = sargv; - /* Execute every command sequentially until the end of the script - * or an error occurs. - */ for (i = 0; i < script->len && retcode == JIM_OK; ) { int argc; int j; Jim_Cmd *cmd; - /* First token of the line is always JIM_TT_LINE */ + argc = token[i].objPtr->internalRep.scriptLineValue.argc; linenr = token[i].objPtr->internalRep.scriptLineValue.line; - /* Allocate the arguments vector if required */ + if (argc > JIM_EVAL_SARGV_LEN) argv = Jim_Alloc(sizeof(Jim_Obj *) * argc); - /* Skip the JIM_TT_LINE token */ + i++; - /* Populate the arguments objects. - * If an error occurs, retcode will be set and - * 'j' will be set to the number of args expanded - */ for (j = 0; j < argc; j++) { long wordtokens = 1; int expand = 0; Jim_Obj *wordObjPtr = NULL; @@ -16417,12 +14360,10 @@ wordtokens = -wordtokens; } } if (wordtokens == 1) { - /* Fast path if the token does not - * need interpolation */ switch (token[i].type) { case JIM_TT_ESC: case JIM_TT_STR: wordObjPtr = token[i].objPtr; @@ -16441,16 +14382,14 @@ if (retcode == JIM_OK) { wordObjPtr = Jim_GetResult(interp); } break; default: - JimPanic((1, interp, "default token type reached " "in Jim_EvalObj().")); + JimPanic((1, "default token type reached " "in Jim_EvalObj().")); } } else { - /* For interpolation we call a helper - * function to do the work for us. */ wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE); } if (!wordObjPtr) { if (retcode == JIM_OK) { @@ -16464,11 +14403,11 @@ if (!expand) { argv[j] = wordObjPtr; } else { - /* Need to expand wordObjPtr into multiple args from argv[j] ... */ + int len = Jim_ListLength(interp, wordObjPtr); int newargc = argc + len - 1; int k; if (len > 1) { @@ -16477,60 +14416,56 @@ argv = Jim_Alloc(sizeof(*argv) * newargc); memcpy(argv, sargv, sizeof(*argv) * j); } } else { - /* Need to realloc to make room for (len - 1) more entries */ + argv = Jim_Realloc(argv, sizeof(*argv) * newargc); } } - /* Now copy in the expanded version */ + for (k = 0; k < len; k++) { argv[j++] = wordObjPtr->internalRep.listValue.ele[k]; Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]); } - /* The original object reference is no longer needed, - * after the expansion it is no longer present on - * the argument vector, but the single elements are - * in its place. */ Jim_DecrRefCount(interp, wordObjPtr); - /* And update the indexes */ + j--; argc += len - 1; } } if (retcode == JIM_OK && argc) { - /* Lookup the command to call */ + cmd = Jim_GetCommand(interp, argv[0], JIM_ERRMSG); if (cmd != NULL) { - /* Call it -- Make sure result is an empty object. */ + JimIncrCmdRefCount(cmd); Jim_SetEmptyResult(interp); if (cmd->isproc) { retcode = - JimCallProcedure(interp, cmd, script->fileName, linenr, argc, argv); + JimCallProcedure(interp, cmd, script->fileNameObj, linenr, argc, argv); } else { interp->cmdPrivData = cmd->u.native.privData; retcode = cmd->u.native.cmdProc(interp, argc, argv); } JimDecrCmdRefCount(interp, cmd); } else { - /* Call [unknown] */ - retcode = JimUnknown(interp, argc, argv, script->fileName, linenr); + + retcode = JimUnknown(interp, argc, argv, script->fileNameObj, linenr); } if (interp->signal_level && interp->sigmask) { - /* Check for a signal after each command */ + retcode = JIM_SIGNAL; } } - /* Finished with the command, so decrement ref counts of each argument */ + while (j-- > 0) { Jim_DecrRefCount(interp, argv[j]); } if (argv != sargv) { @@ -16537,16 +14472,13 @@ Jim_Free(argv); argv = sargv; } } - /* Possibly add to the error stack trace */ - JimAddErrorToStack(interp, retcode, script->fileName, linenr); + + JimAddErrorToStack(interp, retcode, script->fileNameObj, linenr); - /* Note that we don't have to decrement inUse, because the - * following code transfers our use of the reference again to - * the script object. */ Jim_FreeIntRep(interp, scriptObjPtr); scriptObjPtr->typePtr = &scriptObjType; Jim_SetIntRepPtr(scriptObjPtr, script); Jim_DecrRefCount(interp, scriptObjPtr); @@ -16554,14 +14486,14 @@ } static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj) { int retcode; - /* If argObjPtr begins with '&', do an automatic upvar */ + const char *varname = Jim_String(argNameObj); if (*varname == '&') { - /* First check that the target variable exists */ + Jim_Obj *objPtr; Jim_CallFrame *savedCallFrame = interp->framePtr; interp->framePtr = interp->framePtr->parentCallFrame; objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG); @@ -16568,11 +14500,11 @@ interp->framePtr = savedCallFrame; if (!objPtr) { return JIM_ERR; } - /* It exists, so perform the binding. */ + objPtr = Jim_NewStringObj(interp, varname + 1, -1); Jim_IncrRefCount(objPtr); retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parentCallFrame); Jim_DecrRefCount(interp, objPtr); } @@ -16580,31 +14512,28 @@ retcode = Jim_SetVariable(interp, argNameObj, argValObj); } return retcode; } -/** - * Sets the interp result to be an error message indicating the required proc args. - */ static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd) { - /* Create a nice error message, consistent with Tcl 8.5 */ + Jim_Obj *argmsg = Jim_NewStringObj(interp, "", 0); int i; for (i = 0; i < cmd->u.proc.argListLen; i++) { Jim_AppendString(interp, argmsg, " ", 1); if (i == cmd->u.proc.argsPos) { if (cmd->u.proc.arglist[i].defaultObjPtr) { - /* Renamed args */ + Jim_AppendString(interp, argmsg, "?", 1); Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr); Jim_AppendString(interp, argmsg, " ...?", -1); } else { - /* We have plain args */ + Jim_AppendString(interp, argmsg, "?argument ...?", -1); } } else { if (cmd->u.proc.arglist[i].defaultObjPtr) { @@ -16619,68 +14548,64 @@ } Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg); Jim_FreeNewObj(interp, argmsg); } -/* Call a procedure implemented in Tcl. - * It's possible to speed-up a lot this function, currently - * the callframes are not cached, but allocated and - * destroied every time. What is expecially costly is - * to create/destroy the local vars hash table every time. - * - * This can be fixed just implementing callframes caching - * in JimCreateCallFrame() and JimFreeCallFrame(). */ -static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, const char *filename, int linenr, int argc, +static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, Jim_Obj *fileNameObj, int linenr, int argc, Jim_Obj *const *argv) { Jim_CallFrame *callFramePtr; Jim_Stack *prevLocalProcs; int i, d, retcode, optargs; - /* Check arity */ + if (argc - 1 < cmd->u.proc.reqArity || (cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) { JimSetProcWrongArgs(interp, argv[0], cmd); return JIM_ERR; } - /* Check if there are too nested calls */ + if (interp->framePtr->level == interp->maxNestingDepth) { Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1); return JIM_ERR; } - /* Create a new callframe */ + callFramePtr = JimCreateCallFrame(interp, interp->framePtr); callFramePtr->argv = argv; callFramePtr->argc = argc; callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr; callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr; callFramePtr->staticVars = cmd->u.proc.staticVars; - callFramePtr->filename = filename; + callFramePtr->fileNameObj = fileNameObj; callFramePtr->line = linenr; Jim_IncrRefCount(cmd->u.proc.argListObjPtr); Jim_IncrRefCount(cmd->u.proc.bodyObjPtr); interp->framePtr = callFramePtr; - /* How many optional args are available */ + + prevLocalProcs = interp->localProcs; + interp->localProcs = NULL; + + optargs = (argc - 1 - cmd->u.proc.reqArity); - /* Step 'i' along the actual args, and step 'd' along the formal args */ + i = 1; for (d = 0; d < cmd->u.proc.argListLen; d++) { Jim_Obj *nameObjPtr = cmd->u.proc.arglist[d].nameObjPtr; if (d == cmd->u.proc.argsPos) { - /* assign $args */ + Jim_Obj *listObjPtr; int argsLen = 0; if (cmd->u.proc.reqArity + cmd->u.proc.optArity < argc - 1) { argsLen = argc - 1 - (cmd->u.proc.reqArity + cmd->u.proc.optArity); } listObjPtr = Jim_NewListObj(interp, &argv[i], argsLen); - /* It is possible to rename args. */ + if (cmd->u.proc.arglist[d].defaultObjPtr) { nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr; } retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr); if (retcode != JIM_OK) { @@ -16689,53 +14614,48 @@ i += argsLen; continue; } - /* Optional or required? */ + if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) { retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]); } else { - /* Ran out, so use the default */ + retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr); } if (retcode != JIM_OK) { goto badargset; } } - /* Install a new stack for local procs */ - prevLocalProcs = interp->localProcs; - interp->localProcs = NULL; - - /* Eval the body */ + retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr); - /* Delete any local procs */ - JimDeleteLocalProcs(interp); - interp->localProcs = prevLocalProcs; - badargset: - /* Destroy the callframe */ + interp->framePtr = interp->framePtr->parentCallFrame; if (callFramePtr->vars.size != JIM_HT_INITIAL_SIZE) { JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NONE); } else { JimFreeCallFrame(interp, callFramePtr, JIM_FCF_NOHT); } - /* Handle the JIM_EVAL return code */ + while (retcode == JIM_EVAL) { Jim_Obj *resultScriptObjPtr = Jim_GetResult(interp); Jim_IncrRefCount(resultScriptObjPtr); - /* Should be a list! */ - retcode = Jim_EvalObjList(interp, resultScriptObjPtr, filename, linenr); + + retcode = JimEvalObjList(interp, resultScriptObjPtr, fileNameObj, linenr); + if (retcode == JIM_RETURN) { + interp->returnLevel++; + } Jim_DecrRefCount(interp, resultScriptObjPtr); } - /* Handle the JIM_RETURN return code */ + if (retcode == JIM_RETURN) { if (--interp->returnLevel <= 0) { retcode = interp->returnCode; interp->returnCode = JIM_OK; interp->returnLevel = 0; @@ -16745,26 +14665,30 @@ interp->addStackTrace++; Jim_DecrRefCount(interp, interp->errorProc); interp->errorProc = argv[0]; Jim_IncrRefCount(interp->errorProc); } + + + JimDeleteLocalProcs(interp); + interp->localProcs = prevLocalProcs; + return retcode; } -int Jim_Eval_Named(Jim_Interp *interp, const char *script, const char *filename, int lineno) +int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script) { int retval; Jim_Obj *scriptObjPtr; scriptObjPtr = Jim_NewStringObj(interp, script, -1); Jim_IncrRefCount(scriptObjPtr); - if (filename) { Jim_Obj *prevScriptObj; - JimSetSourceInfo(interp, scriptObjPtr, filename, lineno); + JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno); prevScriptObj = interp->currentScriptObj; interp->currentScriptObj = scriptObjPtr; retval = Jim_EvalObj(interp, scriptObjPtr); @@ -16778,14 +14702,14 @@ return retval; } int Jim_Eval(Jim_Interp *interp, const char *script) { - return Jim_Eval_Named(interp, script, NULL, 0); + return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1)); } -/* Execute script in the scope of the global level */ + int Jim_EvalGlobal(Jim_Interp *interp, const char *script) { int retval; Jim_CallFrame *savedFramePtr = interp->framePtr; @@ -16840,14 +14764,14 @@ } fclose(fp); buf[readlen] = 0; scriptObjPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen); - JimSetSourceInfo(interp, scriptObjPtr, filename, 1); + JimSetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), 1); Jim_IncrRefCount(scriptObjPtr); - /* Now check the script for unmatched braces, etc. */ + if (SetScriptFromAny(interp, scriptObjPtr, &result) == JIM_ERR) { const char *msg; char linebuf[20]; switch (result.missing) { @@ -16874,20 +14798,20 @@ prevScriptObj = interp->currentScriptObj; interp->currentScriptObj = scriptObjPtr; retcode = Jim_EvalObj(interp, scriptObjPtr); - /* Handle the JIM_RETURN return code */ + if (retcode == JIM_RETURN) { if (--interp->returnLevel <= 0) { retcode = interp->returnCode; interp->returnCode = JIM_OK; interp->returnLevel = 0; } } if (retcode == JIM_ERR) { - /* EvalFile changes context, so add a stack frame here */ + interp->addStackTrace++; } interp->currentScriptObj = prevScriptObj; @@ -16894,13 +14818,10 @@ Jim_DecrRefCount(interp, scriptObjPtr); return retcode; } -/* ----------------------------------------------------------------------------- - * Subst - * ---------------------------------------------------------------------------*/ static int JimParseSubstStr(struct JimParserCtx *pc) { pc->tstart = pc->p; pc->tline = pc->linenr; while (pc->len && *pc->p != '$' && *pc->p != '[') { @@ -16967,48 +14888,41 @@ break; } return JIM_OK; } -/* The subst object type reuses most of the data structures and functions - * of the script object. Script's data structures are a bit more complex - * for what is needed for [subst]itution tasks, but the reuse helps to - * deal with a single data structure at the cost of some more memory - * usage for substitutions. */ -/* This method takes the string representation of an object - * as a Tcl string where to perform [subst]itution, and generates - * the pre-parsed internal representation. */ static int SetSubstFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr, int flags) { int scriptTextLen; const char *scriptText = Jim_GetString(objPtr, &scriptTextLen); struct JimParserCtx parser; struct ScriptObj *script = Jim_Alloc(sizeof(*script)); ParseTokenList tokenlist; - /* Initially parse the subst into tokens (in tokenlist) */ + ScriptTokenListInit(&tokenlist); JimParserInit(&parser, scriptText, scriptTextLen, 1); while (1) { JimParseSubst(&parser, flags); if (parser.eof) { - /* Note that subst doesn't need the EOL token */ + break; } ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt, parser.tline); } - /* Create the "real" subst/script tokens from the initial token list */ + script->inUse = 1; script->substFlags = flags; - script->fileName = NULL; + script->fileNameObj = interp->emptyObj; + Jim_IncrRefCount(script->fileNameObj); SubstObjAddTokens(interp, script, &tokenlist); - /* No longer need the token list */ + ScriptTokenListFree(&tokenlist); #ifdef DEBUG_SHOW_SUBST { int i; @@ -17019,11 +14933,11 @@ Jim_String(script->token[i].objPtr)); } } #endif - /* Free the old internal rep and set the new one. */ + Jim_FreeIntRep(interp, objPtr); Jim_SetIntRepPtr(objPtr, script); objPtr->typePtr = &scriptObjType; return JIM_OK; } @@ -17033,20 +14947,15 @@ if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags) SetSubstFromAny(interp, objPtr, flags); return (ScriptObj *) Jim_GetIntRepPtr(objPtr); } -/* Performs commands,variables,blackslashes substitution, - * storing the result object (with refcount 0) into - * resObjPtrPtr. */ int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags) { ScriptObj *script = Jim_GetSubst(interp, substObjPtr, flags); - Jim_IncrRefCount(substObjPtr); /* Make sure it's shared. */ - /* In order to preserve the internal rep, we increment the - * inUse field of the script internal rep structure. */ + Jim_IncrRefCount(substObjPtr); script->inUse++; *resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags); script->inUse--; @@ -17055,13 +14964,10 @@ return JIM_ERR; } return JIM_OK; } -/* ----------------------------------------------------------------------------- - * Core commands utility functions - * ---------------------------------------------------------------------------*/ void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg) { int i; Jim_Obj *objPtr = Jim_NewEmptyStringObj(interp); @@ -17076,26 +14982,26 @@ Jim_SetResult(interp, objPtr); } #define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL) -/* type is: 0=commands, 1=procs, 2=channels */ + static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type) { Jim_HashTableIterator *htiter; Jim_HashEntry *he; Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0); - /* Check for the non-pattern case. We can do this much more efficiently. */ + if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) { Jim_Cmd *cmdPtr = Jim_GetCommand(interp, patternObjPtr, JIM_NONE); if (cmdPtr) { if (type == 1 && !cmdPtr->isproc) { - /* not a proc */ + } else if (type == 2 && !Jim_AioFilehandle(interp, patternObjPtr)) { - /* not a channel */ + } else { Jim_ListAppendElement(interp, listObjPtr, patternObjPtr); } } @@ -17106,19 +15012,19 @@ while ((he = Jim_NextHashEntry(htiter)) != NULL) { Jim_Cmd *cmdPtr = he->u.val; Jim_Obj *cmdNameObj; if (type == 1 && !cmdPtr->isproc) { - /* not a proc */ + continue; } if (patternObjPtr && !JimStringMatch(interp, patternObjPtr, he->key, 0)) continue; cmdNameObj = Jim_NewStringObj(interp, he->key, -1); - /* Is it a channel? */ + if (type == 2 && !Jim_AioFilehandle(interp, cmdNameObj)) { Jim_FreeNewObj(interp, cmdNameObj); continue; } @@ -17126,11 +15032,11 @@ } Jim_FreeHashTableIterator(htiter); return listObjPtr; } -/* Keep this in order */ + #define JIM_VARLIST_GLOBALS 0 #define JIM_VARLIST_LOCALS 1 #define JIM_VARLIST_VARS 2 static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode) @@ -17141,12 +15047,10 @@ if (mode == JIM_VARLIST_GLOBALS) { htiter = Jim_GetHashTableIterator(&interp->topFramePtr->vars); } else { - /* For [info locals], if we are at top level an emtpy list - * is returned. I don't agree, but we aim at compatibility (SS) */ if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) return listObjPtr; htiter = Jim_GetHashTableIterator(&interp->framePtr->vars); } while ((he = Jim_NextHashEntry(htiter)) != NULL) { @@ -17171,11 +15075,11 @@ targetCallFrame = JimGetCallFrameByInteger(interp, levelObjPtr); if (targetCallFrame == NULL) { return JIM_ERR; } - /* No proc call at toplevel callframe */ + if (targetCallFrame == interp->topFramePtr) { Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr); return JIM_ERR; } if (info_level_cmd) { @@ -17183,23 +15087,19 @@ } else { Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); Jim_ListAppendElement(interp, listObj, targetCallFrame->argv[0]); - Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, - targetCallFrame->filename ? targetCallFrame->filename : "", -1)); + Jim_ListAppendElement(interp, listObj, targetCallFrame->fileNameObj); Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, targetCallFrame->line)); *objPtrPtr = listObj; } return JIM_OK; } -/* ----------------------------------------------------------------------------- - * Core commands - * ---------------------------------------------------------------------------*/ -/* fake [puts] -- not the real puts, just for debugging. */ + static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string"); return JIM_ERR; @@ -17217,11 +15117,11 @@ puts(Jim_String(argv[1])); } return JIM_OK; } -/* Helper for [+] and [*] */ + static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op) { jim_wide wideValue, res; double doubleValue, doubleRes; int i; @@ -17250,11 +15150,11 @@ } Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); return JIM_OK; } -/* Helper for [-] and [/] */ + static int JimSubDivHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op) { jim_wide wideValue, res = 0; double doubleValue, doubleRes = 0; int i = 2; @@ -17262,12 +15162,10 @@ if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?"); return JIM_ERR; } else if (argc == 2) { - /* The arity = 2 case is different. For [- x] returns -x, - * while [/ x] returns 1/x. */ if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) { if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) { return JIM_ERR; } else { @@ -17324,35 +15222,35 @@ Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes)); return JIM_OK; } -/* [+] */ + static int Jim_AddCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_ADD); } -/* [*] */ + static int Jim_MulCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { return JimAddMulHelper(interp, argc, argv, JIM_EXPROP_MUL); } -/* [-] */ + static int Jim_SubCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_SUB); } -/* [/] */ + static int Jim_DivCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { return JimSubDivHelper(interp, argc, argv, JIM_EXPROP_DIV); } -/* [set] */ + static int Jim_SetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 1, argv, "varName ?newValue?"); return JIM_ERR; @@ -17364,21 +15262,17 @@ if (!objPtr) return JIM_ERR; Jim_SetResult(interp, objPtr); return JIM_OK; } - /* argc == 3 case. */ + if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK) return JIM_ERR; Jim_SetResult(interp, argv[2]); return JIM_OK; } -/* [unset] - * - * unset ?-nocomplain? ?--? ?varName ...? - */ static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int i = 1; int complain = 1; @@ -17403,19 +15297,19 @@ i++; } return JIM_OK; } -/* [while] */ + static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 3) { Jim_WrongNumArgs(interp, 1, argv, "condition body"); return JIM_ERR; } - /* The general purpose implementation of while starts here */ + while (1) { int boolean, retval; if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK) return retval; @@ -17438,11 +15332,11 @@ out: Jim_SetEmptyResult(interp); return JIM_OK; } -/* [for] */ + static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int retval; int boolean = 1; Jim_Obj *varNamePtr = NULL; @@ -17451,50 +15345,36 @@ if (argc != 5) { Jim_WrongNumArgs(interp, 1, argv, "start test next body"); return JIM_ERR; } - /* Do the initialisation */ + if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) { return retval; } - /* And do the first test now. Better for optimisation - * if we can do next/test at the bottom of the loop - */ retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean); - /* Ready to do the body as follows: - * while (1) { - * body // check retcode - * next // check retcode - * test // check retcode/test bool - * } - */ #ifdef JIM_OPTIMIZATION - /* Check if the for is on the form: - * for ... {$i < CONST} {incr i} - * for ... {$i < $j} {incr i} - */ if (retval == JIM_OK && boolean) { ScriptObj *incrScript; ExprByteCode *expr; jim_wide stop, currentVal; unsigned jim_wide procEpoch; Jim_Obj *objPtr; int cmpOffset; - /* Do it only if there aren't shared arguments */ + expr = JimGetExpression(interp, argv[2]); incrScript = Jim_GetScript(interp, argv[3]); - /* Ensure proper lengths to start */ + if (incrScript->len != 3 || !expr || expr->len != 3) { goto evalstart; } - /* Ensure proper token types. */ + 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)) { goto evalstart; } @@ -17507,49 +15387,49 @@ } else { goto evalstart; } - /* Update command must be incr */ + if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) { goto evalstart; } - /* incr, expression must be about the same variable */ + if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->token[0].objPtr)) { goto evalstart; } - /* Get the stop condition (must be a variable or integer) */ + if (expr->token[1].type == JIM_TT_EXPR_INT) { if (Jim_GetWide(interp, expr->token[1].objPtr, &stop) == JIM_ERR) { goto evalstart; } } else { stopVarNamePtr = expr->token[1].objPtr; Jim_IncrRefCount(stopVarNamePtr); - /* Keep the compiler happy */ + stop = 0; } - /* Initialization */ + procEpoch = interp->procEpoch; varNamePtr = expr->token[0].objPtr; Jim_IncrRefCount(varNamePtr); objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE); if (objPtr == NULL || Jim_GetWide(interp, objPtr, ¤tVal) != JIM_OK) { goto testcond; } - /* --- OPTIMIZED FOR --- */ + while (retval == JIM_OK) { - /* === Check condition === */ - /* Note that currentVal is already set here */ + + - /* Immediate or Variable? get the 'stop' value if the latter. */ + if (stopVarNamePtr) { objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE); if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) { goto testcond; } @@ -17557,23 +15437,21 @@ if (currentVal >= stop + cmpOffset) { break; } - /* Eval body */ + retval = Jim_EvalObj(interp, argv[4]); if (retval == JIM_OK || retval == JIM_CONTINUE) { retval = JIM_OK; - /* If there was a change in procedures/command continue - * with the usual [for] command implementation */ if (procEpoch != interp->procEpoch) { goto evalnext; } objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG); - /* Increment */ + if (objPtr == NULL) { retval = JIM_ERR; goto out; } if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) { @@ -17593,19 +15471,19 @@ } evalstart: #endif while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) { - /* Body */ + retval = Jim_EvalObj(interp, argv[4]); if (retval == JIM_OK || retval == JIM_CONTINUE) { - /* increment */ + evalnext: retval = Jim_EvalObj(interp, argv[3]); if (retval == JIM_OK || retval == JIM_CONTINUE) { - /* test */ + testcond: retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean); } } } @@ -17623,11 +15501,11 @@ } return retval; } -/* [loop] */ + static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int retval; jim_wide i; jim_wide limit; @@ -17653,11 +15531,11 @@ if (retval == JIM_OK || retval == JIM_CONTINUE) { Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG); retval = JIM_OK; - /* Increment */ + i += incr; if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) { if (argv[1]->typePtr != &variableObjType) { if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) { @@ -17665,12 +15543,10 @@ } } JimWideValue(objPtr) = i; Jim_InvalidateStringRep(objPtr); - /* The following step is required in order to invalidate the - * string repr of "FOO" if the var name is of the form of "FOO(IDX)" */ if (argv[1]->typePtr != &variableObjType) { if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) { retval = JIM_ERR; break; } @@ -17691,11 +15567,11 @@ return JIM_OK; } return retval; } -/* foreach + lmap implementation. */ + static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap) { int result = JIM_ERR, i, nbrOfLists, *listsIdx, *listsEnd; int nbrOfLoops = 0; Jim_Obj *emptyStr, *script, *mapRes = NULL; @@ -17708,17 +15584,17 @@ mapRes = Jim_NewListObj(interp, NULL, 0); Jim_IncrRefCount(mapRes); } emptyStr = Jim_NewEmptyStringObj(interp); Jim_IncrRefCount(emptyStr); - script = argv[argc - 1]; /* Last argument is a script */ - nbrOfLists = (argc - 1 - 1) / 2; /* argc - 'foreach' - script */ + script = argv[argc - 1]; + nbrOfLists = (argc - 1 - 1) / 2; listsIdx = (int *)Jim_Alloc(nbrOfLists * sizeof(int)); listsEnd = (int *)Jim_Alloc(nbrOfLists * 2 * sizeof(int)); - /* Initialize iterators and remember max nbr elements each list */ + memset(listsIdx, 0, nbrOfLists * sizeof(int)); - /* Remember lengths of all lists and calculate how much rounds to loop */ + for (i = 0; i < nbrOfLists * 2; i += 2) { div_t cnt; int count; listsEnd[i] = Jim_ListLength(interp, argv[i + 1]); @@ -17738,26 +15614,26 @@ while (varIdx < listsEnd[var]) { Jim_Obj *varName, *ele; int lst = i * 2 + 1; - /* List index operations below can't fail */ + Jim_ListIndex(interp, argv[var + 1], varIdx, &varName, JIM_NONE); if (listsIdx[i] < listsEnd[lst]) { Jim_ListIndex(interp, argv[lst + 1], listsIdx[i], &ele, JIM_NONE); - /* Avoid shimmering */ + Jim_IncrRefCount(ele); result = Jim_SetVariable(interp, varName, ele); Jim_DecrRefCount(interp, ele); if (result == JIM_OK) { - ++listsIdx[i]; /* Remember next iterator of current list */ - ++varIdx; /* Next variable */ + ++listsIdx[i]; + ++varIdx; continue; } } else if (Jim_SetVariable(interp, varName, emptyStr) == JIM_OK) { - ++varIdx; /* Next variable */ + ++varIdx; continue; } goto err; } } @@ -17788,62 +15664,60 @@ Jim_Free(listsIdx); Jim_Free(listsEnd); return result; } -/* [foreach] */ + static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { return JimForeachMapHelper(interp, argc, argv, 0); } -/* [lmap] */ + static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { return JimForeachMapHelper(interp, argc, argv, 1); } -/* [if] */ + static int Jim_IfCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int boolean, retval, current = 1, falsebody = 0; if (argc >= 3) { while (1) { - /* Far not enough arguments given! */ + if (current >= argc) goto err; if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean)) != JIM_OK) return retval; - /* There lacks something, isn't it? */ + if (current >= argc) goto err; if (Jim_CompareStringImmediate(interp, argv[current], "then")) current++; - /* Tsk tsk, no then-clause? */ + if (current >= argc) goto err; if (boolean) return Jim_EvalObj(interp, argv[current]); - /* Ok: no else-clause follows */ + if (++current >= argc) { Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); return JIM_OK; } falsebody = current++; if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) { - /* IIICKS - else-clause isn't last cmd? */ + if (current != argc - 1) goto err; return Jim_EvalObj(interp, argv[current]); } else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif")) - /* Ok: elseif follows meaning all the stuff - * again (how boring...) */ continue; - /* OOPS - else-clause is not last cmd? */ + else if (falsebody != argc - 1) goto err; return Jim_EvalObj(interp, argv[falsebody]); } return JIM_OK; @@ -17852,11 +15726,11 @@ Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody"); return JIM_ERR; } -/* Returns 1 if match, 0 if no match or -<error> on error (e.g. -JIM_ERR, -JIM_BREAK)*/ + int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj, Jim_Obj *stringObj, int nocase) { Jim_Obj *parms[4]; int argc = 0; @@ -17880,11 +15754,11 @@ } enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD }; -/* [switch] */ + static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int matchOpt = SWITCH_EXACT, opt = 1, patCount, i; Jim_Obj *command = 0, *const *caseList = 0, *strObj; Jim_Obj *script = 0; @@ -17894,11 +15768,11 @@ Jim_WrongNumArgs(interp, 1, argv, "?options? string " "pattern body ... ?default body? or " "{pattern body ?pattern body ...?}"); return JIM_ERR; } for (opt = 1; opt < argc; ++opt) { - const char *option = Jim_GetString(argv[opt], 0); + const char *option = Jim_String(argv[opt]); if (*option != '-') break; else if (strncmp(option, "--", 2) == 0) { ++opt; @@ -17951,24 +15825,21 @@ if (Jim_StringMatchObj(interp, patObj, strObj, 0)) script = caseList[i + 1]; break; case SWITCH_RE: command = Jim_NewStringObj(interp, "regexp", -1); - /* Fall thru intentionally */ + case SWITCH_CMD:{ int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, 0); - /* After the execution of a command we need to - * make sure to reconvert the object into a list - * again. Only for the single-list style [switch]. */ if (argc - opt == 1) { Jim_Obj **vector; JimListGetElements(interp, argv[opt], &patCount, &vector); caseList = vector; } - /* command is here already decref'd */ + if (rc < 0) { return -rc; } if (rc) script = caseList[i + 1]; @@ -17991,21 +15862,21 @@ return Jim_EvalObj(interp, script); } return JIM_OK; } -/* [list] */ + static int Jim_ListCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *listObjPtr; listObjPtr = Jim_NewListObj(interp, argv + 1, argc - 1); Jim_SetResult(interp, listObjPtr); return JIM_OK; } -/* [lindex] */ + static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr, *listObjPtr; int i; int idx; @@ -18021,12 +15892,10 @@ if (Jim_GetIndex(interp, argv[i], &idx) != JIM_OK) { Jim_DecrRefCount(interp, listObjPtr); return JIM_ERR; } if (Jim_ListIndex(interp, listObjPtr, idx, &objPtr, JIM_NONE) != JIM_OK) { - /* Returns an empty object if the index - * is out of range. */ Jim_DecrRefCount(interp, listObjPtr); Jim_SetEmptyResult(interp); return JIM_OK; } Jim_IncrRefCount(objPtr); @@ -18035,11 +15904,11 @@ Jim_SetResult(interp, objPtr); Jim_DecrRefCount(interp, objPtr); return JIM_OK; } -/* [llength] */ + static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 2) { Jim_WrongNumArgs(interp, 1, argv, "list"); return JIM_ERR; @@ -18046,11 +15915,11 @@ } Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1])); return JIM_OK; } -/* [lsearch] */ + static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { static const char * const options[] = { "-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command", NULL @@ -18104,11 +15973,11 @@ case OPT_COMMAND: if (i >= argc - 2) { goto wrongargs; } commandObj = argv[++i]; - /* fallthru */ + case OPT_EXACT: case OPT_GLOB: case OPT_REGEXP: opt_match = option; break; @@ -18153,17 +16022,17 @@ goto done; } break; } - /* If we have a non-match with opt_bool, opt_not, !opt_all, can't exit early */ + if (!eq && opt_bool && opt_not && !opt_all) { continue; } if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) { - /* Got a match (or non-match for opt_not), or (opt_bool && opt_all) */ + Jim_Obj *resultObj; if (opt_bool) { resultObj = Jim_NewIntObj(interp, eq ^ opt_not); } @@ -18186,11 +16055,11 @@ if (opt_all) { Jim_SetResult(interp, listObjPtr); } else { - /* No match */ + if (opt_bool) { Jim_SetResultBool(interp, opt_not); } else if (!opt_inline) { Jim_SetResultInt(interp, -1); @@ -18202,11 +16071,11 @@ Jim_DecrRefCount(interp, commandObj); } return rc; } -/* [lappend] */ + static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *listObjPtr; int shared, i; @@ -18214,11 +16083,11 @@ Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?"); return JIM_ERR; } listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); if (!listObjPtr) { - /* Create the list if it does not exists */ + listObjPtr = Jim_NewListObj(interp, NULL, 0); if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) { Jim_FreeNewObj(interp, listObjPtr); return JIM_ERR; } @@ -18235,11 +16104,11 @@ } Jim_SetResult(interp, listObjPtr); return JIM_OK; } -/* [linsert] */ + static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int idx, len; Jim_Obj *listPtr; @@ -18265,18 +16134,16 @@ Jim_FreeNewObj(interp, listPtr); } return JIM_ERR; } -/* [lreplace] */ + static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int first, last, len, rangeLen; Jim_Obj *listObj; Jim_Obj *newListObj; - int i; - int shared; if (argc < 4) { Jim_WrongNumArgs(interp, 1, argv, "list first last ?element element ...?"); return JIM_ERR; } @@ -18290,57 +16157,39 @@ first = JimRelToAbsIndex(len, first); last = JimRelToAbsIndex(len, last); JimRelToAbsRange(len, first, last, &first, &last, &rangeLen); - /* Now construct a new list which consists of: - * <elements before first> <supplied elements> <elements after last> - */ - /* Check to see if trying to replace past the end of the list */ + if (first < len) { - /* OK. Not past the end */ + } else if (len == 0) { - /* Special for empty list, adjust first to 0 */ + first = 0; } else { Jim_SetResultString(interp, "list doesn't contain element ", -1); Jim_AppendObj(interp, Jim_GetResult(interp), argv[2]); return JIM_ERR; } - newListObj = Jim_NewListObj(interp, NULL, 0); + + newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first); - shared = Jim_IsShared(listObj); - if (shared) { - listObj = Jim_DuplicateObj(interp, listObj); - } + + ListInsertElements(newListObj, -1, argc - 4, argv + 4); - /* Add the first set of elements */ - for (i = 0; i < first; i++) { - Jim_ListAppendElement(interp, newListObj, listObj->internalRep.listValue.ele[i]); - } + + ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen); - /* Add supplied elements */ - for (i = 4; i < argc; i++) { - Jim_ListAppendElement(interp, newListObj, argv[i]); - } - - /* Add the remaining elements */ - for (i = first + rangeLen; i < len; i++) { - Jim_ListAppendElement(interp, newListObj, listObj->internalRep.listValue.ele[i]); - } Jim_SetResult(interp, newListObj); - if (shared) { - Jim_FreeNewObj(interp, listObj); - } return JIM_OK; } -/* [lset] */ + static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc < 3) { Jim_WrongNumArgs(interp, 1, argv, "listVar ?index...? newVal"); return JIM_ERR; @@ -18355,11 +16204,11 @@ == JIM_ERR) return JIM_ERR; return JIM_OK; } -/* [lsort] */ + static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[]) { static const char * const options[] = { "-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-index", NULL }; @@ -18435,11 +16284,11 @@ Jim_FreeNewObj(interp, resObj); } return retCode; } -/* [append] */ + static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *stringObjPtr; int i; @@ -18454,11 +16303,11 @@ } else { int freeobj = 0; stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED); if (!stringObjPtr) { - /* Create the string if it doesn't exist */ + stringObjPtr = Jim_NewEmptyStringObj(interp); freeobj = 1; } else if (Jim_IsShared(stringObjPtr)) { freeobj = 1; @@ -18476,216 +16325,20 @@ } Jim_SetResult(interp, stringObjPtr); return JIM_OK; } -/* [debug] */ + static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { -#ifdef JIM_DEBUG_COMMAND - static const char * const options[] = { - "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen", - "exprbc", "show", - NULL - }; - enum - { - OPT_REFCOUNT, OPT_OBJCOUNT, OPT_OBJECTS, OPT_INVSTR, OPT_SCRIPTLEN, - OPT_EXPRLEN, OPT_EXPRBC, OPT_SHOW, - }; - int option; - - if (argc < 2) { - Jim_WrongNumArgs(interp, 1, argv, "subcommand ?...?"); - return JIM_ERR; - } - if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG) != JIM_OK) - return JIM_ERR; - if (option == OPT_REFCOUNT) { - if (argc != 3) { - Jim_WrongNumArgs(interp, 2, argv, "object"); - return JIM_ERR; - } - Jim_SetResultInt(interp, argv[2]->refCount); - return JIM_OK; - } - else if (option == OPT_OBJCOUNT) { - int freeobj = 0, liveobj = 0; - char buf[256]; - Jim_Obj *objPtr; - - if (argc != 2) { - Jim_WrongNumArgs(interp, 2, argv, ""); - return JIM_ERR; - } - /* Count the number of free objects. */ - objPtr = interp->freeList; - while (objPtr) { - freeobj++; - objPtr = objPtr->nextObjPtr; - } - /* Count the number of live objects. */ - objPtr = interp->liveList; - while (objPtr) { - liveobj++; - objPtr = objPtr->nextObjPtr; - } - /* Set the result string and return. */ - sprintf(buf, "free %d used %d", freeobj, liveobj); - Jim_SetResultString(interp, buf, -1); - return JIM_OK; - } - else if (option == OPT_OBJECTS) { - Jim_Obj *objPtr, *listObjPtr, *subListObjPtr; - - /* Count the number of live objects. */ - objPtr = interp->liveList; - listObjPtr = Jim_NewListObj(interp, NULL, 0); - while (objPtr) { - char buf[128]; - const char *type = objPtr->typePtr ? objPtr->typePtr->name : ""; - - subListObjPtr = Jim_NewListObj(interp, NULL, 0); - sprintf(buf, "%p", objPtr); - Jim_ListAppendElement(interp, subListObjPtr, Jim_NewStringObj(interp, buf, -1)); - Jim_ListAppendElement(interp, subListObjPtr, Jim_NewStringObj(interp, type, -1)); - Jim_ListAppendElement(interp, subListObjPtr, Jim_NewIntObj(interp, objPtr->refCount)); - Jim_ListAppendElement(interp, subListObjPtr, objPtr); - Jim_ListAppendElement(interp, listObjPtr, subListObjPtr); - objPtr = objPtr->nextObjPtr; - } - Jim_SetResult(interp, listObjPtr); - return JIM_OK; - } - else if (option == OPT_INVSTR) { - Jim_Obj *objPtr; - - if (argc != 3) { - Jim_WrongNumArgs(interp, 2, argv, "object"); - return JIM_ERR; - } - objPtr = argv[2]; - if (objPtr->typePtr != NULL) - Jim_InvalidateStringRep(objPtr); - Jim_SetEmptyResult(interp); - return JIM_OK; - } - else if (option == OPT_SHOW) { - const char *s; - int len, charlen; - - if (argc != 3) { - Jim_WrongNumArgs(interp, 2, argv, "object"); - return JIM_ERR; - } - s = Jim_GetString(argv[2], &len); - charlen = Jim_Utf8Length(interp, argv[2]); - printf("chars (%d): <<%s>>\n", charlen, s); - printf("bytes (%d):", len); - while (len--) { - printf(" %02x", (unsigned char)*s++); - } - printf("\n"); - return JIM_OK; - } - else if (option == OPT_SCRIPTLEN) { - ScriptObj *script; - - if (argc != 3) { - Jim_WrongNumArgs(interp, 2, argv, "script"); - return JIM_ERR; - } - script = Jim_GetScript(interp, argv[2]); - Jim_SetResultInt(interp, script->len); - return JIM_OK; - } - else if (option == OPT_EXPRLEN) { - ExprByteCode *expr; - - if (argc != 3) { - Jim_WrongNumArgs(interp, 2, argv, "expression"); - return JIM_ERR; - } - expr = JimGetExpression(interp, argv[2]); - if (expr == NULL) - return JIM_ERR; - Jim_SetResultInt(interp, expr->len); - return JIM_OK; - } - else if (option == OPT_EXPRBC) { - Jim_Obj *objPtr; - ExprByteCode *expr; - int i; - - if (argc != 3) { - Jim_WrongNumArgs(interp, 2, argv, "expression"); - return JIM_ERR; - } - expr = JimGetExpression(interp, argv[2]); - if (expr == NULL) - return JIM_ERR; - objPtr = Jim_NewListObj(interp, NULL, 0); - for (i = 0; i < expr->len; i++) { - const char *type; - const Jim_ExprOperator *op; - Jim_Obj *obj = expr->token[i].objPtr; - - switch (expr->token[i].type) { - case JIM_TT_EXPR_INT: - type = "int"; - break; - case JIM_TT_EXPR_DOUBLE: - type = "double"; - break; - case JIM_TT_CMD: - type = "command"; - break; - case JIM_TT_VAR: - type = "variable"; - break; - case JIM_TT_DICTSUGAR: - type = "dictsugar"; - break; - case JIM_TT_EXPRSUGAR: - type = "exprsugar"; - break; - case JIM_TT_ESC: - type = "subst"; - break; - case JIM_TT_STR: - type = "string"; - break; - default: - op = JimExprOperatorInfoByOpcode(expr->token[i].type); - if (op == NULL) { - type = "private"; - } - else { - type = "operator"; - } - obj = Jim_NewStringObj(interp, op ? op->name : "", -1); - break; - } - Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, type, -1)); - Jim_ListAppendElement(interp, objPtr, obj); - } - Jim_SetResult(interp, objPtr); - return JIM_OK; - } - else { - Jim_SetResultString(interp, - "bad option. Valid options are refcount, " "objcount, objects, invstr", -1); - return JIM_ERR; - } - /* unreached */ -#else +#if !defined(JIM_DEBUG_COMMAND) Jim_SetResultString(interp, "unsupported", -1); return JIM_ERR; #endif } -/* [eval] */ + static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int rc; if (argc < 2) { @@ -18699,29 +16352,29 @@ else { rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1)); } if (rc == JIM_ERR) { - /* eval is "interesting", so add a stack frame here */ + interp->addStackTrace++; } return rc; } -/* [uplevel] */ + static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc >= 2) { int retcode; Jim_CallFrame *savedCallFrame, *targetCallFrame; Jim_Obj *objPtr; const char *str; - /* Save the old callframe pointer */ + savedCallFrame = interp->framePtr; - /* Lookup the target frame pointer */ + str = Jim_String(argv[1]); if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') { targetCallFrame =Jim_GetCallFrameByLevel(interp, argv[1]); argc--; argv++; @@ -18735,11 +16388,11 @@ if (argc < 2) { argv--; Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?"); return JIM_ERR; } - /* Eval the code in the target callframe. */ + interp->framePtr = targetCallFrame; if (argc == 2) { retcode = Jim_EvalObj(interp, argv[1]); } else { @@ -18755,11 +16408,11 @@ Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?"); return JIM_ERR; } } -/* [expr] */ + static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *exprResultPtr; int retcode; @@ -18783,31 +16436,31 @@ Jim_SetResult(interp, exprResultPtr); Jim_DecrRefCount(interp, exprResultPtr); return JIM_OK; } -/* [break] */ + static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 1) { Jim_WrongNumArgs(interp, 1, argv, ""); return JIM_ERR; } return JIM_BREAK; } -/* [continue] */ + static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 1) { Jim_WrongNumArgs(interp, 1, argv, ""); return JIM_ERR; } return JIM_CONTINUE; } -/* [return] */ + static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int i; Jim_Obj *stackTraceObj = NULL; Jim_Obj *errorCodeObj = NULL; @@ -18840,15 +16493,15 @@ if (i != argc - 1 && i != argc) { Jim_WrongNumArgs(interp, 1, argv, "?-code code? ?-errorinfo stacktrace? ?-level level? ?result?"); } - /* If a stack trace is supplied and code is error, set the stack trace */ + if (stackTraceObj && returnCode == JIM_ERR) { JimSetStackTrace(interp, stackTraceObj); } - /* If an error code list is supplied, set the global $errorCode */ + if (errorCodeObj && returnCode == JIM_ERR) { Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj); } interp->returnCode = returnCode; interp->returnLevel = level; @@ -18857,21 +16510,21 @@ Jim_SetResult(interp, argv[i]); } return JIM_RETURN; } -/* [tailcall] */ + static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr; objPtr = Jim_NewListObj(interp, argv + 1, argc - 1); Jim_SetResult(interp, objPtr); return JIM_EVAL; } -/* [proc] */ + static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 4 && argc != 5) { Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body"); return JIM_ERR; @@ -18883,22 +16536,22 @@ else { return JimCreateProcedure(interp, argv[1], argv[2], argv[3], argv[4]); } } -/* [local] */ + static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int retcode; - /* Evaluate the arguments with 'local' in force */ + interp->local++; retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1); interp->local--; - /* If OK, and the result is a proc, add it to the list of local procs */ + if (retcode == 0) { const char *procname = Jim_String(Jim_GetResult(interp)); if (Jim_FindHashEntry(&interp->commands, procname) == NULL) { Jim_SetResultFormatted(interp, "not a proc: \"%s\"", procname); @@ -18912,11 +16565,11 @@ } return retcode; } -/* [upcall] */ + static int Jim_UpcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?"); return JIM_ERR; @@ -18927,39 +16580,39 @@ Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG); if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->u.proc.prevCmd) { Jim_SetResultFormatted(interp, "no previous proc: \"%#s\"", argv[1]); return JIM_ERR; } - /* OK. Mark this command as being in an upcall */ + cmdPtr->u.proc.upcall++; JimIncrCmdRefCount(cmdPtr); - /* Invoke the command as normal */ + retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1); - /* No longer in an upcall */ + cmdPtr->u.proc.upcall--; JimDecrCmdRefCount(interp, cmdPtr); return retcode; } } -/* [concat] */ + static int Jim_ConcatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_SetResult(interp, Jim_ConcatObj(interp, argc - 1, argv + 1)); return JIM_OK; } -/* [upvar] */ + static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int i; Jim_CallFrame *targetCallFrame; - /* Lookup the target frame pointer */ + if (argc > 3 && (argc % 2 == 0)) { targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]); argc--; argv++; } @@ -18968,46 +16621,43 @@ } if (targetCallFrame == NULL) { return JIM_ERR; } - /* Check for arity */ + if (argc < 3) { Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?"); return JIM_ERR; } - /* Now... for every other/local couple: */ + for (i = 1; i < argc; i += 2) { if (Jim_SetVariableLink(interp, argv[i + 1], argv[i], targetCallFrame) != JIM_OK) return JIM_ERR; } return JIM_OK; } -/* [global] */ + static int Jim_GlobalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int i; if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "varName ?varName ...?"); return JIM_ERR; } - /* Link every var to the toplevel having the same name */ + if (interp->framePtr->level == 0) - return JIM_OK; /* global at toplevel... */ + return JIM_OK; for (i = 1; i < argc; i++) { if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK) return JIM_ERR; } return JIM_OK; } -/* does the [string map] operation. On error NULL is returned, - * otherwise a new string object with the result, having refcount = 0, - * is returned. */ static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr, Jim_Obj *objPtr, int nocase) { int numMaps; const char *str, *noMatchStart = NULL; @@ -19021,11 +16671,11 @@ } str = Jim_String(objPtr); strLen = Jim_Utf8Length(interp, objPtr); - /* Map it */ + resultObjPtr = Jim_NewStringObj(interp, "", 0); while (strLen) { for (i = 0; i < numMaps; i += 2) { Jim_Obj *objPtr; const char *k; @@ -19054,11 +16704,11 @@ strLen -= kl; break; } } } - if (i == numMaps) { /* no match */ + if (i == numMaps) { int c; if (noMatchStart == NULL) noMatchStart = str; str += utf8_tounicode(str, &c); strLen--; @@ -19068,11 +16718,11 @@ Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart); } return resultObjPtr; } -/* [string] */ + static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int len; int opt_case = 1; int option; @@ -19225,14 +16875,10 @@ Jim_WrongNumArgs(interp, 2, argv, "string"); return JIM_ERR; } str = Jim_GetString(argv[2], &len); - if (!str) { - return JIM_ERR; - } - buf = Jim_Alloc(len + 1); p = buf + len; *p = 0; for (i = 0; i < len; ) { int c; @@ -19264,11 +16910,11 @@ } if (idx < 0 || idx >= len || str == NULL) { Jim_SetResultString(interp, "", 0); } else if (len == Jim_Length(argv[2])) { - /* ASCII optimisation */ + Jim_SetResultString(interp, str + idx, 1); } else { int c; int i = utf8_index(str, idx); @@ -19356,11 +17002,11 @@ return JIM_ERR; } return JIM_OK; } -/* [time] */ + static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { long i, count = 1; jim_wide start, elapsed; char buf[60]; @@ -19390,11 +17036,11 @@ sprintf(buf, fmt, count == 0 ? 0 : elapsed / count); Jim_SetResultString(interp, buf, -1); return JIM_OK; } -/* [exit] */ + static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { long exitCode = 0; if (argc > 2) { @@ -19407,32 +17053,29 @@ } interp->exitCode = exitCode; return JIM_EXIT; } -/* [catch] */ + static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int exitCode = 0; int i; int sig = 0; - /* Which return codes are caught? These are the defaults */ - jim_wide mask = - (1 << JIM_OK | 1 << JIM_ERR | 1 << JIM_BREAK | 1 << JIM_CONTINUE | 1 << JIM_RETURN); + + jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL); + static const int max_ignore_code = sizeof(ignore_mask) * 8; - /* Reset the error code before catch. - * Note that this is not strictly correct. - */ Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1)); for (i = 1; i < argc - 1; i++) { const char *arg = Jim_String(argv[i]); jim_wide option; - int add; + int ignore; - /* It's a pity we can't use Jim_GetEnum here :-( */ + if (strcmp(arg, "--") == 0) { i++; break; } if (*arg != '-') { @@ -19439,15 +17082,15 @@ break; } if (strncmp(arg, "-no", 3) == 0) { arg += 3; - add = 0; + ignore = 1; } else { arg++; - add = 1; + ignore = 0; } if (Jim_StringToWide(arg, &option, 10) != JIM_OK) { option = -1; } @@ -19456,15 +17099,15 @@ } if (option < 0) { goto wrongargs; } - if (add) { - mask |= (1 << option); + if (ignore) { + ignore_mask |= (1 << option); } else { - mask &= ~(1 << option); + ignore_mask &= ~(1 << option); } } argc -= i; if (argc < 1 || argc > 3) { @@ -19473,32 +17116,32 @@ "?-?no?code ... --? script ?resultVarName? ?optionVarName?"); return JIM_ERR; } argv += i; - if (mask & (1 << JIM_SIGNAL)) { + if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) { sig++; } interp->signal_level += sig; if (interp->signal_level && interp->sigmask) { - /* If a signal is set, don't even try to execute the body */ + exitCode = JIM_SIGNAL; } else { exitCode = Jim_EvalObj(interp, argv[0]); } interp->signal_level -= sig; - /* Catch or pass through? Only the first 32/64 codes can be passed through */ - if (exitCode >= 0 && exitCode < (int)sizeof(mask) * 8 && ((1 << exitCode) & mask) == 0) { - /* Not caught, pass it up */ + + if (exitCode >= 0 && exitCode < max_ignore_code && ((1 << exitCode) & ignore_mask)) { + return exitCode; } if (sig && exitCode == JIM_SIGNAL) { - /* Catch the signal at this level */ + if (interp->signal_set_result) { interp->signal_set_result(interp, interp->sigmask); } else { Jim_SetResultInt(interp, interp->sigmask); @@ -19539,11 +17182,11 @@ return JIM_OK; } #ifdef JIM_REFERENCES -/* [ref] */ + static int Jim_RefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 3 && argc != 4) { Jim_WrongNumArgs(interp, 1, argv, "string tag ?finalizer?"); return JIM_ERR; @@ -19555,11 +17198,11 @@ Jim_SetResult(interp, Jim_NewReference(interp, argv[1], argv[2], argv[3])); } return JIM_OK; } -/* [getref] */ + static int Jim_GetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Reference *refPtr; if (argc != 2) { @@ -19570,11 +17213,11 @@ return JIM_ERR; Jim_SetResult(interp, refPtr->objPtr); return JIM_OK; } -/* [setref] */ + static int Jim_SetrefCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Reference *refPtr; if (argc != 3) { @@ -19588,30 +17231,30 @@ refPtr->objPtr = argv[2]; Jim_SetResult(interp, argv[2]); return JIM_OK; } -/* [collect] */ + static int Jim_CollectCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 1) { Jim_WrongNumArgs(interp, 1, argv, ""); return JIM_ERR; } Jim_SetResultInt(interp, Jim_Collect(interp)); - /* Free all the freed objects. */ + while (interp->freeList) { Jim_Obj *nextObjPtr = interp->freeList->nextObjPtr; Jim_Free(interp->freeList); interp->freeList = nextObjPtr; } return JIM_OK; } -/* [finalize] reference ?newValue? */ + static int Jim_FinalizeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 1, argv, "reference ?finalizerProc?"); return JIM_ERR; @@ -19619,11 +17262,11 @@ if (argc == 2) { Jim_Obj *cmdNamePtr; if (Jim_GetFinalizer(interp, argv[1], &cmdNamePtr) != JIM_OK) return JIM_ERR; - if (cmdNamePtr != NULL) /* otherwise the null string is returned. */ + if (cmdNamePtr != NULL) Jim_SetResult(interp, cmdNamePtr); } else { if (Jim_SetFinalizer(interp, argv[1], argv[2]) != JIM_OK) return JIM_ERR; @@ -19630,11 +17273,11 @@ Jim_SetResult(interp, argv[2]); } return JIM_OK; } -/* [info references] */ + static int JimInfoReferences(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *listObjPtr; Jim_HashTableIterator *htiter; Jim_HashEntry *he; @@ -19654,11 +17297,11 @@ Jim_SetResult(interp, listObjPtr); return JIM_OK; } #endif -/* [rename] */ + static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *oldName, *newName; if (argc != 3) { @@ -19685,18 +17328,15 @@ if (Jim_DictKeysVector(interp, objPtr, NULL, 0, &dictObj, JIM_ERRMSG) != JIM_OK) { return JIM_ERR; } - /* XXX: Could make the exact-match case much more efficient here. - * See JimCommandsList() - */ if (Jim_DictPairs(interp, dictObj, &dictValuesObj, &len) != JIM_OK) { return JIM_ERR; } - /* Only return the matching values */ + resultObj = Jim_NewListObj(interp, NULL, 0); for (i = 0; i < len; i += 2) { if (patternObj == NULL || Jim_StringMatchObj(interp, patternObj, dictValuesObj[i], 0)) { Jim_ListAppendElement(interp, resultObj, dictValuesObj[i]); @@ -19714,11 +17354,11 @@ return -1; } return ((Jim_HashTable *)objPtr->internalRep.ptr)->used; } -/* [dict] */ + static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr; int option; static const char * const options[] = { @@ -19754,11 +17394,11 @@ case OPT_SET: if (argc < 5) { Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...? value"); return JIM_ERR; } - return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]); + return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG); case OPT_EXIST: if (argc < 3) { Jim_WrongNumArgs(interp, 2, argv, "varName ?key ...?"); return JIM_ERR; @@ -19770,11 +17410,11 @@ case OPT_UNSET: if (argc < 4) { Jim_WrongNumArgs(interp, 2, argv, "varName key ?key ...?"); return JIM_ERR; } - return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL); + return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE); case OPT_KEYS: if (argc != 3 && argc != 4) { Jim_WrongNumArgs(interp, 2, argv, "dictVar ?pattern?"); return JIM_ERR; @@ -19803,11 +17443,11 @@ } else if (argv[2]->typePtr != &dictObjType && SetDictFromAny(interp, argv[2]) != JIM_OK) { return JIM_ERR; } else { - return Jim_EvalObjPrefix(interp, "dict merge", argc - 2, argv + 2); + return Jim_EvalPrefix(interp, "dict merge", argc - 2, argv + 2); } case OPT_WITH: if (argc < 4) { Jim_WrongNumArgs(interp, 2, argv, "dictVar ?key ...? script"); @@ -19815,11 +17455,11 @@ } else if (Jim_GetVariable(interp, argv[2], JIM_ERRMSG) == NULL) { return JIM_ERR; } else { - return Jim_EvalObjPrefix(interp, "dict with", argc - 2, argv + 2); + return Jim_EvalPrefix(interp, "dict with", argc - 2, argv + 2); } case OPT_CREATE: if (argc % 2) { Jim_WrongNumArgs(interp, 2, argv, "?key value ...?"); @@ -19826,17 +17466,15 @@ return JIM_ERR; } objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2); Jim_SetResult(interp, objPtr); return JIM_OK; - - default: - abort(); } + return JIM_ERR; } -/* [subst] */ + static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { static const char * const options[] = { "-nobackslashes", "-nocommands", "-novariables", NULL }; @@ -19874,11 +17512,11 @@ } Jim_SetResult(interp, objPtr); return JIM_OK; } -/* [info] */ + static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int cmd; Jim_Obj *objPtr; int mode = 0; @@ -19903,11 +17541,11 @@ if (Jim_GetEnum(interp, argv[1], commands, &cmd, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { return JIM_ERR; } - /* Test for the the most common commands first, just in case it makes a difference */ + switch (cmd) { case INFO_EXISTS:{ if (argc != 3) { Jim_WrongNumArgs(interp, 2, argv, "varName"); return JIM_ERR; @@ -19930,15 +17568,15 @@ Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, (cmd - INFO_COMMANDS))); break; case INFO_VARS: - mode++; /* JIM_VARLIST_VARS */ + mode++; case INFO_LOCALS: - mode++; /* JIM_VARLIST_LOCALS */ + mode++; case INFO_GLOBALS: - /* mode 0 => JIM_VARLIST_GLOBALS */ + if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 2, argv, "?pattern?"); return JIM_ERR; } Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode)); @@ -19947,34 +17585,37 @@ case INFO_SCRIPT: if (argc != 2) { Jim_WrongNumArgs(interp, 2, argv, ""); return JIM_ERR; } - Jim_SetResultString(interp, Jim_GetScript(interp, interp->currentScriptObj)->fileName, - -1); + Jim_SetResult(interp, Jim_GetScript(interp, interp->currentScriptObj)->fileNameObj); break; case INFO_SOURCE:{ - const char *filename = ""; - int line = 0; + int line; Jim_Obj *resObjPtr; + Jim_Obj *fileNameObj; if (argc != 3) { Jim_WrongNumArgs(interp, 2, argv, "source"); return JIM_ERR; } if (argv[2]->typePtr == &sourceObjType) { - filename = argv[2]->internalRep.sourceValue.fileName; + fileNameObj = argv[2]->internalRep.sourceValue.fileNameObj; line = argv[2]->internalRep.sourceValue.lineNumber; } else if (argv[2]->typePtr == &scriptObjType) { ScriptObj *script = Jim_GetScript(interp, argv[2]); - filename = script->fileName; + fileNameObj = script->fileNameObj; line = script->line; } + else { + fileNameObj = interp->emptyObj; + line = 1; + } resObjPtr = Jim_NewListObj(interp, NULL, 0); - Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObj(interp, filename, -1)); + Jim_ListAppendElement(interp, resObjPtr, fileNameObj); Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line)); Jim_SetResult(interp, resObjPtr); break; } @@ -20047,15 +17688,15 @@ } } break; case INFO_HOSTNAME: - /* Redirect to os.gethostname if it exists */ + return Jim_Eval(interp, "os.gethostname"); case INFO_NAMEOFEXECUTABLE: - /* Redirect to Tcl proc */ + return Jim_Eval(interp, "{info nameofexecutable}"); case INFO_RETURNCODES: if (argc == 2) { int i; @@ -20098,11 +17739,11 @@ #endif } return JIM_OK; } -/* [exists] */ + static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr; static const char * const options[] = { @@ -20127,11 +17768,11 @@ else { Jim_WrongNumArgs(interp, 1, argv, "?option? name"); return JIM_ERR; } - /* Test for the the most common commands first, just in case it makes a difference */ + switch (option) { case OPT_VAR: Jim_SetResultBool(interp, Jim_GetVariable(interp, objPtr, 0) != NULL); break; @@ -20143,11 +17784,11 @@ } } return JIM_OK; } -/* [split] */ + static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *str, *splitChars, *noMatchStart; int splitLen, strLen; Jim_Obj *resObjPtr; @@ -20163,11 +17804,11 @@ if (len == 0) { return JIM_OK; } strLen = Jim_Utf8Length(interp, argv[1]); - /* Init */ + if (argc == 2) { splitChars = " \n\t\r"; splitLen = 4; } else { @@ -20176,11 +17817,11 @@ } noMatchStart = str; resObjPtr = Jim_NewListObj(interp, NULL, 0); - /* Split */ + if (splitLen) { Jim_Obj *objPtr; while (strLen--) { const char *sc = splitChars; int scLen = splitLen; @@ -20199,20 +17840,17 @@ } objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart)); Jim_ListAppendElement(interp, resObjPtr, objPtr); } else { - /* This handles the special case of splitchars eq {} - * Optimise by sharing common (ASCII) characters - */ Jim_Obj **commonObj = NULL; #define NUM_COMMON (128 - 9) while (strLen--) { int n = utf8_tounicode(str, &c); #ifdef JIM_OPTIMIZATION if (c >= 9 && c < 128) { - /* Common ASCII char. Note that 9 is the tab character */ + c -= 9; if (!commonObj) { commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON); memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON); } @@ -20232,11 +17870,11 @@ Jim_SetResult(interp, resObjPtr); return JIM_OK; } -/* [join] */ + static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *joinStr; int joinStrLen, i, listLen; Jim_Obj *resObjPtr; @@ -20243,21 +17881,21 @@ if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?"); return JIM_ERR; } - /* Init */ + if (argc == 2) { joinStr = " "; joinStrLen = 1; } else { joinStr = Jim_GetString(argv[2], &joinStrLen); } listLen = Jim_ListLength(interp, argv[1]); resObjPtr = Jim_NewStringObj(interp, NULL, 0); - /* Split */ + for (i = 0; i < listLen; i++) { Jim_Obj *objPtr = 0; Jim_ListIndex(interp, argv[1], i, &objPtr, JIM_NONE); Jim_AppendObj(interp, resObjPtr, objPtr); @@ -20267,11 +17905,11 @@ } Jim_SetResult(interp, resObjPtr); return JIM_OK; } -/* [format] */ + static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr; if (argc < 2) { @@ -20283,11 +17921,11 @@ return JIM_ERR; Jim_SetResult(interp, objPtr); return JIM_OK; } -/* [scan] */ + static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *listPtr, **outVec; int outc, i; @@ -20359,11 +17997,11 @@ Jim_SetResult(interp, listPtr); } return JIM_OK; } -/* [error] */ + static int Jim_ErrorCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 1, argv, "message ?stacktrace?"); return JIM_ERR; @@ -20375,11 +18013,11 @@ } interp->addStackTrace++; return JIM_ERR; } -/* [lrange] */ + static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr; if (argc != 4) { @@ -20390,11 +18028,11 @@ return JIM_ERR; Jim_SetResult(interp, objPtr); return JIM_OK; } -/* [lrepeat] */ + static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr; long count; @@ -20410,15 +18048,11 @@ argc -= 2; argv += 2; objPtr = Jim_NewListObj(interp, argv, argc); while (--count) { - int i; - - for (i = 0; i < argc; i++) { - ListAppendElement(objPtr, argv[i]); - } + ListInsertElements(objPtr, -1, argc, argv); } Jim_SetResult(interp, objPtr); return JIM_OK; } @@ -20447,11 +18081,11 @@ environ = env; #endif } -/* [env] */ + static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *key; const char *val; @@ -20490,11 +18124,11 @@ } Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1)); return JIM_OK; } -/* [source] */ + static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int retval; if (argc != 2) { @@ -20505,11 +18139,11 @@ if (retval == JIM_RETURN) return JIM_OK; return retval; } -/* [lreverse] */ + static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *revObjPtr, **ele; int len; @@ -20538,23 +18172,20 @@ return -1; else if (step < 0 && end > start) return -1; len = end - start; if (len < 0) - len = -len; /* abs(len) */ + len = -len; if (step < 0) - step = -step; /* abs(step) */ + step = -step; len = 1 + ((len - 1) / step); - /* We can truncate safely to INT_MAX, the range command - * will always return an error for a such long range - * because Tcl lists can't be so long. */ if (len > INT_MAX) len = INT_MAX; return (int)((len < 0) ? -1 : len); } -/* [range] */ + static int Jim_RangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { jim_wide start = 0, end, step = 1; int len, i; Jim_Obj *objPtr; @@ -20583,11 +18214,11 @@ ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step)); Jim_SetResult(interp, objPtr); return JIM_OK; } -/* [rand] */ + static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { jim_wide min = 0, max = 0, len, maxMul; if (argc < 1 || argc > 3) { @@ -20704,13 +18335,10 @@ Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL); i++; } } -/* ----------------------------------------------------------------------------- - * Interactive prompt - * ---------------------------------------------------------------------------*/ void Jim_MakeErrorMessage(Jim_Interp *interp) { Jim_Obj *argv[2]; argv[0] = Jim_NewStringObj(interp, "errorInfo", -1); @@ -20761,18 +18389,15 @@ *indexPtr = -1; for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) { if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) { - /* Found an exact match */ + *indexPtr = i; return JIM_OK; } if (flags & JIM_ENUM_ABBREV) { - /* Accept an unambiguous abbreviation. - * Note that '-' doesnt' consitute a valid abbreviation - */ if (strncmp(arg, *entryPtr, arglen) == 0) { if (*arg == '-' && arglen == 1) { break; } if (match >= 0) { @@ -20782,11 +18407,11 @@ match = i; } } } - /* If we had an unambiguous partial match */ + if (match >= 0) { *indexPtr = match; return JIM_OK; } @@ -20817,27 +18442,13 @@ int Jim_IsList(Jim_Obj *objPtr) { return objPtr->typePtr == &listObjType; } -/** - * Very simple printf-like formatting, designed for error messages. - * - * The format may contain up to 5 '%s' or '%#s', corresponding to variable arguments. - * The resulting string is created and set as the result. - * - * Each '%s' should correspond to a regular string parameter. - * Each '%#s' should correspond to a (Jim_Obj *) parameter. - * Any other printf specifier is not allowed (but %% is allowed for the % character). - * - * e.g. Jim_SetResultFormatted(interp, "Bad option \"%#s\" in proc \"%#s\"", optionObjPtr, procNamePtr); - * - * Note: We take advantage of the fact that printf has the same behaviour for both %s and %#s - */ void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...) { - /* Initial space needed */ + int len = strlen(format); int extra = 0; int n = 0; const char *params[5]; char *buf; @@ -20874,11 +18485,11 @@ len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]); Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len)); } -/* stubs */ + #ifndef jim_ext_package int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags) { return JIM_OK; } @@ -20890,36 +18501,22 @@ return NULL; } #endif -/* - * Local Variables: *** - * c-basic-offset: 4 *** - * tab-width: 4 *** - * End: *** - */ #include <stdio.h> #include <string.h> -/** - * Implements the common 'commands' subcommand - */ static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { - /* Nothing to do, since the result has already been created */ + return JIM_OK; } -/** - * Do-nothing command to support -commands and -usage - */ static const jim_subcmd_type dummy_subcmd = { - .cmd = "dummy", - .function = subcmd_null, - .flags = JIM_MODFLAG_HIDDEN, + "dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN }; static void add_commands(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep) { const char *s = ""; @@ -20959,26 +18556,10 @@ if (ct->args && *ct->args) { Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL); } } -static void show_full_usage(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, - Jim_Obj *const *argv) -{ - Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); - for (; ct->cmd; ct++) { - if (!(ct->flags & JIM_MODFLAG_HIDDEN)) { - /* subcmd */ - add_cmd_usage(interp, ct, argv[0]); - if (ct->description) { - Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n ", ct->description, NULL); - } - Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n", NULL); - } - } -} - static void set_wrong_args(Jim_Interp *interp, const jim_subcmd_type * command_table, Jim_Obj *subcmd) { Jim_SetResultString(interp, "wrong # args: must be \"", -1); add_cmd_usage(interp, command_table, subcmd); Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); @@ -20999,56 +18580,49 @@ 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\" or \"", - cmdname, " -help command\" for help", NULL); + Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help ?command?\" for help", NULL); return 0; } cmd = argv[1]; - if (argc == 2 && Jim_CompareStringImmediate(interp, cmd, "-usage")) { - /* Show full usage */ - show_full_usage(interp, command_table, argc, argv); - return &dummy_subcmd; - } - - /* Check for the help command */ + if (Jim_CompareStringImmediate(interp, cmd, "-help")) { if (argc == 2) { - /* Usage for the command, not the subcommand */ + show_cmd_usage(interp, command_table, argc, argv); return &dummy_subcmd; } help = 1; - /* Skip the 'help' command */ + cmd = argv[2]; } - /* Check for special builtin '-commands' command first */ + if (Jim_CompareStringImmediate(interp, cmd, "-commands")) { - /* Build the result here */ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); add_commands(interp, command_table, " "); return &dummy_subcmd; } cmdstr = Jim_GetString(cmd, &cmdlen); for (ct = command_table; ct->cmd; ct++) { if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) { - /* Found an exact match */ + break; } if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) { if (partial) { - /* Ambiguous */ + if (help) { - /* Just show the top level help here */ + show_cmd_usage(interp, command_table, argc, argv); return &dummy_subcmd; } bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]); return 0; @@ -21056,47 +18630,44 @@ partial = ct; } continue; } - /* If we had an unambiguous partial match */ + if (partial && !ct->cmd) { ct = partial; } if (!ct->cmd) { - /* No matching command */ + if (help) { - /* Just show the top level help here */ + show_cmd_usage(interp, command_table, argc, argv); return &dummy_subcmd; } bad_subcmd(interp, command_table, "unknown", argv[0], argv[1 + help]); return 0; } if (help) { Jim_SetResultString(interp, "Usage: ", -1); - /* subcmd */ + add_cmd_usage(interp, ct, argv[0]); - if (ct->description) { - Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n", ct->description, NULL); - } return &dummy_subcmd; } - /* Check the number of args */ + if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2 > ct->maxargs)) { Jim_SetResultString(interp, "wrong # args: must be \"", -1); - /* subcmd */ + add_cmd_usage(interp, ct, argv[0]); Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); return 0; } - /* Good command */ + return ct; } int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv) { @@ -21123,89 +18694,17 @@ Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv); return Jim_CallSubCmd(interp, ct, argc, argv); } -/* The following two functions are for normal commands */ -int -Jim_CheckCmdUsage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc, - Jim_Obj *const *argv) -{ - /* -usage or -help */ - if (argc == 2) { - if (Jim_CompareStringImmediate(interp, argv[1], "-usage") - || Jim_CompareStringImmediate(interp, argv[1], "-help")) { - Jim_SetResultString(interp, "Usage: ", -1); - add_cmd_usage(interp, command_table, NULL); - if (command_table->description) { - Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n", command_table->description, - NULL); - } - return JIM_OK; - } - } - if (argc >= 2 && command_table->function) { - /* This is actually a sub command table */ - - Jim_Obj *nargv[4]; - int nargc = 0; - const char *subcmd = NULL; - - if (Jim_CompareStringImmediate(interp, argv[1], "-subcommands")) { - Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); - add_commands(interp, (jim_subcmd_type *) command_table->function, " "); - return JIM_OK; - } - - if (Jim_CompareStringImmediate(interp, argv[1], "-subhelp") - || Jim_CompareStringImmediate(interp, argv[1], "-help")) { - subcmd = "-help"; - } - else if (Jim_CompareStringImmediate(interp, argv[1], "-subusage")) { - subcmd = "-usage"; - } - - if (subcmd) { - nargv[nargc++] = Jim_NewStringObj(interp, "$handle", -1); - nargv[nargc++] = Jim_NewStringObj(interp, subcmd, -1); - if (argc >= 3) { - nargv[nargc++] = argv[2]; - } - Jim_ParseSubCmd(interp, (jim_subcmd_type *) command_table->function, nargc, nargv); - Jim_FreeNewObj(interp, nargv[0]); - Jim_FreeNewObj(interp, nargv[1]); - return 0; - } - } - - /* Check the number of args */ - if (argc - 1 < command_table->minargs || (command_table->maxargs >= 0 - && argc - 1 > command_table->maxargs)) { - set_wrong_args(interp, command_table, NULL); - Jim_AppendStrings(interp, Jim_GetResult(interp), "\nUse \"", Jim_String(argv[0]), - " -help\" for help", NULL); - return JIM_ERR; - } - - /* Not usage, but passed arg checking */ - return -1; -} -/** - * UTF-8 utility functions - * - * (c) 2010 Steve Bennett <steveb@workware.net.au> - * - * See LICENCE for licence details. - */ - #include <ctype.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> -/* This one is always implemented */ + int utf8_fromunicode(char *p, unsigned short uc) { if (uc <= 0x7f) { *p = uc; return 1; @@ -21221,167 +18720,1778 @@ *p = 0x80 | (uc & 0x3f); return 3; } } -#ifdef JIM_UTF8 -int utf8_charlen(int c) +#include <ctype.h> +#include <string.h> + + +#define JIM_UTF_MAX 3 +#define JIM_INTEGER_SPACE 24 +#define MAX_FLOAT_WIDTH 320 + +Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv) +{ + const char *span, *format, *formatEnd, *msg; + int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0; + static const char * const mixedXPG = + "cannot mix \"%\" and \"%n$\" conversion specifiers"; + static const char * const badIndex[2] = { + "not enough arguments for all format specifiers", + "\"%n$\" argument index out of range" + }; + int formatLen; + Jim_Obj *resultPtr; + + char *num_buffer = NULL; + int num_buffer_size = 0; + + span = format = Jim_GetString(fmtObjPtr, &formatLen); + formatEnd = format + formatLen; + resultPtr = Jim_NewStringObj(interp, "", 0); + + while (format != formatEnd) { + char *end; + int gotMinus, sawFlag; + int gotPrecision, useShort; + long width, precision; + int newXpg; + int ch; + int step; + int doubleType; + char pad = ' '; + char spec[2*JIM_INTEGER_SPACE + 12]; + char *p; + + int formatted_chars; + int formatted_bytes; + const char *formatted_buf; + + step = utf8_tounicode(format, &ch); + format += step; + if (ch != '%') { + numBytes += step; + continue; + } + if (numBytes) { + Jim_AppendString(interp, resultPtr, span, numBytes); + numBytes = 0; + } + + + step = utf8_tounicode(format, &ch); + if (ch == '%') { + span = format; + numBytes = step; + format += step; + continue; + } + + + newXpg = 0; + if (isdigit(ch)) { + int position = strtoul(format, &end, 10); + if (*end == '$') { + newXpg = 1; + objIndex = position - 1; + format = end + 1; + step = utf8_tounicode(format, &ch); + } + } + if (newXpg) { + if (gotSequential) { + msg = mixedXPG; + goto errorMsg; + } + gotXpg = 1; + } else { + if (gotXpg) { + msg = mixedXPG; + goto errorMsg; + } + gotSequential = 1; + } + if ((objIndex < 0) || (objIndex >= objc)) { + msg = badIndex[gotXpg]; + goto errorMsg; + } + + p = spec; + *p++ = '%'; + + gotMinus = 0; + sawFlag = 1; + do { + switch (ch) { + case '-': + gotMinus = 1; + break; + case '0': + pad = ch; + break; + case ' ': + case '+': + case '#': + break; + default: + sawFlag = 0; + continue; + } + *p++ = ch; + format += step; + step = utf8_tounicode(format, &ch); + } while (sawFlag); + + + width = 0; + if (isdigit(ch)) { + width = strtoul(format, &end, 10); + format = end; + step = utf8_tounicode(format, &ch); + } else if (ch == '*') { + if (objIndex >= objc - 1) { + msg = badIndex[gotXpg]; + goto errorMsg; + } + if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) { + goto error; + } + if (width < 0) { + width = -width; + if (!gotMinus) { + *p++ = '-'; + gotMinus = 1; + } + } + objIndex++; + format += step; + step = utf8_tounicode(format, &ch); + } + + + gotPrecision = precision = 0; + if (ch == '.') { + gotPrecision = 1; + format += step; + step = utf8_tounicode(format, &ch); + } + if (isdigit(ch)) { + precision = strtoul(format, &end, 10); + format = end; + step = utf8_tounicode(format, &ch); + } else if (ch == '*') { + if (objIndex >= objc - 1) { + msg = badIndex[gotXpg]; + goto errorMsg; + } + if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) { + goto error; + } + + + if (precision < 0) { + precision = 0; + } + objIndex++; + format += step; + step = utf8_tounicode(format, &ch); + } + + + useShort = 0; + if (ch == 'h') { + useShort = 1; + format += step; + step = utf8_tounicode(format, &ch); + } else if (ch == 'l') { + + format += step; + step = utf8_tounicode(format, &ch); + if (ch == 'l') { + format += step; + step = utf8_tounicode(format, &ch); + } + } + + format += step; + span = format; + + + if (ch == 'i') { + ch = 'd'; + } + + doubleType = 0; + + switch (ch) { + case '\0': + msg = "format string ended in middle of field specifier"; + goto errorMsg; + case 's': { + formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes); + formatted_chars = Jim_Utf8Length(interp, objv[objIndex]); + if (gotPrecision && (precision < formatted_chars)) { + + formatted_chars = precision; + formatted_bytes = utf8_index(formatted_buf, precision); + } + break; + } + case 'c': { + jim_wide code; + + if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) { + goto error; + } + + formatted_bytes = utf8_fromunicode(spec, code); + formatted_buf = spec; + formatted_chars = 1; + break; + } + + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + doubleType = 1; + + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': { + jim_wide w; + double d; + int length; + + + if (width) { + p += sprintf(p, "%ld", width); + } + if (gotPrecision) { + p += sprintf(p, ".%ld", precision); + } + + + if (doubleType) { + if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) { + goto error; + } + length = MAX_FLOAT_WIDTH; + } + else { + if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) { + goto error; + } + length = JIM_INTEGER_SPACE; + if (useShort) { + *p++ = 'h'; + if (ch == 'd') { + w = (short)w; + } + else { + w = (unsigned short)w; + } + } + else { + *p++ = 'l'; +#ifdef HAVE_LONG_LONG + if (sizeof(long long) == sizeof(jim_wide)) { + *p++ = 'l'; + } +#endif + } + } + + *p++ = (char) ch; + *p = '\0'; + + + if (width > length) { + length = width; + } + if (gotPrecision) { + length += precision; + } + + + if (num_buffer_size < length + 1) { + num_buffer_size = length + 1; + num_buffer = Jim_Realloc(num_buffer, num_buffer_size); + } + + if (doubleType) { + snprintf(num_buffer, length + 1, spec, d); + } + else { + formatted_bytes = snprintf(num_buffer, length + 1, spec, w); + } + formatted_chars = formatted_bytes = strlen(num_buffer); + formatted_buf = num_buffer; + break; + } + + default: { + + spec[0] = ch; + spec[1] = '\0'; + Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec); + goto error; + } + } + + if (!gotMinus) { + while (formatted_chars < width) { + Jim_AppendString(interp, resultPtr, &pad, 1); + formatted_chars++; + } + } + + Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes); + + while (formatted_chars < width) { + Jim_AppendString(interp, resultPtr, &pad, 1); + formatted_chars++; + } + + objIndex += gotSequential; + } + if (numBytes) { + Jim_AppendString(interp, resultPtr, span, numBytes); + } + + Jim_Free(num_buffer); + return resultPtr; + + errorMsg: + Jim_SetResultString(interp, msg, -1); + error: + Jim_FreeNewObj(interp, resultPtr); + Jim_Free(num_buffer); + return NULL; +} +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + + +#if !defined(HAVE_REGCOMP) || defined(JIM_REGEXP) + + + +#define REG_MAX_PAREN 100 + + +#define END 0 +#define BOL 1 +#define EOL 2 +#define ANY 3 +#define ANYOF 4 +#define ANYBUT 5 +#define BRANCH 6 +#define BACK 7 +#define EXACTLY 8 +#define NOTHING 9 +#define REP 10 +#define REPMIN 11 +#define REPX 12 +#define REPXMIN 13 + +#define WORDA 15 +#define WORDZ 16 +#define OPEN 20 + +#define CLOSE (OPEN+REG_MAX_PAREN) +#define CLOSE_END (CLOSE+REG_MAX_PAREN) + +#define REG_MAGIC 0xFADED00D + + +#define OP(preg, p) (preg->program[p]) +#define NEXT(preg, p) (preg->program[p + 1]) +#define OPERAND(p) ((p) + 2) + + + + +#define FAIL(R,M) { (R)->err = (M); return (M); } +#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{') +#define META "^$.[()|?{+*" + +#define HASWIDTH 01 +#define SIMPLE 02 +#define SPSTART 04 +#define WORST 0 + +#define MAX_REP_COUNT 1000000 + +static int reg(regex_t *preg, int paren , int *flagp ); +static int regpiece(regex_t *preg, int *flagp ); +static int regbranch(regex_t *preg, int *flagp ); +static int regatom(regex_t *preg, int *flagp ); +static int regnode(regex_t *preg, int op ); +static int regnext(regex_t *preg, int p ); +static void regc(regex_t *preg, int b ); +static int reginsert(regex_t *preg, int op, int size, int opnd ); +static void regtail_(regex_t *preg, int p, int val, int line ); +static void regoptail(regex_t *preg, int p, int val ); +#define regtail(PREG, P, VAL) regtail_(PREG, P, VAL, __LINE__) + +static int reg_range_find(const int *string, int c); +static const char *str_find(const char *string, int c, int nocase); +static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase); + + +#ifdef DEBUG +int regnarrate = 0; +static void regdump(regex_t *preg); +static const char *regprop( int op ); +#endif + + +static int str_int_len(const int *seq) +{ + int n = 0; + while (*seq++) { + n++; + } + return n; +} + +int regcomp(regex_t *preg, const char *exp, int cflags) +{ + int scan; + int longest; + unsigned len; + int flags; + +#ifdef DEBUG + fprintf(stderr, "Compiling: '%s'\n", exp); +#endif + memset(preg, 0, sizeof(*preg)); + + if (exp == NULL) + FAIL(preg, REG_ERR_NULL_ARGUMENT); + + + preg->cflags = cflags; + preg->regparse = exp; + + preg->program = NULL; + preg->proglen = 0; + +#if 1 + + preg->proglen = (strlen(exp) + 1) * 5; + preg->program = malloc(preg->proglen * sizeof(int)); + if (preg->program == NULL) + FAIL(preg, REG_ERR_NOMEM); +#endif + + regc(preg, REG_MAGIC); + if (reg(preg, 0, &flags) == 0) { + return preg->err; + } + + + if (preg->re_nsub >= REG_MAX_PAREN) + FAIL(preg,REG_ERR_TOO_BIG); + + + preg->regstart = 0; + preg->reganch = 0; + preg->regmust = 0; + preg->regmlen = 0; + scan = 1; + if (OP(preg, regnext(preg, scan)) == END) { + scan = OPERAND(scan); + + + if (OP(preg, scan) == EXACTLY) { + preg->regstart = preg->program[OPERAND(scan)]; + } + else if (OP(preg, scan) == BOL) + preg->reganch++; + + if (flags&SPSTART) { + longest = 0; + len = 0; + for (; scan != 0; scan = regnext(preg, scan)) { + if (OP(preg, scan) == EXACTLY) { + int plen = str_int_len(preg->program + OPERAND(scan)); + if (plen >= len) { + longest = OPERAND(scan); + len = plen; + } + } + } + preg->regmust = longest; + preg->regmlen = len; + } + } + +#ifdef DEBUG + regdump(preg); +#endif + + return 0; +} + +static int reg(regex_t *preg, int paren , int *flagp ) +{ + int ret; + int br; + int ender; + int parno = 0; + int flags; + + *flagp = HASWIDTH; + + + if (paren) { + parno = ++preg->re_nsub; + ret = regnode(preg, OPEN+parno); + } else + ret = 0; + + + br = regbranch(preg, &flags); + if (br == 0) + return 0; + if (ret != 0) + regtail(preg, ret, br); + else + ret = br; + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + while (*preg->regparse == '|') { + preg->regparse++; + br = regbranch(preg, &flags); + if (br == 0) + return 0; + regtail(preg, ret, br); + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + } + + + ender = regnode(preg, (paren) ? CLOSE+parno : END); + regtail(preg, ret, ender); + + + for (br = ret; br != 0; br = regnext(preg, br)) + regoptail(preg, br, ender); + + + if (paren && *preg->regparse++ != ')') { + preg->err = REG_ERR_UNMATCHED_PAREN; + return 0; + } else if (!paren && *preg->regparse != '\0') { + if (*preg->regparse == ')') { + preg->err = REG_ERR_UNMATCHED_PAREN; + return 0; + } else { + preg->err = REG_ERR_JUNK_ON_END; + return 0; + } + } + + return(ret); +} + +static int regbranch(regex_t *preg, int *flagp ) +{ + int ret; + int chain; + int latest; + int flags; + + *flagp = WORST; + + ret = regnode(preg, BRANCH); + chain = 0; + while (*preg->regparse != '\0' && *preg->regparse != ')' && + *preg->regparse != '|') { + latest = regpiece(preg, &flags); + if (latest == 0) + return 0; + *flagp |= flags&HASWIDTH; + if (chain == 0) { + *flagp |= flags&SPSTART; + } + else { + regtail(preg, chain, latest); + } + chain = latest; + } + if (chain == 0) + (void) regnode(preg, NOTHING); + + return(ret); +} + +static int regpiece(regex_t *preg, int *flagp) +{ + int ret; + char op; + int next; + int flags; + int chain = 0; + int min; + int max; + + ret = regatom(preg, &flags); + if (ret == 0) + return 0; + + op = *preg->regparse; + if (!ISMULT(op)) { + *flagp = flags; + return(ret); + } + + if (!(flags&HASWIDTH) && op != '?') { + preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY; + return 0; + } + + + if (op == '{') { + char *end; + + min = strtoul(preg->regparse + 1, &end, 10); + if (end == preg->regparse + 1) { + preg->err = REG_ERR_BAD_COUNT; + return 0; + } + if (*end == '}') { + max = min; + } + else { + preg->regparse = end; + max = strtoul(preg->regparse + 1, &end, 10); + if (*end != '}') { + preg->err = REG_ERR_UNMATCHED_BRACES; + return 0; + } + } + if (end == preg->regparse + 1) { + max = MAX_REP_COUNT; + } + else if (max < min || max >= 100) { + preg->err = REG_ERR_BAD_COUNT; + return 0; + } + if (min >= 100) { + preg->err = REG_ERR_BAD_COUNT; + return 0; + } + + preg->regparse = strchr(preg->regparse, '}'); + } + else { + min = (op == '+'); + max = (op == '?' ? 1 : MAX_REP_COUNT); + } + + if (preg->regparse[1] == '?') { + preg->regparse++; + next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret); + } + else { + next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret); + } + preg->program[ret + 2] = max; + preg->program[ret + 3] = min; + preg->program[ret + 4] = 0; + + *flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART); + + if (!(flags & SIMPLE)) { + int back = regnode(preg, BACK); + regtail(preg, back, ret); + regtail(preg, next, back); + } + + preg->regparse++; + if (ISMULT(*preg->regparse)) { + preg->err = REG_ERR_NESTED_COUNT; + return 0; + } + + return chain ? chain : ret; +} + +static void reg_addrange(regex_t *preg, int lower, int upper) +{ + if (lower > upper) { + reg_addrange(preg, upper, lower); + } + + regc(preg, upper - lower + 1); + regc(preg, lower); +} + +static void reg_addrange_str(regex_t *preg, const char *str) +{ + while (*str) { + reg_addrange(preg, *str, *str); + str++; + } +} + +static int reg_utf8_tounicode_case(const char *s, int *uc, int upper) +{ + int l = utf8_tounicode(s, uc); + if (upper) { + *uc = utf8_upper(*uc); + } + return l; +} + +static int hexdigitval(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + +static int parse_hex(const char *s, int n, int *uc) +{ + int val = 0; + int k; + + for (k = 0; k < n; k++) { + int c = hexdigitval(*s++); + if (c == -1) { + break; + } + val = (val << 4) | c; + } + if (k) { + *uc = val; + } + return k; +} + +static int reg_decode_escape(const char *s, int *ch) +{ + int n; + const char *s0 = s; + + *ch = *s++; + + switch (*ch) { + case 'b': *ch = '\b'; break; + case 'e': *ch = 27; break; + case 'f': *ch = '\f'; break; + case 'n': *ch = '\n'; break; + case 'r': *ch = '\r'; break; + case 't': *ch = '\t'; break; + case 'v': *ch = '\v'; break; + case 'u': + if ((n = parse_hex(s, 4, ch)) > 0) { + s += n; + } + break; + case 'x': + if ((n = parse_hex(s, 2, ch)) > 0) { + s += n; + } + break; + case '\0': + s--; + *ch = '\\'; + break; + } + return s - s0; +} + +static int regatom(regex_t *preg, int *flagp) +{ + int ret; + int flags; + int nocase = (preg->cflags & REG_ICASE); + + int ch; + int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase); + + *flagp = WORST; + + preg->regparse += n; + switch (ch) { + + case '^': + ret = regnode(preg, BOL); + break; + case '$': + ret = regnode(preg, EOL); + break; + case '.': + ret = regnode(preg, ANY); + *flagp |= HASWIDTH|SIMPLE; + break; + case '[': { + const char *pattern = preg->regparse; + + if (*pattern == '^') { + ret = regnode(preg, ANYBUT); + pattern++; + } else + ret = regnode(preg, ANYOF); + + + if (*pattern == ']' || *pattern == '-') { + reg_addrange(preg, *pattern, *pattern); + pattern++; + } + + while (*pattern && *pattern != ']') { + + int start; + int end; + + pattern += reg_utf8_tounicode_case(pattern, &start, nocase); + if (start == '\\') { + pattern += reg_decode_escape(pattern, &start); + if (start == 0) { + preg->err = REG_ERR_NULL_CHAR; + return 0; + } + } + if (pattern[0] == '-' && pattern[1]) { + + pattern += utf8_tounicode(pattern, &end); + pattern += reg_utf8_tounicode_case(pattern, &end, nocase); + if (end == '\\') { + pattern += reg_decode_escape(pattern, &end); + if (end == 0) { + preg->err = REG_ERR_NULL_CHAR; + return 0; + } + } + + reg_addrange(preg, start, end); + continue; + } + if (start == '[') { + if (strncmp(pattern, ":alpha:]", 8) == 0) { + if ((preg->cflags & REG_ICASE) == 0) { + reg_addrange(preg, 'a', 'z'); + } + reg_addrange(preg, 'A', 'Z'); + pattern += 8; + continue; + } + if (strncmp(pattern, ":alnum:]", 8) == 0) { + if ((preg->cflags & REG_ICASE) == 0) { + reg_addrange(preg, 'a', 'z'); + } + reg_addrange(preg, 'A', 'Z'); + reg_addrange(preg, '0', '9'); + pattern += 8; + continue; + } + if (strncmp(pattern, ":space:]", 8) == 0) { + reg_addrange_str(preg, " \t\r\n\f\v"); + pattern += 8; + continue; + } + } + + reg_addrange(preg, start, start); + } + regc(preg, '\0'); + + if (*pattern) { + pattern++; + } + preg->regparse = pattern; + + *flagp |= HASWIDTH|SIMPLE; + } + break; + case '(': + ret = reg(preg, 1, &flags); + if (ret == 0) + return 0; + *flagp |= flags&(HASWIDTH|SPSTART); + break; + case '\0': + case '|': + case ')': + preg->err = REG_ERR_INTERNAL; + return 0; + case '?': + case '+': + case '*': + case '{': + preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING; + return 0; + case '\\': + switch (*preg->regparse++) { + case '\0': + preg->err = REG_ERR_TRAILING_BACKSLASH; + return 0; + case '<': + case 'm': + ret = regnode(preg, WORDA); + break; + case '>': + case 'M': + ret = regnode(preg, WORDZ); + break; + case 'd': + ret = regnode(preg, ANYOF); + reg_addrange(preg, '0', '9'); + regc(preg, '\0'); + *flagp |= HASWIDTH|SIMPLE; + break; + case 'w': + ret = regnode(preg, ANYOF); + if ((preg->cflags & REG_ICASE) == 0) { + reg_addrange(preg, 'a', 'z'); + } + reg_addrange(preg, 'A', 'Z'); + reg_addrange(preg, '0', '9'); + reg_addrange(preg, '_', '_'); + regc(preg, '\0'); + *flagp |= HASWIDTH|SIMPLE; + break; + case 's': + ret = regnode(preg, ANYOF); + reg_addrange_str(preg," \t\r\n\f\v"); + regc(preg, '\0'); + *flagp |= HASWIDTH|SIMPLE; + break; + + default: + + + preg->regparse--; + goto de_fault; + } + break; + de_fault: + default: { + int added = 0; + + + preg->regparse -= n; + + ret = regnode(preg, EXACTLY); + + + + while (*preg->regparse && strchr(META, *preg->regparse) == NULL) { + n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE)); + if (ch == '\\' && preg->regparse[n]) { + if (strchr("<>mMwds", preg->regparse[n])) { + + break; + } + n += reg_decode_escape(preg->regparse + n, &ch); + if (ch == 0) { + preg->err = REG_ERR_NULL_CHAR; + return 0; + } + } + + + if (ISMULT(preg->regparse[n])) { + + if (added) { + + break; + } + + regc(preg, ch); + added++; + preg->regparse += n; + break; + } + + + regc(preg, ch); + added++; + preg->regparse += n; + } + regc(preg, '\0'); + + *flagp |= HASWIDTH; + if (added == 1) + *flagp |= SIMPLE; + break; + } + break; + } + + return(ret); +} + +static void reg_grow(regex_t *preg, int n) +{ + if (preg->p + n >= preg->proglen) { + preg->proglen = (preg->p + n) * 2; + preg->program = realloc(preg->program, preg->proglen * sizeof(int)); + } +} + + +static int regnode(regex_t *preg, int op) +{ + reg_grow(preg, 2); + + preg->program[preg->p++] = op; + preg->program[preg->p++] = 0; + + + return preg->p - 2; +} + +static void regc(regex_t *preg, int b ) +{ + reg_grow(preg, 1); + preg->program[preg->p++] = b; +} + +static int reginsert(regex_t *preg, int op, int size, int opnd ) +{ + reg_grow(preg, size); + + + memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd)); + + memset(preg->program + opnd, 0, sizeof(int) * size); + + preg->program[opnd] = op; + + preg->p += size; + + return opnd + size; +} + +static void regtail_(regex_t *preg, int p, int val, int line ) +{ + int scan; + int temp; + int offset; + + + scan = p; + for (;;) { + temp = regnext(preg, scan); + if (temp == 0) + break; + scan = temp; + } + + if (OP(preg, scan) == BACK) + offset = scan - val; + else + offset = val - scan; + + preg->program[scan + 1] = offset; +} + + +static void regoptail(regex_t *preg, int p, int val ) +{ + + if (p != 0 && OP(preg, p) == BRANCH) { + regtail(preg, OPERAND(p), val); + } +} + + +static int regtry(regex_t *preg, const char *string ); +static int regmatch(regex_t *preg, int prog); +static int regrepeat(regex_t *preg, int p, int max); + +int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags) +{ + const char *s; + int scan; + + + if (preg == NULL || preg->program == NULL || string == NULL) { + return REG_ERR_NULL_ARGUMENT; + } + + + if (*preg->program != REG_MAGIC) { + return REG_ERR_CORRUPTED; + } + +#ifdef DEBUG + fprintf(stderr, "regexec: %s\n", string); + regdump(preg); +#endif + + preg->eflags = eflags; + preg->pmatch = pmatch; + preg->nmatch = nmatch; + preg->start = string; + + + for (scan = OPERAND(1); scan != 0; scan = regnext(preg, scan)) { + switch (OP(preg, scan)) { + case REP: + case REPMIN: + case REPX: + case REPXMIN: + preg->program[scan + 4] = 0; + break; + } + } + + + if (preg->regmust != 0) { + s = string; + while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) { + if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) { + break; + } + s++; + } + if (s == NULL) + return REG_NOMATCH; + } + + + preg->regbol = string; + + + if (preg->reganch) { + if (eflags & REG_NOTBOL) { + + goto nextline; + } + while (1) { + int ret = regtry(preg, string); + if (ret) { + return REG_NOERROR; + } + if (*string) { +nextline: + if (preg->cflags & REG_NEWLINE) { + + string = strchr(string, '\n'); + if (string) { + preg->regbol = ++string; + continue; + } + } + } + return REG_NOMATCH; + } + } + + + s = string; + if (preg->regstart != '\0') { + + while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) { + if (regtry(preg, s)) + return REG_NOERROR; + s++; + } + } + else + + while (1) { + if (regtry(preg, s)) + return REG_NOERROR; + if (*s == '\0') { + break; + } + s += utf8_charlen(*s); + } + + + return REG_NOMATCH; +} + + +static int regtry( regex_t *preg, const char *string ) +{ + int i; + + preg->reginput = string; + + for (i = 0; i < preg->nmatch; i++) { + preg->pmatch[i].rm_so = -1; + preg->pmatch[i].rm_eo = -1; + } + if (regmatch(preg, 1)) { + preg->pmatch[0].rm_so = string - preg->start; + preg->pmatch[0].rm_eo = preg->reginput - preg->start; + return(1); + } else + return(0); +} + +static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase) +{ + const char *s = string; + while (proglen && *s) { + int ch; + int n = reg_utf8_tounicode_case(s, &ch, nocase); + if (ch != *prog) { + return -1; + } + prog++; + s += n; + proglen--; + } + if (proglen == 0) { + return s - string; + } + return -1; +} + +static int reg_range_find(const int *range, int c) +{ + while (*range) { + + if (c >= range[1] && c <= (range[0] + range[1] - 1)) { + return 1; + } + range += 2; + } + return 0; +} + +static const char *str_find(const char *string, int c, int nocase) +{ + if (nocase) { + + c = utf8_upper(c); + } + while (*string) { + int ch; + int n = reg_utf8_tounicode_case(string, &ch, nocase); + if (c == ch) { + return string; + } + string += n; + } + return NULL; +} + +static int reg_iseol(regex_t *preg, int ch) +{ + if (preg->cflags & REG_NEWLINE) { + return ch == '\0' || ch == '\n'; + } + else { + return ch == '\0'; + } +} + +static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin) +{ + int nextch = '\0'; + const char *save; + int no; + int c; + + int max = preg->program[scan + 2]; + int min = preg->program[scan + 3]; + int next = regnext(preg, scan); + + if (OP(preg, next) == EXACTLY) { + nextch = preg->program[OPERAND(next)]; + } + save = preg->reginput; + no = regrepeat(preg, scan + 5, max); + if (no < min) { + return 0; + } + if (matchmin) { + + max = no; + no = min; + } + + while (1) { + if (matchmin) { + if (no > max) { + break; + } + } + else { + if (no < min) { + break; + } + } + preg->reginput = save + utf8_index(save, no); + reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE)); + + if (reg_iseol(preg, nextch) || c == nextch) { + if (regmatch(preg, next)) { + return(1); + } + } + if (matchmin) { + + no++; + } + else { + + no--; + } + } + return(0); +} + +static int regmatchrepeat(regex_t *preg, int scan, int matchmin) +{ + int *scanpt = preg->program + scan; + + int max = scanpt[2]; + int min = scanpt[3]; + + + if (scanpt[4] < min) { + + scanpt[4]++; + if (regmatch(preg, scan + 5)) { + return 1; + } + scanpt[4]--; + return 0; + } + if (scanpt[4] > max) { + return 0; + } + + if (matchmin) { + + if (regmatch(preg, regnext(preg, scan))) { + return 1; + } + + scanpt[4]++; + if (regmatch(preg, scan + 5)) { + return 1; + } + scanpt[4]--; + return 0; + } + + if (scanpt[4] < max) { + scanpt[4]++; + if (regmatch(preg, scan + 5)) { + return 1; + } + scanpt[4]--; + } + + return regmatch(preg, regnext(preg, scan)); +} + + +static int regmatch(regex_t *preg, int prog) +{ + int scan; + int next; + + scan = prog; + +#ifdef DEBUG + if (scan != 0 && regnarrate) + fprintf(stderr, "%s(\n", regprop(scan)); +#endif + while (scan != 0) { + int n; + int c; +#ifdef DEBUG + if (regnarrate) { + fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan))); + } +#endif + next = regnext(preg, scan); + n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE)); + + switch (OP(preg, scan)) { + case BOL: + if (preg->reginput != preg->regbol) + return(0); + break; + case EOL: + if (!reg_iseol(preg, c)) { + return(0); + } + break; + case WORDA: + + if ((!isalnum(UCHAR(c))) && c != '_') + return(0); + + if (preg->reginput > preg->regbol && + (isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_')) + return(0); + break; + case WORDZ: + + if (preg->reginput > preg->regbol) { + + if (reg_iseol(preg, c) || !isalnum(UCHAR(c)) || c != '_') { + c = preg->reginput[-1]; + + if (isalnum(UCHAR(c)) || c == '_') { + break; + } + } + } + + return(0); + + case ANY: + if (reg_iseol(preg, c)) + return 0; + preg->reginput += n; + break; + case EXACTLY: { + int opnd; + int len; + int slen; + + opnd = OPERAND(scan); + len = str_int_len(preg->program + opnd); + + slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE); + if (slen < 0) { + return(0); + } + preg->reginput += slen; + } + break; + case ANYOF: + if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) { + return(0); + } + preg->reginput += n; + break; + case ANYBUT: + if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) { + return(0); + } + preg->reginput += n; + break; + case NOTHING: + break; + case BACK: + break; + case BRANCH: { + const char *save; + + if (OP(preg, next) != BRANCH) + next = OPERAND(scan); + else { + do { + save = preg->reginput; + if (regmatch(preg, OPERAND(scan))) { + return(1); + } + preg->reginput = save; + scan = regnext(preg, scan); + } while (scan != 0 && OP(preg, scan) == BRANCH); + return(0); + + } + } + break; + case REP: + case REPMIN: + return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN); + + case REPX: + case REPXMIN: + return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN); + + case END: + return(1); + break; + default: + if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) { + const char *save; + + save = preg->reginput; + + if (regmatch(preg, next)) { + int no; + if (OP(preg, scan) < CLOSE) { + no = OP(preg, scan) - OPEN; + if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) { + preg->pmatch[no].rm_so = save - preg->start; + } + } + else { + no = OP(preg, scan) - CLOSE; + if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) { + preg->pmatch[no].rm_eo = save - preg->start; + } + } + return(1); + } else + return(0); + } + return REG_ERR_INTERNAL; + } + + scan = next; + } + + return REG_ERR_INTERNAL; +} + +static int regrepeat(regex_t *preg, int p, int max) +{ + int count = 0; + const char *scan; + int opnd; + int ch; + int n; + + scan = preg->reginput; + opnd = OPERAND(p); + switch (OP(preg, p)) { + case ANY: + + while (!reg_iseol(preg, *scan) && count < max) { + count++; + scan++; + } + break; + case EXACTLY: + while (count < max) { + n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); + if (preg->program[opnd] != ch) { + break; + } + count++; + scan += n; + } + break; + case ANYOF: + while (count < max) { + n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); + if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) { + break; + } + count++; + scan += n; + } + break; + case ANYBUT: + while (count < max) { + n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); + if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) { + break; + } + count++; + scan += n; + } + break; + default: + preg->err = REG_ERR_INTERNAL; + count = 0; + break; + } + preg->reginput = scan; + + return(count); +} + +static int regnext(regex_t *preg, int p ) +{ + int offset; + + offset = NEXT(preg, p); + + if (offset == 0) + return 0; + + if (OP(preg, p) == BACK) + return(p-offset); + else + return(p+offset); +} + + +size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) +{ + static const char *error_strings[] = { + "success", + "no match", + "bad pattern", + "null argument", + "unknown error", + "too big", + "out of memory", + "too many ()", + "parentheses () not balanced", + "braces {} not balanced", + "invalid repetition count(s)", + "extra characters", + "*+ of empty atom", + "nested count", + "internal error", + "count follows nothing", + "trailing backslash", + "corrupted program", + "contains null char", + }; + const char *err; + + if (errcode < 0 || errcode >= REG_ERR_NUM) { + err = "Bad error code"; + } + else { + err = error_strings[errcode]; + } + + return snprintf(errbuf, errbuf_size, "%s", err); +} + +void regfree(regex_t *preg) +{ + free(preg->program); +} + +#endif + +#if defined(_WIN32) || defined(WIN32) +#ifndef STRICT +#define STRICT +#endif +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#if defined(HAVE_DLOPEN_COMPAT) +void *dlopen(const char *path, int mode) +{ + JIM_NOTUSED(mode); + + return (void *)LoadLibraryA(path); +} + +int dlclose(void *handle) +{ + FreeLibrary((HANDLE)handle); + return 0; +} + +void *dlsym(void *handle, const char *symbol) +{ + return GetProcAddress((HMODULE)handle, symbol); +} + +char *dlerror(void) { - if ((c & 0x80) == 0) { - return 1; - } - if ((c & 0xe0) == 0xc0) { - return 2; - } - if ((c & 0xf0) == 0xe0) { - return 3; - } - if ((c & 0xf8) == 0xf0) { - return 4; - } - /* Invalid sequence */ - return -1; + static char msg[121]; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), + LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL); + return msg; } +#endif -int utf8_strlen(const char *str, int bytelen) +#ifdef _MSC_VER + +#include <sys/timeb.h> + + +int gettimeofday(struct timeval *tv, void *unused) { - int charlen = 0; - if (bytelen < 0) { - bytelen = strlen(str); - } - while (bytelen) { - int c; - int l = utf8_tounicode(str, &c); - charlen++; - str += l; - bytelen -= l; - } - return charlen; + struct _timeb tb; + + _ftime(&tb); + tv->tv_sec = tb.time; + tv->tv_usec = tb.millitm * 1000; + + return 0; } -int utf8_index(const char *str, int index) + +DIR *opendir(const char *name) { - const char *s = str; - while (index--) { - int c; - s += utf8_tounicode(s, &c); + DIR *dir = 0; + + if (name && name[0]) { + size_t base_length = strlen(name); + const char *all = + strchr("/\\", name[base_length - 1]) ? "*" : "/*"; + + if ((dir = (DIR *) Jim_Alloc(sizeof *dir)) != 0 && + (dir->name = (char *)Jim_Alloc(base_length + strlen(all) + 1)) != 0) { + strcat(strcpy(dir->name, name), all); + + if ((dir->handle = (long)_findfirst(dir->name, &dir->info)) != -1) + dir->result.d_name = 0; + else { + Jim_Free(dir->name); + Jim_Free(dir); + dir = 0; + } + } + else { + Jim_Free(dir); + dir = 0; + errno = ENOMEM; + } } - return s - str; + else { + errno = EINVAL; + } + return dir; } -int utf8_charequal(const char *s1, const char *s2) +int closedir(DIR * dir) { - int c1, c2; + int result = -1; - utf8_tounicode(s1, &c1); - utf8_tounicode(s2, &c2); - - return c1 == c2; -} - -int utf8_prev_len(const char *str, int len) -{ - int n = 1; - - assert(len > 0); - - /* Look up to len chars backward for a start-of-char byte */ - while (--len) { - if ((str[-n] & 0x80) == 0) { - /* Start of a 1-byte char */ - break; - } - if ((str[-n] & 0xc0) == 0xc0) { - /* Start of a multi-byte char */ - break; - } - n++; + if (dir) { + if (dir->handle != -1) + result = _findclose(dir->handle); + Jim_Free(dir->name); + Jim_Free(dir); } - return n; + if (result == -1) + errno = EBADF; + return result; } -int utf8_tounicode(const char *str, int *uc) -{ - unsigned const char *s = (unsigned const char *)str; - - if (s[0] < 0xc0) { - *uc = s[0]; - return 1; - } - if (s[0] < 0xe0) { - if ((s[1] & 0xc0) == 0x80) { - *uc = ((s[0] & ~0xc0) << 6) | (s[1] & ~0x80); - return 2; - } - } - else if (s[0] < 0xf0) { - if (((str[1] & 0xc0) == 0x80) && ((str[2] & 0xc0) == 0x80)) { - *uc = ((s[0] & ~0xe0) << 12) | ((s[1] & ~0x80) << 6) | (s[2] & ~0x80); - return 3; - } - } - - /* Invalid sequence, so just return the byte */ - *uc = *s; - return 1; -} - -struct casemap { - unsigned short code; /* code point */ - signed char lowerdelta; /* add for lowercase, or if -128 use the ext table */ - signed char upperdelta; /* add for uppercase, or offset into the ext table */ -}; - -/* Extended table for codepoints where |delta| > 127 */ -struct caseextmap { - unsigned short lower; - unsigned short upper; -}; - -/* Generated mapping tables */ -#include "_unicode_mapping.c" - -#define NUMCASEMAP sizeof(unicode_case_mapping) / sizeof(*unicode_case_mapping) - -static int cmp_casemap(const void *key, const void *cm) +struct dirent *readdir(DIR * dir) { - return *(int *)key - (int)((const struct casemap *)cm)->code; -} + struct dirent *result = 0; -static int utf8_map_case(int uc, int upper) -{ - const struct casemap *cm = bsearch(&uc, unicode_case_mapping, NUMCASEMAP, sizeof(*unicode_case_mapping), cmp_casemap); - - if (cm) { - if (cm->lowerdelta == -128) { - uc = upper ? unicode_extmap[cm->upperdelta].upper : unicode_extmap[cm->upperdelta].lower; + if (dir && dir->handle != -1) { + if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) { + result = &dir->result; + result->d_name = dir->info.name; } - else { - uc += upper ? cm->upperdelta : cm->lowerdelta; - } + } + else { + errno = EBADF; } - return uc; + return result; } - -int utf8_upper(int uc) -{ - if (isascii(uc)) { - return toupper(uc); - } - return utf8_map_case(uc, 1); -} - -int utf8_lower(int uc) -{ - if (isascii(uc)) { - return tolower(uc); - } - - return utf8_map_case(uc, 0); -} - +#endif #endif +#ifndef JIM_BOOTSTRAP_LIB_ONLY #include <errno.h> #include <string.h> #ifdef USE_LINENOISE #include <unistd.h> @@ -21476,11 +20586,11 @@ snprintf(prompt, sizeof(prompt), "%c> ", state); } #ifdef USE_LINENOISE if (strcmp(str, "h") == 0) { - /* built-in history command */ + int i; int len; char **history = linenoiseHistory(&len); for (i = 0; i < len; i++) { printf("%4d %s\n", i + 1, history[i]); @@ -21513,2228 +20623,25 @@ } out: Jim_Free(history_file); return JIM_OK; } -/* - * Implements the internals of the format command for jim - * - * The FreeBSD license - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of the Jim Tcl Project. - * - * Based on code originally from Tcl 8.5: - * - * Copyright (c) 1995-1997 Sun Microsystems, Inc. - * Copyright (c) 1999 by Scriptics Corporation. - * - * See the file "tcl.license.terms" for information on usage and redistribution of - * this file, and for a DISCLAIMER OF ALL WARRANTIES. - */ -#include <ctype.h> -#include <string.h> - - -#define JIM_UTF_MAX 3 -#define JIM_INTEGER_SPACE 24 -#define MAX_FLOAT_WIDTH 320 - -/** - * Apply the printf-like format in fmtObjPtr with the given arguments. - * - * Returns a new object with zero reference count if OK, or NULL on error. - */ -Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv) -{ - const char *span, *format, *formatEnd, *msg; - int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0; - static const char *mixedXPG = - "cannot mix \"%\" and \"%n$\" conversion specifiers"; - static const char *badIndex[2] = { - "not enough arguments for all format specifiers", - "\"%n$\" argument index out of range" - }; - int formatLen; - Jim_Obj *resultPtr; - - /* A single buffer is used to store numeric fields (with sprintf()) - * This buffer is allocated/reallocated as necessary - */ - char *num_buffer = NULL; - int num_buffer_size = 0; - - span = format = Jim_GetString(fmtObjPtr, &formatLen); - formatEnd = format + formatLen; - resultPtr = Jim_NewStringObj(interp, "", 0); - - while (format != formatEnd) { - char *end; - int gotMinus, sawFlag; - int gotPrecision, useShort; - long width, precision; - int newXpg; - int ch; - int step; - int doubleType; - char pad = ' '; - char spec[2*JIM_INTEGER_SPACE + 12]; - char *p; - - int formatted_chars; - int formatted_bytes; - const char *formatted_buf; - - step = utf8_tounicode(format, &ch); - format += step; - if (ch != '%') { - numBytes += step; - continue; - } - if (numBytes) { - Jim_AppendString(interp, resultPtr, span, numBytes); - numBytes = 0; - } - - /* - * Saw a % : process the format specifier. - * - * Step 0. Handle special case of escaped format marker (i.e., %%). - */ - - step = utf8_tounicode(format, &ch); - if (ch == '%') { - span = format; - numBytes = step; - format += step; - continue; - } - - /* - * Step 1. XPG3 position specifier - */ - - newXpg = 0; - if (isdigit(ch)) { - int position = strtoul(format, &end, 10); - if (*end == '$') { - newXpg = 1; - objIndex = position - 1; - format = end + 1; - step = utf8_tounicode(format, &ch); - } - } - if (newXpg) { - if (gotSequential) { - msg = mixedXPG; - goto errorMsg; - } - gotXpg = 1; - } else { - if (gotXpg) { - msg = mixedXPG; - goto errorMsg; - } - gotSequential = 1; - } - if ((objIndex < 0) || (objIndex >= objc)) { - msg = badIndex[gotXpg]; - goto errorMsg; - } - - /* - * Step 2. Set of flags. Also build up the sprintf spec. - */ - p = spec; - *p++ = '%'; - - gotMinus = 0; - sawFlag = 1; - do { - switch (ch) { - case '-': - gotMinus = 1; - break; - case '0': - pad = ch; - break; - case ' ': - case '+': - case '#': - break; - default: - sawFlag = 0; - continue; - } - *p++ = ch; - format += step; - step = utf8_tounicode(format, &ch); - } while (sawFlag); - - /* - * Step 3. Minimum field width. - */ - - width = 0; - if (isdigit(ch)) { - width = strtoul(format, &end, 10); - format = end; - step = utf8_tounicode(format, &ch); - } else if (ch == '*') { - if (objIndex >= objc - 1) { - msg = badIndex[gotXpg]; - goto errorMsg; - } - if (Jim_GetLong(interp, objv[objIndex], &width) != JIM_OK) { - goto error; - } - if (width < 0) { - width = -width; - if (!gotMinus) { - *p++ = '-'; - gotMinus = 1; - } - } - objIndex++; - format += step; - step = utf8_tounicode(format, &ch); - } - - /* - * Step 4. Precision. - */ - - gotPrecision = precision = 0; - if (ch == '.') { - gotPrecision = 1; - format += step; - step = utf8_tounicode(format, &ch); - } - if (isdigit(ch)) { - precision = strtoul(format, &end, 10); - format = end; - step = utf8_tounicode(format, &ch); - } else if (ch == '*') { - if (objIndex >= objc - 1) { - msg = badIndex[gotXpg]; - goto errorMsg; - } - if (Jim_GetLong(interp, objv[objIndex], &precision) != JIM_OK) { - goto error; - } - - /* - * TODO: Check this truncation logic. - */ - - if (precision < 0) { - precision = 0; - } - objIndex++; - format += step; - step = utf8_tounicode(format, &ch); - } - - /* - * Step 5. Length modifier. - */ - - useShort = 0; - if (ch == 'h') { - useShort = 1; - format += step; - step = utf8_tounicode(format, &ch); - } else if (ch == 'l') { - /* Just for compatibility. All non-short integers are wide. */ - format += step; - step = utf8_tounicode(format, &ch); - if (ch == 'l') { - format += step; - step = utf8_tounicode(format, &ch); - } - } - - format += step; - span = format; - - /* - * Step 6. The actual conversion character. - */ - - if (ch == 'i') { - ch = 'd'; - } - - doubleType = 0; - - /* Each valid conversion will set: - * formatted_buf - the result to be added - * formatted_chars - the length of formatted_buf in characters - * formatted_bytes - the length of formatted_buf in bytes - */ - switch (ch) { - case '\0': - msg = "format string ended in middle of field specifier"; - goto errorMsg; - case 's': { - formatted_buf = Jim_GetString(objv[objIndex], &formatted_bytes); - formatted_chars = Jim_Utf8Length(interp, objv[objIndex]); - if (gotPrecision && (precision < formatted_chars)) { - /* Need to build a (null terminated) truncated string */ - formatted_chars = precision; - formatted_bytes = utf8_index(formatted_buf, precision); - } - break; - } - case 'c': { - jim_wide code; - - if (Jim_GetWide(interp, objv[objIndex], &code) != JIM_OK) { - goto error; - } - /* Just store the value in the 'spec' buffer */ - formatted_bytes = utf8_fromunicode(spec, code); - formatted_buf = spec; - formatted_chars = 1; - break; - } - - case 'e': - case 'E': - case 'f': - case 'g': - case 'G': - doubleType = 1; - /* fall through */ - case 'd': - case 'u': - case 'o': - case 'x': - case 'X': { - jim_wide w; - double d; - int length; - - /* Fill in the width and precision */ - if (width) { - p += sprintf(p, "%ld", width); - } - if (gotPrecision) { - p += sprintf(p, ".%ld", precision); - } - - /* Now the modifier, and get the actual value here */ - if (doubleType) { - if (Jim_GetDouble(interp, objv[objIndex], &d) != JIM_OK) { - goto error; - } - length = MAX_FLOAT_WIDTH; - } - else { - if (Jim_GetWide(interp, objv[objIndex], &w) != JIM_OK) { - goto error; - } - length = JIM_INTEGER_SPACE; - if (useShort) { - *p++ = 'h'; - if (ch == 'd') { - w = (short)w; - } - else { - w = (unsigned short)w; - } - } - else { - *p++ = 'l'; -#ifdef HAVE_LONG_LONG - if (sizeof(long long) == sizeof(jim_wide)) { - *p++ = 'l'; - } -#endif - } - } - - *p++ = (char) ch; - *p = '\0'; - - /* Adjust length for width and precision */ - if (width > length) { - length = width; - } - if (gotPrecision) { - length += precision; - } - - /* Increase the size of the buffer if needed */ - if (num_buffer_size < length + 1) { - num_buffer_size = length + 1; - num_buffer = Jim_Realloc(num_buffer, num_buffer_size); - } - - if (doubleType) { - snprintf(num_buffer, length + 1, spec, d); - } - else { - formatted_bytes = snprintf(num_buffer, length + 1, spec, w); - } - formatted_chars = formatted_bytes = strlen(num_buffer); - formatted_buf = num_buffer; - break; - } - - default: { - /* Just reuse the 'spec' buffer */ - spec[0] = ch; - spec[1] = '\0'; - Jim_SetResultFormatted(interp, "bad field specifier \"%s\"", spec); - goto error; - } - } - - if (!gotMinus) { - while (formatted_chars < width) { - Jim_AppendString(interp, resultPtr, &pad, 1); - formatted_chars++; - } - } - - Jim_AppendString(interp, resultPtr, formatted_buf, formatted_bytes); - - while (formatted_chars < width) { - Jim_AppendString(interp, resultPtr, &pad, 1); - formatted_chars++; - } - - objIndex += gotSequential; - } - if (numBytes) { - Jim_AppendString(interp, resultPtr, span, numBytes); - } - - Jim_Free(num_buffer); - return resultPtr; - - errorMsg: - Jim_SetResultString(interp, msg, -1); - error: - Jim_FreeNewObj(interp, resultPtr); - Jim_Free(num_buffer); - return NULL; -} -/* - * regcomp and regexec -- regsub and regerror are elsewhere - * - * Copyright (c) 1986 by University of Toronto. - * Written by Henry Spencer. Not derived from licensed software. - * - * Permission is granted to anyone to use this software for any - * purpose on any computer system, and to redistribute it freely, - * subject to the following restrictions: - * - * 1. The author is not responsible for the consequences of use of - * this software, no matter how awful, even if they arise - * from defects in it. - * - * 2. The origin of this software must not be misrepresented, either - * by explicit claim or by omission. - * - * 3. Altered versions must be plainly marked as such, and must not - * be misrepresented as being the original software. - *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, - *** hoptoad!gnu, on 27 Dec 1986, to add \n as an alternative to | - *** to assist in implementing egrep. - *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, - *** hoptoad!gnu, on 27 Dec 1986, to add \< and \> for word-matching - *** as in BSD grep and ex. - *** THIS IS AN ALTERED VERSION. It was altered by John Gilmore, - *** hoptoad!gnu, on 28 Dec 1986, to optimize characters quoted with \. - *** THIS IS AN ALTERED VERSION. It was altered by James A. Woods, - *** ames!jaw, on 19 June 1987, to quash a regcomp() redundancy. - *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald - *** seiwald@vix.com, on 28 August 1993, for use in jam. Regmagic.h - *** was moved into regexp.h, and the include of regexp.h now uses "'s - *** to avoid conflicting with the system regexp.h. Const, bless its - *** soul, was removed so it can compile everywhere. The declaration - *** of strchr() was in conflict on AIX, so it was removed (as it is - *** happily defined in string.h). - *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald - *** seiwald@perforce.com, on 20 January 2000, to use function prototypes. - *** THIS IS AN ALTERED VERSION. It was altered by Christopher Seiwald - *** seiwald@perforce.com, on 05 November 2002, to const string literals. - * - * THIS IS AN ALTERED VERSION. It was altered by Steve Bennett <steveb@workware.net.au> - * on 16 October 2010, to remove static state and add better Tcl ARE compatibility. - * This includes counted repetitions, UTF-8 support, character classes, - * shorthand character classes, increased number of parentheses to 100, - * backslash escape sequences. It also removes \n as an alternative to |. - * - * Beware that some of this code is subtly aware of the way operator - * precedence is structured in regular expressions. Serious changes in - * regular-expression syntax might require a total rethink. - */ -#include <stdio.h> -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - - -#if !defined(HAVE_REGCOMP) || defined(JIM_REGEXP) - -/* - * Structure for regexp "program". This is essentially a linear encoding - * of a nondeterministic finite-state machine (aka syntax charts or - * "railroad normal form" in parsing technology). Each node is an opcode - * plus a "next" pointer, possibly plus an operand. "Next" pointers of - * all nodes except BRANCH implement concatenation; a "next" pointer with - * a BRANCH on both ends of it is connecting two alternatives. (Here we - * have one of the subtle syntax dependencies: an individual BRANCH (as - * opposed to a collection of them) is never concatenated with anything - * because of operator precedence.) The operand of some types of node is - * a literal string; for others, it is a node leading into a sub-FSM. In - * particular, the operand of a BRANCH node is the first node of the branch. - * (NB this is *not* a tree structure: the tail of the branch connects - * to the thing following the set of BRANCHes.) The opcodes are: - */ - -/* This *MUST* be less than (255-20)/2=117 */ -#define REG_MAX_PAREN 100 - -/* definition number opnd? meaning */ -#define END 0 /* no End of program. */ -#define BOL 1 /* no Match "" at beginning of line. */ -#define EOL 2 /* no Match "" at end of line. */ -#define ANY 3 /* no Match any one character. */ -#define ANYOF 4 /* str Match any character in this string. */ -#define ANYBUT 5 /* str Match any character not in this string. */ -#define BRANCH 6 /* node Match this alternative, or the next... */ -#define BACK 7 /* no Match "", "next" ptr points backward. */ -#define EXACTLY 8 /* str Match this string. */ -#define NOTHING 9 /* no Match empty string. */ -#define REP 10 /* max,min Match this (simple) thing [min,max] times. */ -#define REPMIN 11 /* max,min Match this (simple) thing [min,max] times, mininal match. */ -#define REPX 12 /* max,min Match this (complex) thing [min,max] times. */ -#define REPXMIN 13 /* max,min Match this (complex) thing [min,max] times, minimal match. */ - -#define WORDA 15 /* no Match "" at wordchar, where prev is nonword */ -#define WORDZ 16 /* no Match "" at nonwordchar, where prev is word */ -#define OPEN 20 /* no Mark this point in input as start of #n. */ - /* OPEN+1 is number 1, etc. */ -#define CLOSE (OPEN+REG_MAX_PAREN) /* no Analogous to OPEN. */ -#define CLOSE_END (CLOSE+REG_MAX_PAREN) - -/* - * The first byte of the regexp internal "program" is actually this magic - * number; the start node begins in the second byte. - */ -#define REG_MAGIC 0xFADED00D - -/* - * Opcode notes: - * - * BRANCH The set of branches constituting a single choice are hooked - * together with their "next" pointers, since precedence prevents - * anything being concatenated to any individual branch. The - * "next" pointer of the last BRANCH in a choice points to the - * thing following the whole choice. This is also where the - * final "next" pointer of each individual branch points; each - * branch starts with the operand node of a BRANCH node. - * - * BACK Normal "next" pointers all implicitly point forward; BACK - * exists to make loop structures possible. - * - * STAR,PLUS '?', and complex '*' and '+', are implemented as circular - * BRANCH structures using BACK. Simple cases (one character - * per match) are implemented with STAR and PLUS for speed - * and to minimize recursive plunges. - * - * OPEN,CLOSE ...are numbered at compile time. - */ - -/* - * A node is one char of opcode followed by two chars of "next" pointer. - * "Next" pointers are stored as two 8-bit pieces, high order first. The - * value is a positive offset from the opcode of the node containing it. - * An operand, if any, simply follows the node. (Note that much of the - * code generation knows about this implicit relationship.) - * - * Using two bytes for the "next" pointer is vast overkill for most things, - * but allows patterns to get big without disasters. - */ -#define OP(preg, p) (preg->program[p]) -#define NEXT(preg, p) (preg->program[p + 1]) -#define OPERAND(p) ((p) + 2) - -/* - * See regmagic.h for one further detail of program structure. - */ - - -/* - * Utility definitions. - */ - -#define FAIL(R,M) { (R)->err = (M); return (M); } -#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?' || (c) == '{') -#define META "^$.[()|?{+*" - -/* - * Flags to be passed up and down. - */ -#define HASWIDTH 01 /* Known never to match null string. */ -#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ -#define SPSTART 04 /* Starts with * or +. */ -#define WORST 0 /* Worst case. */ - -#define MAX_REP_COUNT 1000000 - -/* - * Forward declarations for regcomp()'s friends. - */ -static int reg(regex_t *preg, int paren /* Parenthesized? */, int *flagp ); -static int regpiece(regex_t *preg, int *flagp ); -static int regbranch(regex_t *preg, int *flagp ); -static int regatom(regex_t *preg, int *flagp ); -static int regnode(regex_t *preg, int op ); -static int regnext(regex_t *preg, int p ); -static void regc(regex_t *preg, int b ); -static int reginsert(regex_t *preg, int op, int size, int opnd ); -static void regtail_(regex_t *preg, int p, int val, int line ); -static void regoptail(regex_t *preg, int p, int val ); -#define regtail(PREG, P, VAL) regtail_(PREG, P, VAL, __LINE__) - -static int reg_range_find(const int *string, int c); -static const char *str_find(const char *string, int c, int nocase); -static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase); - -/*#define DEBUG*/ -#ifdef DEBUG -int regnarrate = 0; -static void regdump(regex_t *preg); -static const char *regprop( int op ); -#endif - - -/** - * Returns the length of the null-terminated integer sequence. - */ -static int str_int_len(const int *seq) -{ - int n = 0; - while (*seq++) { - n++; - } - return n; -} - -/* - - regcomp - compile a regular expression into internal code - * - * We can't allocate space until we know how big the compiled form will be, - * but we can't compile it (and thus know how big it is) until we've got a - * place to put the code. So we cheat: we compile it twice, once with code - * generation turned off and size counting turned on, and once "for real". - * This also means that we don't allocate space until we are sure that the - * thing really will compile successfully, and we never have to move the - * code and thus invalidate pointers into it. (Note that it has to be in - * one piece because free() must be able to free it all.) - * - * Beware that the optimization-preparation code in here knows about some - * of the structure of the compiled regexp. - */ -int regcomp(regex_t *preg, const char *exp, int cflags) -{ - int scan; - int longest; - unsigned len; - int flags; - -#ifdef DEBUG - fprintf(stderr, "Compiling: '%s'\n", exp); -#endif - memset(preg, 0, sizeof(*preg)); - - if (exp == NULL) - FAIL(preg, REG_ERR_NULL_ARGUMENT); - - /* First pass: determine size, legality. */ - preg->cflags = cflags; - preg->regparse = exp; - /* XXX: For now, start unallocated */ - preg->program = NULL; - preg->proglen = 0; - -#if 1 - /* Allocate space. */ - preg->proglen = (strlen(exp) + 1) * 5; - preg->program = malloc(preg->proglen * sizeof(int)); - if (preg->program == NULL) - FAIL(preg, REG_ERR_NOMEM); -#endif - - /* Note that since we store a magic value as the first item in the program, - * program offsets will never be 0 - */ - regc(preg, REG_MAGIC); - if (reg(preg, 0, &flags) == 0) { - return preg->err; - } - - /* Small enough for pointer-storage convention? */ - if (preg->re_nsub >= REG_MAX_PAREN) /* Probably could be 65535L. */ - FAIL(preg,REG_ERR_TOO_BIG); - - /* Dig out information for optimizations. */ - preg->regstart = 0; /* Worst-case defaults. */ - preg->reganch = 0; - preg->regmust = 0; - preg->regmlen = 0; - scan = 1; /* First BRANCH. */ - if (OP(preg, regnext(preg, scan)) == END) { /* Only one top-level choice. */ - scan = OPERAND(scan); - - /* Starting-point info. */ - if (OP(preg, scan) == EXACTLY) { - preg->regstart = preg->program[OPERAND(scan)]; - } - else if (OP(preg, scan) == BOL) - preg->reganch++; - - /* - * If there's something expensive in the r.e., find the - * longest literal string that must appear and make it the - * regmust. Resolve ties in favor of later strings, since - * the regstart check works with the beginning of the r.e. - * and avoiding duplication strengthens checking. Not a - * strong reason, but sufficient in the absence of others. - */ - if (flags&SPSTART) { - longest = 0; - len = 0; - for (; scan != 0; scan = regnext(preg, scan)) { - if (OP(preg, scan) == EXACTLY) { - int plen = str_int_len(preg->program + OPERAND(scan)); - if (plen >= len) { - longest = OPERAND(scan); - len = plen; - } - } - } - preg->regmust = longest; - preg->regmlen = len; - } - } - -#ifdef DEBUG - regdump(preg); -#endif - - return 0; -} - -/* - - reg - regular expression, i.e. main body or parenthesized thing - * - * Caller must absorb opening parenthesis. - * - * Combining parenthesis handling with the base level of regular expression - * is a trifle forced, but the need to tie the tails of the branches to what - * follows makes it hard to avoid. - */ -static int reg(regex_t *preg, int paren /* Parenthesized? */, int *flagp ) -{ - int ret; - int br; - int ender; - int parno = 0; - int flags; - - *flagp = HASWIDTH; /* Tentatively. */ - - /* Make an OPEN node, if parenthesized. */ - if (paren) { - parno = ++preg->re_nsub; - ret = regnode(preg, OPEN+parno); - } else - ret = 0; - - /* Pick up the branches, linking them together. */ - br = regbranch(preg, &flags); - if (br == 0) - return 0; - if (ret != 0) - regtail(preg, ret, br); /* OPEN -> first. */ - else - ret = br; - if (!(flags&HASWIDTH)) - *flagp &= ~HASWIDTH; - *flagp |= flags&SPSTART; - while (*preg->regparse == '|') { - preg->regparse++; - br = regbranch(preg, &flags); - if (br == 0) - return 0; - regtail(preg, ret, br); /* BRANCH -> BRANCH. */ - if (!(flags&HASWIDTH)) - *flagp &= ~HASWIDTH; - *flagp |= flags&SPSTART; - } - - /* Make a closing node, and hook it on the end. */ - ender = regnode(preg, (paren) ? CLOSE+parno : END); - regtail(preg, ret, ender); - - /* Hook the tails of the branches to the closing node. */ - for (br = ret; br != 0; br = regnext(preg, br)) - regoptail(preg, br, ender); - - /* Check for proper termination. */ - if (paren && *preg->regparse++ != ')') { - preg->err = REG_ERR_UNMATCHED_PAREN; - return 0; - } else if (!paren && *preg->regparse != '\0') { - if (*preg->regparse == ')') { - preg->err = REG_ERR_UNMATCHED_PAREN; - return 0; - } else { - preg->err = REG_ERR_JUNK_ON_END; - return 0; - } - } - - return(ret); -} - -/* - - regbranch - one alternative of an | operator - * - * Implements the concatenation operator. - */ -static int regbranch(regex_t *preg, int *flagp ) -{ - int ret; - int chain; - int latest; - int flags; - - *flagp = WORST; /* Tentatively. */ - - ret = regnode(preg, BRANCH); - chain = 0; - while (*preg->regparse != '\0' && *preg->regparse != ')' && - *preg->regparse != '|') { - latest = regpiece(preg, &flags); - if (latest == 0) - return 0; - *flagp |= flags&HASWIDTH; - if (chain == 0) {/* First piece. */ - *flagp |= flags&SPSTART; - } - else { - regtail(preg, chain, latest); - } - chain = latest; - } - if (chain == 0) /* Loop ran zero times. */ - (void) regnode(preg, NOTHING); - - return(ret); -} - -/* - - regpiece - something followed by possible [*+?] - * - * Note that the branching code sequences used for ? and the general cases - * of * and + are somewhat optimized: they use the same NOTHING node as - * both the endmarker for their branch list and the body of the last branch. - * It might seem that this node could be dispensed with entirely, but the - * endmarker role is not redundant. - */ -static int regpiece(regex_t *preg, int *flagp) -{ - int ret; - char op; - int next; - int flags; - int chain = 0; - int min; - int max; - - ret = regatom(preg, &flags); - if (ret == 0) - return 0; - - op = *preg->regparse; - if (!ISMULT(op)) { - *flagp = flags; - return(ret); - } - - if (!(flags&HASWIDTH) && op != '?') { - preg->err = REG_ERR_OPERAND_COULD_BE_EMPTY; - return 0; - } - - /* Handle braces (counted repetition) by expansion */ - if (op == '{') { - char *end; - - min = strtoul(preg->regparse + 1, &end, 10); - if (end == preg->regparse + 1) { - preg->err = REG_ERR_BAD_COUNT; - return 0; - } - if (*end == '}') { - max = min; - } - else { - preg->regparse = end; - max = strtoul(preg->regparse + 1, &end, 10); - if (*end != '}') { - preg->err = REG_ERR_UNMATCHED_BRACES; - return 0; - } - } - if (end == preg->regparse + 1) { - max = MAX_REP_COUNT; - } - else if (max < min || max >= 100) { - preg->err = REG_ERR_BAD_COUNT; - return 0; - } - if (min >= 100) { - preg->err = REG_ERR_BAD_COUNT; - return 0; - } - - preg->regparse = strchr(preg->regparse, '}'); - } - else { - min = (op == '+'); - max = (op == '?' ? 1 : MAX_REP_COUNT); - } - - if (preg->regparse[1] == '?') { - preg->regparse++; - next = reginsert(preg, flags & SIMPLE ? REPMIN : REPXMIN, 5, ret); - } - else { - next = reginsert(preg, flags & SIMPLE ? REP: REPX, 5, ret); - } - preg->program[ret + 2] = max; - preg->program[ret + 3] = min; - preg->program[ret + 4] = 0; - - *flagp = (min) ? (WORST|HASWIDTH) : (WORST|SPSTART); - - if (!(flags & SIMPLE)) { - int back = regnode(preg, BACK); - regtail(preg, back, ret); - regtail(preg, next, back); - } - - preg->regparse++; - if (ISMULT(*preg->regparse)) { - preg->err = REG_ERR_NESTED_COUNT; - return 0; - } - - return chain ? chain : ret; -} - -/** - * Add all characters in the inclusive range between lower and upper. - * - * Handles a swapped range (upper < lower). - */ -static void reg_addrange(regex_t *preg, int lower, int upper) -{ - if (lower > upper) { - reg_addrange(preg, upper, lower); - } - /* Add a range as length, start */ - regc(preg, upper - lower + 1); - regc(preg, lower); -} - -/** - * Add a null-terminated literal string as a set of ranges. - */ -static void reg_addrange_str(regex_t *preg, const char *str) -{ - while (*str) { - reg_addrange(preg, *str, *str); - str++; - } -} - -/** - * Extracts the next unicode char from utf8. - * - * If 'upper' is set, converts the char to uppercase. - */ -static int reg_utf8_tounicode_case(const char *s, int *uc, int upper) -{ - int l = utf8_tounicode(s, uc); - if (upper) { - *uc = utf8_upper(*uc); - } - return l; -} - -/** - * Converts a hex digit to decimal. - * - * Returns -1 for an invalid hex digit. - */ -static int hexdigitval(int c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - return -1; -} - -/** - * Parses up to 'n' hex digits at 's' and stores the result in *uc. - * - * Returns the number of hex digits parsed. - * If there are no hex digits, returns 0 and stores nothing. - */ -static int parse_hex(const char *s, int n, int *uc) -{ - int val = 0; - int k; - - for (k = 0; k < n; k++) { - int c = hexdigitval(*s++); - if (c == -1) { - break; - } - val = (val << 4) | c; - } - if (k) { - *uc = val; - } - return k; -} - -/** - * Call for chars after a backlash to decode the escape sequence. - * - * Stores the result in *ch. - * - * Returns the number of bytes consumed. - */ -static int reg_decode_escape(const char *s, int *ch) -{ - int n; - const char *s0 = s; - - *ch = *s++; - - switch (*ch) { - case 'b': *ch = '\b'; break; - case 'e': *ch = 27; break; - case 'f': *ch = '\f'; break; - case 'n': *ch = '\n'; break; - case 'r': *ch = '\r'; break; - case 't': *ch = '\t'; break; - case 'v': *ch = '\v'; break; - case 'u': - if ((n = parse_hex(s, 4, ch)) > 0) { - s += n; - } - break; - case 'x': - if ((n = parse_hex(s, 2, ch)) > 0) { - s += n; - } - break; - case '\0': - s--; - *ch = '\\'; - break; - } - return s - s0; -} - -/* - - regatom - the lowest level - * - * Optimization: gobbles an entire sequence of ordinary characters so that - * it can turn them into a single node, which is smaller to store and - * faster to run. Backslashed characters are exceptions, each becoming a - * separate node; the code is simpler that way and it's not worth fixing. - */ -static int regatom(regex_t *preg, int *flagp) -{ - int ret; - int flags; - int nocase = (preg->cflags & REG_ICASE); - - int ch; - int n = reg_utf8_tounicode_case(preg->regparse, &ch, nocase); - - *flagp = WORST; /* Tentatively. */ - - preg->regparse += n; - switch (ch) { - /* FIXME: these chars only have meaning at beg/end of pat? */ - case '^': - ret = regnode(preg, BOL); - break; - case '$': - ret = regnode(preg, EOL); - break; - case '.': - ret = regnode(preg, ANY); - *flagp |= HASWIDTH|SIMPLE; - break; - case '[': { - const char *pattern = preg->regparse; - - if (*pattern == '^') { /* Complement of range. */ - ret = regnode(preg, ANYBUT); - pattern++; - } else - ret = regnode(preg, ANYOF); - - /* Special case. If the first char is ']' or '-', it is part of the set */ - if (*pattern == ']' || *pattern == '-') { - reg_addrange(preg, *pattern, *pattern); - pattern++; - } - - while (*pattern && *pattern != ']') { - /* Is this a range? a-z */ - int start; - int end; - - pattern += reg_utf8_tounicode_case(pattern, &start, nocase); - if (start == '\\') { - pattern += reg_decode_escape(pattern, &start); - if (start == 0) { - preg->err = REG_ERR_NULL_CHAR; - return 0; - } - } - if (pattern[0] == '-' && pattern[1]) { - /* skip '-' */ - pattern += utf8_tounicode(pattern, &end); - pattern += reg_utf8_tounicode_case(pattern, &end, nocase); - if (end == '\\') { - pattern += reg_decode_escape(pattern, &end); - if (end == 0) { - preg->err = REG_ERR_NULL_CHAR; - return 0; - } - } - - reg_addrange(preg, start, end); - continue; - } - if (start == '[') { - if (strncmp(pattern, ":alpha:]", 8) == 0) { - if ((preg->cflags & REG_ICASE) == 0) { - reg_addrange(preg, 'a', 'z'); - } - reg_addrange(preg, 'A', 'Z'); - pattern += 8; - continue; - } - if (strncmp(pattern, ":alnum:]", 8) == 0) { - if ((preg->cflags & REG_ICASE) == 0) { - reg_addrange(preg, 'a', 'z'); - } - reg_addrange(preg, 'A', 'Z'); - reg_addrange(preg, '0', '9'); - pattern += 8; - continue; - } - if (strncmp(pattern, ":space:]", 8) == 0) { - reg_addrange_str(preg, " \t\r\n\f\v"); - pattern += 8; - continue; - } - } - /* Not a range, so just add the char */ - reg_addrange(preg, start, start); - } - regc(preg, '\0'); - - if (*pattern) { - pattern++; - } - preg->regparse = pattern; - - *flagp |= HASWIDTH|SIMPLE; - } - break; - case '(': - ret = reg(preg, 1, &flags); - if (ret == 0) - return 0; - *flagp |= flags&(HASWIDTH|SPSTART); - break; - case '\0': - case '|': - case ')': - preg->err = REG_ERR_INTERNAL; - return 0; /* Supposed to be caught earlier. */ - case '?': - case '+': - case '*': - case '{': - preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING; - return 0; - case '\\': - switch (*preg->regparse++) { - case '\0': - preg->err = REG_ERR_TRAILING_BACKSLASH; - return 0; - case '<': - case 'm': - ret = regnode(preg, WORDA); - break; - case '>': - case 'M': - ret = regnode(preg, WORDZ); - break; - case 'd': - ret = regnode(preg, ANYOF); - reg_addrange(preg, '0', '9'); - regc(preg, '\0'); - *flagp |= HASWIDTH|SIMPLE; - break; - case 'w': - ret = regnode(preg, ANYOF); - if ((preg->cflags & REG_ICASE) == 0) { - reg_addrange(preg, 'a', 'z'); - } - reg_addrange(preg, 'A', 'Z'); - reg_addrange(preg, '0', '9'); - reg_addrange(preg, '_', '_'); - regc(preg, '\0'); - *flagp |= HASWIDTH|SIMPLE; - break; - case 's': - ret = regnode(preg, ANYOF); - reg_addrange_str(preg," \t\r\n\f\v"); - regc(preg, '\0'); - *flagp |= HASWIDTH|SIMPLE; - break; - /* FIXME: Someday handle \1, \2, ... */ - default: - /* Handle general quoted chars in exact-match routine */ - /* Back up to include the backslash */ - preg->regparse--; - goto de_fault; - } - break; - de_fault: - default: { - /* - * Encode a string of characters to be matched exactly. - */ - int added = 0; - - /* Back up to pick up the first char of interest */ - preg->regparse -= n; - - ret = regnode(preg, EXACTLY); - - /* Note that a META operator such as ? or * consumes the - * preceding char. - * Thus we must be careful to look ahead by 2 and add the - * last char as it's own EXACTLY if necessary - */ - - /* Until end of string or a META char is reached */ - while (*preg->regparse && strchr(META, *preg->regparse) == NULL) { - n = reg_utf8_tounicode_case(preg->regparse, &ch, (preg->cflags & REG_ICASE)); - if (ch == '\\' && preg->regparse[n]) { - /* Non-trailing backslash. - * Is this a special escape, or a regular escape? - */ - if (strchr("<>mMwds", preg->regparse[n])) { - /* A special escape. All done with EXACTLY */ - break; - } - /* Decode it. Note that we add the length for the escape - * sequence to the length for the backlash so we can skip - * the entire sequence, or not as required. - */ - n += reg_decode_escape(preg->regparse + n, &ch); - if (ch == 0) { - preg->err = REG_ERR_NULL_CHAR; - return 0; - } - } - - /* Now we have one char 'ch' of length 'n'. - * Check to see if the following char is a MULT - */ - - if (ISMULT(preg->regparse[n])) { - /* Yes. But do we already have some EXACTLY chars? */ - if (added) { - /* Yes, so return what we have and pick up the current char next time around */ - break; - } - /* No, so add this single char and finish */ - regc(preg, ch); - added++; - preg->regparse += n; - break; - } - - /* No, so just add this char normally */ - regc(preg, ch); - added++; - preg->regparse += n; - } - regc(preg, '\0'); - - *flagp |= HASWIDTH; - if (added == 1) - *flagp |= SIMPLE; - break; - } - break; - } - - return(ret); -} - -static void reg_grow(regex_t *preg, int n) -{ - if (preg->p + n >= preg->proglen) { - preg->proglen = (preg->p + n) * 2; - preg->program = realloc(preg->program, preg->proglen * sizeof(int)); - } -} - -/* - - regnode - emit a node - */ -/* Location. */ -static int regnode(regex_t *preg, int op) -{ - reg_grow(preg, 2); - - preg->program[preg->p++] = op; - preg->program[preg->p++] = 0; - - /* Return the start of the node */ - return preg->p - 2; -} - -/* - - regc - emit (if appropriate) a byte of code - */ -static void regc(regex_t *preg, int b ) -{ - reg_grow(preg, 1); - preg->program[preg->p++] = b; -} - -/* - - reginsert - insert an operator in front of already-emitted operand - * - * Means relocating the operand. - * Returns the new location of the original operand. - */ -static int reginsert(regex_t *preg, int op, int size, int opnd ) -{ - reg_grow(preg, size); - - /* Move everything from opnd up */ - memmove(preg->program + opnd + size, preg->program + opnd, sizeof(int) * (preg->p - opnd)); - /* Zero out the new space */ - memset(preg->program + opnd, 0, sizeof(int) * size); - - preg->program[opnd] = op; - - preg->p += size; - - return opnd + size; -} - -/* - - regtail - set the next-pointer at the end of a node chain - */ -static void regtail_(regex_t *preg, int p, int val, int line ) -{ - int scan; - int temp; - int offset; - - /* Find last node. */ - scan = p; - for (;;) { - temp = regnext(preg, scan); - if (temp == 0) - break; - scan = temp; - } - - if (OP(preg, scan) == BACK) - offset = scan - val; - else - offset = val - scan; - - preg->program[scan + 1] = offset; -} - -/* - - regoptail - regtail on operand of first argument; nop if operandless - */ - -static void regoptail(regex_t *preg, int p, int val ) -{ - /* "Operandless" and "op != BRANCH" are synonymous in practice. */ - if (p != 0 && OP(preg, p) == BRANCH) { - regtail(preg, OPERAND(p), val); - } -} - -/* - * regexec and friends - */ - -/* - * Forwards. - */ -static int regtry(regex_t *preg, const char *string ); -static int regmatch(regex_t *preg, int prog); -static int regrepeat(regex_t *preg, int p, int max); - -/* - - regexec - match a regexp against a string - */ -int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags) -{ - const char *s; - int scan; - - /* Be paranoid... */ - if (preg == NULL || preg->program == NULL || string == NULL) { - return REG_ERR_NULL_ARGUMENT; - } - - /* Check validity of program. */ - if (*preg->program != REG_MAGIC) { - return REG_ERR_CORRUPTED; - } - -#ifdef DEBUG - fprintf(stderr, "regexec: %s\n", string); - regdump(preg); -#endif - - preg->eflags = eflags; - preg->pmatch = pmatch; - preg->nmatch = nmatch; - preg->start = string; /* All offsets are computed from here */ - - /* Must clear out the embedded repeat counts */ - for (scan = OPERAND(1); scan != 0; scan = regnext(preg, scan)) { - switch (OP(preg, scan)) { - case REP: - case REPMIN: - case REPX: - case REPXMIN: - preg->program[scan + 4] = 0; - break; - } - } - - /* If there is a "must appear" string, look for it. */ - if (preg->regmust != 0) { - s = string; - while ((s = str_find(s, preg->program[preg->regmust], preg->cflags & REG_ICASE)) != NULL) { - if (prefix_cmp(preg->program + preg->regmust, preg->regmlen, s, preg->cflags & REG_ICASE) >= 0) { - break; - } - s++; - } - if (s == NULL) /* Not present. */ - return REG_NOMATCH; - } - - /* Mark beginning of line for ^ . */ - preg->regbol = string; - - /* Simplest case: anchored match need be tried only once (maybe per line). */ - if (preg->reganch) { - if (eflags & REG_NOTBOL) { - /* This is an anchored search, but not an BOL, so possibly skip to the next line */ - goto nextline; - } - while (1) { - int ret = regtry(preg, string); - if (ret) { - return REG_NOERROR; - } - if (*string) { -nextline: - if (preg->cflags & REG_NEWLINE) { - /* Try the next anchor? */ - string = strchr(string, '\n'); - if (string) { - preg->regbol = ++string; - continue; - } - } - } - return REG_NOMATCH; - } - } - - /* Messy cases: unanchored match. */ - s = string; - if (preg->regstart != '\0') { - /* We know what char it must start with. */ - while ((s = str_find(s, preg->regstart, preg->cflags & REG_ICASE)) != NULL) { - if (regtry(preg, s)) - return REG_NOERROR; - s++; - } - } - else - /* We don't -- general case. */ - while (1) { - if (regtry(preg, s)) - return REG_NOERROR; - if (*s == '\0') { - break; - } - s += utf8_charlen(*s); - } - - /* Failure. */ - return REG_NOMATCH; -} - -/* - - regtry - try match at specific point - */ - /* 0 failure, 1 success */ -static int regtry( regex_t *preg, const char *string ) -{ - int i; - - preg->reginput = string; - - for (i = 0; i < preg->nmatch; i++) { - preg->pmatch[i].rm_so = -1; - preg->pmatch[i].rm_eo = -1; - } - if (regmatch(preg, 1)) { - preg->pmatch[0].rm_so = string - preg->start; - preg->pmatch[0].rm_eo = preg->reginput - preg->start; - return(1); - } else - return(0); -} - -/** - * Returns bytes matched if 'pattern' is a prefix of 'string'. - * - * If 'nocase' is non-zero, does a case-insensitive match. - * - * Returns -1 on not found. - */ -static int prefix_cmp(const int *prog, int proglen, const char *string, int nocase) -{ - const char *s = string; - while (proglen && *s) { - int ch; - int n = reg_utf8_tounicode_case(s, &ch, nocase); - if (ch != *prog) { - return -1; - } - prog++; - s += n; - proglen--; - } - if (proglen == 0) { - return s - string; - } - return -1; -} - -/** - * Searchs for 'c' in the range 'range'. - * - * Returns 1 if found, or 0 if not. - */ -static int reg_range_find(const int *range, int c) -{ - while (*range) { - /*printf("Checking %d in range [%d,%d]\n", c, range[1], (range[0] + range[1] - 1));*/ - if (c >= range[1] && c <= (range[0] + range[1] - 1)) { - return 1; - } - range += 2; - } - return 0; -} - -/** - * Search for the character 'c' in the utf-8 string 'string'. - * - * If 'nocase' is set, the 'string' is assumed to be uppercase - * and 'c' is converted to uppercase before matching. - * - * Returns the byte position in the string where the 'c' was found, or - * NULL if not found. - */ -static const char *str_find(const char *string, int c, int nocase) -{ - if (nocase) { - /* The "string" should already be converted to uppercase */ - c = utf8_upper(c); - } - while (*string) { - int ch; - int n = reg_utf8_tounicode_case(string, &ch, nocase); - if (c == ch) { - return string; - } - string += n; - } - return NULL; -} - -/** - * Returns true if 'ch' is an end-of-line char. - * - * In REG_NEWLINE mode, \n is considered EOL in - * addition to \0 - */ -static int reg_iseol(regex_t *preg, int ch) -{ - if (preg->cflags & REG_NEWLINE) { - return ch == '\0' || ch == '\n'; - } - else { - return ch == '\0'; - } -} - -static int regmatchsimplerepeat(regex_t *preg, int scan, int matchmin) -{ - int nextch = '\0'; - const char *save; - int no; - int c; - - int max = preg->program[scan + 2]; - int min = preg->program[scan + 3]; - int next = regnext(preg, scan); - - /* - * Lookahead to avoid useless match attempts - * when we know what character comes next. - */ - if (OP(preg, next) == EXACTLY) { - nextch = preg->program[OPERAND(next)]; - } - save = preg->reginput; - no = regrepeat(preg, scan + 5, max); - if (no < min) { - return 0; - } - if (matchmin) { - /* from min up to no */ - max = no; - no = min; - } - /* else from no down to min */ - while (1) { - if (matchmin) { - if (no > max) { - break; - } - } - else { - if (no < min) { - break; - } - } - preg->reginput = save + utf8_index(save, no); - reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE)); - /* If it could work, try it. */ - if (reg_iseol(preg, nextch) || c == nextch) { - if (regmatch(preg, next)) { - return(1); - } - } - if (matchmin) { - /* Couldn't or didn't, add one more */ - no++; - } - else { - /* Couldn't or didn't -- back up. */ - no--; - } - } - return(0); -} - -static int regmatchrepeat(regex_t *preg, int scan, int matchmin) -{ - int *scanpt = preg->program + scan; - - int max = scanpt[2]; - int min = scanpt[3]; - - /* Have we reached min? */ - if (scanpt[4] < min) { - /* No, so get another one */ - scanpt[4]++; - if (regmatch(preg, scan + 5)) { - return 1; - } - scanpt[4]--; - return 0; - } - if (scanpt[4] > max) { - return 0; - } - - if (matchmin) { - /* minimal, so try other branch first */ - if (regmatch(preg, regnext(preg, scan))) { - return 1; - } - /* No, so try one more */ - scanpt[4]++; - if (regmatch(preg, scan + 5)) { - return 1; - } - scanpt[4]--; - return 0; - } - /* maximal, so try this branch again */ - if (scanpt[4] < max) { - scanpt[4]++; - if (regmatch(preg, scan + 5)) { - return 1; - } - scanpt[4]--; - } - /* At this point we are at max with no match. Try the other branch */ - return regmatch(preg, regnext(preg, scan)); -} - -/* - - regmatch - main matching routine - * - * Conceptually the strategy is simple: check to see whether the current - * node matches, call self recursively to see whether the rest matches, - * and then act accordingly. In practice we make some effort to avoid - * recursion, in particular by going through "ordinary" nodes (that don't - * need to know whether the rest of the match failed) by a loop instead of - * by recursion. - */ -/* 0 failure, 1 success */ -static int regmatch(regex_t *preg, int prog) -{ - int scan; /* Current node. */ - int next; /* Next node. */ - - scan = prog; - -#ifdef DEBUG - if (scan != 0 && regnarrate) - fprintf(stderr, "%s(\n", regprop(scan)); -#endif - while (scan != 0) { - int n; - int c; -#ifdef DEBUG - if (regnarrate) { - //fprintf(stderr, "%s...\n", regprop(scan)); - fprintf(stderr, "%3d: %s...\n", scan, regprop(OP(preg, scan))); /* Where, what. */ - } -#endif - next = regnext(preg, scan); - n = reg_utf8_tounicode_case(preg->reginput, &c, (preg->cflags & REG_ICASE)); - - switch (OP(preg, scan)) { - case BOL: - if (preg->reginput != preg->regbol) - return(0); - break; - case EOL: - if (!reg_iseol(preg, c)) { - return(0); - } - break; - case WORDA: - /* Must be looking at a letter, digit, or _ */ - if ((!isalnum(UCHAR(c))) && c != '_') - return(0); - /* Prev must be BOL or nonword */ - if (preg->reginput > preg->regbol && - (isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_')) - return(0); - break; - case WORDZ: - /* Can't match at BOL */ - if (preg->reginput > preg->regbol) { - /* Current must be EOL or nonword */ - if (reg_iseol(preg, c) || !isalnum(UCHAR(c)) || c != '_') { - c = preg->reginput[-1]; - /* Previous must be word */ - if (isalnum(UCHAR(c)) || c == '_') { - break; - } - } - } - /* No */ - return(0); - - case ANY: - if (reg_iseol(preg, c)) - return 0; - preg->reginput += n; - break; - case EXACTLY: { - int opnd; - int len; - int slen; - - opnd = OPERAND(scan); - len = str_int_len(preg->program + opnd); - - slen = prefix_cmp(preg->program + opnd, len, preg->reginput, preg->cflags & REG_ICASE); - if (slen < 0) { - return(0); - } - preg->reginput += slen; - } - break; - case ANYOF: - if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) == 0) { - return(0); - } - preg->reginput += n; - break; - case ANYBUT: - if (reg_iseol(preg, c) || reg_range_find(preg->program + OPERAND(scan), c) != 0) { - return(0); - } - preg->reginput += n; - break; - case NOTHING: - break; - case BACK: - break; - case BRANCH: { - const char *save; - - if (OP(preg, next) != BRANCH) /* No choice. */ - next = OPERAND(scan); /* Avoid recursion. */ - else { - do { - save = preg->reginput; - if (regmatch(preg, OPERAND(scan))) { - return(1); - } - preg->reginput = save; - scan = regnext(preg, scan); - } while (scan != 0 && OP(preg, scan) == BRANCH); - return(0); - /* NOTREACHED */ - } - } - break; - case REP: - case REPMIN: - return regmatchsimplerepeat(preg, scan, OP(preg, scan) == REPMIN); - - case REPX: - case REPXMIN: - return regmatchrepeat(preg, scan, OP(preg, scan) == REPXMIN); - - case END: - return(1); /* Success! */ - break; - default: - if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) { - const char *save; - - save = preg->reginput; - - if (regmatch(preg, next)) { - int no; - /* - * Don't set startp if some later - * invocation of the same parentheses - * already has. - */ - if (OP(preg, scan) < CLOSE) { - no = OP(preg, scan) - OPEN; - if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) { - preg->pmatch[no].rm_so = save - preg->start; - } - } - else { - no = OP(preg, scan) - CLOSE; - if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) { - preg->pmatch[no].rm_eo = save - preg->start; - } - } - return(1); - } else - return(0); - } - return REG_ERR_INTERNAL; - } - - scan = next; - } - - /* - * We get here only if there's trouble -- normally "case END" is - * the terminating point. - */ - return REG_ERR_INTERNAL; -} - -/* - - regrepeat - repeatedly match something simple, report how many - */ -static int regrepeat(regex_t *preg, int p, int max) -{ - int count = 0; - const char *scan; - int opnd; - int ch; - int n; - - scan = preg->reginput; - opnd = OPERAND(p); - switch (OP(preg, p)) { - case ANY: - /* No need to handle utf8 specially here */ - while (!reg_iseol(preg, *scan) && count < max) { - count++; - scan++; - } - break; - case EXACTLY: - while (count < max) { - n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); - if (preg->program[opnd] != ch) { - break; - } - count++; - scan += n; - } - break; - case ANYOF: - while (count < max) { - n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); - if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) == 0) { - break; - } - count++; - scan += n; - } - break; - case ANYBUT: - while (count < max) { - n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE); - if (reg_iseol(preg, ch) || reg_range_find(preg->program + opnd, ch) != 0) { - break; - } - count++; - scan += n; - } - break; - default: /* Oh dear. Called inappropriately. */ - preg->err = REG_ERR_INTERNAL; - count = 0; /* Best compromise. */ - break; - } - preg->reginput = scan; - - return(count); -} - -/* - - regnext - dig the "next" pointer out of a node - */ -static int regnext(regex_t *preg, int p ) -{ - int offset; - - offset = NEXT(preg, p); - - if (offset == 0) - return 0; - - if (OP(preg, p) == BACK) - return(p-offset); - else - return(p+offset); -} - -#ifdef DEBUG - -/* - - regdump - dump a regexp onto stdout in vaguely comprehensible form - */ -static void regdump(regex_t *preg) -{ - int s; - int op = EXACTLY; /* Arbitrary non-END op. */ - int next; - char buf[4]; - - int i; - for (i = 1; i < preg->p; i++) { - printf("%02x ", preg->program[i]); - if (i % 16 == 15) { - printf("\n"); - } - } - printf("\n"); - - s = 1; - while (op != END && s < preg->p) { /* While that wasn't END last time... */ - op = OP(preg, s); - printf("%3d: %s", s, regprop(op)); /* Where, what. */ - next = regnext(preg, s); - if (next == 0) /* Next ptr. */ - printf("(0)"); - else - printf("(%d)", next); - s += 2; - if (op == REP || op == REPMIN || op == REPX || op == REPXMIN) { - int max = preg->program[s]; - int min = preg->program[s + 1]; - if (max == 65535) { - printf("{%d,*}", min); - } - else { - printf("{%d,%d}", min, max); - } - printf(" %d", preg->program[s + 2]); - s += 3; - } - else if (op == ANYOF || op == ANYBUT) { - /* set of ranges */ - - while (preg->program[s]) { - int len = preg->program[s++]; - int first = preg->program[s++]; - buf[utf8_fromunicode(buf, first)] = 0; - printf("%s", buf); - if (len > 1) { - buf[utf8_fromunicode(buf, first + len - 1)] = 0; - printf("-%s", buf); - } - } - s++; - } - else if (op == EXACTLY) { - /* Literal string, where present. */ - - while (preg->program[s]) { - buf[utf8_fromunicode(buf, preg->program[s])] = 0; - printf("%s", buf); - s++; - } - s++; - } - putchar('\n'); - } - - if (op == END) { - /* Header fields of interest. */ - if (preg->regstart) { - buf[utf8_fromunicode(buf, preg->regstart)] = 0; - printf("start '%s' ", buf); - } - if (preg->reganch) - printf("anchored "); - if (preg->regmust != 0) { - int i; - printf("must have:"); - for (i = 0; i < preg->regmlen; i++) { - putchar(preg->program[preg->regmust + i]); - } - putchar('\n'); - } - } - printf("\n"); -} - -/* - - regprop - printable representation of opcode - */ -static const char *regprop( int op ) -{ - static char buf[50]; - - switch (op) { - case BOL: - return "BOL"; - case EOL: - return "EOL"; - case ANY: - return "ANY"; - case ANYOF: - return "ANYOF"; - case ANYBUT: - return "ANYBUT"; - case BRANCH: - return "BRANCH"; - case EXACTLY: - return "EXACTLY"; - case NOTHING: - return "NOTHING"; - case BACK: - return "BACK"; - case END: - return "END"; - case REP: - return "REP"; - case REPMIN: - return "REPMIN"; - case REPX: - return "REPX"; - case REPXMIN: - return "REPXMIN"; - case WORDA: - return "WORDA"; - case WORDZ: - return "WORDZ"; - default: - if (op >= OPEN && op < CLOSE) { - snprintf(buf, sizeof(buf), "OPEN%d", op-OPEN); - } - else if (op >= CLOSE && op < CLOSE_END) { - snprintf(buf, sizeof(buf), "CLOSE%d", op-CLOSE); - } - else { - snprintf(buf, sizeof(buf), "?%d?\n", op); - } - return(buf); - } -} -#endif - -size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) -{ - static const char *error_strings[] = { - "success", - "no match", - "bad pattern", - "null argument", - "unknown error", - "too big", - "out of memory", - "too many ()", - "parentheses () not balanced", - "braces {} not balanced", - "invalid repetition count(s)", - "extra characters", - "*+ of empty atom", - "nested count", - "internal error", - "count follows nothing", - "trailing backslash", - "corrupted program", - "contains null char", - }; - const char *err; - - if (errcode < 0 || errcode >= REG_ERR_NUM) { - err = "Bad error code"; - } - else { - err = error_strings[errcode]; - } - - return snprintf(errbuf, errbuf_size, "%s", err); -} - -void regfree(regex_t *preg) -{ - free(preg->program); -} - -#endif - -/* Jimsh - An interactive shell for Jim - * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org> - * Copyright 2009 Steve Bennett <steveb@workware.net.au> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * A copy of the license is also included in the source distribution - * of Jim, as a TXT file name called LICENSE. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ #include <stdio.h> #include <stdlib.h> #include <string.h> -/* From initjimsh.tcl */ + extern int Jim_initjimshInit(Jim_Interp *interp); static void JimSetArgv(Jim_Interp *interp, int argc, char *const argv[]) { int n; Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); - /* Populate argv global var */ + for (n = 0; n < argc; n++) { Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1); Jim_ListAppendElement(interp, listObj, obj); } @@ -23751,15 +20658,15 @@ if (argc > 1 && strcmp(argv[1], "--version") == 0) { printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100); return 0; } - /* Create and initialize the interpreter */ + interp = Jim_CreateInterp(); Jim_RegisterCoreCommands(interp); - /* Register static extensions */ + if (Jim_InitStaticExtensions(interp) != JIM_OK) { Jim_MakeErrorMessage(interp); fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp))); } @@ -23805,5 +20712,6 @@ retcode = 0; } Jim_FreeInterp(interp); return retcode; } +#endif Index: autosetup/system.tcl ================================================================== --- autosetup/system.tcl +++ autosetup/system.tcl @@ -226,25 +226,30 @@ define srcdir $autosetup(srcdir) # Allow this to come from the environment define top_srcdir [get-env top_srcdir [get-define srcdir]] # autoconf supports all of these -define exec_prefix [opt-val exec-prefix [get-env exec-prefix \${prefix}]] +set exec_prefix [opt-val exec-prefix $prefix] +define exec_prefix $exec_prefix +foreach {name defpath} { + bindir /bin + sbindir /sbin + libexecdir /libexec + libdir /lib +} { + define $name [opt-val $name $exec_prefix$defpath] +} foreach {name defpath} { - bindir \${exec_prefix}/bin - sbindir \${exec_prefix}/sbin - libexecdir \${exec_prefix}/libexec - libdir \${exec_prefix}/lib - datadir \${prefix}/share - sysconfdir \${prefix}/etc - sharedstatedir \${prefix}/com - localstatedir \${prefix}/var - infodir \${prefix}/share/info - mandir \${prefix}/share/man - includedir \${prefix}/include + datadir /share + sysconfdir /etc + sharedstatedir /com + localstatedir /var + infodir /share/info + mandir /share/man + includedir /include } { - define $name [opt-val $name [get-env $name $defpath]] + define $name [opt-val $name $prefix$defpath] } define SHELL [get-env SHELL [find-an-executable sh bash ksh]] # Windows vs. non-Windows Index: autosetup/test-tclsh ================================================================== --- autosetup/test-tclsh +++ autosetup/test-tclsh @@ -3,20 +3,18 @@ # an interpreter for a different arch. # Outputs the full path to the interpreter if {[catch {info version} version] == 0} { # This is Jim Tcl - if {$version >= 0.70} { + if {$version >= 0.72} { # Ensure that regexp works regexp (a.*?) a - - # Older versions of jimsh may return a relative path for [info nameofexecutable] - puts [file join [pwd] [info nameofexecutable]] + puts [info nameofexecutable] exit 0 } } elseif {[catch {info tclversion} version] == 0} { if {$version >= 8.5 && ![string match 8.5a* [info patchlevel]]} { puts [info nameofexecutable] exit 0 } } exit 1 ADDED src/jim-config.h Index: src/jim-config.h ================================================================== --- src/jim-config.h +++ src/jim-config.h @@ -0,0 +1,5 @@ +#ifndef _JIM_CONFIG_H +#define _JIM_CONFIG_H +/*#undef HAVE_LONG_LONG*/ +/*#undef JIM_UTF8*/ +#endif ADDED src/jim-win32compat.h Index: src/jim-win32compat.h ================================================================== --- src/jim-win32compat.h +++ src/jim-win32compat.h @@ -0,0 +1,69 @@ +#ifndef JIM_WIN32COMPAT_H +#define JIM_WIN32COMPAT_H + +/* Compatibility for Windows (mingw and msvc, not cygwin */ + +/* Note that at this point we don't yet have access to jimautoconf.h */ +#if defined(_WIN32) || defined(WIN32) + +#define HAVE_DLOPEN +void *dlopen(const char *path, int mode); +int dlclose(void *handle); +void *dlsym(void *handle, const char *symbol); +char *dlerror(void); + +#ifdef _MSC_VER +/* These are msvc vs gcc */ + +#if _MSC_VER >= 1000 + #pragma warning(disable:4146) +#endif + +#include <limits.h> +#define jim_wide _int64 +#ifndef LLONG_MAX + #define LLONG_MAX 9223372036854775807I64 +#endif +#ifndef LLONG_MIN + #define LLONG_MIN (-LLONG_MAX - 1I64) +#endif +#define JIM_WIDE_MIN LLONG_MIN +#define JIM_WIDE_MAX LLONG_MAX +#define JIM_WIDE_MODIFIER "I64d" +#define strcasecmp _stricmp +#define strtoull _strtoui64 +#define snprintf _snprintf + +#include <io.h> + +#ifndef NO_TIMEVAL +struct timeval { + long tv_sec; + long tv_usec; +}; +#endif + +int gettimeofday(struct timeval *tv, void *unused); + +#define HAVE_OPENDIR +#ifndef NO_DIRENT +struct dirent { + char *d_name; +}; + +typedef struct DIR { + long handle; /* -1 for failed rewind */ + struct _finddata_t info; + struct dirent result; /* d_name null iff first time */ + char *name; /* null-terminated char string */ +} DIR; + +DIR *opendir(const char *name); +int closedir(DIR *dir); +struct dirent *readdir(DIR *dir); +#endif +#endif /* _MSC_VER */ + +#endif /* WIN32 */ + +#endif ADDED src/jim.h Index: src/jim.h ================================================================== --- src/jim.h +++ src/jim.h @@ -0,0 +1,917 @@ +/* Jim - A small embeddable Tcl interpreter + * + * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org> + * Copyright 2005 Clemens Hintze <c.hintze@gmx.net> + * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net> + * Copyright 2008 oharboe - �yvind Harboe - oyvind.harboe@zylin.com + * Copyright 2008 Andrew Lunn <andrew@lunn.ch> + * Copyright 2008 Duane Ellis <openocd@duaneellis.com> + * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + * + *--- Inline Header File Documentation --- + * [By Duane Ellis, openocd@duaneellis.com, 8/18/8] + * + * Belief is "Jim" would greatly benifit if Jim Internals where + * documented in some way - form whatever, and perhaps - the package: + * 'doxygen' is the correct approach to do that. + * + * Details, see: http://www.stack.nl/~dimitri/doxygen/ + * + * To that end please follow these guide lines: + * + * (A) Document the PUBLIC api in the .H file. + * + * (B) Document JIM Internals, in the .C file. + * + * (C) Remember JIM is embedded in other packages, to that end do + * not assume that your way of documenting is the right way, Jim's + * public documentation should be agnostic, such that it is some + * what agreeable with the "package" that is embedding JIM inside + * of it's own doxygen documentation. + * + * (D) Use minimal Doxygen tags. + * + * This will be an "ongoing work in progress" for some time. + **/ + +#ifndef __JIM__H +#define __JIM__H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <time.h> +#include <limits.h> +#include <stdio.h> /* for the FILE typedef definition */ +#include <stdlib.h> /* In order to export the Jim_Free() macro */ +#include <stdarg.h> /* In order to get type va_list */ + +/* ----------------------------------------------------------------------------- + * System configuration + * autoconf (configure) will set these + * ---------------------------------------------------------------------------*/ +#include <jim-win32compat.h> + +#ifndef HAVE_NO_AUTOCONF +#include <jim-config.h> +#endif + +/* ----------------------------------------------------------------------------- + * Compiler specific fixes. + * ---------------------------------------------------------------------------*/ + +/* Long Long type and related issues */ +#ifndef jim_wide +# ifdef HAVE_LONG_LONG +# define jim_wide long long +# ifndef LLONG_MAX +# define LLONG_MAX 9223372036854775807LL +# endif +# ifndef LLONG_MIN +# define LLONG_MIN (-LLONG_MAX - 1LL) +# endif +# define JIM_WIDE_MIN LLONG_MIN +# define JIM_WIDE_MAX LLONG_MAX +# else +# define jim_wide long +# define JIM_WIDE_MIN LONG_MIN +# define JIM_WIDE_MAX LONG_MAX +# endif + +/* ----------------------------------------------------------------------------- + * LIBC specific fixes + * ---------------------------------------------------------------------------*/ + +# ifdef HAVE_LONG_LONG +# define JIM_WIDE_MODIFIER "lld" +# else +# define JIM_WIDE_MODIFIER "ld" +# define strtoull strtoul +# endif +#endif + +#define UCHAR(c) ((unsigned char)(c)) + +/* ----------------------------------------------------------------------------- + * Exported defines + * ---------------------------------------------------------------------------*/ + +/* Jim version numbering: every version of jim is marked with a + * successive integer number. This is version 0. The first + * stable version will be 1, then 2, 3, and so on. */ +#define JIM_VERSION 73 + +#define JIM_OK 0 +#define JIM_ERR 1 +#define JIM_RETURN 2 +#define JIM_BREAK 3 +#define JIM_CONTINUE 4 +#define JIM_SIGNAL 5 +#define JIM_EXIT 6 +/* The following are internal codes and should never been seen/used */ +#define JIM_EVAL 7 + +#define JIM_MAX_NESTING_DEPTH 1000 /* default max nesting depth */ + +/* Some function get an integer argument with flags to change + * the behaviour. */ +#define JIM_NONE 0 /* no flags set */ +#define JIM_ERRMSG 1 /* set an error message in the interpreter. */ + +#define JIM_UNSHARED 4 /* Flag to Jim_GetVariable() */ + +/* Flags for Jim_SubstObj() */ +#define JIM_SUBST_NOVAR 1 /* don't perform variables substitutions */ +#define JIM_SUBST_NOCMD 2 /* don't perform command substitutions */ +#define JIM_SUBST_NOESC 4 /* don't perform escapes substitutions */ +#define JIM_SUBST_FLAG 128 /* flag to indicate that this is a real substition object */ + +/* Unused arguments generate annoying warnings... */ +#define JIM_NOTUSED(V) ((void) V) + +/* Flags for Jim_GetEnum() */ +#define JIM_ENUM_ABBREV 2 /* Allow unambiguous abbreviation */ + +/* Flags used by API calls getting a 'nocase' argument. */ +#define JIM_CASESENS 0 /* case sensitive */ +#define JIM_NOCASE 1 /* no case */ + +/* Filesystem related */ +#define JIM_PATH_LEN 1024 + +/* Newline, some embedded system may need -DJIM_CRLF */ +#ifdef JIM_CRLF +#define JIM_NL "\r\n" +#else +#define JIM_NL "\n" +#endif + +#define JIM_LIBPATH "auto_path" +#define JIM_INTERACTIVE "tcl_interactive" + +/* ----------------------------------------------------------------------------- + * Stack + * ---------------------------------------------------------------------------*/ + +typedef struct Jim_Stack { + int len; + int maxlen; + void **vector; +} Jim_Stack; + +/* ----------------------------------------------------------------------------- + * Hash table + * ---------------------------------------------------------------------------*/ + +typedef struct Jim_HashEntry { + const void *key; + union { + void *val; + int intval; + } u; + struct Jim_HashEntry *next; +} Jim_HashEntry; + +typedef struct Jim_HashTableType { + unsigned int (*hashFunction)(const void *key); + const void *(*keyDup)(void *privdata, const void *key); + void *(*valDup)(void *privdata, const void *obj); + int (*keyCompare)(void *privdata, const void *key1, const void *key2); + void (*keyDestructor)(void *privdata, const void *key); + void (*valDestructor)(void *privdata, void *obj); +} Jim_HashTableType; + +typedef struct Jim_HashTable { + Jim_HashEntry **table; + const Jim_HashTableType *type; + unsigned int size; + unsigned int sizemask; + unsigned int used; + unsigned int collisions; + void *privdata; +} Jim_HashTable; + +typedef struct Jim_HashTableIterator { + Jim_HashTable *ht; + int index; + Jim_HashEntry *entry, *nextEntry; +} Jim_HashTableIterator; + +/* This is the initial size of every hash table */ +#define JIM_HT_INITIAL_SIZE 16 + +/* ------------------------------- Macros ------------------------------------*/ +#define Jim_FreeEntryVal(ht, entry) \ + if ((ht)->type->valDestructor) \ + (ht)->type->valDestructor((ht)->privdata, (entry)->u.val) + +#define Jim_SetHashVal(ht, entry, _val_) do { \ + if ((ht)->type->valDup) \ + entry->u.val = (ht)->type->valDup((ht)->privdata, _val_); \ + else \ + entry->u.val = (_val_); \ +} while(0) + +#define Jim_FreeEntryKey(ht, entry) \ + if ((ht)->type->keyDestructor) \ + (ht)->type->keyDestructor((ht)->privdata, (entry)->key) + +#define Jim_SetHashKey(ht, entry, _key_) do { \ + if ((ht)->type->keyDup) \ + entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ + else \ + entry->key = (_key_); \ +} while(0) + +#define Jim_CompareHashKeys(ht, key1, key2) \ + (((ht)->type->keyCompare) ? \ + (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ + (key1) == (key2)) + +#define Jim_HashKey(ht, key) (ht)->type->hashFunction(key) + +#define Jim_GetHashEntryKey(he) ((he)->key) +#define Jim_GetHashEntryVal(he) ((he)->val) +#define Jim_GetHashTableCollisions(ht) ((ht)->collisions) +#define Jim_GetHashTableSize(ht) ((ht)->size) +#define Jim_GetHashTableUsed(ht) ((ht)->used) + +/* ----------------------------------------------------------------------------- + * Jim_Obj structure + * ---------------------------------------------------------------------------*/ + +/* ----------------------------------------------------------------------------- + * Jim object. This is mostly the same as Tcl_Obj itself, + * with the addition of the 'prev' and 'next' pointers. + * In Jim all the objects are stored into a linked list for GC purposes, + * so that it's possible to access every object living in a given interpreter + * sequentially. When an object is freed, it's moved into a different + * linked list, used as object pool. + * + * The refcount of a freed object is always -1. + * ---------------------------------------------------------------------------*/ +typedef struct Jim_Obj { + int refCount; /* reference count */ + char *bytes; /* string representation buffer. NULL = no string repr. */ + int length; /* number of bytes in 'bytes', not including the numterm. */ + const struct Jim_ObjType *typePtr; /* object type. */ + /* Internal representation union */ + union { + /* integer number type */ + jim_wide wideValue; + /* hashed object type value */ + int hashValue; + /* index type */ + int indexValue; + /* return code type */ + int returnCode; + /* double number type */ + double doubleValue; + /* Generic pointer */ + void *ptr; + /* Generic two pointers value */ + struct { + void *ptr1; + void *ptr2; + } twoPtrValue; + /* Variable object */ + struct { + unsigned jim_wide callFrameId; + struct Jim_Var *varPtr; + } varValue; + /* Command object */ + struct { + unsigned jim_wide procEpoch; + struct Jim_Cmd *cmdPtr; + } cmdValue; + /* List object */ + struct { + struct Jim_Obj **ele; /* Elements vector */ + int len; /* Length */ + int maxLen; /* Allocated 'ele' length */ + } listValue; + /* String type */ + struct { + int maxLength; + int charLength; /* utf-8 char length. -1 if unknown */ + } strValue; + /* Reference type */ + struct { + jim_wide id; + struct Jim_Reference *refPtr; + } refValue; + /* Source type */ + struct { + struct Jim_Obj *fileNameObj; + int lineNumber; + } sourceValue; + /* Dict substitution type */ + struct { + struct Jim_Obj *varNameObjPtr; + struct Jim_Obj *indexObjPtr; + } dictSubstValue; + /* tagged binary type */ + struct { + unsigned char *data; + size_t len; + } binaryValue; + /* Regular expression pattern */ + struct { + unsigned flags; + void *compre; /* really an allocated (regex_t *) */ + } regexpValue; + struct { + int line; + int argc; + } scriptLineValue; + } internalRep; + /* This are 8 or 16 bytes more for every object + * but this is required for efficient garbage collection + * of Jim references. */ + struct Jim_Obj *prevObjPtr; /* pointer to the prev object. */ + struct Jim_Obj *nextObjPtr; /* pointer to the next object. */ +} Jim_Obj; + +/* Jim_Obj related macros */ +#define Jim_IncrRefCount(objPtr) \ + ++(objPtr)->refCount +#define Jim_DecrRefCount(interp, objPtr) \ + if (--(objPtr)->refCount <= 0) Jim_FreeObj(interp, objPtr) +#define Jim_IsShared(objPtr) \ + ((objPtr)->refCount > 1) + +/* This macro is used when we allocate a new object using + * Jim_New...Obj(), but for some error we need to destroy it. + * Instead to use Jim_IncrRefCount() + Jim_DecrRefCount() we + * can just call Jim_FreeNewObj. To call Jim_Free directly + * seems too raw, the object handling may change and we want + * that Jim_FreeNewObj() can be called only against objects + * that are belived to have refcount == 0. */ +#define Jim_FreeNewObj Jim_FreeObj + +/* Free the internal representation of the object. */ +#define Jim_FreeIntRep(i,o) \ + if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \ + (o)->typePtr->freeIntRepProc(i, o) + +/* Get the internal representation pointer */ +#define Jim_GetIntRepPtr(o) (o)->internalRep.ptr + +/* Set the internal representation pointer */ +#define Jim_SetIntRepPtr(o, p) \ + (o)->internalRep.ptr = (p) + +/* The object type structure. + * There are four methods. + * + * - FreeIntRep is used to free the internal representation of the object. + * Can be NULL if there is nothing to free. + * - DupIntRep is used to duplicate the internal representation of the object. + * If NULL, when an object is duplicated, the internalRep union is + * directly copied from an object to another. + * Note that it's up to the caller to free the old internal repr of the + * object before to call the Dup method. + * - UpdateString is used to create the string from the internal repr. + * - setFromAny is used to convert the current object into one of this type. + */ + +struct Jim_Interp; + +typedef void (Jim_FreeInternalRepProc)(struct Jim_Interp *interp, + struct Jim_Obj *objPtr); +typedef void (Jim_DupInternalRepProc)(struct Jim_Interp *interp, + struct Jim_Obj *srcPtr, Jim_Obj *dupPtr); +typedef void (Jim_UpdateStringProc)(struct Jim_Obj *objPtr); + +typedef struct Jim_ObjType { + const char *name; /* The name of the type. */ + Jim_FreeInternalRepProc *freeIntRepProc; + Jim_DupInternalRepProc *dupIntRepProc; + Jim_UpdateStringProc *updateStringProc; + int flags; +} Jim_ObjType; + +/* Jim_ObjType flags */ +#define JIM_TYPE_NONE 0 /* No flags */ +#define JIM_TYPE_REFERENCES 1 /* The object may contain referneces. */ + +/* Starting from 1 << 20 flags are reserved for private uses of + * different calls. This way the same 'flags' argument may be used + * to pass both global flags and private flags. */ +#define JIM_PRIV_FLAG_SHIFT 20 + +/* ----------------------------------------------------------------------------- + * Call frame, vars, commands structures + * ---------------------------------------------------------------------------*/ + +/* Call frame */ +typedef struct Jim_CallFrame { + unsigned jim_wide id; /* Call Frame ID. Used for caching. */ + int level; /* Level of this call frame. 0 = global */ + struct Jim_HashTable vars; /* Where local vars are stored */ + struct Jim_HashTable *staticVars; /* pointer to procedure static vars */ + struct Jim_CallFrame *parentCallFrame; + Jim_Obj *const *argv; /* object vector of the current procedure call. */ + int argc; /* number of args of the current procedure call. */ + Jim_Obj *procArgsObjPtr; /* arglist object of the running procedure */ + Jim_Obj *procBodyObjPtr; /* body object of the running procedure */ + struct Jim_CallFrame *nextFramePtr; + Jim_Obj *fileNameObj; /* file and line of caller of this proc (if available) */ + int line; +} Jim_CallFrame; + +/* The var structure. It just holds the pointer of the referenced + * object. If linkFramePtr is not NULL the variable is a link + * to a variable of name store on objPtr living on the given callframe + * (this happens when the [global] or [upvar] command is used). + * The interp in order to always know how to free the Jim_Obj associated + * with a given variable because In Jim objects memory managment is + * bound to interpreters. */ +typedef struct Jim_Var { + Jim_Obj *objPtr; + struct Jim_CallFrame *linkFramePtr; +} Jim_Var; + +/* The cmd structure. */ +typedef int (*Jim_CmdProc)(struct Jim_Interp *interp, int argc, + Jim_Obj *const *argv); +typedef void (*Jim_DelCmdProc)(struct Jim_Interp *interp, void *privData); + + + +/* A command is implemented in C if funcPtr is != NULL, otherwise + * it's a Tcl procedure with the arglist and body represented by the + * two objects referenced by arglistObjPtr and bodyoObjPtr. */ +typedef struct Jim_Cmd { + int inUse; /* Reference count */ + int isproc; /* Is this a procedure? */ + union { + struct { + /* native (C) command */ + Jim_CmdProc cmdProc; /* The command implementation */ + Jim_DelCmdProc delProc; /* Called when the command is deleted if != NULL */ + void *privData; /* command-private data available via Jim_CmdPrivData() */ + } native; + struct { + /* Tcl procedure */ + Jim_Obj *argListObjPtr; + Jim_Obj *bodyObjPtr; + Jim_HashTable *staticVars; /* Static vars hash table. NULL if no statics. */ + struct Jim_Cmd *prevCmd; /* Previous command defn if proc created 'local' */ + int argListLen; /* Length of argListObjPtr */ + int reqArity; /* Number of required parameters */ + int optArity; /* Number of optional parameters */ + int argsPos; /* Position of 'args', if specified, or -1 */ + int upcall; /* True if proc is currently in upcall */ + struct Jim_ProcArg { + Jim_Obj *nameObjPtr; /* Name of this arg */ + Jim_Obj *defaultObjPtr; /* Default value, (or rename for $args) */ + } *arglist; + } proc; + } u; +} Jim_Cmd; + +/* Pseudo Random Number Generator State structure */ +typedef struct Jim_PrngState { + unsigned char sbox[256]; + unsigned int i, j; +} Jim_PrngState; + +/* ----------------------------------------------------------------------------- + * Jim interpreter structure. + * Fields similar to the real Tcl interpreter structure have the same names. + * ---------------------------------------------------------------------------*/ +typedef struct Jim_Interp { + Jim_Obj *result; /* object returned by the last command called. */ + int errorLine; /* Error line where an error occurred. */ + Jim_Obj *errorFileNameObj; /* Error file where an error occurred. */ + int addStackTrace; /* > 0 If a level should be added to the stack trace */ + int maxNestingDepth; /* Used for infinite loop detection. */ + int returnCode; /* Completion code to return on JIM_RETURN. */ + int returnLevel; /* Current level of 'return -level' */ + int exitCode; /* Code to return to the OS on JIM_EXIT. */ + long id; /* Hold unique id for various purposes */ + int signal_level; /* A nesting level of catch -signal */ + jim_wide sigmask; /* Bit mask of caught signals, or 0 if none */ + int (*signal_set_result)(struct Jim_Interp *interp, jim_wide sigmask); /* Set a result for the sigmask */ + Jim_CallFrame *framePtr; /* Pointer to the current call frame */ + Jim_CallFrame *topFramePtr; /* toplevel/global frame pointer. */ + struct Jim_HashTable commands; /* Commands hash table */ + unsigned jim_wide procEpoch; /* Incremented every time the result + of procedures names lookup caching + may no longer be valid. */ + unsigned jim_wide callFrameEpoch; /* Incremented every time a new + callframe is created. This id is used for the + 'ID' field contained in the Jim_CallFrame + structure. */ + int local; /* If 'local' is in effect, newly defined procs keep a reference to the old defn */ + Jim_Obj *liveList; /* Linked list of all the live objects. */ + Jim_Obj *freeList; /* Linked list of all the unused objects. */ + Jim_Obj *currentScriptObj; /* Script currently in execution. */ + Jim_Obj *emptyObj; /* Shared empty string object. */ + Jim_Obj *trueObj; /* Shared true int object. */ + Jim_Obj *falseObj; /* Shared false int object. */ + unsigned jim_wide referenceNextId; /* Next id for reference. */ + struct Jim_HashTable references; /* References hash table. */ + jim_wide lastCollectId; /* reference max Id of the last GC + execution. It's set to -1 while the collection + is running as sentinel to avoid to recursive + calls via the [collect] command inside + finalizers. */ + time_t lastCollectTime; /* unix time of the last GC execution */ + Jim_Obj *stackTrace; /* Stack trace object. */ + Jim_Obj *errorProc; /* Name of last procedure which returned an error */ + Jim_Obj *unknown; /* Unknown command cache */ + int unknown_called; /* The unknown command has been invoked */ + int errorFlag; /* Set if an error occurred during execution. */ + void *cmdPrivData; /* Used to pass the private data pointer to + a command. It is set to what the user specified + via Jim_CreateCommand(). */ + + struct Jim_CallFrame *freeFramesList; /* list of CallFrame structures. */ + struct Jim_HashTable assocData; /* per-interp storage for use by packages */ + Jim_PrngState *prngState; /* per interpreter Random Number Gen. state. */ + struct Jim_HashTable packages; /* Provided packages hash table */ + Jim_Stack *localProcs; /* procs to be destroyed on end of evaluation */ + Jim_Stack *loadHandles; /* handles of loaded modules [load] */ +} Jim_Interp; + +/* Currently provided as macro that performs the increment. + * At some point may be a real function doing more work. + * The proc epoch is used in order to know when a command lookup + * cached can no longer considered valid. */ +#define Jim_InterpIncrProcEpoch(i) (i)->procEpoch++ +#define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l)) +#define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval)) +/* Note: Using trueObj and falseObj here makes some things slower...*/ +#define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b) +#define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj) +#define Jim_GetResult(i) ((i)->result) +#define Jim_CmdPrivData(i) ((i)->cmdPrivData) +#define Jim_String(o) Jim_GetString((o), NULL) + +/* Note that 'o' is expanded only one time inside this macro, + * so it's safe to use side effects. */ +#define Jim_SetResult(i,o) do { \ + Jim_Obj *_resultObjPtr_ = (o); \ + Jim_IncrRefCount(_resultObjPtr_); \ + Jim_DecrRefCount(i,(i)->result); \ + (i)->result = _resultObjPtr_; \ +} while(0) + +/* Use this for filehandles, etc. which need a unique id */ +#define Jim_GetId(i) (++(i)->id) + +/* Reference structure. The interpreter pointer is held within privdata member in HashTable */ +#define JIM_REFERENCE_TAGLEN 7 /* The tag is fixed-length, because the reference + string representation must be fixed length. */ +typedef struct Jim_Reference { + Jim_Obj *objPtr; + Jim_Obj *finalizerCmdNamePtr; + char tag[JIM_REFERENCE_TAGLEN+1]; +} Jim_Reference; + +/* ----------------------------------------------------------------------------- + * Exported API prototypes. + * ---------------------------------------------------------------------------*/ + +/* Macros that are common for extensions and core. */ +#define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0) + +/* The core includes real prototypes, extensions instead + * include a global function pointer for every function exported. + * Once the extension calls Jim_InitExtension(), the global + * functon pointers are set to the value of the STUB table + * contained in the Jim_Interp structure. + * + * This makes Jim able to load extensions even if it is statically + * linked itself, and to load extensions compiled with different + * versions of Jim (as long as the API is still compatible.) */ + +/* Macros are common for core and extensions */ +#define Jim_FreeHashTableIterator(iter) Jim_Free(iter) + +#define JIM_EXPORT + +/* Memory allocation */ +JIM_EXPORT void *Jim_Alloc (int size); +JIM_EXPORT void *Jim_Realloc(void *ptr, int size); +JIM_EXPORT void Jim_Free (void *ptr); +JIM_EXPORT char * Jim_StrDup (const char *s); +JIM_EXPORT char *Jim_StrDupLen(const char *s, int l); + +/* environment */ +JIM_EXPORT char **Jim_GetEnviron(void); +JIM_EXPORT void Jim_SetEnviron(char **env); + +/* evaluation */ +JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script); +/* in C code, you can do this and get better error messages */ +/* Jim_EvalSource( interp, __FILE__, __LINE__ , "some tcl commands"); */ +JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script); +/* Backwards compatibility */ +#define Jim_Eval_Named(I, S, F, L) Jim_EvalSource((I), (F), (L), (S)) + +JIM_EXPORT int Jim_EvalGlobal(Jim_Interp *interp, const char *script); +JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename); +JIM_EXPORT int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename); +JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr); +JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc, + Jim_Obj *const *objv); +JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, + int objc, Jim_Obj *const *objv); +#define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov)) +JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr, + Jim_Obj **resObjPtrPtr, int flags); + +/* stack */ +JIM_EXPORT void Jim_InitStack(Jim_Stack *stack); +JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack); +JIM_EXPORT int Jim_StackLen(Jim_Stack *stack); +JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element); +JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack); +JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack); +JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr)); + +/* hash table */ +JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht, + const Jim_HashTableType *type, void *privdata); +JIM_EXPORT int Jim_ExpandHashTable (Jim_HashTable *ht, + unsigned int size); +JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key, + void *val); +JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht, + const void *key, void *val); +JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht, + const void *key); +JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht); +JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht, + const void *key); +JIM_EXPORT int Jim_ResizeHashTable (Jim_HashTable *ht); +JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator + (Jim_HashTable *ht); +JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry + (Jim_HashTableIterator *iter); + +/* objects */ +JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp); +JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr); +JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr); +JIM_EXPORT void Jim_InitStringRep (Jim_Obj *objPtr, const char *bytes, + int length); +JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp, + Jim_Obj *objPtr); +JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr, + int *lenPtr); +JIM_EXPORT int Jim_Length(Jim_Obj *objPtr); + +/* string object */ +JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp, + const char *s, int len); +JIM_EXPORT Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, + const char *s, int charlen); +JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp, + char *s, int len); +JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr, + const char *str, int len); +JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr, + Jim_Obj *appendObjPtr); +JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp, + Jim_Obj *objPtr, ...); +JIM_EXPORT int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr); +JIM_EXPORT int Jim_StringMatchObj (Jim_Interp *interp, Jim_Obj *patternObjPtr, + Jim_Obj *objPtr, int nocase); +JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp, + Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, + Jim_Obj *lastObjPtr); +JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp, + Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv); +JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr, + Jim_Obj *fmtObjPtr, int flags); +JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp, + Jim_Obj *objPtr, const char *str); +JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, + Jim_Obj *secondObjPtr, int nocase); +JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr); + +/* reference object */ +JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp, + Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr); +JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp, + Jim_Obj *objPtr); +JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr); +JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr); + +/* interpreter */ +JIM_EXPORT Jim_Interp * Jim_CreateInterp (void); +JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i); +JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp); +JIM_EXPORT const char *Jim_ReturnCode(int code); +JIM_EXPORT void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...); + +/* commands */ +JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp); +JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp, + const char *cmdName, Jim_CmdProc cmdProc, void *privData, + Jim_DelCmdProc delProc); +JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp, + const char *cmdName); +JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp, + const char *oldName, const char *newName); +JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp, + Jim_Obj *objPtr, int flags); +JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp, + Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr); +JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp, + const char *name, Jim_Obj *objPtr); +JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp, + const char *name, Jim_Obj *objPtr); +JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp, + const char *name, const char *val); +JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp, + Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr, + Jim_CallFrame *targetCallFrame); +JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp, + Jim_Obj *nameObjPtr, int flags); +JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp, + Jim_Obj *nameObjPtr, int flags); +JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp, + const char *name, int flags); +JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp, + const char *name, int flags); +JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp, + Jim_Obj *nameObjPtr, int flags); + +/* call frame */ +JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, + Jim_Obj *levelObjPtr); + +/* garbage collection */ +JIM_EXPORT int Jim_Collect (Jim_Interp *interp); +JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp); + +/* index object */ +JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr, + int *indexPtr); + +/* list object */ +JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp, + Jim_Obj *const *elements, int len); +JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp, + Jim_Obj *listPtr, int listindex, int objc, Jim_Obj *const *objVec); +JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp, + Jim_Obj *listPtr, Jim_Obj *objPtr); +JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp, + Jim_Obj *listPtr, Jim_Obj *appendListPtr); +JIM_EXPORT int Jim_ListLength (Jim_Interp *interp, Jim_Obj *objPtr); +JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt, + int listindex, Jim_Obj **objPtrPtr, int seterr); +JIM_EXPORT int Jim_SetListIndex (Jim_Interp *interp, + Jim_Obj *varNamePtr, Jim_Obj *const *indexv, int indexc, + Jim_Obj *newObjPtr); +JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc, + Jim_Obj *const *objv); + +/* dict object */ +JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp, + Jim_Obj *const *elements, int len); +JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr, + Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags); +JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp, + Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc, + Jim_Obj **objPtrPtr, int flags); +JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp, + Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc, + Jim_Obj *newObjPtr, int flags); +JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp, + Jim_Obj *dictPtr, Jim_Obj ***objPtrPtr, int *len); +JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, + Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr); +JIM_EXPORT int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj); +JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr); + +/* return code object */ +JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr, + int *intPtr); + +/* expression object */ +JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp, + Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr); +JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp, + Jim_Obj *exprObjPtr, int *boolPtr); + +/* integer object */ +JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr, + jim_wide *widePtr); +JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr, + long *longPtr); +#define Jim_NewWideObj Jim_NewIntObj +JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp, + jim_wide wideValue); + +/* double object */ +JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, + double *doublePtr); +JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, + double doubleValue); +JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue); + +/* shared strings */ +JIM_EXPORT const char * Jim_GetSharedString (Jim_Interp *interp, + const char *str); +JIM_EXPORT void Jim_ReleaseSharedString (Jim_Interp *interp, + const char *str); + +/* commands utilities */ +JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc, + Jim_Obj *const *argv, const char *msg); +JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr, + const char * const *tablePtr, int *indexPtr, const char *name, int flags); +JIM_EXPORT int Jim_ScriptIsComplete (const char *s, int len, + char *stateCharPtr); +/** + * Find a matching name in the array of the given length. + * + * NULL entries are ignored. + * + * Returns the matching index if found, or -1 if not. + */ +JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len); + +/* package utilities */ +typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data); +JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key); +JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key, + Jim_InterpDeleteProc *delProc, void *data); +JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key); + +/* Packages C API */ +/* jim-package.c */ +JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp, + const char *name, const char *ver, int flags); +JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp, + const char *name, int flags); + +/* error messages */ +JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp); + +/* interactive mode */ +JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp); + +/* Misc */ +JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp); +JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base); + +/* jim-load.c */ +JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName); +JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp); + +/* jim-aio.c */ +JIM_EXPORT FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command); + + +/* type inspection - avoid where possible */ +JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr); +JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr); + +#ifdef __cplusplus +} +#endif + +#endif /* __JIM__H */ + +/* + * Local Variables: *** + * c-basic-offset: 4 *** + * tab-width: 4 *** + * End: *** + */ Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -26,10 +26,12 @@ #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> /* atexit() */ #if INTERFACE +#include <jim.h> + #ifdef FOSSIL_ENABLE_JSON # include "cson_amalgamation.h" /* JSON API. Needed inside the INTERFACE block! */ # include "json_detail.h" #endif #ifdef FOSSIL_ENABLE_TCL @@ -127,11 +129,11 @@ int sslNotAvailable; /* SSL is not available. Do not redirect to https: */ Blob cgiIn; /* Input to an xfer www method */ int cgiOutput; /* Write error and status messages to CGI */ int xferPanic; /* Write error messages in XFER protocol */ int fullHttpReply; /* True for full HTTP reply. False for CGI reply */ - Th_Interp *interp; /* The TH1 interpreter */ + Jim_Interp *interp; /* The script interpreter */ FILE *httpIn; /* Accept HTTP input from here */ FILE *httpOut; /* Send HTTP output here */ int xlinkClusterOnly; /* Set when cloning. Only process clusters */ int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ int *aCommitFile; /* Array of files to be committed */ @@ -178,12 +180,12 @@ char zCsrfToken[12]; /* Value of the anti-CSRF token */ int okCsrf; /* Anti-CSRF token is present and valid */ int parseCnt[10]; /* Counts of artifacts parsed */ FILE *fDebug; /* Write debug information here, if the file exists */ - int thTrace; /* True to enable TH1 debugging output */ - Blob thLog; /* Text of the TH1 debugging output */ + int thTrace; /* True to enable script debugging output */ + Blob thLog; /* Text of the script debugging output */ int isHome; /* True if rendering the "home" page */ /* Storage for the aux() and/or option() SQL function arguments */ int nAux; /* Number of distinct aux() or option() values */ Index: src/main.mk ================================================================== --- src/main.mk +++ src/main.mk @@ -347,11 +347,11 @@ # link to the Tcl library. TCL_OBJ.0 = TCL_OBJ.1 = $(OBJDIR)/th_tcl.o TCL_OBJ. = $(TCL_OBJ.0) -EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) $(OBJDIR)/cson_amalgamation.o +EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/shell.o $(OBJDIR)/jimtcl.o $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) $(OBJDIR)/cson_amalgamation.o $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) # This rule prevents make from using its default rules to try build @@ -365,11 +365,11 @@ $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex $(OBJDIR)/mkindex $(TRANS_SRC) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h - $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h + $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/jim.h $(OBJDIR)/VERSION.h touch $(OBJDIR)/headers $(OBJDIR)/headers: Makefile $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h Makefile: $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate @@ -1040,12 +1040,15 @@ $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o +$(OBJDIR)/jimtcl.o: $(SRCDIR)/../autosetup/jimsh0.c + $(XTCC) -I$(SRCDIR) -DJIM_BOOTSTRAP_LIB_ONLY -c $(SRCDIR)/../autosetup/jimsh0.c -o $(OBJDIR)/jimtcl.o + $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE Index: src/makemake.tcl ================================================================== --- src/makemake.tcl +++ src/makemake.tcl @@ -223,12 +223,11 @@ TCL_OBJ. = $(TCL_OBJ.0) EXTRAOBJ = \ $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \ $(OBJDIR)/shell.o \ - $(OBJDIR)/th.o \ - $(OBJDIR)/th_lang.o \ + $(OBJDIR)/jimtcl.o \ $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) \ $(OBJDIR)/cson_amalgamation.o $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) @@ -248,11 +247,11 @@ foreach s [lsort $src] { append mhargs " \$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h" set extra_h($s) {} } append mhargs " \$(SRCDIR)/sqlite3.h" -append mhargs " \$(SRCDIR)/th.h" +append mhargs " \$(SRCDIR)/jim.h" #append mhargs " \$(SRCDIR)/cson_amalgamation.h" append mhargs " \$(OBJDIR)/VERSION.h" writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex" writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >$@" writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h" @@ -291,10 +290,13 @@ writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n" writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n" +writeln "\$(OBJDIR)/jimtcl.o:\t\$(SRCDIR)/../autosetup/jimsh0.c" +writeln "\t\$(XTCC) -I\$(SRCDIR) -DJIM_BOOTSTRAP_LIB_ONLY -c \$(SRCDIR)/../autosetup/jimsh0.c -o \$(OBJDIR)/jimtcl.o\n" + writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c" writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n" set opt {} writeln { @@ -571,11 +573,11 @@ foreach s [lsort $src] { append mhargs " \$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h" set extra_h($s) {} } append mhargs " \$(SRCDIR)/sqlite3.h" -append mhargs " \$(SRCDIR)/th.h" +append mhargs " \$(SRCDIR)/jim.h" append mhargs " \$(OBJDIR)/VERSION.h" writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex" writeln "\t\$(MKINDEX) \$(TRANS_SRC) >$@" writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h" writeln "\t\$(MAKEHEADERS) $mhargs" @@ -763,11 +765,12 @@ writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\t +makeheaders\$E " foreach s [lsort $src] { writeln -nonewline "${s}_.c:$s.h " } -writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h" +writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\jim.h VERSION.h" +writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\jim.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h" writeln "\t@copy /Y nul: headers" close $output_file # # End of the win/Makefile.dmc output @@ -919,11 +922,11 @@ writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\tmakeheaders\$E " foreach s [lsort $src] { writeln -nonewline "${s}_.c:$s.h " } -writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h" +writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\jim.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h" writeln "\t@copy /Y nul: headers" close $output_file # @@ -1084,12 +1087,12 @@ # extracting version info from manifest VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION version.exe ..\manifest.uuid ..\manifest ..\VERSION > $@ # generate the simplified headers -headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h - makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h +headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/jim.h VERSION.h + makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/jim.h VERSION.h echo Done >$@ # compile C sources with relevant options $(TRANSLATEDOBJ): %_.obj: %_.c %.h @@ -1099,11 +1102,11 @@ $(CC) $(CCFLAGS) $(SQLITEDEFINES) $(INCLUDE) "$<" -Fo"$@" $(SQLITESHELLOBJ): %.obj: $(SRCDIR)%.c $(CC) $(CCFLAGS) $(SQLITESHELLDEFINES) $(INCLUDE) "$<" -Fo"$@" -$(THOBJ): %.obj: $(SRCDIR)%.c $(SRCDIR)th.h +$(THOBJ): %.obj: $(SRCDIR)%.c $(SRCDIR)jim.h $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" $(ZLIBOBJ): %.obj: $(ZLIBSRCDIR)%.c $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" Index: src/name.c ================================================================== --- src/name.c +++ src/name.c @@ -426,11 +426,11 @@ db_prepare(&q, "SELECT type, datetime(mtime,'localtime')," " coalesce(euser,user), coalesce(ecomment,comment)" " FROM event WHERE objid=%d", rid); if( db_step(&q)==SQLITE_ROW ){ - const char *zType; + const char *zType = 0; switch( db_column_text(&q,0)[0] ){ case 'c': zType = "Check-in"; break; case 'w': zType = "Wiki-edit"; break; case 'e': zType = "Event"; break; case 't': zType = "Ticket-change"; break; DELETED src/th.c Index: src/th.c ================================================================== --- src/th.c +++ src/th.c @@ -1,2609 +0,0 @@ - -/* -** The implementation of the TH core. This file contains the parser, and -** the implementation of the interface in th.h. -*/ - -#include "th.h" -#include <string.h> -#include <assert.h> - -typedef struct Th_Command Th_Command; -typedef struct Th_Frame Th_Frame; -typedef struct Th_Variable Th_Variable; - -/* -** Interpreter structure. -*/ -struct Th_Interp { - Th_Vtab *pVtab; /* Copy of the argument passed to Th_CreateInterp() */ - char *zResult; /* Current interpreter result (Th_Malloc()ed) */ - int nResult; /* number of bytes in zResult */ - Th_Hash *paCmd; /* Table of registered commands */ - Th_Frame *pFrame; /* Current execution frame */ - int isListMode; /* True if thSplitList() should operate in "list" mode */ -}; - -/* -** Each TH command registered using Th_CreateCommand() is represented -** by an instance of the following structure stored in the Th_Interp.paCmd -** hash-table. -*/ -struct Th_Command { - int (*xProc)(Th_Interp *, void *, int, const char **, int *); - void *pContext; - void (*xDel)(Th_Interp *, void *); -}; - -/* -** Each stack frame (variable scope) is represented by an instance -** of this structure. Variable values set using the Th_SetVar command -** are stored in the Th_Frame.paVar hash table member of the associated -** stack frame object. -** -** When an interpreter is created, a single Th_Frame structure is also -** allocated - the global variable scope. Th_Interp.pFrame (the current -** interpreter frame) is initialised to point to this Th_Frame. It is -** not deleted for the lifetime of the interpreter (because the global -** frame never goes out of scope). -** -** New stack frames are created by the Th_InFrame() function. Before -** invoking its callback function, Th_InFrame() allocates a new Th_Frame -** structure with pCaller set to the current frame (Th_Interp.pFrame), -** and sets the current frame to the new frame object. After the callback -** has been invoked, the allocated Th_Frame is deleted and the value -** of the current frame pointer restored. -** -** By default, the Th_SetVar(), Th_UnsetVar() and Th_GetVar() functions -** access variable values in the current frame. If they need to access -** the global frame, they do so by traversing the pCaller pointer list. -** Likewise, the Th_LinkVar() function uses the pCaller pointers to -** link to variables located in the global or other stack frames. -*/ -struct Th_Frame { - Th_Hash *paVar; /* Variables defined in this scope */ - Th_Frame *pCaller; /* Calling frame */ -}; - -/* -** This structure represents a value assigned to a th1 variable. -** -** The Th_Frame.paVar hash table maps from variable name (a th1 string) -** to a pointer to an instance of the following structure. More than -** one hash table entry may map to a single structure if variable -** links have been created using Th_LinkVar(). The number of references -** is stored in Th_Variable.nRef. -** -** For scalar variables, Th_Variable.zData is never 0. Th_Variable.nData -** stores the number of bytes in the value pointed to by zData. -** -** For an array variable, Th_Variable.zData is 0 and pHash points to -** a hash table mapping between array key name (a th1 string) and -** a the pointer to the Th_Variable structure holding the scalar -** value. -*/ -struct Th_Variable { - int nRef; /* Number of references to this structure */ - int nData; /* Number of bytes at Th_Variable.zData */ - char *zData; /* Data for scalar variables */ - Th_Hash *pHash; /* Data for array variables */ -}; - -/* -** Hash table API: -*/ -#define TH_HASHSIZE 257 -struct Th_Hash { - Th_HashEntry *a[TH_HASHSIZE]; -}; - -static int thEvalLocal(Th_Interp *, const char *, int); -static int thSplitList(Th_Interp*, const char*, int, char***, int **, int*); - -static int thHexdigit(char c); -static int thEndOfLine(const char *, int); - -static int thPushFrame(Th_Interp*, Th_Frame*); -static void thPopFrame(Th_Interp*); - -static void thFreeVariable(Th_HashEntry*, void*); -static void thFreeCommand(Th_HashEntry*, void*); - -/* -** The following are used by both the expression and language parsers. -** Given that the start of the input string (z, n) is a language -** construct of the relevant type (a command enclosed in [], an escape -** sequence etc.), these functions determine the number of bytes -** of the input consumed by the construct. For example: -** -** int nByte; -** thNextCommand(interp, "[expr $a+1] $nIter", 18, &nByte); -** -** results in variable nByte being set to 11. Or, -** -** thNextVarname(interp, "$a+1", 4, &nByte); -** -** results in nByte being set to 2. -*/ -static int thNextCommand(Th_Interp*, const char *z, int n, int *pN); -static int thNextEscape (Th_Interp*, const char *z, int n, int *pN); -static int thNextVarname(Th_Interp*, const char *z, int n, int *pN); -static int thNextNumber (Th_Interp*, const char *z, int n, int *pN); -static int thNextSpace (Th_Interp*, const char *z, int n, int *pN); - -/* -** Given that the input string (z, n) contains a language construct of -** the relevant type (a command enclosed in [], an escape sequence -** like "\xFF" or a variable reference like "${varname}", perform -** substitution on the string and store the resulting string in -** the interpreter result. -*/ -static int thSubstCommand(Th_Interp*, const char *z, int n); -static int thSubstEscape (Th_Interp*, const char *z, int n); -static int thSubstVarname(Th_Interp*, const char *z, int n); - -/* -** Given that there is a th1 word located at the start of the input -** string (z, n), determine the length in bytes of that word. If the -** isCmd argument is non-zero, then an unescaped ";" byte not -** located inside of a block or quoted string is considered to mark -** the end of the word. -*/ -static int thNextWord(Th_Interp*, const char *z, int n, int *pN, int isCmd); - -/* -** Perform substitution on the word contained in the input string (z, n). -** Store the resulting string in the interpreter result. -*/ -static int thSubstWord(Th_Interp*, const char *z, int n); - -/* -** The Buffer structure and the thBufferXXX() functions are used to make -** memory allocation easier when building up a result. -*/ -struct Buffer { - char *zBuf; - int nBuf; - int nBufAlloc; -}; -typedef struct Buffer Buffer; -static int thBufferWrite(Th_Interp *interp, Buffer *, const char *, int); -static void thBufferInit(Buffer *); -static void thBufferFree(Th_Interp *interp, Buffer *); - -/* -** Append nAdd bytes of content copied from zAdd to the end of buffer -** pBuffer. If there is not enough space currently allocated, resize -** the allocation to make space. -*/ -static int thBufferWrite( - Th_Interp *interp, - Buffer *pBuffer, - const char *zAdd, - int nAdd -){ - int nReq; - - if( nAdd<0 ){ - nAdd = th_strlen(zAdd); - } - nReq = pBuffer->nBuf+nAdd+1; - - if( nReq>pBuffer->nBufAlloc ){ - char *zNew; - int nNew; - - nNew = nReq*2; - zNew = (char *)Th_Malloc(interp, nNew); - memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf); - Th_Free(interp, pBuffer->zBuf); - pBuffer->nBufAlloc = nNew; - pBuffer->zBuf = zNew; - } - - memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd); - pBuffer->nBuf += nAdd; - pBuffer->zBuf[pBuffer->nBuf] = '\0'; - - return TH_OK; -} -#define thBufferWrite(a,b,c,d) thBufferWrite(a,b,(const char *)c,d) - -/* -** Initialize the Buffer structure pointed to by pBuffer. -*/ -static void thBufferInit(Buffer *pBuffer){ - memset(pBuffer, 0, sizeof(Buffer)); -} - -/* -** Zero the buffer pointed to by pBuffer and free the associated memory -** allocation. -*/ -static void thBufferFree(Th_Interp *interp, Buffer *pBuffer){ - Th_Free(interp, pBuffer->zBuf); - thBufferInit(pBuffer); -} - -/* -** Assuming parameter c contains a hexadecimal digit character, -** return the corresponding value of that digit. If c is not -** a hexadecimal digit character, -1 is returned. -*/ -static int thHexdigit(char c){ - switch (c) { - case '0': return 0; - case '1': return 1; - case '2': return 2; - case '3': return 3; - case '4': return 4; - case '5': return 5; - case '6': return 6; - case '7': return 7; - case '8': return 8; - case '9': return 9; - case 'a': case 'A': return 10; - case 'b': case 'B': return 11; - case 'c': case 'C': return 12; - case 'd': case 'D': return 13; - case 'e': case 'E': return 14; - case 'f': case 'F': return 15; - } - return -1; -} - -/* -** Argument pEntry points to an entry in a stack frame hash table -** (Th_Frame.paVar). Decrement the refrerence count of the Th_Variable -** structure that the entry points to. Free the Th_Variable if its -** reference count reaches 0. -** -** Argument pContext is a pointer to the interpreter structure. -*/ -static void thFreeVariable(Th_HashEntry *pEntry, void *pContext){ - Th_Variable *pValue = (Th_Variable *)pEntry->pData; - pValue->nRef--; - assert( pValue->nRef>=0 ); - if( pValue->nRef==0 ){ - Th_Interp *interp = (Th_Interp *)pContext; - Th_Free(interp, pValue->zData); - if( pValue->pHash ){ - Th_HashIterate(interp, pValue->pHash, thFreeVariable, pContext); - Th_HashDelete(interp, pValue->pHash); - } - Th_Free(interp, pValue); - } -} - -/* -** Argument pEntry points to an entry in the command hash table -** (Th_Interp.paCmd). Delete the Th_Command structure that the -** entry points to. -** -** Argument pContext is a pointer to the interpreter structure. -*/ -static void thFreeCommand(Th_HashEntry *pEntry, void *pContext){ - Th_Command *pCommand = (Th_Command *)pEntry->pData; - if( pCommand->xDel ){ - pCommand->xDel((Th_Interp *)pContext, pCommand->pContext); - } - Th_Free((Th_Interp *)pContext, pEntry->pData); - pEntry->pData = 0; -} - -/* -** Push a new frame onto the stack. -*/ -static int thPushFrame(Th_Interp *interp, Th_Frame *pFrame){ - pFrame->paVar = Th_HashNew(interp); - pFrame->pCaller = interp->pFrame; - interp->pFrame = pFrame; - return TH_OK; -} - -/* -** Pop a frame off the top of the stack. -*/ -static void thPopFrame(Th_Interp *interp){ - Th_Frame *pFrame = interp->pFrame; - Th_HashIterate(interp, pFrame->paVar, thFreeVariable, (void *)interp); - Th_HashDelete(interp, pFrame->paVar); - interp->pFrame = pFrame->pCaller; -} - -/* -** The first part of the string (zInput,nInput) contains an escape -** sequence. Set *pnEscape to the number of bytes in the escape sequence. -** If there is a parse error, return TH_ERROR and set the interpreter -** result to an error message. Otherwise return TH_OK. -*/ -static int thNextEscape( - Th_Interp *interp, - const char *zInput, - int nInput, - int *pnEscape -){ - int i = 2; - - assert(nInput>0); - assert(zInput[0]=='\\'); - - if( nInput<=1 ){ - return TH_ERROR; - } - - switch( zInput[1] ){ - case 'x': i = 4; - } - - if( i>nInput ){ - return TH_ERROR; - } - *pnEscape = i; - return TH_OK; -} - -/* -** The first part of the string (zInput,nInput) contains a variable -** reference. Set *pnVarname to the number of bytes in the variable -** reference. If there is a parse error, return TH_ERROR and set the -** interpreter result to an error message. Otherwise return TH_OK. -*/ -int thNextVarname( - Th_Interp *interp, - const char *zInput, - int nInput, - int *pnVarname -){ - int i; - - assert(nInput>0); - assert(zInput[0]=='$'); - - if( nInput>0 && zInput[1]=='{' ){ - for(i=2; i<nInput && zInput[i]!='}'; i++); - if( i==nInput ){ - return TH_ERROR; - } - i++; - }else{ - i = 1; - if( nInput>2 && zInput[1]==':' && zInput[2]==':' ){ - i += 2; - } - for(; i<nInput; i++){ - if( zInput[i]=='(' ){ - for(i++; i<nInput; i++){ - if( zInput[i]==')' ) break; - if( zInput[i]=='\\' ) i++; - if( zInput[i]=='{' || zInput[i]=='[' || zInput[i]=='"' ){ - int nWord; - int rc = thNextWord(interp, &zInput[i], nInput-i, &nWord, 0); - if( rc!=TH_OK ){ - return rc; - } - i += nWord; - } - } - if( i>=nInput ){ - Th_ErrorMessage(interp, "Unmatched brackets:", zInput, nInput); - return TH_ERROR; - } - i++; - break; - } - if( !th_isalnum(zInput[i]) && zInput[i]!='_' ) break; - } - } - - *pnVarname = i; - return TH_OK; -} - -/* -** The first part of the string (zInput,nInput) contains a command -** enclosed in a "[]" block. Set *pnCommand to the number of bytes in -** the variable reference. If there is a parse error, return TH_ERROR -** and set the interpreter result to an error message. Otherwise return -** TH_OK. -*/ -int thNextCommand( - Th_Interp *interp, - const char *zInput, - int nInput, - int *pnCommand -){ - int nBrace = 0; - int nSquare = 0; - int i; - - assert(nInput>0); - assert( zInput[0]=='[' || zInput[0]=='{' ); - - for(i=0; i<nInput && (i==0 || nBrace>0 || nSquare>0); i++){ - switch( zInput[i] ){ - case '\\': i++; break; - case '{': nBrace++; break; - case '}': nBrace--; break; - case '[': nSquare++; break; - case ']': nSquare--; break; - } - } - if( nBrace || nSquare ){ - return TH_ERROR; - } - - *pnCommand = i; - - return TH_OK; -} - -/* -** Set *pnSpace to the number of whitespace bytes at the start of -** input string (zInput, nInput). Always return TH_OK. -*/ -int thNextSpace( - Th_Interp *interp, - const char *zInput, - int nInput, - int *pnSpace -){ - int i; - for(i=0; i<nInput && th_isspace(zInput[i]); i++); - *pnSpace = i; - return TH_OK; -} - -/* -** The first byte of the string (zInput,nInput) is not white-space. -** Set *pnWord to the number of bytes in the th1 word that starts -** with this byte. If a complete word cannot be parsed or some other -** error occurs, return TH_ERROR and set the interpreter result to -** an error message. Otherwise return TH_OK. -** -** If the isCmd argument is non-zero, then an unescaped ";" byte not -** located inside of a block or quoted string is considered to mark -** the end of the word. -*/ -static int thNextWord( - Th_Interp *interp, - const char *zInput, - int nInput, - int *pnWord, - int isCmd -){ - int iEnd = 0; - - assert( !th_isspace(zInput[0]) ); - - if( zInput[0]=='"' ){ - /* The word is terminated by the next unescaped '"' character. */ - iEnd++; - while( iEnd<nInput && zInput[iEnd]!='"' ){ - if( zInput[iEnd]=='\\' ){ - iEnd++; - } - iEnd++; - } - iEnd++; - }else{ - int nBrace = 0; - int nSq = 0; - while( iEnd<nInput && (nBrace>0 || nSq>0 || - (!th_isspace(zInput[iEnd]) && (!isCmd || zInput[iEnd]!=';')) - )){ - switch( zInput[iEnd] ){ - case '\\': iEnd++; break; - case '{': if( nSq==0 ) nBrace++; break; - case '}': if( nSq==0 ) nBrace--; break; - case '[': if( nBrace==0 ) nSq++; break; - case ']': if( nBrace==0 ) nSq--; break; - } - iEnd++; - } - if( nBrace>0 || nSq>0 ){ - /* Parse error */ - return TH_ERROR; - } - } - - if( iEnd>nInput ){ - /* Parse error */ - return TH_ERROR; - } - *pnWord = iEnd; - return TH_OK; -} - -/* -** The input string (zWord, nWord) contains a th1 script enclosed in -** a [] block. Perform substitution on the input string and store the -** resulting string in the interpreter result. -*/ -static int thSubstCommand( - Th_Interp *interp, - const char *zWord, - int nWord -){ - assert(nWord>=2); - assert(zWord[0]=='[' && zWord[nWord-1]==']'); - return thEvalLocal(interp, &zWord[1], nWord-2); -} - -/* -** The input string (zWord, nWord) contains a th1 variable reference -** (a '$' byte followed by a variable name). Perform substitution on -** the input string and store the resulting string in the interpreter -** result. -*/ -static int thSubstVarname( - Th_Interp *interp, - const char *zWord, - int nWord -){ - assert(nWord>=1); - assert(zWord[0]=='$'); - assert(nWord==1 || zWord[1]!='{' || zWord[nWord-1]=='}'); - if( nWord>1 && zWord[1]=='{' ){ - zWord++; - nWord -= 2; - }else if( zWord[nWord-1]==')' ){ - int i; - for(i=1; i<nWord && zWord[i]!='('; i++); - if( i<nWord ){ - Buffer varname; - int nInner; - const char *zInner; - - int rc = thSubstWord(interp, &zWord[i+1], nWord-i-2); - if( rc!=TH_OK ) return rc; - - zInner = Th_GetResult(interp, &nInner); - thBufferInit(&varname); - thBufferWrite(interp, &varname, &zWord[1], i); - thBufferWrite(interp, &varname, zInner, nInner); - thBufferWrite(interp, &varname, ")", 1); - rc = Th_GetVar(interp, varname.zBuf, varname.nBuf); - thBufferFree(interp, &varname); - return rc; - } - } - return Th_GetVar(interp, &zWord[1], nWord-1); -} - -/* -** The input string (zWord, nWord) contains a th1 escape sequence. -** Perform substitution on the input string and store the resulting -** string in the interpreter result. -*/ -static int thSubstEscape( - Th_Interp *interp, - const char *zWord, - int nWord -){ - char c; - - assert(nWord>=2); - assert(zWord[0]=='\\'); - - switch( zWord[1] ){ - case 'x': { - assert(nWord==4); - c = ((thHexdigit(zWord[2])<<4) + thHexdigit(zWord[3])); - break; - } - case 'n': { - c = '\n'; - break; - } - default: { - assert(nWord==2); - c = zWord[1]; - break; - } - } - - Th_SetResult(interp, &c, 1); - return TH_OK; -} - -/* -** The input string (zWord, nWord) contains a th1 word. Perform -** substitution on the input string and store the resulting -** string in the interpreter result. -*/ -static int thSubstWord( - Th_Interp *interp, - const char *zWord, - int nWord -){ - int rc = TH_OK; - Buffer output; - int i; - - thBufferInit(&output); - - if( nWord>1 && (zWord[0]=='{' && zWord[nWord-1]=='}') ){ - rc = thBufferWrite(interp, &output, &zWord[1], nWord-2); - }else{ - - /* If the word is surrounded by double-quotes strip these away. */ - if( nWord>1 && (zWord[0]=='"' && zWord[nWord-1]=='"') ){ - zWord++; - nWord -= 2; - } - - for(i=0; rc==TH_OK && i<nWord; i++){ - int nGet; - - int (*xGet)(Th_Interp *, const char*, int, int *) = 0; - int (*xSubst)(Th_Interp *, const char*, int) = 0; - - switch( zWord[i] ){ - case '\\': - xGet = thNextEscape; xSubst = thSubstEscape; - break; - case '[': - if( !interp->isListMode ){ - xGet = thNextCommand; xSubst = thSubstCommand; - break; - } - case '$': - if( !interp->isListMode ){ - xGet = thNextVarname; xSubst = thSubstVarname; - break; - } - default: { - thBufferWrite(interp, &output, &zWord[i], 1); - continue; /* Go to the next iteration of the for(...) loop */ - } - } - - rc = xGet(interp, &zWord[i], nWord-i, &nGet); - if( rc==TH_OK ){ - rc = xSubst(interp, &zWord[i], nGet); - } - if( rc==TH_OK ){ - const char *zRes; - int nRes; - zRes = Th_GetResult(interp, &nRes); - rc = thBufferWrite(interp, &output, zRes, nRes); - i += (nGet-1); - } - } - } - - if( rc==TH_OK ){ - Th_SetResult(interp, output.zBuf, output.nBuf); - } - thBufferFree(interp, &output); - return rc; -} - -/* -** Return true if one of the following is true of the buffer pointed -** to by zInput, length nInput: -** -** + It is empty, or -** + It contains nothing but white-space, or -** + It contains no non-white-space characters before the first -** newline character. -** -** Otherwise return false. -*/ -static int thEndOfLine(const char *zInput, int nInput){ - int i; - for(i=0; i<nInput && zInput[i]!='\n' && th_isspace(zInput[i]); i++); - return ((i==nInput || zInput[i]=='\n')?1:0); -} - -/* -** This function splits the supplied th1 list (contained in buffer zList, -** size nList) into elements and performs word-substitution on each -** element. If the Th_Interp.isListMode variable is true, then only -** escape sequences are substituted (used by the Th_SplitList() function). -** If Th_Interp.isListMode is false, then variable and command substitution -** is also performed (used by Th_Eval()). -** -** If zList/nList does not contain a valid list, TH_ERROR is returned -** and an error message stored in interp. -** -** If TH_OK is returned and pazElem is not NULL, the caller should free the -** pointer written to (*pazElem) using Th_Free(). This releases memory -** allocated for both the (*pazElem) and (*panElem) arrays. Example: -** -** char **argv; -** int *argl; -** int argc; -** -** // After this call, argv and argl point to valid arrays. The -** // number of elements in each is argc. -** // -** Th_SplitList(interp, zList, nList, &argv, &argl, &argc); -** -** // Free all memory allocated by Th_SplitList(). The arrays pointed -** // to by argv and argl are invalidated by this call. -** // -** Th_Free(interp, argv); -** -*/ -static int thSplitList( - Th_Interp *interp, /* Interpreter context */ - const char *zList, /* Pointer to buffer containing input list */ - int nList, /* Size of buffer pointed to by zList */ - char ***pazElem, /* OUT: Array of list elements */ - int **panElem, /* OUT: Lengths of each list element */ - int *pnCount /* OUT: Number of list elements */ -){ - int rc = TH_OK; - - Buffer strbuf; - Buffer lenbuf; - int nCount = 0; - - const char *zInput = zList; - int nInput = nList; - - thBufferInit(&strbuf); - thBufferInit(&lenbuf); - - while( nInput>0 ){ - const char *zWord; - int nWord; - - thNextSpace(interp, zInput, nInput, &nWord); - zInput += nWord; - nInput = nList-(zInput-zList); - - if( TH_OK!=(rc = thNextWord(interp, zInput, nInput, &nWord, 0)) - || TH_OK!=(rc = thSubstWord(interp, zInput, nWord)) - ){ - goto finish; - } - zInput = &zInput[nWord]; - nInput = nList-(zInput-zList); - if( nWord>0 ){ - zWord = Th_GetResult(interp, &nWord); - thBufferWrite(interp, &strbuf, zWord, nWord); - thBufferWrite(interp, &strbuf, "\0", 1); - thBufferWrite(interp, &lenbuf, &nWord, sizeof(int)); - nCount++; - } - } - assert((lenbuf.nBuf/sizeof(int))==nCount); - - assert((pazElem && panElem) || (!pazElem && !panElem)); - if( pazElem && rc==TH_OK ){ - int i; - char *zElem; - int *anElem; - char **azElem = Th_Malloc(interp, - sizeof(char*) * nCount + /* azElem */ - sizeof(int) * nCount + /* anElem */ - strbuf.nBuf /* space for list element strings */ - ); - anElem = (int *)&azElem[nCount]; - zElem = (char *)&anElem[nCount]; - memcpy(anElem, lenbuf.zBuf, lenbuf.nBuf); - memcpy(zElem, strbuf.zBuf, strbuf.nBuf); - for(i=0; i<nCount;i++){ - azElem[i] = zElem; - zElem += (anElem[i] + 1); - } - *pazElem = azElem; - *panElem = anElem; - } - if( pnCount ){ - *pnCount = nCount; - } - - finish: - thBufferFree(interp, &strbuf); - thBufferFree(interp, &lenbuf); - return rc; -} - -/* -** Evaluate the th1 script contained in the string (zProgram, nProgram) -** in the current stack frame. -*/ -static int thEvalLocal(Th_Interp *interp, const char *zProgram, int nProgram){ - int rc = TH_OK; - const char *zInput = zProgram; - int nInput = nProgram; - - while( rc==TH_OK && nInput ){ - Th_HashEntry *pEntry; - int nSpace; - const char *zFirst; - - char **argv; - int *argl; - int argc; - - assert(nInput>=0); - - /* Skip a semi-colon */ - if( *zInput==';' ){ - zInput++; - nInput--; - } - - /* Skip past leading white-space. */ - thNextSpace(interp, zInput, nInput, &nSpace); - zInput += nSpace; - nInput -= nSpace; - zFirst = zInput; - - /* Check for a comment. If found, skip to the end of the line. */ - if( zInput[0]=='#' ){ - while( !thEndOfLine(zInput, nInput) ){ - zInput++; - nInput--; - } - continue; - } - - /* Gobble up input a word at a time until the end of the command - ** (a semi-colon or end of line). - */ - while( rc==TH_OK && *zInput!=';' && !thEndOfLine(zInput, nInput) ){ - int nWord=0; - thNextSpace(interp, zInput, nInput, &nSpace); - rc = thNextWord(interp, &zInput[nSpace], nInput-nSpace, &nWord, 1); - zInput += (nSpace+nWord); - nInput -= (nSpace+nWord); - } - if( rc!=TH_OK ) continue; - - /* Split the command into an array of words. This call also does - ** substitution of each individual word. - */ - rc = thSplitList(interp, zFirst, zInput-zFirst, &argv, &argl, &argc); - if( rc!=TH_OK ) continue; - - if( argc>0 ){ - - /* Look up the command name in the command hash-table. */ - pEntry = Th_HashFind(interp, interp->paCmd, argv[0], argl[0], 0); - if( !pEntry ){ - Th_ErrorMessage(interp, "no such command: ", argv[0], argl[0]); - rc = TH_ERROR; - } - - /* Call the command procedure. */ - if( rc==TH_OK ){ - Th_Command *p = (Th_Command *)(pEntry->pData); - const char **azArg = (const char **)argv; - rc = p->xProc(interp, p->pContext, argc, azArg, argl); - } - - /* If an error occured, add this command to the stack trace report. */ - if( rc==TH_ERROR ){ - char *zRes; - int nRes; - char *zStack = 0; - int nStack = 0; - - zRes = Th_TakeResult(interp, &nRes); - if( TH_OK==Th_GetVar(interp, (char *)"::th_stack_trace", -1) ){ - zStack = Th_TakeResult(interp, &nStack); - } - Th_ListAppend(interp, &zStack, &nStack, zFirst, zInput-zFirst); - Th_SetVar(interp, (char *)"::th_stack_trace", -1, zStack, nStack); - Th_SetResult(interp, zRes, nRes); - Th_Free(interp, zRes); - Th_Free(interp, zStack); - } - } - - Th_Free(interp, argv); - } - - return rc; -} - -/* -** Interpret an integer frame identifier passed to either Th_Eval() or -** Th_LinkVar(). If successful, return a pointer to the identified -** Th_Frame structure. If unsuccessful (no such frame), return 0 and -** leave an error message in the interpreter result. -** -** Argument iFrame is interpreted as follows: -** -** * If iFrame is 0, this means the current frame. -** -** * If iFrame is negative, then the nth frame up the stack, where -** n is the absolute value of iFrame. A value of -1 means the -** calling procedure. -** -** * If iFrame is +ve, then the nth frame from the bottom of the -** stack. An iFrame value of 1 means the toplevel (global) frame. -*/ -static Th_Frame *getFrame(Th_Interp *interp, int iFrame){ - Th_Frame *p = interp->pFrame; - int i; - if( iFrame>0 ){ - for(i=0; p; i++){ - p = p->pCaller; - } - iFrame = (i*-1) + iFrame; - p = interp->pFrame; - } - for(i=0; p && i<(iFrame*-1); i++){ - p = p->pCaller; - } - - if( !p ){ - char *zFrame; - int nFrame; - Th_SetResultInt(interp, iFrame); - zFrame = Th_TakeResult(interp, &nFrame); - Th_ErrorMessage(interp, "no such frame:", zFrame, nFrame); - Th_Free(interp, zFrame); - } - return p; -} - - -/* -** Evaluate th1 script (zProgram, nProgram) in the frame identified by -** argument iFrame. Leave either an error message or a result in the -** interpreter result and return a th1 error code (TH_OK, TH_ERROR, -** TH_RETURN, TH_CONTINUE or TH_BREAK). -*/ -int Th_Eval(Th_Interp *interp, int iFrame, const char *zProgram, int nProgram){ - int rc = TH_OK; - Th_Frame *pSavedFrame = interp->pFrame; - - /* Set Th_Interp.pFrame to the frame that this script is to be - ** evaluated in. The current frame is saved in pSavedFrame and will - ** be restored before this function returns. - */ - interp->pFrame = getFrame(interp, iFrame); - - if( !interp->pFrame ){ - rc = TH_ERROR; - }else{ - int nInput = nProgram; - - if( nInput<0 ){ - nInput = th_strlen(zProgram); - } - rc = thEvalLocal(interp, zProgram, nInput); - } - - interp->pFrame = pSavedFrame; - return rc; -} - -/* -** Input string (zVarname, nVarname) contains a th1 variable name. It -** may be a simple scalar variable name or it may be a reference -** to an array member. The variable name may or may not begin with -** "::", indicating that the name refers to a global variable, not -** a local scope one. -** -** This function inspects and categorizes the supplied variable name. -** -** If the name is a global reference, *pisGlobal is set to true. Otherwise -** false. Output string (*pzOuter, *pnOuter) is set to the variable name -** if it is a scalar reference, or the name of the array if it is an -** array variable. If the variable is a scalar, *pzInner is set to 0. -** If it is an array variable, (*pzInner, *pnInner) is set to the -** array key name. -*/ -static int thAnalyseVarname( - const char *zVarname, - int nVarname, - const char **pzOuter, /* OUT: Pointer to scalar/array name */ - int *pnOuter, /* OUT: Number of bytes at *pzOuter */ - const char **pzInner, /* OUT: Pointer to array key (or null) */ - int *pnInner, /* OUT: Number of bytes at *pzInner */ - int *pisGlobal /* OUT: Set to true if this is a global ref */ -){ - const char *zOuter = zVarname; - int nOuter; - const char *zInner = 0; - int nInner = 0; - int isGlobal = 0; - int i; - - if( nVarname<0 ){ - nVarname = th_strlen(zVarname); - } - nOuter = nVarname; - - /* If the variable name starts with "::", then do the lookup is in the - ** uppermost (global) frame. - */ - if( nVarname>2 && zVarname[0]==':' && zVarname[1]==':' ){ - zOuter += 2; - nOuter -= 2; - isGlobal = 1; - } - - /* Check if this is an array reference. */ - if( zOuter[nOuter-1]==')' ){ - for(i=0; i<nOuter; i++){ - if( zOuter[i]=='(' ){ - zInner = &zOuter[i+1]; - nInner = nOuter-i-2; - nOuter = i; - break; - } - } - } - - *pzOuter = zOuter; - *pnOuter = nOuter; - *pzInner = zInner; - *pnInner = nInner; - *pisGlobal = isGlobal; - return TH_OK; -} - -/* -** Input string (zVar, nVar) contains a variable name. This function locates -** the Th_Variable structure associated with the named variable. The -** variable name may be a global or local scalar or array variable -** -** If the create argument is non-zero and the named variable does not exist -** it is created. Otherwise, an error is left in the interpreter result -** and NULL returned. -** -** If the arrayok argument is false and the named variable is an array, -** an error is left in the interpreter result and NULL returned. If -** arrayok is true an array name is Ok. -*/ -static Th_Variable *thFindValue( - Th_Interp *interp, - const char *zVar, /* Pointer to variable name */ - int nVar, /* Number of bytes at nVar */ - int create, /* If true, create the variable if not found */ - int arrayok /* If true, an array is Ok. Othewise array==error */ -){ - const char *zOuter; - int nOuter; - const char *zInner; - int nInner; - int isGlobal; - - Th_HashEntry *pEntry; - Th_Frame *pFrame = interp->pFrame; - Th_Variable *pValue; - - thAnalyseVarname(zVar, nVar, &zOuter, &nOuter, &zInner, &nInner, &isGlobal); - if( isGlobal ){ - while( pFrame->pCaller ) pFrame = pFrame->pCaller; - } - - pEntry = Th_HashFind(interp, pFrame->paVar, zOuter, nOuter, create); - assert(pEntry || !create); - if( !pEntry ){ - goto no_such_var; - } - - pValue = (Th_Variable *)pEntry->pData; - if( !pValue ){ - assert(create); - pValue = Th_Malloc(interp, sizeof(Th_Variable)); - pValue->nRef = 1; - pEntry->pData = (void *)pValue; - } - - if( zInner ){ - if( pValue->zData ){ - Th_ErrorMessage(interp, "variable is a scalar:", zOuter, nOuter); - return 0; - } - if( !pValue->pHash ){ - if( !create ){ - goto no_such_var; - } - pValue->pHash = Th_HashNew(interp); - } - pEntry = Th_HashFind(interp, pValue->pHash, zInner, nInner, create); - if( !pEntry ){ - goto no_such_var; - } - pValue = (Th_Variable *)pEntry->pData; - if( !pValue ){ - assert(create); - pValue = Th_Malloc(interp, sizeof(Th_Variable)); - pValue->nRef = 1; - pEntry->pData = (void *)pValue; - } - }else{ - if( pValue->pHash && !arrayok ){ - Th_ErrorMessage(interp, "variable is an array:", zOuter, nOuter); - return 0; - } - } - - return pValue; - -no_such_var: - Th_ErrorMessage(interp, "no such variable:", zVar, nVar); - return 0; -} - -/* -** String (zVar, nVar) must contain the name of a scalar variable or -** array member. Look up the variable, store its current value in -** the interpreter result and return TH_OK. -** -** If the named variable does not exist, return TH_ERROR and leave -** an error message in the interpreter result. -*/ -int Th_GetVar(Th_Interp *interp, const char *zVar, int nVar){ - Th_Variable *pValue; - - pValue = thFindValue(interp, zVar, nVar, 0, 0); - if( !pValue ){ - return TH_ERROR; - } - if( !pValue->zData ){ - Th_ErrorMessage(interp, "no such variable:", zVar, nVar); - return TH_ERROR; - } - - return Th_SetResult(interp, pValue->zData, pValue->nData); -} - -/* -** String (zVar, nVar) must contain the name of a scalar variable or -** array member. If the variable does not exist it is created. The -** variable is set to the value supplied in string (zValue, nValue). -** -** If (zVar, nVar) refers to an existing array, TH_ERROR is returned -** and an error message left in the interpreter result. -*/ -int Th_SetVar( - Th_Interp *interp, - const char *zVar, - int nVar, - const char *zValue, - int nValue -){ - Th_Variable *pValue; - - pValue = thFindValue(interp, zVar, nVar, 1, 0); - if( !pValue ){ - return TH_ERROR; - } - - if( nValue<0 ){ - nValue = th_strlen(zValue); - } - if( pValue->zData ){ - Th_Free(interp, pValue->zData); - pValue->zData = 0; - } - - assert(zValue || nValue==0); - pValue->zData = Th_Malloc(interp, nValue+1); - pValue->zData[nValue] = '\0'; - memcpy(pValue->zData, zValue, nValue); - pValue->nData = nValue; - - return TH_OK; -} - -/* -** Create a variable link so that accessing variable (zLocal, nLocal) is -** the same as accessing variable (zLink, nLink) in stack frame iFrame. -*/ -int Th_LinkVar( - Th_Interp *interp, /* Interpreter */ - const char *zLocal, int nLocal, /* Local varname */ - int iFrame, /* Stack frame of linked var */ - const char *zLink, int nLink /* Linked varname */ -){ - Th_Frame *pSavedFrame = interp->pFrame; - Th_Frame *pFrame; - Th_HashEntry *pEntry; - Th_Variable *pValue; - - pFrame = getFrame(interp, iFrame); - if( !pFrame ){ - return TH_ERROR; - } - pSavedFrame = interp->pFrame; - interp->pFrame = pFrame; - pValue = thFindValue(interp, zLink, nLink, 1, 1); - interp->pFrame = pSavedFrame; - - pEntry = Th_HashFind(interp, interp->pFrame->paVar, zLocal, nLocal, 1); - if( pEntry->pData ){ - Th_ErrorMessage(interp, "variable exists:", zLocal, nLocal); - return TH_ERROR; - } - pEntry->pData = (void *)pValue; - pValue->nRef++; - - return TH_OK; -} - -/* -** Input string (zVar, nVar) must contain the name of a scalar variable, -** an array, or an array member. If the identified variable exists, it -** is deleted and TH_OK returned. Otherwise, an error message is left -** in the interpreter result and TH_ERROR is returned. -*/ -int Th_UnsetVar(Th_Interp *interp, const char *zVar, int nVar){ - Th_Variable *pValue; - - pValue = thFindValue(interp, zVar, nVar, 1, 1); - if( !pValue ){ - return TH_ERROR; - } - - Th_Free(interp, pValue->zData); - pValue->zData = 0; - if( pValue->pHash ){ - Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp); - Th_HashDelete(interp, pValue->pHash); - pValue->pHash = 0; - } - return TH_OK; -} - -/* -** Return an allocated buffer containing a copy of string (z, n). The -** caller is responsible for eventually calling Th_Free() to free -** the returned buffer. -*/ -char *th_strdup(Th_Interp *interp, const char *z, int n){ - char *zRes; - if( n<0 ){ - n = th_strlen(z); - } - zRes = Th_Malloc(interp, n+1); - memcpy(zRes, z, n); - zRes[n] = '\0'; - return zRes; -} - -/* -** Argument zPre must be a nul-terminated string. Set the interpreter -** result to a string containing the contents of zPre, followed by -** a space (" ") character, followed by a copy of string (z, n). -** -** In other words, the equivalent of: -* -** printf("%s %.*s", zPre, n, z); -** -** Example: -** -** Th_ErrorMessage(interp, "no such variable:", zVarname, nVarname); -** -*/ -int Th_ErrorMessage(Th_Interp *interp, const char *zPre, const char *z, int n){ - if( interp ){ - char *zRes = 0; - int nRes = 0; - - Th_SetVar(interp, (char *)"::th_stack_trace", -1, 0, 0); - - Th_StringAppend(interp, &zRes, &nRes, zPre, -1); - if( zRes[nRes-1]=='"' ){ - Th_StringAppend(interp, &zRes, &nRes, z, n); - Th_StringAppend(interp, &zRes, &nRes, (const char *)"\"", 1); - }else{ - Th_StringAppend(interp, &zRes, &nRes, (const char *)" ", 1); - Th_StringAppend(interp, &zRes, &nRes, z, n); - } - - Th_SetResult(interp, zRes, nRes); - Th_Free(interp, zRes); - } - - return TH_OK; -} - -/* -** Set the current interpreter result by taking a copy of the buffer -** pointed to by z, size n bytes. TH_OK is always returned. -*/ -int Th_SetResult(Th_Interp *pInterp, const char *z, int n){ - - /* Free the current result */ - Th_Free(pInterp, pInterp->zResult); - pInterp->zResult = 0; - pInterp->nResult = 0; - - if( n<0 ){ - n = th_strlen(z); - } - - if( z && n>0 ){ - char *zResult; - zResult = Th_Malloc(pInterp, n+1); - memcpy(zResult, z, n); - zResult[n] = '\0'; - pInterp->zResult = zResult; - pInterp->nResult = n; - } - - return TH_OK; -} - -/* -** Return a pointer to the buffer containing the current interpreter -** result. If pN is not NULL, set *pN to the size of the returned -** buffer. -*/ -const char *Th_GetResult(Th_Interp *pInterp, int *pN){ - assert(pInterp->zResult || pInterp->nResult==0); - if( pN ){ - *pN = pInterp->nResult; - } - return (pInterp->zResult ? pInterp->zResult : (const char *)""); -} - -/* -** Return a pointer to the buffer containing the current interpreter -** result. If pN is not NULL, set *pN to the size of the returned -** buffer. -** -** This function is the same as Th_GetResult() except that the -** caller is responsible for eventually calling Th_Free() on the -** returned buffer. The internal interpreter result is cleared -** after this function is called. -*/ -char *Th_TakeResult(Th_Interp *pInterp, int *pN){ - if( pN ){ - *pN = pInterp->nResult; - } - if( pInterp->zResult ){ - char *zResult = pInterp->zResult; - pInterp->zResult = 0; - pInterp->nResult = 0; - return zResult; - }else{ - return (char *)Th_Malloc(pInterp, 1); - } -} - - -/* -** Wrappers around the supplied malloc() and free() -*/ -void *Th_Malloc(Th_Interp *pInterp, int nByte){ - void *p = pInterp->pVtab->xMalloc(nByte); - if( p ){ - memset(p, 0, nByte); - } - return p; -} -void Th_Free(Th_Interp *pInterp, void *z){ - if( z ){ - pInterp->pVtab->xFree(z); - } -} - -/* -** Install a new th1 command. -** -** If a command of the same name already exists, it is deleted automatically. -*/ -int Th_CreateCommand( - Th_Interp *interp, - const char *zName, /* New command name */ - Th_CommandProc xProc, /* Command callback proc */ - void *pContext, /* Value to pass as second arg to xProc */ - void (*xDel)(Th_Interp *, void *) /* Command destructor callback */ -){ - Th_HashEntry *pEntry; - Th_Command *pCommand; - - pEntry = Th_HashFind(interp, interp->paCmd, (const char *)zName, -1, 1); - if( pEntry->pData ){ - pCommand = pEntry->pData; - if( pCommand->xDel ){ - pCommand->xDel(interp, pCommand->pContext); - } - }else{ - pCommand = Th_Malloc(interp, sizeof(Th_Command)); - } - pCommand->xProc = xProc; - pCommand->pContext = pContext; - pCommand->xDel = xDel; - pEntry->pData = (void *)pCommand; - - return TH_OK; -} - -/* -** Rename the existing command (zName, nName) to (zNew, nNew). If nNew is 0, -** the command is deleted instead of renamed. -** -** If successful, TH_OK is returned. If command zName does not exist, or -** if command zNew already exists, an error message is left in the -** interpreter result and TH_ERROR is returned. -*/ -int Th_RenameCommand( - Th_Interp *interp, - const char *zName, /* Existing command name */ - int nName, /* Number of bytes at zName */ - const char *zNew, /* New command name */ - int nNew /* Number of bytes at zNew */ -){ - Th_HashEntry *pEntry; - Th_HashEntry *pNewEntry; - - pEntry = Th_HashFind(interp, interp->paCmd, zName, nName, 0); - if( !pEntry ){ - Th_ErrorMessage(interp, "no such command:", zName, nName); - return TH_ERROR; - } - assert(pEntry->pData); - - if( nNew>0 ){ - pNewEntry = Th_HashFind(interp, interp->paCmd, zNew, nNew, 1); - if( pNewEntry->pData ){ - Th_ErrorMessage(interp, "command exists:", zNew, nNew); - return TH_ERROR; - } - pNewEntry->pData = pEntry->pData; - }else{ - Th_Command *pCommand = (Th_Command *)(pEntry->pData); - if( pCommand->xDel ){ - pCommand->xDel(interp, pCommand->pContext); - } - Th_Free(interp, pCommand); - } - - Th_HashFind(interp, interp->paCmd, zName, nName, -1); - return TH_OK; -} - -/* -** Push a stack frame onto the interpreter stack, invoke the -** callback, and pop the frame back off again. See the implementation -** of [proc] (th_lang.c) for an example. -*/ -int Th_InFrame(Th_Interp *interp, - int (*xCall)(Th_Interp *, void *pContext1, void *pContext2), - void *pContext1, - void *pContext2 -){ - Th_Frame frame; - int rc; - thPushFrame(interp, &frame); - rc = xCall(interp, pContext1, pContext2); - thPopFrame(interp); - return rc; -} - -/* -** Split a th1 list into its component elements. The list to split is -** passed via arguments (zList, nList). If successful, TH_OK is returned. -** If an error occurs (if (zList, nList) is not a valid list) an error -** message is left in the interpreter result and TH_ERROR returned. -** -** If successful, *pnCount is set to the number of elements in the list. -** panElem is set to point at an array of *pnCount integers - the lengths -** of the element values. *pazElem is set to point at an array of -** pointers to buffers containing the array element's data. -** -** To free the arrays allocated at *pazElem and *panElem, the caller -** should call Th_Free() on *pazElem only. Exactly one such call to -** Th_Free() must be made per call to Th_SplitList(). -** -** Example: -** -** int nElem; -** int *anElem; -** char **azElem; -** int i; -** -** Th_SplitList(interp, zList, nList, &azElem, &anElem, &nElem); -** for(i=0; i<nElem; i++){ -** int nData = anElem[i]; -** char *zData = azElem[i]; -** ... -** } -** -** Th_Free(interp, azElem); -** -*/ -int Th_SplitList( - Th_Interp *interp, - const char *zList, /* Pointer to buffer containing list */ - int nList, /* Number of bytes at zList */ - char ***pazElem, /* OUT: Array of pointers to element data */ - int **panElem, /* OUT: Array of element data lengths */ - int *pnCount /* OUT: Number of elements in list */ -){ - int rc; - interp->isListMode = 1; - rc = thSplitList(interp, zList, nList, pazElem, panElem, pnCount); - interp->isListMode = 0; - if( rc ){ - Th_ErrorMessage(interp, "Expected list, got: \"", zList, nList); - } - return rc; -} - -/* -** Append a new element to an existing th1 list. The element to append -** to the list is (zElem, nElem). -** -** A pointer to the existing list must be stored at *pzList when this -** function is called. The length must be stored in *pnList. The value -** of *pzList must either be NULL (in which case *pnList must be 0), or -** a pointer to memory obtained from Th_Malloc(). -** -** This function calls Th_Free() to free the buffer at *pzList and sets -** *pzList to point to a new buffer containing the new list value. *pnList -** is similarly updated before returning. The return value is always TH_OK. -** -** Example: -** -** char *zList = 0; -** int nList = 0; -** for (...) { -** char *zElem = <some expression>; -** Th_ListAppend(interp, &zList, &nList, zElem, -1); -** } -** Th_SetResult(interp, zList, nList); -** Th_Free(interp, zList); -** -*/ -int Th_ListAppend( - Th_Interp *interp, /* Interpreter context */ - char **pzList, /* IN/OUT: Ptr to ptr to list */ - int *pnList, /* IN/OUT: Current length of *pzList */ - const char *zElem, /* Data to append */ - int nElem /* Length of nElem */ -){ - Buffer output; - int i; - - int hasSpecialChar = 0; - int hasEscapeChar = 0; - int nBrace = 0; - - output.zBuf = *pzList; - output.nBuf = *pnList; - output.nBufAlloc = output.nBuf; - - if( nElem<0 ){ - nElem = th_strlen(zElem); - } - if( output.nBuf>0 ){ - thBufferWrite(interp, &output, " ", 1); - } - - for(i=0; i<nElem; i++){ - char c = zElem[i]; - if( th_isspecial(c) ) hasSpecialChar = 1; - if( c=='\\' ) hasEscapeChar = 1; - if( c=='{' ) nBrace++; - if( c=='}' ) nBrace--; - } - - if( nElem==0 || (!hasEscapeChar && hasSpecialChar && nBrace==0) ){ - thBufferWrite(interp, &output, "{", 1); - thBufferWrite(interp, &output, zElem, nElem); - thBufferWrite(interp, &output, "}", 1); - }else{ - for(i=0; i<nElem; i++){ - char c = zElem[i]; - if( th_isspecial(c) ) thBufferWrite(interp, &output, "\\", 1); - thBufferWrite(interp, &output, &c, 1); - } - } - - *pzList = output.zBuf; - *pnList = output.nBuf; - - return TH_OK; -} - -/* -** Append a new element to an existing th1 string. This function uses -** the same interface as the Th_ListAppend() function. -*/ -int Th_StringAppend( - Th_Interp *interp, /* Interpreter context */ - char **pzStr, /* IN/OUT: Ptr to ptr to list */ - int *pnStr, /* IN/OUT: Current length of *pzStr */ - const char *zElem, /* Data to append */ - int nElem /* Length of nElem */ -){ - char *zNew; - int nNew; - - if( nElem<0 ){ - nElem = th_strlen(zElem); - } - - nNew = *pnStr + nElem; - zNew = Th_Malloc(interp, nNew); - memcpy(zNew, *pzStr, *pnStr); - memcpy(&zNew[*pnStr], zElem, nElem); - - Th_Free(interp, *pzStr); - *pzStr = zNew; - *pnStr = nNew; - - return TH_OK; -} - -/* -** Delete an interpreter. -*/ -void Th_DeleteInterp(Th_Interp *interp){ - assert(interp->pFrame); - assert(0==interp->pFrame->pCaller); - - /* Delete the contents of the global frame. */ - thPopFrame(interp); - - /* Delete any result currently stored in the interpreter. */ - Th_SetResult(interp, 0, 0); - - /* Delete all registered commands and the command hash-table itself. */ - Th_HashIterate(interp, interp->paCmd, thFreeCommand, (void *)interp); - Th_HashDelete(interp, interp->paCmd); - - /* Delete the interpreter structure itself. */ - Th_Free(interp, (void *)interp); -} - -/* -** Create a new interpreter. -*/ -Th_Interp * Th_CreateInterp(Th_Vtab *pVtab){ - Th_Interp *p; - - /* Allocate and initialise the interpreter and the global frame */ - p = pVtab->xMalloc(sizeof(Th_Interp) + sizeof(Th_Frame)); - memset(p, 0, sizeof(Th_Interp)); - p->pVtab = pVtab; - p->paCmd = Th_HashNew(p); - thPushFrame(p, (Th_Frame *)&p[1]); - - return p; -} - -/* -** These two types are used only by the expression module, where -** the expression module means the Th_Expr() and exprXXX() functions. -*/ -typedef struct Operator Operator; -struct Operator { - const char *zOp; - int eOp; - int iPrecedence; - int eArgType; -}; -typedef struct Expr Expr; -struct Expr { - Operator *pOp; - Expr *pParent; - Expr *pLeft; - Expr *pRight; - - char *zValue; /* Pointer to literal value */ - int nValue; /* Length of literal value buffer */ -}; - -/* Unary operators */ -#define OP_UNARY_MINUS 2 -#define OP_UNARY_PLUS 3 -#define OP_BITWISE_NOT 4 -#define OP_LOGICAL_NOT 5 - -/* Binary operators */ -#define OP_MULTIPLY 6 -#define OP_DIVIDE 7 -#define OP_MODULUS 8 -#define OP_ADD 9 -#define OP_SUBTRACT 10 -#define OP_LEFTSHIFT 11 -#define OP_RIGHTSHIFT 12 -#define OP_LT 13 -#define OP_GT 14 -#define OP_LE 15 -#define OP_GE 16 -#define OP_EQ 17 -#define OP_NE 18 -#define OP_SEQ 19 -#define OP_SNE 20 -#define OP_BITWISE_AND 21 -#define OP_BITWISE_XOR 22 -#define OP_BITWISE_OR 24 -#define OP_LOGICAL_AND 25 -#define OP_LOGICAL_OR 26 - -/* Other symbols */ -#define OP_OPEN_BRACKET 27 -#define OP_CLOSE_BRACKET 28 - -/* Argument types. Each operator in the expression syntax is defined -** as requiring either integer, number (real or integer) or string -** operands. -*/ -#define ARG_INTEGER 1 -#define ARG_NUMBER 2 -#define ARG_STRING 3 - -static Operator aOperator[] = { - - {"(", OP_OPEN_BRACKET, -1, 0}, - {")", OP_CLOSE_BRACKET, -1, 0}, - - /* Note: all unary operators have (iPrecedence==1) */ - {"-", OP_UNARY_MINUS, 1, ARG_NUMBER}, - {"+", OP_UNARY_PLUS, 1, ARG_NUMBER}, - {"~", OP_BITWISE_NOT, 1, ARG_INTEGER}, - {"!", OP_LOGICAL_NOT, 1, ARG_INTEGER}, - - /* Binary operators. It is important to the parsing in Th_Expr() that - * the two-character symbols ("==") appear before the one-character - * ones ("="). And that the priorities of all binary operators are - * integers between 2 and 12. - */ - {"<<", OP_LEFTSHIFT, 4, ARG_INTEGER}, - {">>", OP_RIGHTSHIFT, 4, ARG_INTEGER}, - {"<=", OP_LE, 5, ARG_NUMBER}, - {">=", OP_GE, 5, ARG_NUMBER}, - {"==", OP_EQ, 6, ARG_NUMBER}, - {"!=", OP_NE, 6, ARG_NUMBER}, - {"eq", OP_SEQ, 7, ARG_STRING}, - {"ne", OP_SNE, 7, ARG_STRING}, - {"&&", OP_LOGICAL_AND, 11, ARG_INTEGER}, - {"||", OP_LOGICAL_OR, 12, ARG_INTEGER}, - - {"*", OP_MULTIPLY, 2, ARG_NUMBER}, - {"/", OP_DIVIDE, 2, ARG_NUMBER}, - {"%", OP_MODULUS, 2, ARG_INTEGER}, - {"+", OP_ADD, 3, ARG_NUMBER}, - {"-", OP_SUBTRACT, 3, ARG_NUMBER}, - {"<", OP_LT, 5, ARG_NUMBER}, - {">", OP_GT, 5, ARG_NUMBER}, - {"&", OP_BITWISE_AND, 8, ARG_INTEGER}, - {"^", OP_BITWISE_XOR, 9, ARG_INTEGER}, - {"|", OP_BITWISE_OR, 10, ARG_INTEGER}, - - {0,0,0} -}; - -/* -** The first part of the string (zInput,nInput) contains a number. -** Set *pnVarname to the number of bytes in the numeric string. -*/ -static int thNextNumber( - Th_Interp *interp, - const char *zInput, - int nInput, - int *pnLiteral -){ - int i; - int seenDot = 0; - for(i=0; i<nInput; i++){ - char c = zInput[i]; - if( (seenDot || c!='.') && !th_isdigit(c) ) break; - if( c=='.' ) seenDot = 1; - } - *pnLiteral = i; - return TH_OK; -} - -/* -** Free an expression tree. -*/ -static void exprFree(Th_Interp *interp, Expr *pExpr){ - if( pExpr ){ - exprFree(interp, pExpr->pLeft); - exprFree(interp, pExpr->pRight); - Th_Free(interp, pExpr->zValue); - Th_Free(interp, pExpr); - } -} - -/* -** Evaluate an expression tree. -*/ -static int exprEval(Th_Interp *interp, Expr *pExpr){ - int rc = TH_OK; - - if( pExpr->pOp==0 ){ - /* A literal */ - rc = thSubstWord(interp, pExpr->zValue, pExpr->nValue); - }else{ - int eArgType = 0; /* Actual type of arguments */ - - /* Argument values */ - int iLeft = 0; - int iRight = 0; - double fLeft; - double fRight; - - /* Left and right arguments as strings */ - char *zLeft = 0; int nLeft = 0; - char *zRight = 0; int nRight = 0; - - /* Evaluate left and right arguments, if they exist. */ - if( pExpr->pLeft ){ - rc = exprEval(interp, pExpr->pLeft); - if( rc==TH_OK ){ - zLeft = Th_TakeResult(interp, &nLeft); - } - } - if( rc==TH_OK && pExpr->pRight ){ - rc = exprEval(interp, pExpr->pRight); - if( rc==TH_OK ){ - zRight = Th_TakeResult(interp, &nRight); - } - } - - /* Convert arguments to their required forms. */ - if( rc==TH_OK ){ - eArgType = pExpr->pOp->eArgType; - if( eArgType==ARG_NUMBER ){ - if( (zLeft==0 || TH_OK==Th_ToInt(0, zLeft, nLeft, &iLeft)) - && (zRight==0 || TH_OK==Th_ToInt(0, zRight, nRight, &iRight)) - ){ - eArgType = ARG_INTEGER; - }else if( - (zLeft && TH_OK!=Th_ToDouble(interp, zLeft, nLeft, &fLeft)) || - (zRight && TH_OK!=Th_ToDouble(interp, zRight, nRight, &fRight)) - ){ - /* A type error. */ - rc = TH_ERROR; - } - }else if( eArgType==ARG_INTEGER ){ - rc = Th_ToInt(interp, zLeft, nLeft, &iLeft); - if( rc==TH_OK && zRight ){ - rc = Th_ToInt(interp, zRight, nRight, &iRight); - } - } - } - - if( rc==TH_OK && eArgType==ARG_INTEGER ){ - int iRes = 0; - switch( pExpr->pOp->eOp ) { - case OP_MULTIPLY: iRes = iLeft*iRight; break; - case OP_DIVIDE: iRes = iLeft/iRight; break; - case OP_MODULUS: iRes = iLeft%iRight; break; - case OP_ADD: iRes = iLeft+iRight; break; - case OP_SUBTRACT: iRes = iLeft-iRight; break; - case OP_LEFTSHIFT: iRes = iLeft<<iRight; break; - case OP_RIGHTSHIFT: iRes = iLeft>>iRight; break; - case OP_LT: iRes = iLeft<iRight; break; - case OP_GT: iRes = iLeft>iRight; break; - case OP_LE: iRes = iLeft<=iRight; break; - case OP_GE: iRes = iLeft>=iRight; break; - case OP_EQ: iRes = iLeft==iRight; break; - case OP_NE: iRes = iLeft!=iRight; break; - case OP_BITWISE_AND: iRes = iLeft&iRight; break; - case OP_BITWISE_XOR: iRes = iLeft^iRight; break; - case OP_BITWISE_OR: iRes = iLeft|iRight; break; - case OP_LOGICAL_AND: iRes = iLeft&&iRight; break; - case OP_LOGICAL_OR: iRes = iLeft||iRight; break; - case OP_UNARY_MINUS: iRes = -iLeft; break; - case OP_UNARY_PLUS: iRes = +iLeft; break; - case OP_LOGICAL_NOT: iRes = !iLeft; break; - default: assert(!"Internal error"); - } - Th_SetResultInt(interp, iRes); - }else if( rc==TH_OK && eArgType==ARG_NUMBER ){ - switch( pExpr->pOp->eOp ) { - case OP_MULTIPLY: Th_SetResultDouble(interp, fLeft*fRight); break; - case OP_DIVIDE: Th_SetResultDouble(interp, fLeft/fRight); break; - case OP_ADD: Th_SetResultDouble(interp, fLeft+fRight); break; - case OP_SUBTRACT: Th_SetResultDouble(interp, fLeft-fRight); break; - case OP_LT: Th_SetResultInt(interp, fLeft<fRight); break; - case OP_GT: Th_SetResultInt(interp, fLeft>fRight); break; - case OP_LE: Th_SetResultInt(interp, fLeft<=fRight); break; - case OP_GE: Th_SetResultInt(interp, fLeft>=fRight); break; - case OP_EQ: Th_SetResultInt(interp, fLeft==fRight); break; - case OP_NE: Th_SetResultInt(interp, fLeft!=fRight); break; - default: assert(!"Internal error"); - } - }else if( rc==TH_OK ){ - int iEqual = 0; - assert( eArgType==ARG_STRING ); - if( nRight==nLeft && 0==memcmp(zRight, zLeft, nRight) ){ - iEqual = 1; - } - switch( pExpr->pOp->eOp ) { - case OP_SEQ: Th_SetResultInt(interp, iEqual); break; - case OP_SNE: Th_SetResultInt(interp, !iEqual); break; - default: assert(!"Internal error"); - } - } - - Th_Free(interp, zLeft); - Th_Free(interp, zRight); - } - - return rc; -} - -/* -** Create an expression tree from an array of tokens. If successful, -** the root of the tree is stored in apToken[0]. -*/ -int exprMakeTree(Th_Interp *interp, Expr **apToken, int nToken){ - int iLeft; - int i; - int jj; - - assert(nToken>0); -#define ISTERM(x) (apToken[x] && (!apToken[x]->pOp || apToken[x]->pLeft)) - - for(jj=0; jj<nToken; jj++){ - if( apToken[jj]->pOp && apToken[jj]->pOp->eOp==OP_OPEN_BRACKET ){ - int nNest = 1; - int iLeft = jj; - - for(jj++; jj<nToken; jj++){ - Operator *pOp = apToken[jj]->pOp; - if( pOp && pOp->eOp==OP_OPEN_BRACKET ) nNest++; - if( pOp && pOp->eOp==OP_CLOSE_BRACKET ) nNest--; - if( nNest==0 ) break; - } - if( jj==nToken ){ - return TH_ERROR; - } - if( (jj-iLeft)>1 ){ - if( exprMakeTree(interp, &apToken[iLeft+1], jj-iLeft-1) ){ - return TH_ERROR; - } - exprFree(interp, apToken[jj]); - exprFree(interp, apToken[iLeft]); - apToken[jj] = 0; - apToken[iLeft] = 0; - } - } - } - - iLeft = 0; - for(jj=nToken-1; jj>=0; jj--){ - if( apToken[jj] ){ - if( apToken[jj]->pOp && apToken[jj]->pOp->iPrecedence==1 && iLeft>0 ){ - apToken[jj]->pLeft = apToken[iLeft]; - apToken[jj]->pLeft->pParent = apToken[jj]; - apToken[iLeft] = 0; - } - iLeft = jj; - } - } - for(i=2; i<=12; i++){ - iLeft = -1; - for(jj=0; jj<nToken; jj++){ - Expr *pToken = apToken[jj]; - if( apToken[jj] ){ - if( pToken->pOp && !pToken->pLeft && pToken->pOp->iPrecedence==i ){ - int iRight = jj+1; - - iRight = jj+1; - for(iRight=jj+1; !apToken[iRight] && iRight<nToken; iRight++); - if( iRight==nToken || iLeft<0 || !ISTERM(iRight) || !ISTERM(iLeft) ){ - return TH_ERROR; - } - pToken->pLeft = apToken[iLeft]; - apToken[iLeft] = 0; - pToken->pLeft->pParent = pToken; - pToken->pRight = apToken[iRight]; - apToken[iRight] = 0; - pToken->pRight->pParent = pToken; - } - iLeft = jj; - } - } - } - for(jj=1; jj<nToken; jj++){ - assert( !apToken[jj] || !apToken[0] ); - if( apToken[jj] ){ - apToken[0] = apToken[jj]; - apToken[jj] = 0; - } - } - - return TH_OK; -} - -/* -** Parse a string containing a TH expression to a list of tokens. -*/ -static int exprParse( - Th_Interp *interp, /* Interpreter to leave error message in */ - const char *zExpr, /* Pointer to input string */ - int nExpr, /* Number of bytes at zExpr */ - Expr ***papToken, /* OUT: Array of tokens. */ - int *pnToken /* OUT: Size of token array */ -){ - int i; - - int rc = TH_OK; - int nToken = 0; - Expr **apToken = 0; - - for(i=0; rc==TH_OK && i<nExpr; ){ - char c = zExpr[i]; - if( th_isspace(c) ){ /* White-space */ - i++; - }else{ - Expr *pNew = (Expr *)Th_Malloc(interp, sizeof(Expr)); - const char *z = &zExpr[i]; - - switch (c) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - thNextNumber(interp, z, nExpr-i, &pNew->nValue); - break; - - case '$': - thNextVarname(interp, z, nExpr-i, &pNew->nValue); - break; - - case '{': case '[': { - thNextCommand(interp, z, nExpr-i, &pNew->nValue); - break; - } - - case '"': { - int iEnd = i; - while( ++iEnd<nExpr && zExpr[iEnd]!='"' ){ - if( zExpr[iEnd]=='\\' ) iEnd++; - } - if( iEnd<nExpr ){ - pNew->nValue = iEnd+1-i; - } - break; - } - - default: { - int j; - for(j=0; aOperator[j].zOp; j++){ - int nOp; - if( aOperator[j].iPrecedence==1 && nToken>0 ){ - Expr *pPrev = apToken[nToken-1]; - if( !pPrev->pOp || pPrev->pOp->eOp==OP_CLOSE_BRACKET ){ - continue; - } - } - nOp = th_strlen((const char *)aOperator[j].zOp); - if( (nExpr-i)>=nOp && 0==memcmp(aOperator[j].zOp, &zExpr[i], nOp) ){ - pNew->pOp = &aOperator[j]; - i += nOp; - break; - } - } - } - } - - if( pNew->pOp || pNew->nValue ){ - if( pNew->nValue ){ - /* A terminal. Copy the string value. */ - assert( !pNew->pOp ); - pNew->zValue = Th_Malloc(interp, pNew->nValue); - memcpy(pNew->zValue, z, pNew->nValue); - i += pNew->nValue; - } - if( (nToken%16)==0 ){ - /* Grow the apToken array. */ - Expr **apTokenOld = apToken; - apToken = Th_Malloc(interp, sizeof(Expr *)*(nToken+16)); - memcpy(apToken, apTokenOld, sizeof(Expr *)*nToken); - } - - /* Put the new token at the end of the apToken array */ - apToken[nToken] = pNew; - nToken++; - }else{ - Th_Free(interp, pNew); - rc = TH_ERROR; - } - } - } - - *papToken = apToken; - *pnToken = nToken; - return rc; -} - -/* -** Evaluate the string (zExpr, nExpr) as a Th expression. Store -** the result in the interpreter interp and return TH_OK if -** successful. If an error occurs, store an error message in -** the interpreter result and return an error code. -*/ -int Th_Expr(Th_Interp *interp, const char *zExpr, int nExpr){ - int rc; /* Return Code */ - int i; /* Loop counter */ - - int nToken = 0; - Expr **apToken = 0; - - if( nExpr<0 ){ - nExpr = th_strlen(zExpr); - } - - /* Parse the expression to a list of tokens. */ - rc = exprParse(interp, zExpr, nExpr, &apToken, &nToken); - - /* If the parsing was successful, create an expression tree from - ** the parsed list of tokens. If successful, apToken[0] is set - ** to point to the root of the expression tree. - */ - if( rc==TH_OK ){ - rc = exprMakeTree(interp, apToken, nToken); - } - - if( rc!=TH_OK ){ - Th_ErrorMessage(interp, "syntax error in expression: \"", zExpr, nExpr); - } - - /* Evaluate the expression tree. */ - if( rc==TH_OK ){ - rc = exprEval(interp, apToken[0]); - } - - /* Free memory allocated by exprParse(). */ - for(i=0; i<nToken; i++){ - exprFree(interp, apToken[i]); - } - Th_Free(interp, apToken); - - return rc; -} - -/* -** Allocate and return a pointer to a new hash-table. The caller should -** (eventually) delete the hash-table by passing it to Th_HashDelete(). -*/ -Th_Hash *Th_HashNew(Th_Interp *interp){ - Th_Hash *p; - p = Th_Malloc(interp, sizeof(Th_Hash)); - return p; -} - -/* -** Iterate through all values currently stored in the hash table. Invoke -** the callback function xCallback for each entry. The second argument -** passed to xCallback is a copy of the fourth argument passed to this -** function. -*/ -void Th_HashIterate( - Th_Interp *interp, - Th_Hash *pHash, - void (*xCallback)(Th_HashEntry *pEntry, void *pContext), - void *pContext -){ - int i; - for(i=0; i<TH_HASHSIZE; i++){ - Th_HashEntry *pEntry; - Th_HashEntry *pNext; - for(pEntry=pHash->a[i]; pEntry; pEntry=pNext){ - pNext = pEntry->pNext; - xCallback(pEntry, pContext); - } - } -} - -/* -** Helper function for Th_HashDelete(). -*/ -static void xFreeHashEntry(Th_HashEntry *pEntry, void *pContext){ - Th_Free((Th_Interp *)pContext, (void *)pEntry); -} - -/* -** Free a hash-table previously allocated by Th_HashNew(). -*/ -void Th_HashDelete(Th_Interp *interp, Th_Hash *pHash){ - if( pHash ){ - Th_HashIterate(interp, pHash, xFreeHashEntry, (void *)interp); - Th_Free(interp, pHash); - } -} - -/* -** This function is used to insert or delete hash table items, or to -** query a hash table for an existing item. -** -** If parameter op is less than zero, then the hash-table element -** identified by (zKey, nKey) is removed from the hash-table if it -** exists. NULL is returned. -** -** Otherwise, if the hash-table contains an item with key (zKey, nKey), -** a pointer to the associated Th_HashEntry is returned. If parameter -** op is greater than zero, then a new entry is added if one cannot -** be found. If op is zero, then NULL is returned if the item is -** not already present in the hash-table. -*/ -Th_HashEntry *Th_HashFind( - Th_Interp *interp, - Th_Hash *pHash, - const char *zKey, - int nKey, - int op /* -ve = delete, 0 = find, +ve = insert */ -){ - unsigned int iKey = 0; - int i; - Th_HashEntry *pRet; - Th_HashEntry **ppRet; - - if( nKey<0 ){ - nKey = th_strlen(zKey); - } - - for(i=0; i<nKey; i++){ - iKey = (iKey<<3) ^ iKey ^ zKey[i]; - } - iKey = iKey % TH_HASHSIZE; - - for(ppRet=&pHash->a[iKey]; (pRet=*ppRet); ppRet=&pRet->pNext){ - assert( pRet && ppRet && *ppRet==pRet ); - if( pRet->nKey==nKey && 0==memcmp(pRet->zKey, zKey, nKey) ) break; - } - - if( op<0 && pRet ){ - assert( ppRet && *ppRet==pRet ); - *ppRet = pRet->pNext; - Th_Free(interp, pRet); - pRet = 0; - } - - if( op>0 && !pRet ){ - pRet = (Th_HashEntry *)Th_Malloc(interp, sizeof(Th_HashEntry) + nKey); - pRet->zKey = (char *)&pRet[1]; - pRet->nKey = nKey; - memcpy(pRet->zKey, zKey, nKey); - pRet->pNext = pHash->a[iKey]; - pHash->a[iKey] = pRet; - } - - return pRet; -} - -/* -** This function is the same as the standard strlen() function, except -** that it returns 0 (instead of being undefined) if the argument is -** a null pointer. -*/ -int th_strlen(const char *zStr){ - int n = 0; - if( zStr ){ - while( zStr[n] ) n++; - } - return n; -} - -/* Whitespace characters: -** -** ' ' 0x20 -** '\t' 0x09 -** '\n' 0x0A -** '\v' 0x0B -** '\f' 0x0C -** '\r' 0x0D -** -** Whitespace characters have the 0x01 flag set. Decimal digits have the -** 0x2 flag set. Single byte printable characters have the 0x4 flag set. -** Alphabet characters have the 0x8 bit set. -** -** The special list characters have the 0x10 flag set -** -** { } [ ] \ ; ' " -** -** " 0x22 -** -*/ -static unsigned char aCharProp[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, /* 0x0. */ - 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x1. */ - 5, 4, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x2. */ - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 20, 4, 4, 4, 4, /* 0x3. */ - 4, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, /* 0x4. */ - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 20, 20, 20, 4, 4, /* 0x5. */ - 4, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, /* 0x6. */ - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 20, 4, 20, 4, 4, /* 0x7. */ - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x8. */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x9. */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA. */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB. */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC. */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD. */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE. */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xF. */ -}; - -/* -** Clone of the standard isspace() and isdigit function/macros. -*/ -int th_isspace(char c){ - return (aCharProp[(unsigned char)c] & 0x01); -} -int th_isdigit(char c){ - return (aCharProp[(unsigned char)c] & 0x02); -} -int th_isspecial(char c){ - return (aCharProp[(unsigned char)c] & 0x11); -} -int th_isalnum(char c){ - return (aCharProp[(unsigned char)c] & 0x0A); -} - -#ifndef LONGDOUBLE_TYPE -# define LONGDOUBLE_TYPE long double -#endif -typedef char u8; - - -/* -** Return TRUE if z is a pure numeric string. Return FALSE if the -** string contains any character which is not part of a number. If -** the string is numeric and contains the '.' character, set *realnum -** to TRUE (otherwise FALSE). -** -** An empty string is considered non-numeric. -*/ -static int sqlite3IsNumber(const char *z, int *realnum){ - int incr = 1; - if( *z=='-' || *z=='+' ) z += incr; - if( !th_isdigit(*(u8*)z) ){ - return 0; - } - z += incr; - if( realnum ) *realnum = 0; - while( th_isdigit(*(u8*)z) ){ z += incr; } - if( *z=='.' ){ - z += incr; - if( !th_isdigit(*(u8*)z) ) return 0; - while( th_isdigit(*(u8*)z) ){ z += incr; } - if( realnum ) *realnum = 1; - } - if( *z=='e' || *z=='E' ){ - z += incr; - if( *z=='+' || *z=='-' ) z += incr; - if( !th_isdigit(*(u8*)z) ) return 0; - while( th_isdigit(*(u8*)z) ){ z += incr; } - if( realnum ) *realnum = 1; - } - return *z==0; -} - -/* -** The string z[] is an ascii representation of a real number. -** Convert this string to a double. -** -** This routine assumes that z[] really is a valid number. If it -** is not, the result is undefined. -** -** This routine is used instead of the library atof() function because -** the library atof() might want to use "," as the decimal point instead -** of "." depending on how locale is set. But that would cause problems -** for SQL. So this routine always uses "." regardless of locale. -*/ -static int sqlite3AtoF(const char *z, double *pResult){ - int sign = 1; - const char *zBegin = z; - LONGDOUBLE_TYPE v1 = 0.0; - while( th_isspace(*(u8*)z) ) z++; - if( *z=='-' ){ - sign = -1; - z++; - }else if( *z=='+' ){ - z++; - } - while( th_isdigit(*(u8*)z) ){ - v1 = v1*10.0 + (*z - '0'); - z++; - } - if( *z=='.' ){ - LONGDOUBLE_TYPE divisor = 1.0; - z++; - while( th_isdigit(*(u8*)z) ){ - v1 = v1*10.0 + (*z - '0'); - divisor *= 10.0; - z++; - } - v1 /= divisor; - } - if( *z=='e' || *z=='E' ){ - int esign = 1; - int eval = 0; - LONGDOUBLE_TYPE scale = 1.0; - z++; - if( *z=='-' ){ - esign = -1; - z++; - }else if( *z=='+' ){ - z++; - } - while( th_isdigit(*(u8*)z) ){ - eval = eval*10 + *z - '0'; - z++; - } - while( eval>=64 ){ scale *= 1.0e+64; eval -= 64; } - while( eval>=16 ){ scale *= 1.0e+16; eval -= 16; } - while( eval>=4 ){ scale *= 1.0e+4; eval -= 4; } - while( eval>=1 ){ scale *= 1.0e+1; eval -= 1; } - if( esign<0 ){ - v1 /= scale; - }else{ - v1 *= scale; - } - } - *pResult = sign<0 ? -v1 : v1; - return z - zBegin; -} - -/* -** Try to convert the string passed as arguments (z, n) to an integer. -** If successful, store the result in *piOut and return TH_OK. -** -** If the string cannot be converted to an integer, return TH_ERROR. -** If the interp argument is not NULL, leave an error message in the -** interpreter result too. -*/ -int Th_ToInt(Th_Interp *interp, const char *z, int n, int *piOut){ - int i = 0; - int iOut = 0; - - if( n<0 ){ - n = th_strlen(z); - } - - if( n>0 && (z[0]=='-' || z[0]=='+') ){ - i = 1; - } - for(; i<n; i++){ - if( !th_isdigit(z[i]) ){ - Th_ErrorMessage(interp, "expected integer, got: \"", z, n); - return TH_ERROR; - } - iOut = iOut * 10 + (z[i] - 48); - } - - if( n>0 && z[0]=='-' ){ - iOut *= -1; - } - - *piOut = iOut; - return TH_OK; -} - -/* -** Try to convert the string passed as arguments (z, n) to a double. -** If successful, store the result in *pfOut and return TH_OK. -** -** If the string cannot be converted to a double, return TH_ERROR. -** If the interp argument is not NULL, leave an error message in the -** interpreter result too. -*/ -int Th_ToDouble( - Th_Interp *interp, - const char *z, - int n, - double *pfOut -){ - if( !sqlite3IsNumber((const char *)z, 0) ){ - Th_ErrorMessage(interp, "expected number, got: \"", z, n); - return TH_ERROR; - } - - sqlite3AtoF((const char *)z, pfOut); - return TH_OK; -} - -/* -** Set the result of the interpreter to the th1 representation of -** the integer iVal and return TH_OK. -*/ -int Th_SetResultInt(Th_Interp *interp, int iVal){ - int isNegative = 0; - char zBuf[32]; - char *z = &zBuf[32]; - - if( iVal<0 ){ - isNegative = 1; - iVal = iVal * -1; - } - *(--z) = '\0'; - *(--z) = (char)(48+(iVal%10)); - while( (iVal = (iVal/10))>0 ){ - *(--z) = (char)(48+(iVal%10)); - assert(z>zBuf); - } - if( isNegative ){ - *(--z) = '-'; - } - - return Th_SetResult(interp, z, -1); -} - -/* -** Set the result of the interpreter to the th1 representation of -** the double fVal and return TH_OK. -*/ -int Th_SetResultDouble(Th_Interp *interp, double fVal){ - int i; /* Iterator variable */ - double v = fVal; /* Input value */ - char zBuf[128]; /* Output buffer */ - char *z = zBuf; /* Output cursor */ - int iDot = 0; /* Digit after which to place decimal point */ - int iExp = 0; /* Exponent (NN in eNN) */ - const char *zExp; /* String representation of iExp */ - - /* Precision: */ - #define INSIGNIFICANT 0.000000000001 - #define ROUNDER 0.0000000000005 - double insignificant = INSIGNIFICANT; - - /* If the real value is negative, write a '-' character to the - * output and transform v to the corresponding positive number. - */ - if( v<0.0 ){ - *z++ = '-'; - v *= -1.0; - } - - /* Normalize v to a value between 1.0 and 10.0. Integer - * variable iExp is set to the exponent. i.e the original - * value is (v * 10^iExp) (or the negative thereof). - */ - if( v>0.0 ){ - while( (v+ROUNDER)>=10.0 ) { iExp++; v *= 0.1; } - while( (v+ROUNDER)<1.0 ) { iExp--; v *= 10.0; } - } - v += ROUNDER; - - /* For a small (<12) positive exponent, move the decimal point - * instead of using the "eXX" notation. - */ - if( iExp>0 && iExp<12 ){ - iDot = iExp; - iExp = 0; - } - - /* For a small (>-4) negative exponent, write leading zeroes - * instead of using the "eXX" notation. - */ - if( iExp<0 && iExp>-4 ){ - *z++ = '0'; - *z++ = '.'; - for(i=0; i>(iExp+1); i--){ - *z++ = '0'; - } - iDot = -1; - iExp = 0; - } - - /* Output the digits in real value v. The value of iDot determines - * where (if at all) the decimal point is placed. - */ - for(i=0; i<=(iDot+1) || v>=insignificant; i++){ - *z++ = (char)(48 + (int)v); - v = (v - ((double)(int)v)) * 10.0; - insignificant *= 10.0; - if( iDot==i ){ - *z++ = '.'; - } - } - - /* If the exponent is not zero, add the "eXX" notation to the - * end of the string. - */ - if( iExp!=0 ){ - *z++ = 'e'; - Th_SetResultInt(interp, iExp); - zExp = Th_GetResult(interp, 0); - for(i=0; zExp[i]; i++){ - *z++ = zExp[i]; - } - } - - *z = '\0'; - return Th_SetResult(interp, zBuf, -1); -} DELETED src/th.h Index: src/th.h ================================================================== --- src/th.h +++ src/th.h @@ -1,183 +0,0 @@ - -/* This header file defines the external interface to the custom Scripting -** Language (TH) interpreter. TH is very similar to TCL but is not an -** exact clone. -*/ - -/* -** Before creating an interpreter, the application must allocate and -** populate an instance of the following structure. It must remain valid -** for the lifetime of the interpreter. -*/ -struct Th_Vtab { - void *(*xMalloc)(unsigned int); - void (*xFree)(void *); -}; -typedef struct Th_Vtab Th_Vtab; - -/* -** Opaque handle for interpeter. -*/ -typedef struct Th_Interp Th_Interp; - -/* -** Create and delete interpreters. -*/ -Th_Interp * Th_CreateInterp(Th_Vtab *pVtab); -void Th_DeleteInterp(Th_Interp *); - -/* -** Evaluate an TH program in the stack frame identified by parameter -** iFrame, according to the following rules: -** -** * If iFrame is 0, this means the current frame. -** -** * If iFrame is negative, then the nth frame up the stack, where n is -** the absolute value of iFrame. A value of -1 means the calling -** procedure. -** -** * If iFrame is +ve, then the nth frame from the bottom of the stack. -** An iFrame value of 1 means the toplevel (global) frame. -*/ -int Th_Eval(Th_Interp *interp, int iFrame, const char *zProg, int nProg); - -/* -** Evaluate a TH expression. The result is stored in the -** interpreter result. -*/ -int Th_Expr(Th_Interp *interp, const char *, int); - -/* -** Access TH variables in the current stack frame. If the variable name -** begins with "::", the lookup is in the top level (global) frame. -*/ -int Th_GetVar(Th_Interp *, const char *, int); -int Th_SetVar(Th_Interp *, const char *, int, const char *, int); -int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int); -int Th_UnsetVar(Th_Interp *, const char *, int); - -typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *); - -/* -** Register new commands. -*/ -int Th_CreateCommand( - Th_Interp *interp, - const char *zName, - /* int (*xProc)(Th_Interp *, void *, int, const char **, int *), */ - Th_CommandProc xProc, - void *pContext, - void (*xDel)(Th_Interp *, void *) -); - -/* -** Delete or rename commands. -*/ -int Th_RenameCommand(Th_Interp *, const char *, int, const char *, int); - -/* -** Push a new stack frame (local variable context) onto the interpreter -** stack, call the function supplied as parameter xCall with the two -** context arguments, -** -** xCall(interp, pContext1, pContext2) -** -** , then pop the frame off of the interpreter stack. The value returned -** by the xCall() function is returned as the result of this function. -** -** This is intended for use by the implementation of commands such as -** those created by [proc]. -*/ -int Th_InFrame(Th_Interp *interp, - int (*xCall)(Th_Interp *, void *pContext1, void *pContext2), - void *pContext1, - void *pContext2 -); - -/* -** Valid return codes for xProc callbacks. -*/ -#define TH_OK 0 -#define TH_ERROR 1 -#define TH_BREAK 2 -#define TH_RETURN 3 -#define TH_CONTINUE 4 - -/* -** Set and get the interpreter result. -*/ -int Th_SetResult(Th_Interp *, const char *, int); -const char *Th_GetResult(Th_Interp *, int *); -char *Th_TakeResult(Th_Interp *, int *); - -/* -** Set an error message as the interpreter result. This also -** sets the global stack-trace variable $::th_stack_trace. -*/ -int Th_ErrorMessage(Th_Interp *, const char *, const char *, int); - -/* -** Access the memory management functions associated with the specified -** interpreter. -*/ -void *Th_Malloc(Th_Interp *, int); -void Th_Free(Th_Interp *, void *); - -/* -** Functions for handling TH lists. -*/ -int Th_ListAppend(Th_Interp *, char **, int *, const char *, int); -int Th_SplitList(Th_Interp *, const char *, int, char ***, int **, int *); - -int Th_StringAppend(Th_Interp *, char **, int *, const char *, int); - -/* -** Functions for handling numbers and pointers. -*/ -int Th_ToInt(Th_Interp *, const char *, int, int *); -int Th_ToDouble(Th_Interp *, const char *, int, double *); -int Th_SetResultInt(Th_Interp *, int); -int Th_SetResultDouble(Th_Interp *, double); - -/* -** Drop in replacements for the corresponding standard library functions. -*/ -int th_strlen(const char *); -int th_isdigit(char); -int th_isspace(char); -int th_isalnum(char); -int th_isspecial(char); -char *th_strdup(Th_Interp *interp, const char *z, int n); - -/* -** Interfaces to register the language extensions. -*/ -int th_register_language(Th_Interp *interp); /* th_lang.c */ -int th_register_sqlite(Th_Interp *interp); /* th_sqlite.c */ -int th_register_vfs(Th_Interp *interp); /* th_vfs.c */ -int th_register_testvfs(Th_Interp *interp); /* th_testvfs.c */ -int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */ - -/* -** General purpose hash table from th_lang.c. -*/ -typedef struct Th_Hash Th_Hash; -typedef struct Th_HashEntry Th_HashEntry; -struct Th_HashEntry { - void *pData; - char *zKey; - int nKey; - Th_HashEntry *pNext; /* Internal use only */ -}; -Th_Hash *Th_HashNew(Th_Interp *); -void Th_HashDelete(Th_Interp *, Th_Hash *); -void Th_HashIterate(Th_Interp*,Th_Hash*,void (*x)(Th_HashEntry*, void*),void*); -Th_HashEntry *Th_HashFind(Th_Interp*, Th_Hash*, const char*, int, int); - -/* -** Useful functions from th_lang.c. -*/ -int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg); - -typedef struct Th_SubCommand {char *zName; Th_CommandProc xProc;} Th_SubCommand; -int Th_CallSubCommand(Th_Interp*,void*,int,const char**,int*,Th_SubCommand*); DELETED src/th_lang.c Index: src/th_lang.c ================================================================== --- src/th_lang.c +++ src/th_lang.c @@ -1,1072 +0,0 @@ - -/* -** This file contains the implementation of all of the TH language -** built-in commands. -** -** All built-in commands are implemented using the public interface -** declared in th.h, so this file serves as both a part of the language -** implementation and an example of how to extend the language with -** new commands. -*/ - -#include "config.h" -#include "th.h" -#include <string.h> -#include <assert.h> - -int Th_WrongNumArgs(Th_Interp *interp, const char *zMsg){ - Th_ErrorMessage(interp, "wrong # args: should be \"", zMsg, -1); - return TH_ERROR; -} - -/* -** Syntax: -** -** catch script ?varname? -*/ -static int catch_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - int rc; - - if( argc!=2 && argc!=3 ){ - return Th_WrongNumArgs(interp, "catch script ?varname?"); - } - - rc = Th_Eval(interp, 0, argv[1], -1); - if( argc==3 ){ - int nResult; - const char *zResult = Th_GetResult(interp, &nResult); - Th_SetVar(interp, argv[2], argl[2], zResult, nResult); - } - - Th_SetResultInt(interp, rc); - return TH_OK; -} - -/* -** TH Syntax: -** -** if expr1 body1 ?elseif expr2 body2? ? ?else? bodyN? -*/ -static int if_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - int rc = TH_OK; - - int iCond; /* Result of evaluating expression */ - int i; - - const char *zResult; - int nResult; - - if( argc<3 ){ - goto wrong_args; - } - - for(i=0; i<argc && rc==TH_OK; i+=3){ - if( i>argc-3 ){ - i = argc-3; - iCond = 1; - }else{ - if( TH_OK!=Th_Expr(interp, argv[i+1], argl[i+1]) ){ - return TH_ERROR; - } - zResult = Th_GetResult(interp, &nResult); - rc = Th_ToInt(interp, zResult, nResult, &iCond); - } - if( iCond && rc==TH_OK ){ - rc = Th_Eval(interp, 0, argv[i+2], -1); - break; - } - } - - return rc; - -wrong_args: - return Th_WrongNumArgs(interp, "if ..."); -} - -/* -** TH Syntax: -** -** expr expr -*/ -static int expr_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - if( argc!=2 ){ - return Th_WrongNumArgs(interp, "expr expression"); - } - - return Th_Expr(interp, argv[1], argl[1]); -} - -/* -** Evaluate the th1 script (zBody, nBody) in the local stack frame. -** Return the result of the evaluation, except if the result -** is TH_CONTINUE, return TH_OK instead. -*/ -static int eval_loopbody(Th_Interp *interp, const char *zBody, int nBody){ - int rc = Th_Eval(interp, 0, zBody, nBody); - if( rc==TH_CONTINUE ){ - rc = TH_OK; - } - return rc; -} - -/* -** TH Syntax: -** -** for init condition incr script -*/ -static int for_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - int rc; - int iCond; - - if( argc!=5 ){ - return Th_WrongNumArgs(interp, "for init condition incr script"); - } - - /* Evaluate the 'init' script */ - rc = Th_Eval(interp, 0, argv[1], -1); - - while( rc==TH_OK - && TH_OK==(rc = Th_Expr(interp, argv[2], -1)) - && TH_OK==(rc = Th_ToInt(interp, Th_GetResult(interp, 0), -1, &iCond)) - && iCond - && TH_OK==(rc = eval_loopbody(interp, argv[4], argl[4])) - ){ - rc = Th_Eval(interp, 0, argv[3], -1); - } - - if( rc==TH_BREAK ) rc = TH_OK; - return rc; -} - -/* -** TH Syntax: -** -** list ?arg1 ?arg2? ...? -*/ -static int list_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - char *zList = 0; - int nList = 0; - int i; - - for(i=1; i<argc; i++){ - Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); - } - - Th_SetResult(interp, zList, nList); - Th_Free(interp, zList); - - return TH_OK; -} - -/* -** TH Syntax: -** -** lindex list index -*/ -static int lindex_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - int iElem; - int rc; - - char **azElem; - int *anElem; - int nCount; - - if( argc!=3 ){ - return Th_WrongNumArgs(interp, "lindex list index"); - } - - if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){ - return TH_ERROR; - } - - rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount); - if( rc==TH_OK ){ - if( iElem<nCount && iElem>=0 ){ - Th_SetResult(interp, azElem[iElem], anElem[iElem]); - }else{ - Th_SetResult(interp, 0, 0); - } - Th_Free(interp, azElem); - } - - return rc; -} - -/* -** TH Syntax: -** -** llength list -*/ -static int llength_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - int nElem; - int rc; - - if( argc!=2 ){ - return Th_WrongNumArgs(interp, "llength list"); - } - - rc = Th_SplitList(interp, argv[1], argl[1], 0, 0, &nElem); - if( rc==TH_OK ){ - Th_SetResultInt(interp, nElem); - } - - return rc; -} - -/* -** TH Syntax: -** -** set varname ?value? -*/ -static int set_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - if( argc!=2 && argc!=3 ){ - return Th_WrongNumArgs(interp, "set varname ?value?"); - } - - if( argc==3 ){ - Th_SetVar(interp, argv[1], argl[1], argv[2], argl[2]); - } - return Th_GetVar(interp, argv[1], argl[1]); -} - -/* -** When a new command is created using the built-in [proc] command, an -** instance of the following structure is allocated and populated. A -** pointer to the structure is passed as the context (second) argument -** to function proc_call1() when the new command is executed. -*/ -typedef struct ProcDefn ProcDefn; -struct ProcDefn { - int nParam; /* Number of formal (non "args") parameters */ - char **azParam; /* Parameter names */ - int *anParam; /* Lengths of parameter names */ - char **azDefault; /* Default values */ - int *anDefault; /* Lengths of default values */ - int hasArgs; /* True if there is an "args" parameter */ - char *zProgram; /* Body of proc */ - int nProgram; /* Number of bytes at zProgram */ - char *zUsage; /* Usage message */ - int nUsage; /* Number of bytes at zUsage */ -}; - -/* This structure is used to temporarily store arguments passed to an -** invocation of a command created using [proc]. A pointer to an -** instance is passed as the second argument to the proc_call2() function. -*/ -typedef struct ProcArgs ProcArgs; -struct ProcArgs { - int argc; - const char **argv; - int *argl; -}; - -/* -** Each time a command created using [proc] is invoked, a new -** th1 stack frame is allocated (for the proc's local variables) and -** this function invoked. -** -** Argument pContext1 points to the associated ProcDefn structure. -** Argument pContext2 points to a ProcArgs structure that contains -** the arguments passed to this specific invocation of the proc. -*/ -static int proc_call2(Th_Interp *interp, void *pContext1, void *pContext2){ - int i; - ProcDefn *p = (ProcDefn *)pContext1; - ProcArgs *pArgs = (ProcArgs *)pContext2; - - /* Check if there are the right number of arguments. If there are - ** not, generate a usage message for the command. - */ - if( (pArgs->argc>(p->nParam+1) && !p->hasArgs) - || (pArgs->argc<=(p->nParam) && !p->azDefault[pArgs->argc-1]) - ){ - char *zUsage = 0; - int nUsage = 0; - Th_StringAppend(interp, &zUsage, &nUsage, pArgs->argv[0], pArgs->argl[0]); - Th_StringAppend(interp, &zUsage, &nUsage, p->zUsage, p->nUsage); - Th_StringAppend(interp, &zUsage, &nUsage, (const char *)"", 1); - Th_WrongNumArgs(interp, zUsage); - Th_Free(interp, zUsage); - return TH_ERROR; - } - - /* Populate the formal proc parameters. */ - for(i=0; i<p->nParam; i++){ - const char *zVal; - int nVal; - if( pArgs->argc>(i+1) ){ - zVal = pArgs->argv[i+1]; - nVal = pArgs->argl[i+1]; - }else{ - zVal = p->azDefault[i]; - nVal = p->anDefault[i]; - } - Th_SetVar(interp, p->azParam[i], p->anParam[i], zVal, nVal); - } - - /* Populate the "args" parameter, if it exists */ - if( p->hasArgs ){ - char *zArgs = 0; - int nArgs = 0; - for(i=p->nParam+1; i<pArgs->argc; i++){ - Th_ListAppend(interp, &zArgs, &nArgs, pArgs->argv[i], pArgs->argl[i]); - } - Th_SetVar(interp, (const char *)"args", -1, zArgs, nArgs); - } - - Th_SetResult(interp, 0, 0); - return Th_Eval(interp, 0, p->zProgram, p->nProgram); -} - -/* -** This function is the command callback registered for all commands -** created using the [proc] command. The second argument, pContext, -** is a pointer to the associated ProcDefn structure. -*/ -static int proc_call1( - Th_Interp *interp, - void *pContext, - int argc, - const char **argv, - int *argl -){ - int rc; - - ProcDefn *p = (ProcDefn *)pContext; - ProcArgs procargs; - - /* Call function proc_call2(), which will call Th_Eval() to evaluate - ** the body of the [proc], in a new Th stack frame. This is so that - ** the proc body has its own local variable context. - */ - procargs.argc = argc; - procargs.argv = argv; - procargs.argl = argl; - rc = Th_InFrame(interp, proc_call2, (void *)p, (void *)&procargs); - - if( rc==TH_RETURN ){ - rc = TH_OK; - } - return rc; -} - -/* -** This function is registered as the delete callback for all commands -** created using the built-in [proc] command. It is called automatically -** when a command created using [proc] is deleted. -** -** It frees the ProcDefn structure allocated when the command was created. -*/ -static void proc_del(Th_Interp *interp, void *pContext){ - ProcDefn *p = (ProcDefn *)pContext; - Th_Free(interp, (void *)p->zUsage); - Th_Free(interp, (void *)p); -} - -/* -** TH Syntax: -** -** proc name arglist code -*/ -static int proc_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - int rc; - char *zName; - - ProcDefn *p; - int nByte; - int i; - char *zSpace; - - char **azParam; - int *anParam; - int nParam; - - char *zUsage = 0; /* Build up a usage message here */ - int nUsage = 0; /* Number of bytes at zUsage */ - - if( argc!=4 ){ - return Th_WrongNumArgs(interp, "proc name arglist code"); - } - if( Th_SplitList(interp, argv[2], argl[2], &azParam, &anParam, &nParam) ){ - return TH_ERROR; - } - - /* Allocate the new ProcDefn structure. */ - nByte = sizeof(ProcDefn) + /* ProcDefn structure */ - (sizeof(char *) + sizeof(int)) * nParam + /* azParam, anParam */ - (sizeof(char *) + sizeof(int)) * nParam + /* azDefault, anDefault */ - argl[3] + /* zProgram */ - argl[2]; /* Space for copies of parameter names and default values */ - p = (ProcDefn *)Th_Malloc(interp, nByte); - - /* If the last parameter in the parameter list is "args", then set the - ** ProcDefn.hasArgs flag. The "args" parameter does not require an - ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays. - */ - if( anParam[nParam-1]==4 && 0==memcmp(azParam[nParam-1], "args", 4) ){ - p->hasArgs = 1; - nParam--; - } - - p->nParam = nParam; - p->azParam = (char **)&p[1]; - p->anParam = (int *)&p->azParam[nParam]; - p->azDefault = (char **)&p->anParam[nParam]; - p->anDefault = (int *)&p->azDefault[nParam]; - p->zProgram = (char *)&p->anDefault[nParam]; - memcpy(p->zProgram, argv[3], argl[3]); - p->nProgram = argl[3]; - zSpace = &p->zProgram[p->nProgram]; - - for(i=0; i<nParam; i++){ - char **az; - int *an; - int n; - if( Th_SplitList(interp, azParam[i], anParam[i], &az, &an, &n) ){ - goto error_out; - } - if( n<1 || n>2 ){ - const char expected[] = "expected parameter, got \""; - Th_ErrorMessage(interp, expected, azParam[i], anParam[i]); - Th_Free(interp, az); - goto error_out; - } - p->anParam[i] = an[0]; - p->azParam[i] = zSpace; - memcpy(zSpace, az[0], an[0]); - zSpace += an[0]; - if( n==2 ){ - p->anDefault[i] = an[1]; - p->azDefault[i] = zSpace; - memcpy(zSpace, az[1], an[1]); - zSpace += an[1]; - } - - Th_StringAppend(interp, &zUsage, &nUsage, (const char *)" ", 1); - if( n==2 ){ - Th_StringAppend(interp, &zUsage, &nUsage, (const char *)"?", 1); - Th_StringAppend(interp, &zUsage, &nUsage, az[0], an[0]); - Th_StringAppend(interp, &zUsage, &nUsage, (const char *)"?", 1); - }else{ - Th_StringAppend(interp, &zUsage, &nUsage, az[0], an[0]); - } - - Th_Free(interp, az); - } - assert( zSpace-(char *)p<=nByte ); - - /* If there is an "args" parameter, append it to the end of the usage - ** message. Set ProcDefn.zUsage to point at the usage message. It will - ** be freed along with the rest of the proc-definition by proc_del(). - */ - if( p->hasArgs ){ - Th_StringAppend(interp, &zUsage, &nUsage, (const char *)" ?args...?", -1); - } - p->zUsage = zUsage; - p->nUsage = nUsage; - - /* Register the new command with the th1 interpreter. */ - zName = (char *)argv[1]; - rc = Th_CreateCommand(interp, zName, proc_call1, (void *)p, proc_del); - if( rc==TH_OK ){ - Th_SetResult(interp, 0, 0); - } - - Th_Free(interp, azParam); - return TH_OK; - - error_out: - Th_Free(interp, azParam); - Th_Free(interp, zUsage); - return TH_ERROR; -} - -/* -** TH Syntax: -** -** rename oldcmd newcmd -*/ -static int rename_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - if( argc!=3 ){ - return Th_WrongNumArgs(interp, "rename oldcmd newcmd"); - } - return Th_RenameCommand(interp, argv[1], argl[1], argv[2], argl[2]); -} - -/* -** TH Syntax: -** -** break ?value...? -** continue ?value...? -** ok ?value...? -** error ?value...? -*/ -static int simple_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - if( argc!=1 && argc!=2 ){ - return Th_WrongNumArgs(interp, "return ?value?"); - } - if( argc==2 ){ - Th_SetResult(interp, argv[1], argl[1]); - } - return FOSSIL_PTR_TO_INT(ctx); -} - -/* -** TH Syntax: -** -** return ?-code code? ?value? -*/ -static int return_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - int iCode = TH_RETURN; - if( argc<1 || argc>4 ){ - return Th_WrongNumArgs(interp, "return ?-code code? ?value?"); - } - if( argc>2 ){ - int rc = Th_ToInt(interp, argv[2], argl[2], &iCode); - if( rc!=TH_OK ){ - return rc; - } - } - if( argc==2 || argc==4 ){ - Th_SetResult(interp, argv[argc-1], argl[argc-1]); - } - return iCode; -} - -/* -** TH Syntax: -** -** string compare STRING1 STRING2 -*/ -static int string_compare_command( - Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl -){ - const char *zRight; int nRight; - const char *zLeft; int nLeft; - - int i; - int iRes = 0; - - if( argc!=4 ){ - return Th_WrongNumArgs(interp, "string compare str1 str2"); - } - - zLeft = argv[2]; - nLeft = argl[2]; - zRight = argv[3]; - nRight = argl[3]; - - for(i=0; iRes==0 && i<nLeft && i<nRight; i++){ - iRes = zLeft[i]-zRight[i]; - } - if( iRes==0 ){ - iRes = nLeft-nRight; - } - - if( iRes<0 ) iRes = -1; - if( iRes>0 ) iRes = 1; - - return Th_SetResultInt(interp, iRes); -} - -/* -** TH Syntax: -** -** string first NEEDLE HAYSTACK -*/ -static int string_first_command( - Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl -){ - const char *zNeedle; - int nNeedle; - const char *zHaystack; - int nHaystack; - int i; - int iRes = -1; - - if( argc!=4 ){ - return Th_WrongNumArgs(interp, "string first needle haystack"); - } - - zNeedle = argv[2]; - nNeedle = argl[2]; - zHaystack = argv[3]; - nHaystack = argl[3]; - - for(i=0; i<(nHaystack-nNeedle); i++){ - if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){ - iRes = i; - break; - } - } - - return Th_SetResultInt(interp, iRes); -} - -/* -** TH Syntax: -** -** string is CLASS STRING -*/ -static int string_is_command( - Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl -){ - int i; - int iRes = 1; - if( argc!=4 ){ - return Th_WrongNumArgs(interp, "string is class string"); - } - if( argl[2]!=5 || 0!=memcmp(argv[2], "alnum", 5) ){ - Th_ErrorMessage(interp, "Expected alnum, got: ", argv[2], argl[2]); - return TH_ERROR; - } - - for(i=0; i<argl[3]; i++){ - if( !th_isalnum(argv[3][i]) ){ - iRes = 0; - } - } - - return Th_SetResultInt(interp, iRes); -} - -/* -** TH Syntax: -** -** string last NEEDLE HAYSTACK -*/ -static int string_last_command( - Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl -){ - const char *zNeedle; - int nNeedle; - const char *zHaystack; - int nHaystack; - int i; - int iRes = -1; - - if( argc!=4 ){ - return Th_WrongNumArgs(interp, "string first needle haystack"); - } - - zNeedle = argv[2]; - nNeedle = argl[2]; - zHaystack = argv[3]; - nHaystack = argl[3]; - - for(i=nHaystack-nNeedle-1; i>=0; i--){ - if( 0==memcmp(zNeedle, &zHaystack[i], nNeedle) ){ - iRes = i; - break; - } - } - - return Th_SetResultInt(interp, iRes); -} - -/* -** TH Syntax: -** -** string length STRING -*/ -static int string_length_command( - Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl -){ - if( argc!=3 ){ - return Th_WrongNumArgs(interp, "string length string"); - } - return Th_SetResultInt(interp, argl[2]); -} - -/* -** TH Syntax: -** -** string range STRING FIRST LAST -*/ -static int string_range_command( - Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl -){ - int iStart; - int iEnd; - - if( argc!=5 ){ - return Th_WrongNumArgs(interp, "string range string first last"); - } - - if( argl[4]==3 && 0==memcmp("end", argv[4], 3) ){ - iEnd = argl[2]; - }else if( Th_ToInt(interp, argv[4], argl[4], &iEnd) ){ - Th_ErrorMessage( - interp, "Expected \"end\" or integer, got:", argv[4], argl[4]); - return TH_ERROR; - } - if( Th_ToInt(interp, argv[3], argl[3], &iStart) ){ - return TH_ERROR; - } - - if( iStart<0 ) iStart = 0; - if( iEnd>=argl[2] ) iEnd = argl[2]-1; - if( iStart>iEnd ) iEnd = iStart-1; - - return Th_SetResult(interp, &argv[2][iStart], iEnd-iStart+1); -} - -/* -** TH Syntax: -** -** string repeat STRING COUNT -*/ -static int string_repeat_command( - Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl -){ - int n; - int i; - int nByte; - char *zByte; - - if( argc!=4 ){ - return Th_WrongNumArgs(interp, "string repeat string n"); - } - if( Th_ToInt(interp, argv[3], argl[3], &n) ){ - return TH_ERROR; - } - - nByte = argl[2] * n; - zByte = Th_Malloc(interp, nByte+1); - for(i=0; i<nByte; i+=argl[2]){ - memcpy(&zByte[i], argv[2], argl[2]); - } - - Th_SetResult(interp, zByte, nByte); - Th_Free(interp, zByte); - return TH_OK; -} - -/* -** TH Syntax: -** -** info exists VAR -*/ -static int info_exists_command( - Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl -){ - int rc; - - if( argc!=3 ){ - return Th_WrongNumArgs(interp, "info exists var"); - } - rc = Th_GetVar(interp, argv[2], argl[2]); - Th_SetResultInt(interp, rc?0:1); - return TH_OK; -} - -/* -** TH Syntax: -** -** unset VAR -*/ -static int unset_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - if( argc!=2 ){ - return Th_WrongNumArgs(interp, "unset var"); - } - return Th_UnsetVar(interp, argv[1], argl[1]); -} - -int Th_CallSubCommand( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl, - Th_SubCommand *aSub -){ - int i; - for(i=0; aSub[i].zName; i++){ - char *zName = (char *)aSub[i].zName; - if( th_strlen(zName)==argl[1] && 0==memcmp(zName, argv[1], argl[1]) ){ - return aSub[i].xProc(interp, ctx, argc, argv, argl); - } - } - - Th_ErrorMessage(interp, "Expected sub-command, got:", argv[1], argl[1]); - return TH_ERROR; -} - -/* -** TH Syntax: -** -** string compare STR1 STR2 -** string first NEEDLE HAYSTACK ?STARTINDEX? -** string is CLASS STRING -** string last NEEDLE HAYSTACK ?STARTINDEX? -** string length STRING -** string range STRING FIRST LAST -** string repeat STRING COUNT -*/ -static int string_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - Th_SubCommand aSub[] = { - { "compare", string_compare_command }, - { "first", string_first_command }, - { "is", string_is_command }, - { "last", string_last_command }, - { "length", string_length_command }, - { "range", string_range_command }, - { "repeat", string_repeat_command }, - { 0, 0 } - }; - return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub); -} - -/* -** TH Syntax: -** -** info exists VARNAME -*/ -static int info_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - Th_SubCommand aSub[] = { - { "exists", info_exists_command }, - { 0, 0 } - }; - return Th_CallSubCommand(interp, ctx, argc, argv, argl, aSub); -} - -/* -** Convert the script level frame specification (used by the commands -** [uplevel] and [upvar]) in (zFrame, nFrame) to an integer frame as -** used by Th_LinkVar() and Th_Eval(). If successful, write the integer -** frame level to *piFrame and return TH_OK. Otherwise, return TH_ERROR -** and leave an error message in the interpreter result. -*/ -static int thToFrame( - Th_Interp *interp, - const char *zFrame, - int nFrame, - int *piFrame -){ - int iFrame; - if( th_isdigit(zFrame[0]) ){ - int rc = Th_ToInt(interp, zFrame, nFrame, &iFrame); - if( rc!=TH_OK ) return rc; - iFrame = iFrame * -1; - }else if( zFrame[0]=='#' ){ - int rc = Th_ToInt(interp, &zFrame[1], nFrame-1, &iFrame); - if( rc!=TH_OK ) return rc; - iFrame = iFrame + 1; - }else{ - return TH_ERROR; - } - *piFrame = iFrame; - return TH_OK; -} - -/* -** TH Syntax: -** -** uplevel ?LEVEL? SCRIPT -*/ -static int uplevel_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - int iFrame = -1; - - if( argc!=2 && argc!=3 ){ - return Th_WrongNumArgs(interp, "uplevel ?level? script..."); - } - if( argc==3 && TH_OK!=thToFrame(interp, argv[1], argl[1], &iFrame) ){ - return TH_ERROR; - } - return Th_Eval(interp, iFrame, argv[argc-1], -1); -} - -/* -** TH Syntax: -** -** upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR ...? -*/ -static int upvar_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - int iVar = 1; - int iFrame = -1; - int rc = TH_OK; - int i; - - if( TH_OK==thToFrame(0, argv[1], argl[1], &iFrame) ){ - iVar++; - } - if( argc==iVar || (argc-iVar)%2 ){ - return Th_WrongNumArgs(interp, - "upvar frame othervar myvar ?othervar myvar...?"); - } - for(i=iVar; rc==TH_OK && i<argc; i=i+2){ - rc = Th_LinkVar(interp, argv[i+1], argl[i+1], iFrame, argv[i], argl[i]); - } - return rc; -} - -/* -** TH Syntax: -** -** breakpoint ARGS -** -** This command does nothing at all. Its purpose in life is to serve -** as a point for setting breakpoints in a debugger. -*/ -static int breakpoint_command( - Th_Interp *interp, - void *ctx, - int argc, - const char **argv, - int *argl -){ - int cnt = 0; - cnt++; - return TH_OK; -} - -/* -** Register the built-in th1 language commands with interpreter interp. -** Usually this is called soon after interpreter creation. -*/ -int th_register_language(Th_Interp *interp){ - /* Array of built-in commands. */ - struct _Command { - const char *zName; - Th_CommandProc xProc; - void *pContext; - } aCommand[] = { - {"catch", catch_command, 0}, - {"expr", expr_command, 0}, - {"for", for_command, 0}, - {"if", if_command, 0}, - {"info", info_command, 0}, - {"lindex", lindex_command, 0}, - {"list", list_command, 0}, - {"llength", llength_command, 0}, - {"proc", proc_command, 0}, - {"rename", rename_command, 0}, - {"set", set_command, 0}, - {"string", string_command, 0}, - {"unset", unset_command, 0}, - {"uplevel", uplevel_command, 0}, - {"upvar", upvar_command, 0}, - - {"breakpoint", breakpoint_command, 0}, - - {"return", return_command, 0}, - {"break", simple_command, (void *)TH_BREAK}, - {"continue", simple_command, (void *)TH_CONTINUE}, - {"error", simple_command, (void *)TH_ERROR}, - - {0, 0, 0} - }; - int i; - - /* Add the language commands. */ - for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){ - void *ctx; - if ( !aCommand[i].zName || !aCommand[i].xProc ) continue; - ctx = aCommand[i].pContext; - Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0); - } - - return TH_OK; -} Index: src/th_main.c ================================================================== --- src/th_main.c +++ src/th_main.c @@ -20,33 +20,13 @@ */ #include "config.h" #include "th_main.h" /* -** Global variable counting the number of outstanding calls to malloc() -** made by the th1 implementation. This is used to catch memory leaks -** in the interpreter. Obviously, it also means th1 is not threadsafe. +** Interfaces to register the scripting language extensions. */ -static int nOutstandingMalloc = 0; - -/* -** Implementations of malloc() and free() to pass to the interpreter. -*/ -static void *xMalloc(unsigned int n){ - void *p = fossil_malloc(n); - if( p ){ - nOutstandingMalloc++; - } - return p; -} -static void xFree(void *p){ - if( p ){ - nOutstandingMalloc--; - } - free(p); -} -static Th_Vtab vtab = { xMalloc, xFree }; +int register_tcl(Jim_Interp *interp, void *pContext); /* th_tcl.c */ /* ** Generate a TH1 trace message if debugging is enabled. */ void Th_Trace(const char *zFormat, ...){ @@ -58,28 +38,24 @@ /* ** True if output is enabled. False if disabled. */ -static int enableOutput = 1; +static long enableOutput = 1; /* ** TH command: enable_output BOOLEAN ** ** Enable or disable the puts and hputs commands. */ -static int enableOutputCmd( - Th_Interp *interp, - void *p, - int argc, - const char **argv, - int *argl -){ +static int enableOutputCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ if( argc!=2 ){ - return Th_WrongNumArgs(interp, "enable_output BOOLEAN"); + Jim_WrongNumArgs(interp, 1, argv, "BOOLEAN"); + return JIM_ERR; } - return Th_ToInt(interp, argv[1], argl[1], &enableOutput); + return Jim_GetLong(interp, argv[1], &enableOutput); } /* ** Send text to the appropriate output: Either to the console ** or to the CGI reply buffer. @@ -98,152 +74,152 @@ fflush(stdout); } if( encode ) free((char*)z); } } + +static void sendTextObj(Jim_Obj *objPtr, int encode) +{ + sendText(Jim_String(objPtr), Jim_Length(objPtr), encode); +} /* ** TH command: puts STRING +** +** Output STRING as HTML +*/ +static int putsCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + if( argc!=2 ){ + Jim_WrongNumArgs(interp, 1, argv, "STRING"); + return JIM_ERR; + } + sendText(Jim_String(argv[1]), -1, 1); + return JIM_OK; +} + +/* ** TH command: html STRING ** -** Output STRING as HTML (html) or unchanged (puts). +** Output STRING unchanged */ -static int putsCmd( - Th_Interp *interp, - void *pConvert, - int argc, - const char **argv, - int *argl -){ +static int htmlCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ if( argc!=2 ){ - return Th_WrongNumArgs(interp, "puts STRING"); + Jim_WrongNumArgs(interp, 1, argv, "STRING"); + return JIM_ERR; } - sendText((char*)argv[1], argl[1], pConvert!=0); - return TH_OK; + sendText(Jim_String(argv[1]), -1, 0); + return JIM_OK; } /* ** TH command: wiki STRING ** ** Render the input string as wiki. */ -static int wikiCmd( - Th_Interp *interp, - void *p, - int argc, - const char **argv, - int *argl -){ +static int wikiCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ if( argc!=2 ){ - return Th_WrongNumArgs(interp, "wiki STRING"); + Jim_WrongNumArgs(interp, 1, argv, "STRING"); + return JIM_ERR; } if( enableOutput ){ Blob src; - blob_init(&src, (char*)argv[1], argl[1]); + blob_init(&src, Jim_String(argv[1]), Jim_Length(argv[1])); wiki_convert(&src, 0, WIKI_INLINE); blob_reset(&src); } - return TH_OK; + return JIM_OK; } /* ** TH command: htmlize STRING ** ** Escape all characters of STRING which have special meaning in HTML. ** Return a new string result. */ -static int htmlizeCmd( - Th_Interp *interp, - void *p, - int argc, - const char **argv, - int *argl -){ +static int htmlizeCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ char *zOut; if( argc!=2 ){ - return Th_WrongNumArgs(interp, "htmlize STRING"); + Jim_WrongNumArgs(interp, 1, argv, "STRING"); + return JIM_ERR; } - zOut = htmlize((char*)argv[1], argl[1]); - Th_SetResult(interp, zOut, -1); + zOut = htmlize(Jim_String(argv[1]), Jim_Length(argv[1])); + Jim_SetResultString(interp, zOut, -1); free(zOut); - return TH_OK; + return JIM_OK; } /* ** TH command: date ** ** Return a string which is the current time and date. If the ** -local option is used, the date appears using localtime instead ** of UTC. */ -static int dateCmd( - Th_Interp *interp, - void *p, - int argc, - const char **argv, - int *argl -){ +static int dateCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ char *zOut; - if( argc>=2 && argl[1]==6 && memcmp(argv[1],"-local",6)==0 ){ + if( argc>=2 && Jim_CompareStringImmediate(interp, argv[1], "-local")) { zOut = db_text("??", "SELECT datetime('now','localtime')"); }else{ zOut = db_text("??", "SELECT datetime('now')"); } - Th_SetResult(interp, zOut, -1); + Jim_SetResultString(interp, zOut, -1); free(zOut); - return TH_OK; + return JIM_OK; } /* ** TH command: hascap STRING ** ** Return true if the user has all of the capabilities listed in STRING. */ -static int hascapCmd( - Th_Interp *interp, - void *p, - int argc, - const char **argv, - int *argl -){ +static int hascapCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ int rc; + const char *str; + int len; if( argc!=2 ){ - return Th_WrongNumArgs(interp, "hascap STRING"); + Jim_WrongNumArgs(interp, 1, argv, "STRING"); + return JIM_ERR; } - rc = login_has_capability((char*)argv[1],argl[1]); + str = Jim_GetString(argv[1], &len); + rc = login_has_capability(str, len); if( g.thTrace ){ - Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc); + Th_Trace("[hascap %#h] => %d<br />\n", len, str, rc); } - Th_SetResultInt(interp, rc); - return TH_OK; + Jim_SetResultInt(interp, rc); + return JIM_OK; } /* ** TH command: anycap STRING ** ** Return true if the user has any one of the capabilities listed in STRING. */ -static int anycapCmd( - Th_Interp *interp, - void *p, - int argc, - const char **argv, - int *argl -){ +static int anycapCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ int rc = 0; int i; + const char *str; + int len; if( argc!=2 ){ - return Th_WrongNumArgs(interp, "anycap STRING"); + Jim_WrongNumArgs(interp, 1, argv, "STRING"); + return JIM_ERR; } - for(i=0; rc==0 && i<argl[1]; i++){ - rc = login_has_capability((char*)&argv[1][i],1); + str = Jim_GetString(argv[1], &len); + for(i=0; rc==0 && i<len; i++){ + rc = login_has_capability(&str[i],1); } if( g.thTrace ){ - Th_Trace("[hascap %#h] => %d<br />\n", argl[1], argv[1], rc); + Th_Trace("[hascap %#h] => %d<br />\n", len, str, rc); } - Th_SetResultInt(interp, rc); - return TH_OK; + Jim_SetResultInt(interp, rc); + return JIM_OK; } /* ** TH1 command: combobox NAME TEXT-LIST NUMLINES ** @@ -252,44 +228,36 @@ ** currently selected value. TEXT-LIST is a list of possible ** values for the combobox. NUMLINES is 1 for a true combobox. ** If NUMLINES is greater than one then the display is a listbox ** with the number of lines given. */ -static int comboboxCmd( - Th_Interp *interp, - void *p, - int argc, - const char **argv, - int *argl -){ +static int comboboxCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ if( argc!=4 ){ - return Th_WrongNumArgs(interp, "combobox NAME TEXT-LIST NUMLINES"); + Jim_WrongNumArgs(interp, 1, argv, "NAME TEXT-LIST NUMLINES"); + return JIM_ERR; } if( enableOutput ){ - int height; - Blob name; - int nValue; - const char *zValue; + long height; char *z, *zH; int nElem; - int *aszElem; - char **azElem; int i; + Jim_Obj *objPtr; + Jim_Obj *varObjPtr; - if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR; - Th_SplitList(interp, argv[2], argl[2], &azElem, &aszElem, &nElem); - blob_init(&name, (char*)argv[1], argl[1]); - zValue = Th_Fetch(blob_str(&name), &nValue); + if( Jim_GetLong(interp, argv[3], &height) ) return JIM_ERR; + nElem = Jim_ListLength(interp, argv[2]); + + varObjPtr = Jim_GetVariable(g.interp, argv[1], JIM_NONE); z = mprintf("<select name=\"%z\" size=\"%d\">", - htmlize(blob_buffer(&name), blob_size(&name)), height); + htmlize(Jim_String(varObjPtr), Jim_Length(varObjPtr)), height); sendText(z, -1, 0); free(z); - blob_reset(&name); for(i=0; i<nElem; i++){ - zH = htmlize((char*)azElem[i], aszElem[i]); - if( zValue && aszElem[i]==nValue - && memcmp(zValue, azElem[i], nValue)==0 ){ + Jim_ListIndex(interp, argv[2], i, &objPtr, JIM_NONE); + zH = htmlize(Jim_String(objPtr), Jim_Length(objPtr)); + if( varObjPtr && Jim_StringEqObj(varObjPtr, objPtr)) { z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>", zH, zH); }else{ z = mprintf("<option value=\"%s\">%s</option>", zH, zH); } @@ -296,77 +264,67 @@ free(zH); sendText(z, -1, 0); free(z); } sendText("</select>", -1, 0); - Th_Free(interp, azElem); } - return TH_OK; + return JIM_OK; } /* ** TH1 command: linecount STRING MAX MIN ** ** Return one more than the number of \n characters in STRING. But ** never return less than MIN or more than MAX. */ -static int linecntCmd( - Th_Interp *interp, - void *p, - int argc, - const char **argv, - int *argl -){ +static int linecntCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ const char *z; int size, n, i; - int iMin, iMax; + jim_wide iMin, iMax; if( argc!=4 ){ - return Th_WrongNumArgs(interp, "linecount STRING MAX MIN"); + Jim_WrongNumArgs(interp, 1, argv, "STRING MAX MIN"); + return JIM_ERR; } - if( Th_ToInt(interp, argv[2], argl[2], &iMax) ) return TH_ERROR; - if( Th_ToInt(interp, argv[3], argl[3], &iMin) ) return TH_ERROR; - z = argv[1]; - size = argl[1]; + if( Jim_GetWide(interp, argv[2], &iMax) ) return JIM_ERR; + if( Jim_GetWide(interp, argv[3], &iMin) ) return JIM_ERR; + z = Jim_GetString(argv[1], &size); for(n=1, i=0; i<size; i++){ if( z[i]=='\n' ){ n++; if( n>=iMax ) break; } } if( n<iMin ) n = iMin; if( n>iMax ) n = iMax; - Th_SetResultInt(interp, n); - return TH_OK; + Jim_SetResultInt(interp, n); + return JIM_OK; } /* ** TH1 command: repository ?BOOLEAN? ** ** Return the fully qualified file name of the open repository or an empty ** string if one is not currently open. Optionally, it will attempt to open ** the repository if the boolean argument is non-zero. */ -static int repositoryCmd( - Th_Interp *interp, - void *p, - int argc, - const char **argv, - int *argl -){ - int openRepository; +static int repositoryCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + long openRepository; if( argc!=1 && argc!=2 ){ - return Th_WrongNumArgs(interp, "repository ?BOOLEAN?"); + Jim_WrongNumArgs(interp, 1, argv, "BOOLEAN"); + return JIM_ERR; } if( argc==2 ){ - if( Th_ToInt(interp, argv[1], argl[1], &openRepository) ){ - return TH_ERROR; + if( Jim_GetLong(interp, argv[1], &openRepository) != JIM_OK){ + return JIM_ERR; } if( openRepository ) db_find_and_open_repository(OPEN_OK_NOT_FOUND, 0); } - Th_SetResult(interp, g.zRepositoryName, -1); - return TH_OK; + Jim_SetResultString(interp, g.zRepositoryName, -1); + return JIM_OK; } /* ** Make sure the interpreter has been initialized. Initialize it if ** it has not been already. @@ -374,39 +332,43 @@ ** The interpreter is stored in the g.interp global variable. */ void Th_FossilInit(void){ static struct _Command { const char *zName; - Th_CommandProc xProc; - void *pContext; + Jim_CmdProc xProc; } aCommand[] = { - {"anycap", anycapCmd, 0}, - {"combobox", comboboxCmd, 0}, - {"enable_output", enableOutputCmd, 0}, - {"linecount", linecntCmd, 0}, - {"hascap", hascapCmd, 0}, - {"htmlize", htmlizeCmd, 0}, - {"date", dateCmd, 0}, - {"html", putsCmd, 0}, - {"puts", putsCmd, (void*)1}, - {"wiki", wikiCmd, 0}, - {"repository", repositoryCmd, 0}, - {0, 0, 0} + {"anycap", anycapCmd, }, + {"combobox", comboboxCmd, }, + {"enable_output", enableOutputCmd, }, + {"linecount", linecntCmd, }, + {"hascap", hascapCmd, }, + {"htmlize", htmlizeCmd, }, + {"date", dateCmd, }, + {"html", htmlCmd, }, + {"puts", putsCmd, }, + {"wiki", wikiCmd, }, + {"repository", repositoryCmd, }, + {0, 0} }; if( g.interp==0 ){ int i; - g.interp = Th_CreateInterp(&vtab); - th_register_language(g.interp); /* Basic scripting commands. */ + /* Create and initialize the interpreter */ + g.interp = Jim_CreateInterp(); + Jim_RegisterCoreCommands(g.interp); + + /* Register static extensions */ + Jim_InitStaticExtensions(g.interp); + #ifdef FOSSIL_ENABLE_TCL - if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){ - th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */ + if( getenv("FOSSIL_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){ + register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */ } #endif for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){ if ( !aCommand[i].zName || !aCommand[i].xProc ) continue; - Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc, - aCommand[i].pContext, 0); + Jim_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc, NULL, + NULL); } } } /* @@ -416,43 +378,60 @@ Th_FossilInit(); if( zValue ){ if( g.thTrace ){ Th_Trace("set %h {%h}<br />\n", zName, zValue); } - Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); + Jim_SetVariableStrWithStr(g.interp, zName, zValue); } } /* ** Unset a variable. */ void Th_Unstore(const char *zName){ if( g.interp ){ - Th_UnsetVar(g.interp, (char*)zName, -1); + Jim_Obj *nameObjPtr = Jim_NewStringObj(g.interp, zName, -1); + Jim_UnsetVariable(g.interp, nameObjPtr, JIM_NONE); + Jim_FreeNewObj(g.interp, nameObjPtr); } } /* -** Retrieve a string value from the interpreter. If no such +** Retrieve a string value (variable) from the interpreter. If no such ** variable exists, return NULL. */ -char *Th_Fetch(const char *zName, int *pSize){ - int rc; +const char *Th_Fetch(const char *zName){ + Jim_Obj *objPtr; + Th_FossilInit(); - rc = Th_GetVar(g.interp, (char*)zName, -1); - if( rc==TH_OK ){ - return (char*)Th_GetResult(g.interp, pSize); - }else{ - return 0; - } + + objPtr = Jim_GetVariableStr(g.interp, zName, JIM_NONE); + + return objPtr ? Jim_String(objPtr) : NULL; +} + +/** + * Like Th_Fetch() except the variable name may not be null terminated. + * Instead, the length of the name is supplied as 'namelen'. + */ +const char *Th_GetVar(Jim_Interp *interp, const char *name, int namelen){ + Jim_Obj *nameObjPtr, *varObjPtr; + + nameObjPtr = Jim_NewStringObj(interp, name, namelen); + Jim_IncrRefCount(nameObjPtr); + varObjPtr = Jim_GetVariable(interp, nameObjPtr, 0); + Jim_DecrRefCount(interp, nameObjPtr); + + return varObjPtr ? Jim_String(varObjPtr) : NULL; } /* ** Return true if the string begins with the TH1 begin-script ** tag: <th1>. */ static int isBeginScriptTag(const char *z){ + /* XXX: Should we also allow <tcl>? */ return z[0]=='<' && (z[1]=='t' || z[1]=='T') && (z[2]=='h' || z[2]=='H') && z[3]=='1' && z[4]=='>'; @@ -461,10 +440,11 @@ /* ** Return true if the string begins with the TH1 end-script ** tag: </th1>. */ static int isEndScriptTag(const char *z){ + /* XXX: Should we also allow </tcl>? */ return z[0]=='<' && z[1]=='/' && (z[2]=='t' || z[2]=='T') && (z[3]=='h' || z[3]=='H') && z[4]=='1' @@ -513,12 +493,12 @@ ** on either stdout or into CGI. */ int Th_Render(const char *z){ int i = 0; int n; - int rc = TH_OK; - char *zResult; + int rc = JIM_OK; + const char *zResult; Th_FossilInit(); while( z[i] ){ if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){ const char *zVar; int nVar; @@ -532,47 +512,50 @@ /* Variables of the form $aaa are output raw */ zVar = &z[i+1]; nVar = n; encode = 0; } - rc = Th_GetVar(g.interp, (char*)zVar, nVar); + zResult = Th_GetVar(g.interp, zVar, nVar); z += i+1+n; i = 0; - zResult = (char*)Th_GetResult(g.interp, &n); - sendText((char*)zResult, n, encode); + if (zResult) { + sendText(zResult, -1, encode); + } }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ + Jim_Obj *objPtr; sendText(z, i, 0); z += i+5; for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){} - rc = Th_Eval(g.interp, 0, (const char*)z, i); - if( rc!=TH_OK ) break; + /* XXX: Would be nice to record the source location in case of error */ + objPtr = Jim_NewStringObj(g.interp, z, i); + rc = Jim_EvalObj(g.interp, objPtr); + if( rc!=JIM_OK ) break; z += i; if( z[0] ){ z += 6; } i = 0; }else{ i++; } } - if( rc==TH_ERROR ){ + if( rc==JIM_ERR ){ sendText("<hr><p class=\"thmainError\">ERROR: ", -1, 0); - zResult = (char*)Th_GetResult(g.interp, &n); - sendText((char*)zResult, n, 1); + sendTextObj(Jim_GetResult(g.interp), 1); sendText("</p>", -1, 0); }else{ sendText(z, i, 0); } return rc; } /* -** COMMAND: test-th-render +** COMMAND: test-script-render */ -void test_th_render(void){ +void test_script_render(void){ Blob in; if( g.argc<3 ){ usage("FILE"); } db_open_config(0); /* Needed for global "tcl" setting. */ blob_zero(&in); blob_read_from_file(&in, g.argv[2]); Th_Render(blob_str(&in)); } Index: src/th_tcl.c ================================================================== --- src/th_tcl.c +++ src/th_tcl.c @@ -6,18 +6,18 @@ ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ******************************************************************************* -** This file contains code used to bridge the TH1 and Tcl scripting languages. +** This file contains code used to bridge the Jim and Tcl scripting languages. */ #include "config.h" #ifdef FOSSIL_ENABLE_TCL -#include "th.h" +#include "jim.h" #include "tcl.h" /* ** Are we being compiled against Tcl 8.6 or higher? */ @@ -30,23 +30,23 @@ #define USE_TCL_EVALOBJV 1 #endif /* ** These macros are designed to reduce the redundant code required to marshal -** arguments from TH1 to Tcl. +** arguments from Jim to Tcl. */ #define USE_ARGV_TO_OBJV() \ int objc; \ Tcl_Obj **objv; \ int i; -#define COPY_ARGV_TO_OBJV() \ - objc = argc-1; \ - objv = (Tcl_Obj **)ckalloc((unsigned)(objc * sizeof(Tcl_Obj *))); \ - for(i=1; i<argc; i++){ \ - objv[i-1] = Tcl_NewStringObj(argv[i], argl[i]); \ - Tcl_IncrRefCount(objv[i-1]); \ +#define COPY_ARGV_TO_OBJV() \ + objc = argc-1; \ + objv = (Tcl_Obj **)ckalloc((unsigned)(objc * sizeof(Tcl_Obj *))); \ + for(i=1; i<argc; i++){ \ + objv[i-1] = Tcl_NewStringObj(Jim_String(argv[i]), Jim_Length(argv[i])); \ + Tcl_IncrRefCount(objv[i-1]); \ } #define FREE_ARGV_TO_OBJV() \ for(i=1; i<argc; i++){ \ Tcl_DecrRefCount(objv[i-1]); \ @@ -59,16 +59,16 @@ */ #define GET_CTX_TCL_INTERP(ctx) \ ((struct TclContext *)(ctx))->interp /* -** Creates and initializes a Tcl interpreter for use with the specified TH1 +** Creates and initializes a Tcl interpreter for use with the specified Jim ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied ** by the caller. This must be declared here because quite a few functions in ** this file need to use it before it can be defined. */ -static int createTclInterp(Th_Interp *interp, void *pContext); +static int createTclInterp(Jim_Interp *interp, void *pContext); /* ** Returns the Tcl interpreter result as a string with the associated length. ** If the Tcl interpreter or the Tcl result are NULL, the length will be 0. ** If the length pointer is NULL, the length will not be stored. @@ -89,11 +89,11 @@ } return Tcl_GetStringFromObj(resultPtr, pN); } /* -** Tcl context information used by TH1. This structure definition has been +** Tcl context information used by Jim. This structure definition has been ** copied from and should be kept in sync with the one in "main.c". */ struct TclContext { int argc; char **argv; @@ -104,36 +104,36 @@ ** Syntax: ** ** tclEval arg ?arg ...? */ static int tclEval_command( - Th_Interp *interp, - void *ctx, + Jim_Interp *interp, int argc, - const char **argv, - int *argl + Jim_Obj *const *argv ){ Tcl_Interp *tclInterp; Tcl_Obj *objPtr; int rc; int nResult; const char *zResult; + void *ctx = Jim_CmdPrivData(interp); - if ( createTclInterp(interp, ctx)!=TH_OK ){ - return TH_ERROR; + if ( createTclInterp(interp, ctx)!=JIM_OK ){ + return JIM_ERR; } if( argc<2 ){ - return Th_WrongNumArgs(interp, "tclEval arg ?arg ...?"); + Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?"); + return JIM_ERR; } tclInterp = GET_CTX_TCL_INTERP(ctx); if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ - Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); - return TH_ERROR; + Jim_SetResultString(interp, "invalid Tcl interpreter", -1); + return JIM_ERR; } Tcl_Preserve((ClientData)tclInterp); if( argc==2 ){ - objPtr = Tcl_NewStringObj(argv[1], argl[1]); + objPtr = Tcl_NewStringObj(Jim_String(argv[1]), Jim_Length(argv[1])); Tcl_IncrRefCount(objPtr); rc = Tcl_EvalObjEx(tclInterp, objPtr, 0); Tcl_DecrRefCount(objPtr); }else{ USE_ARGV_TO_OBJV(); @@ -143,11 +143,11 @@ rc = Tcl_EvalObjEx(tclInterp, objPtr, 0); Tcl_DecrRefCount(objPtr); FREE_ARGV_TO_OBJV(); } zResult = getTclResult(tclInterp, &nResult); - Th_SetResult(interp, zResult, nResult); + Jim_SetResultString(interp, zResult, nResult); Tcl_Release((ClientData)tclInterp); return rc; } /* @@ -154,37 +154,37 @@ ** Syntax: ** ** tclExpr arg ?arg ...? */ static int tclExpr_command( - Th_Interp *interp, - void *ctx, + Jim_Interp *interp, int argc, - const char **argv, - int *argl + Jim_Obj *const *argv ){ Tcl_Interp *tclInterp; Tcl_Obj *objPtr; Tcl_Obj *resultObjPtr; int rc; int nResult; const char *zResult; + void *ctx = Jim_CmdPrivData(interp); - if ( createTclInterp(interp, ctx)!=TH_OK ){ - return TH_ERROR; + if ( createTclInterp(interp, ctx)!=JIM_OK ){ + return JIM_ERR; } if( argc<2 ){ - return Th_WrongNumArgs(interp, "tclExpr arg ?arg ...?"); + Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?"); + return JIM_ERR; } tclInterp = GET_CTX_TCL_INTERP(ctx); if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ - Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); - return TH_ERROR; + Jim_SetResultString(interp, "invalid Tcl interpreter", -1); + return JIM_ERR; } Tcl_Preserve((ClientData)tclInterp); if( argc==2 ){ - objPtr = Tcl_NewStringObj(argv[1], argl[1]); + objPtr = Tcl_NewStringObj(Jim_String(argv[1]), Jim_Length(argv[1])); Tcl_IncrRefCount(objPtr); rc = Tcl_ExprObj(tclInterp, objPtr, &resultObjPtr); Tcl_DecrRefCount(objPtr); }else{ USE_ARGV_TO_OBJV(); @@ -198,11 +198,11 @@ if( rc==TCL_OK ){ zResult = Tcl_GetStringFromObj(resultObjPtr, &nResult); }else{ zResult = getTclResult(tclInterp, &nResult); } - Th_SetResult(interp, zResult, nResult); + Jim_SetResultString(interp, zResult, nResult); if( rc==TCL_OK ) Tcl_DecrRefCount(resultObjPtr); Tcl_Release((ClientData)tclInterp); return rc; } @@ -210,56 +210,57 @@ ** Syntax: ** ** tclInvoke command ?arg ...? */ static int tclInvoke_command( - Th_Interp *interp, - void *ctx, + Jim_Interp *interp, int argc, - const char **argv, - int *argl + Jim_Obj *const *argv ){ Tcl_Interp *tclInterp; #ifndef USE_TCL_EVALOBJV Tcl_Command command; Tcl_CmdInfo cmdInfo; #endif int rc; int nResult; const char *zResult; + void *ctx = Jim_CmdPrivData(interp); #ifndef USE_TCL_EVALOBJV Tcl_Obj *objPtr; #endif USE_ARGV_TO_OBJV(); - if ( createTclInterp(interp, ctx)!=TH_OK ){ - return TH_ERROR; + if ( createTclInterp(interp, ctx)!=JIM_OK ){ + return JIM_ERR; } if( argc<2 ){ - return Th_WrongNumArgs(interp, "tclInvoke command ?arg ...?"); + Jim_WrongNumArgs(interp, 1, argv, "command ?arg ...?"); + return JIM_ERR; } tclInterp = GET_CTX_TCL_INTERP(ctx); if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ - Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0); - return TH_ERROR; + Jim_SetResultString(interp, "invalid Tcl interpreter", -1); + return JIM_ERR; } Tcl_Preserve((ClientData)tclInterp); #ifndef USE_TCL_EVALOBJV - objPtr = Tcl_NewStringObj(argv[1], argl[1]); + objPtr = Tcl_NewStringObj(Jim_String(argv[1]), Jim_Length(argv[1])); Tcl_IncrRefCount(objPtr); command = Tcl_GetCommandFromObj(tclInterp, objPtr); if( !command || Tcl_GetCommandInfoFromToken(command,&cmdInfo)==0 ){ - Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]); + Jim_SetResultFormatted(interp, "Tcl command not found: %#s", argv[1]); Tcl_DecrRefCount(objPtr); Tcl_Release((ClientData)tclInterp); - return TH_ERROR; + return JIM_ERR; } if( !cmdInfo.objProc ){ - Th_ErrorMessage(interp, "Cannot invoke Tcl command:", argv[1], argl[1]); + Jim_SetResultFormatted(interp, "Cannot invoke command not found: %#s", + argv[1]); Tcl_DecrRefCount(objPtr); Tcl_Release((ClientData)tclInterp); - return TH_ERROR; + return JIM_ERR; } Tcl_DecrRefCount(objPtr); #endif COPY_ARGV_TO_OBJV(); #ifdef USE_TCL_EVALOBJV @@ -268,86 +269,99 @@ Tcl_ResetResult(tclInterp); rc = cmdInfo.objProc(cmdInfo.objClientData, tclInterp, objc, objv); #endif FREE_ARGV_TO_OBJV(); zResult = getTclResult(tclInterp, &nResult); - Th_SetResult(interp, zResult, nResult); + Jim_SetResultString(interp, zResult, nResult); Tcl_Release((ClientData)tclInterp); return rc; } /* ** Syntax: ** -** th1Eval arg +** bridgeEval arg */ -static int Th1EvalObjCmd( +static int BridgeEvalObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ - Th_Interp *th1Interp; + Jim_Interp *jimInterp; int nArg; const char *arg; int rc; + Jim_Obj *argObj; + Jim_Obj *resultObj; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "arg"); return TCL_ERROR; } - th1Interp = (Th_Interp *)clientData; - if( !th1Interp ){ - Tcl_AppendResult(interp, "invalid TH1 interpreter", NULL); + jimInterp = (Jim_Interp *)clientData; + if( !jimInterp ){ + Tcl_AppendResult(interp, "invalid bridge interpreter", NULL); return TCL_ERROR; } arg = Tcl_GetStringFromObj(objv[1], &nArg); - rc = Th_Eval(th1Interp, 0, arg, nArg); - arg = Th_GetResult(th1Interp, &nArg); + argObj = Jim_NewStringObj(jimInterp, arg, nArg); + Jim_IncrRefCount(argObj); + rc = Jim_EvalObj(jimInterp, argObj); + Jim_DecrRefCount(jimInterp, argObj); + resultObj = Jim_GetResult(jimInterp); + arg = Jim_GetString(resultObj, &nArg); Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, nArg)); return rc; } /* ** Syntax: ** -** th1Expr arg +** bridgeExpr arg */ -static int Th1ExprObjCmd( +static int BridgeExprObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ - Th_Interp *th1Interp; + Jim_Interp *jimInterp; int nArg; const char *arg; int rc; + Jim_Obj *argObj; + Jim_Obj *resultObj; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "arg"); return TCL_ERROR; } - th1Interp = (Th_Interp *)clientData; - if( !th1Interp ){ - Tcl_AppendResult(interp, "invalid TH1 interpreter", NULL); + jimInterp = (Jim_Interp *)clientData; + if( !jimInterp ){ + Tcl_AppendResult(interp, "invalid bridge interpreter", NULL); return TCL_ERROR; } arg = Tcl_GetStringFromObj(objv[1], &nArg); - rc = Th_Expr(th1Interp, arg, nArg); - arg = Th_GetResult(th1Interp, &nArg); - Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, nArg)); + argObj = Jim_NewStringObj(jimInterp, arg, nArg); + Jim_IncrRefCount(argObj); + rc = Jim_EvalExpression(jimInterp, argObj, &resultObj); + Jim_DecrRefCount(jimInterp, argObj); + if (rc == JIM_OK) { + arg = Jim_GetString(resultObj, &nArg); + Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, nArg)); + } return rc; } /* ** Array of Tcl integration commands. Used when adding or removing the Tcl -** integration commands from TH1. +** integration commands from Jim. */ static struct _Command { const char *zName; - Th_CommandProc xProc; + Jim_CmdProc xProc; void *pContext; } aCommand[] = { {"tclEval", tclEval_command, 0}, {"tclExpr", tclExpr_command, 0}, {"tclInvoke", tclInvoke_command, 0}, @@ -354,85 +368,83 @@ {0, 0, 0} }; /* ** Called if the Tcl interpreter is deleted. Removes the Tcl integration -** commands from the TH1 interpreter. +** commands from the Jim interpreter. */ -static void Th1DeleteProc( +static void BridgeDeleteProc( ClientData clientData, Tcl_Interp *interp ){ int i; - Th_Interp *th1Interp = (Th_Interp *)clientData; - if( !th1Interp ) return; + Jim_Interp *jimInterp = (Jim_Interp *)clientData; + if( !jimInterp ) return; /* Remove the Tcl integration commands. */ for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){ - Th_RenameCommand(th1Interp, aCommand[i].zName, -1, NULL, 0); + Jim_DeleteCommand(jimInterp, aCommand[i].zName); } } /* -** Creates and initializes a Tcl interpreter for use with the specified TH1 +** Creates and initializes a Tcl interpreter for use with the specified Jim ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied ** by the caller. */ static int createTclInterp( - Th_Interp *interp, + Jim_Interp *interp, void *pContext ){ struct TclContext *tclContext = (struct TclContext *)pContext; Tcl_Interp *tclInterp; if ( !tclContext ){ - Th_ErrorMessage(interp, - "Invalid Tcl context", (const char *)"", 0); - return TH_ERROR; + Jim_SetResultString(interp, "Invalid Tcl context", -1); + return JIM_ERR; } if ( tclContext->interp ){ - return TH_OK; + return JIM_OK; } if ( tclContext->argc>0 && tclContext->argv ) { Tcl_FindExecutable(tclContext->argv[0]); } tclInterp = tclContext->interp = Tcl_CreateInterp(); if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){ - Th_ErrorMessage(interp, - "Could not create Tcl interpreter", (const char *)"", 0); - return TH_ERROR; + Jim_SetResultString(interp, "Could not create Tcl interpreter", -1); + return JIM_ERR; } if( Tcl_Init(tclInterp)!=TCL_OK ){ - Th_ErrorMessage(interp, - "Tcl initialization error:", Tcl_GetStringResult(tclInterp), -1); + Jim_SetResultFormatted(interp, "Tcl initialization error: %s", + Tcl_GetStringResult(tclInterp)); Tcl_DeleteInterp(tclInterp); tclContext->interp = tclInterp = 0; - return TH_ERROR; + return JIM_ERR; } - /* Add the TH1 integration commands to Tcl. */ - Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp); - Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL); - Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL); - return TH_OK; + /* Add the Jim integration commands to Tcl. */ + Tcl_CallWhenDeleted(tclInterp, BridgeDeleteProc, interp); + Tcl_CreateObjCommand(tclInterp, "bridgeEval", BridgeEvalObjCmd, interp, NULL); + Tcl_CreateObjCommand(tclInterp, "bridgeExpr", BridgeExprObjCmd, interp, NULL); + return JIM_OK; } /* ** Register the Tcl language commands with interpreter interp. ** Usually this is called soon after interpreter creation. */ -int th_register_tcl( - Th_Interp *interp, +int register_tcl( + Jim_Interp *interp, void *pContext ){ int i; - /* Add the Tcl integration commands to TH1. */ + /* Add the Tcl integration commands to Jim. */ for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){ void *ctx; if ( !aCommand[i].zName || !aCommand[i].xProc ) continue; ctx = aCommand[i].pContext; /* Use Tcl interpreter for context? */ if( !ctx ) ctx = pContext; - Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0); + Jim_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, NULL); } - return TH_OK; + return JIM_OK; } #endif /* FOSSIL_ENABLE_TCL */ Index: src/tkt.c ================================================================== --- src/tkt.c +++ src/tkt.c @@ -98,11 +98,11 @@ ** obscured. */ static void initializeVariablesFromDb(void){ const char *zName; Stmt q; - int i, n, size, j; + int i, n, j; zName = PD("name","-none-"); db_prepare(&q, "SELECT datetime(tkt_mtime,'localtime') AS tkt_datetime, *" " FROM ticket WHERE tkt_uuid GLOB '%q*'", zName); if( db_step(&q)==SQLITE_ROW ){ @@ -120,28 +120,28 @@ if( fossil_strcmp(azField[j],zName)==0 ){ azValue[j] = mprintf("%s", zVal); break; } } - if( Th_Fetch(zName, &size)==0 ){ + if( Th_Fetch(zName)==0 ){ Th_Store(zName, zVal); } free(zRevealed); } }else{ db_finalize(&q); db_prepare(&q, "PRAGMA table_info(ticket)"); - if( Th_Fetch("tkt_uuid",&size)==0 ){ + if( Th_Fetch("tkt_uuid")==0 ){ Th_Store("tkt_uuid",zName); } while( db_step(&q)==SQLITE_ROW ){ const char *zField = db_column_text(&q, 1); - if( Th_Fetch(zField, &size)==0 ){ + if( Th_Fetch(zField)==0 ){ Th_Store(zField, ""); } } - if( Th_Fetch("tkt_datetime",&size)==0 ){ + if( Th_Fetch("tkt_datetime")==0 ){ Th_Store("tkt_datetime",""); } } db_finalize(&q); } @@ -240,11 +240,11 @@ */ void ticket_init(void){ const char *zConfig; Th_FossilInit(); zConfig = ticket_common_code(); - Th_Eval(g.interp, 0, zConfig, -1); + Jim_Eval(g.interp, zConfig); } /* ** Recreate the ticket table. */ @@ -373,38 +373,37 @@ ** FIELD is the name of a database column to which we might want ** to append text. STRING is the text to be appended to that ** column. The append does not actually occur until the ** submit_ticket command is run. */ -static int appendRemarkCmd( - Th_Interp *interp, - void *p, - int argc, - const char **argv, - int *argl -){ +static int appendRemarkCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ int idx; + const char *str; + int len; if( argc!=3 ){ - return Th_WrongNumArgs(interp, "append_field FIELD STRING"); + Jim_WrongNumArgs(interp, 1, argv, "FIELD STRING"); + return JIM_ERR; } + str = Jim_GetString(argv[1], &len); if( g.thTrace ){ Th_Trace("append_field %#h {%#h}<br />\n", - argl[1], argv[1], argl[2], argv[2]); + len, str, Jim_Length(argv[2]), Jim_String(argv[2])); } for(idx=0; idx<nField; idx++){ - if( strncmp(azField[idx], argv[1], argl[1])==0 - && azField[idx][argl[1]]==0 ){ + if( strncmp(azField[idx], str, len)==0 + && azField[idx][len]==0 ){ break; } } if( idx>=nField ){ - Th_ErrorMessage(g.interp, "no such TICKET column: ", argv[1], argl[1]); - return TH_ERROR; + Jim_SetResultFormatted(g.interp, "no such TICKET column: %#s", argv[1]); + return JIM_ERR; } - azAppend[idx] = mprintf("%.*s", argl[2], argv[2]); - return TH_OK; + azAppend[idx] = mprintf("%.*s", Jim_Length(argv[2]), Jim_String(argv[2])); + return JIM_OK; } /* ** Subscript command: submit_ticket ** @@ -412,18 +411,14 @@ ** are the names of the columns in the TICKET table. The content is ** taken from TH variables. If the content is unchanged, the field is ** omitted from the artifact. Fields whose names begin with "private_" ** are concealed using the db_conceal() function. */ -static int submitTicketCmd( - Th_Interp *interp, - void *pUuid, - int argc, - const char **argv, - int *argl -){ +static int submitTicketCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ char *zDate; + void *pUuid = Jim_CmdPrivData(interp); const char *zUuid; int i; int rid; Blob tktchng, cksum; @@ -441,12 +436,13 @@ } for(i=0; i<nField; i++){ const char *zValue; int nValue; if( azAppend[i] ) continue; - zValue = Th_Fetch(azField[i], &nValue); + zValue = Th_Fetch(azField[i]); if( zValue ){ + nValue = strlen(zValue); while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; } if( strncmp(zValue, azValue[i], nValue) || strlen(azValue[i])!=nValue ){ if( strncmp(azField[i], "private_", 8)==0 ){ zValue = db_conceal(zValue, nValue); blob_appendf(&tktchng, "J %s %s\n", azField[i], zValue); @@ -472,11 +468,11 @@ /* If called from /debug_tktnew or /debug_tktedit... */ @ <font color="blue"> @ <p>Ticket artifact that would have been submitted:</p> @ <blockquote><pre>%h(blob_str(&tktchng))</pre></blockquote> @ <hr /></font> - return TH_OK; + return JIM_OK; }else if( g.thTrace ){ Th_Trace("submit_ticket {\n<blockquote><pre>\n%h\n</pre></blockquote>\n" "}<br />\n", blob_str(&tktchng)); }else{ @@ -487,11 +483,11 @@ manifest_crosslink_begin(); manifest_crosslink(rid, &tktchng); assert( blob_is_reset(&tktchng) ); manifest_crosslink_end(); } - return TH_RETURN; + return JIM_RETURN; } /* ** WEBPAGE: tktnew @@ -527,14 +523,13 @@ } @ </p> zScript = ticket_newpage_code(); Th_Store("login", g.zLogin); Th_Store("date", db_text(0, "SELECT datetime('now')")); - Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, - (void*)&zNewUuid, 0); + Jim_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void *)&zNewUuid, NULL); if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1); - if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){ + if( Th_Render(zScript)==JIM_RETURN && !g.thTrace && zNewUuid ){ cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid)); return; } @ </form> if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); @@ -594,14 +589,14 @@ login_insert_csrf_secret(); @ </p> zScript = ticket_editpage_code(); Th_Store("login", g.zLogin); Th_Store("date", db_text(0, "SELECT datetime('now')")); - Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); - Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0); + Jim_CreateCommand(g.interp, "append_field", appendRemarkCmd, NULL, NULL); + Jim_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName, NULL); if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1); - if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){ + if( Th_Render(zScript)==JIM_RETURN && !g.thTrace && zName ){ cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName)); return; } @ </form> if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1); Index: src/xfer.c ================================================================== --- src/xfer.c +++ src/xfer.c @@ -791,19 +791,19 @@ static void server_private_xfer_not_authorized(void){ @ error not\sauthorized\sto\ssync\sprivate\scontent } /* -** Run the specified TH1 script, if any, and returns the return code or TH_OK +** Run the specified TH1 script, if any, and returns the return code or JIM_OK ** when there is no script. */ static int run_script(const char *zScript){ if( !zScript ){ - return TH_OK; /* No script, return success. */ + return JIM_OK; /* No script, return success. */ } Th_FossilInit(); /* Make sure TH1 is ready. */ - return Th_Eval(g.interp, 0, zScript, -1); + return Jim_Eval(g.interp, zScript); } /* ** Run the pre-transfer TH1 script, if any, and returns the return code. */ @@ -872,13 +872,13 @@ db_begin_transaction(); db_multi_exec( "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);" ); manifest_crosslink_begin(); - if( run_common_script()==TH_ERROR ){ + if( run_common_script()!=JIM_OK ){ cgi_reset_content(); - @ error common\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0)) + @ error common\sscript\sfailed:\s%F(Jim_String(Jim_GetResult(g.interp))) nErr++; } while( blob_line(xfer.pIn, &xfer.line) ){ if( blob_buffer(&xfer.line)[0]=='#' ) continue; if( blob_size(&xfer.line)==0 ) continue; @@ -1190,13 +1190,13 @@ @ error bad\scommand:\s%F(blob_str(&xfer.line)) } blobarray_reset(xfer.aToken, xfer.nToken); } if( isPush ){ - if( run_push_script()==TH_ERROR ){ + if( run_push_script()!=JIM_OK ){ cgi_reset_content(); - @ error push\sscript\sfailed:\s%F(Th_GetResult(g.interp, 0)) + @ error push\sscript\sfailed:\s%F(Jim_String(Jim_GetResult(g.interp))) nErr++; } request_phantoms(&xfer, 500); } if( isClone && nGimme==0 ){ Index: test/th1-tcl.test ================================================================== --- test/th1-tcl.test +++ test/th1-tcl.test @@ -24,11 +24,11 @@ set env(TH1_ENABLE_TCL) 1; # Tcl integration must be enabled for this test. ############################################################################### -fossil test-th-render [file nativename [file join $dir th1-tcl1.txt]] +fossil test-script-render [file nativename [file join $dir th1-tcl1.txt]] test th1-tcl-1 {[regexp -- {^\d+ \d+ \d+ via Tcl invoke @@ -48,55 +48,55 @@ three words now $} [string map [list \r\n \n] $RESULT]]} ############################################################################### -fossil test-th-render [file nativename [file join $dir th1-tcl2.txt]] +fossil test-script-render [file nativename [file join $dir th1-tcl2.txt]] test th1-tcl-2 {[regexp -- {^\d+ $} [string map [list \r\n \n] $RESULT]]} ############################################################################### -fossil test-th-render [file nativename [file join $dir th1-tcl3.txt]] +fossil test-script-render [file nativename [file join $dir th1-tcl3.txt]] test th1-tcl-3 {$RESULT eq {<hr><p class="thmainError">ERROR:\ invalid command name "bad_command"</p>}} ############################################################################### -fossil test-th-render [file nativename [file join $dir th1-tcl4.txt]] +fossil test-script-render [file nativename [file join $dir th1-tcl4.txt]] test th1-tcl-4 {$RESULT eq {<hr><p class="thmainError">ERROR:\ divide by zero</p>}} ############################################################################### -fossil test-th-render [file nativename [file join $dir th1-tcl5.txt]] +fossil test-script-render [file nativename [file join $dir th1-tcl5.txt]] test th1-tcl-5 {$RESULT eq {<hr><p class="thmainError">ERROR:\ Tcl command not found: bad_command</p>} || $RESULT eq {<hr><p\ class="thmainError">ERROR: invalid command name "bad_command"</p>}} ############################################################################### -fossil test-th-render [file nativename [file join $dir th1-tcl6.txt]] +fossil test-script-render [file nativename [file join $dir th1-tcl6.txt]] test th1-tcl-6 {$RESULT eq {<hr><p class="thmainError">ERROR:\ no such command: bad_command</p>}} ############################################################################### -fossil test-th-render [file nativename [file join $dir th1-tcl7.txt]] +fossil test-script-render [file nativename [file join $dir th1-tcl7.txt]] test th1-tcl-7 {$RESULT eq {<hr><p class="thmainError">ERROR:\ syntax error in expression: "2**0"</p>}} ############################################################################### -fossil test-th-render [file nativename [file join $dir th1-tcl8.txt]] +fossil test-script-render [file nativename [file join $dir th1-tcl8.txt]] test th1-tcl-8 {$RESULT eq {<hr><p class="thmainError">ERROR:\ Cannot invoke Tcl command: tailcall</p>} || $RESULT eq {<hr><p\ class="thmainError">ERROR: tailcall can only be called from a proc or\ lambda</p>}} Index: test/th1-tcl1.txt ================================================================== --- test/th1-tcl1.txt +++ test/th1-tcl1.txt @@ -1,10 +1,10 @@ <th1> # - # This is a "TH1 fragment" used to test the Tcl integration features of TH1. - # The corresponding test file executes this file using the test-th-render - # Fossil command. + # This is a "script fragment" used to test the Tcl integration features of + # Fossil. The corresponding test file executes this file using the + # test-script-render Fossil command. # set channel stdout; tclInvoke set channel $channel proc doOut {msg} {puts $msg; puts \n} doOut [tclEval clock seconds] doOut [tclEval {set x [clock seconds]}] @@ -14,14 +14,14 @@ doOut [tclExpr 2 + 2] doOut [tclInvoke set x "two words"] doOut [tclInvoke eval set y one_word] doOut [tclInvoke eval {set z "three words now"}] doOut [set x [tclEval {set x [clock seconds]}]] - doOut [tclInvoke th1Eval {set y "two words"}] - doOut [set z [tclInvoke th1Expr {2+2}]] + doOut [tclInvoke bridgeEval {set y "two words"}] + doOut [set z [tclInvoke bridgeExpr {2+2}]] doOut $x doOut $y doOut $z doOut [tclEval set x] doOut [tclEval set y] doOut [tclEval set z] </th1> Index: test/th1-tcl2.txt ================================================================== --- test/th1-tcl2.txt +++ test/th1-tcl2.txt @@ -1,10 +1,10 @@ <th1> # - # This is a "TH1 fragment" used to test the Tcl integration features of TH1. - # The corresponding test file executes this file using the test-th-render - # Fossil command. + # This is a "script fragment" used to test the Tcl integration features of + # Fossil. The corresponding test file executes this file using the + # test-script-render Fossil command. # # NOTE: This test requires that the SQLite package be available for the Tcl # interpreter that is linked to the Fossil executable. # tclInvoke set repository_name [repository 1] Index: test/th1-tcl3.txt ================================================================== --- test/th1-tcl3.txt +++ test/th1-tcl3.txt @@ -1,9 +1,9 @@ <th1> # - # This is a "TH1 fragment" used to test the Tcl integration features of TH1. - # The corresponding test file executes this file using the test-th-render - # Fossil command. + # This is a "script fragment" used to test the Tcl integration features of + # Fossil. The corresponding test file executes this file using the + # test-script-render Fossil command. # proc doOut {msg} {puts $msg; puts \n} doOut [tclEval bad_command] </th1> Index: test/th1-tcl4.txt ================================================================== --- test/th1-tcl4.txt +++ test/th1-tcl4.txt @@ -1,9 +1,9 @@ <th1> # - # This is a "TH1 fragment" used to test the Tcl integration features of TH1. - # The corresponding test file executes this file using the test-th-render - # Fossil command. + # This is a "script fragment" used to test the Tcl integration features of + # Fossil. The corresponding test file executes this file using the + # test-script-render Fossil command. # proc doOut {msg} {puts $msg; puts \n} doOut [tclExpr 2/0] </th1> Index: test/th1-tcl5.txt ================================================================== --- test/th1-tcl5.txt +++ test/th1-tcl5.txt @@ -1,9 +1,9 @@ <th1> # - # This is a "TH1 fragment" used to test the Tcl integration features of TH1. - # The corresponding test file executes this file using the test-th-render - # Fossil command. + # This is a "script fragment" used to test the Tcl integration features of + # Fossil. The corresponding test file executes this file using the + # test-script-render Fossil command. # proc doOut {msg} {puts $msg; puts \n} doOut [tclInvoke bad_command] </th1> Index: test/th1-tcl6.txt ================================================================== --- test/th1-tcl6.txt +++ test/th1-tcl6.txt @@ -1,9 +1,9 @@ <th1> # - # This is a "TH1 fragment" used to test the Tcl integration features of TH1. - # The corresponding test file executes this file using the test-th-render - # Fossil command. + # This is a "script fragment" used to test the Tcl integration features of + # Fossil. The corresponding test file executes this file using the + # test-script-render Fossil command. # proc doOut {msg} {puts $msg; puts \n} - doOut [tclEval th1Eval bad_command] + doOut [tclEval bridgeEval bad_command] </th1> Index: test/th1-tcl7.txt ================================================================== --- test/th1-tcl7.txt +++ test/th1-tcl7.txt @@ -1,19 +1,19 @@ <th1> # - # This is a "TH1 fragment" used to test the Tcl integration features of TH1. - # The corresponding test file executes this file using the test-th-render - # Fossil command. + # This is a "script fragment" used to test the Tcl integration features of + # Fossil. The corresponding test file executes this file using the + # test-script-render Fossil command. # proc doOut {msg} {puts $msg; puts \n} # # BUGBUG: Attempting to divide by zero will crash TH1 with the error: # "child killed: floating-point exception" # - # doOut [tclEval th1Expr 2/0] + # doOut [tclEval bridgeExpr 2/0] # # NOTE: For now, just cause an expression syntax error. # - doOut [tclEval th1Expr 2**0] + doOut [tclEval bridgeExpr 2**0] </th1> Index: test/th1-tcl8.txt ================================================================== --- test/th1-tcl8.txt +++ test/th1-tcl8.txt @@ -1,14 +1,14 @@ <th1> # - # This is a "TH1 fragment" used to test the Tcl integration features of TH1. - # The corresponding test file executes this file using the test-th-render - # Fossil command. + # This is a "script fragment" used to test the Tcl integration features of + # Fossil. The corresponding test file executes this file using the + # test-script-render Fossil command. # proc doOut {msg} {puts $msg; puts \n} if {[tclInvoke set tcl_version] >= 8.6} { doOut [tclInvoke tailcall set x 1] } else { doOut "This test requires Tcl 8.6 or higher." } </th1> Index: win/Makefile.PellesCGMake ================================================================== --- win/Makefile.PellesCGMake +++ win/Makefile.PellesCGMake @@ -145,12 +145,12 @@ # extracting version info from manifest VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION version.exe ..\manifest.uuid ..\manifest ..\VERSION > $@ # generate the simplified headers -headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/th.h VERSION.h - makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/th.h VERSION.h +headers: makeheaders.exe page_index.h VERSION.h ../src/sqlite3.h ../src/jim.h VERSION.h + makeheaders.exe $(foreach ts,$(TRANSLATEDSRC),$(ts):$(ts:_.c=.h)) ../src/sqlite3.h ../src/jim.h VERSION.h echo Done >$@ # compile C sources with relevant options $(TRANSLATEDOBJ): %_.obj: %_.c %.h @@ -160,11 +160,11 @@ $(CC) $(CCFLAGS) $(SQLITEDEFINES) $(INCLUDE) "$<" -Fo"$@" $(SQLITESHELLOBJ): %.obj: $(SRCDIR)%.c $(CC) $(CCFLAGS) $(SQLITESHELLDEFINES) $(INCLUDE) "$<" -Fo"$@" -$(THOBJ): %.obj: $(SRCDIR)%.c $(SRCDIR)th.h +$(THOBJ): %.obj: $(SRCDIR)%.c $(SRCDIR)jim.h $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" $(ZLIBOBJ): %.obj: $(ZLIBSRCDIR)%.c $(CC) $(CCFLAGS) $(INCLUDE) "$<" -Fo"$@" Index: win/Makefile.dmc ================================================================== --- win/Makefile.dmc +++ win/Makefile.dmc @@ -672,7 +672,8 @@ zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h VERSION.h - +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_diff_.c:json_diff.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h + +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_diff_.c:json_diff.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\jim.h VERSION.h +$(SRCDIR)\sqlite3.h $(SRCDIR)\jim.h VERSION.h $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers Index: win/Makefile.mingw ================================================================== --- win/Makefile.mingw +++ win/Makefile.mingw @@ -518,11 +518,11 @@ $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex $(MKINDEX) $(TRANS_SRC) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h - $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h + $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/jim.h $(OBJDIR)/VERSION.h echo Done >$(OBJDIR)/headers $(OBJDIR)/headers: Makefile Makefile: $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate Index: win/Makefile.mingw.mistachkin ================================================================== --- win/Makefile.mingw.mistachkin +++ win/Makefile.mingw.mistachkin @@ -489,11 +489,11 @@ $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION) $(VERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h -EXTRAOBJ = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(OBJDIR)/cson_amalgamation.o +EXTRAOBJ = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o $(OBJDIR)/jimtcl.o $(OBJDIR)/cson_amalgamation.o ifdef FOSSIL_ENABLE_TCL EXTRAOBJ += $(OBJDIR)/th_tcl.o endif @@ -518,11 +518,11 @@ $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex $(MKINDEX) $(TRANS_SRC) >$@ $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h - $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h + $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/xfersetup_.c:$(OBJDIR)/xfersetup.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/jim.h $(OBJDIR)/VERSION.h echo Done >$(OBJDIR)/headers $(OBJDIR)/headers: Makefile Makefile: $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate @@ -1192,16 +1192,13 @@ $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o -$(OBJDIR)/th.o: $(SRCDIR)/th.c - $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o - -$(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c - $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o +$(OBJDIR)/jimtcl.o: $(SRCDIR)/../autosetup/jimsh0.c + $(XTCC) -I$(SRCDIR) -DJIM_BOOTSTRAP_LIB_ONLY -c $(SRCDIR)/../autosetup/jimsh0.c -o $(OBJDIR)/jimtcl.o ifdef FOSSIL_ENABLE_TCL $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o endif Index: win/Makefile.msc ================================================================== --- win/Makefile.msc +++ win/Makefile.msc @@ -778,7 +778,7 @@ zip_.c : $(SRCDIR)\zip.c translate$E $** > $@ headers: makeheaders$E page_index.h VERSION.h - makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_diff_.c:json_diff.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h + makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_diff_.c:json_diff.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\jim.h VERSION.h $(SRCDIR)\cson_amalgamation.h @copy /Y nul: headers