Files
sqlite/autosetup/jimsh0.c
drh 4f7cd07abb Change our local copy of the third-party jimsh0.c program (used only
during the build process) so that it prints an error message and
exits if malloc() fails.  This does not fix any actual bugs nor make
the build process any "safer", but it might help prevent bothersome
warnings from pedantic static analyzers.

FossilOrigin-Name: 72c8a5b94cdf5da7e2b197e02c9d5d98fe415cd731c3893acf3ba69bb4dde37a
2025-11-24 11:00:02 +00:00

24509 lines
615 KiB
C

/* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */
#define JIM_COMPAT
#define JIM_ANSIC
#define JIM_REGEXP
#define HAVE_NO_AUTOCONF
#define JIM_TINY
#define _JIMAUTOCONF_H
#define TCL_LIBRARY "."
#define jim_ext_bootstrap
#define jim_ext_aio
#define jim_ext_readdir
#define jim_ext_regexp
#define jim_ext_file
#define jim_ext_glob
#define jim_ext_exec
#define jim_ext_clock
#define jim_ext_array
#define jim_ext_stdlib
#define jim_ext_tclcompat
#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
#define HAVE_UMASK
#include <sys/stat.h>
#ifndef S_IRWXG
#define S_IRWXG 0
#endif
#ifndef S_IRWXO
#define S_IRWXO 0
#endif
#else
#define TCL_PLATFORM_OS "unknown"
#define TCL_PLATFORM_PLATFORM "unix"
#define TCL_PLATFORM_PATH_SEPARATOR ":"
#ifdef _MINIX
#define vfork fork
#define _POSIX_SOURCE
#else
#define _GNU_SOURCE
#endif
#define HAVE_FORK
#define HAVE_WAITPID
#define HAVE_ISATTY
#define HAVE_MKSTEMP
#define HAVE_LINK
#define HAVE_SYS_TIME_H
#define HAVE_DIRENT_H
#define HAVE_UNISTD_H
#define HAVE_UMASK
#define HAVE_PIPE
#define _FILE_OFFSET_BITS 64
#endif
#define JIM_VERSION 84
#ifndef JIM_WIN32COMPAT_H
#define JIM_WIN32COMPAT_H
#ifdef __cplusplus
extern "C" {
#endif
#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);
#if defined(__MINGW32__)
#define JIM_SPRINTF_DOUBLE_NEEDS_FIX
#endif
#ifdef _MSC_VER
#if _MSC_VER >= 1000
#pragma warning(disable:4146)
#endif
#include <limits.h>
#define jim_wide _int64
#ifndef HAVE_LONG_LONG
#define HAVE_LONG_LONG
#endif
#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
#include <io.h>
#include <winsock.h>
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
#ifdef __cplusplus
}
#endif
#endif
#ifndef UTF8_UTIL_H
#define UTF8_UTIL_H
#ifdef __cplusplus
extern "C" {
#endif
#define MAX_UTF8_LEN 4
int utf8_fromunicode(char *p, unsigned uc);
#ifndef JIM_UTF8
#include <ctype.h>
#define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
#define utf8_strwidth(S, B) utf8_strlen((S), (B))
#define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
#define utf8_getchars(CP, C) (*(CP) = (C), 1)
#define utf8_upper(C) toupper(C)
#define utf8_title(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
#define utf8_width(C) 1
#else
#endif
#ifdef __cplusplus
}
#endif
#endif
#ifndef __JIM__H
#define __JIM__H
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
#include <limits.h>
#include <stdlib.h>
#include <stdarg.h>
#ifndef HAVE_NO_AUTOCONF
#endif
#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
# 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))
#define JIM_ABI_VERSION 101
#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
#define JIM_EVAL 7
#define JIM_MAX_CALLFRAME_DEPTH 1000
#define JIM_MAX_EVAL_DEPTH 2000
#define JIM_PRIV_FLAG_SHIFT 20
#define JIM_NONE 0
#define JIM_ERRMSG 1
#define JIM_ENUM_ABBREV 2
#define JIM_UNSHARED 4
#define JIM_MUSTEXIST 8
#define JIM_NORESULT 16
#define JIM_SUBST_NOVAR 1
#define JIM_SUBST_NOCMD 2
#define JIM_SUBST_NOESC 4
#define JIM_SUBST_FLAG 128
#define JIM_CASESENS 0
#define JIM_NOCASE 1
#define JIM_OPT_END 2
#define JIM_PATH_LEN 1024
#define JIM_NOTUSED(V) ((void) V)
#define JIM_LIBPATH "auto_path"
#define JIM_INTERACTIVE "tcl_interactive"
typedef struct Jim_Stack {
int len;
int maxlen;
void **vector;
} Jim_Stack;
typedef struct Jim_HashEntry {
void *key;
union {
void *val;
int intval;
} u;
struct Jim_HashEntry *next;
} Jim_HashEntry;
typedef struct Jim_HashTableType {
unsigned int (*hashFunction)(const void *key);
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, void *key);
void (*valDestructor)(void *privdata, void *obj);
} Jim_HashTableType;
typedef struct Jim_HashTable {
Jim_HashEntry **table;
const Jim_HashTableType *type;
void *privdata;
unsigned int size;
unsigned int sizemask;
unsigned int used;
unsigned int collisions;
unsigned int uniq;
} Jim_HashTable;
typedef struct Jim_HashTableIterator {
Jim_HashTable *ht;
Jim_HashEntry *entry, *nextEntry;
int index;
} Jim_HashTableIterator;
#define JIM_HT_INITIAL_SIZE 16
#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_SetHashIntVal(ht, entry, _val_) (entry)->u.intval = (_val_)
#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 = (void *)(_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) + (ht)->uniq)
#define Jim_GetHashEntryKey(he) ((he)->key)
#define Jim_GetHashEntryVal(he) ((he)->u.val)
#define Jim_GetHashEntryIntVal(he) ((he)->u.intval)
#define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
#define Jim_GetHashTableSize(ht) ((ht)->size)
#define Jim_GetHashTableUsed(ht) ((ht)->used)
typedef struct Jim_Obj {
char *bytes;
const struct Jim_ObjType *typePtr;
int refCount;
int length;
union {
jim_wide wideValue;
int intValue;
double doubleValue;
void *ptr;
struct {
void *ptr1;
void *ptr2;
} twoPtrValue;
struct {
void *ptr;
int int1;
int int2;
} ptrIntValue;
struct {
struct Jim_VarVal *vv;
unsigned long callFrameId;
int global;
} varValue;
struct {
struct Jim_Obj *nsObj;
struct Jim_Cmd *cmdPtr;
unsigned long procEpoch;
} cmdValue;
struct {
struct Jim_Obj **ele;
int len;
int maxLen;
} listValue;
struct Jim_Dict *dictValue;
struct {
int maxLength;
int charLength;
} strValue;
struct {
unsigned long id;
struct Jim_Reference *refPtr;
} refValue;
struct {
struct Jim_Obj *fileNameObj;
int lineNumber;
} sourceValue;
struct {
struct Jim_Obj *varNameObjPtr;
struct Jim_Obj *indexObjPtr;
} dictSubstValue;
struct {
int line;
int argc;
} scriptLineValue;
} internalRep;
struct Jim_Obj *prevObjPtr;
struct Jim_Obj *nextObjPtr;
} Jim_Obj;
#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)
#define Jim_FreeNewObj Jim_FreeObj
#define Jim_FreeIntRep(i,o) \
if ((o)->typePtr && (o)->typePtr->freeIntRepProc) \
(o)->typePtr->freeIntRepProc(i, o)
#define Jim_GetIntRepPtr(o) (o)->internalRep.ptr
#define Jim_SetIntRepPtr(o, p) \
(o)->internalRep.ptr = (p)
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;
Jim_FreeInternalRepProc *freeIntRepProc;
Jim_DupInternalRepProc *dupIntRepProc;
Jim_UpdateStringProc *updateStringProc;
int flags;
} Jim_ObjType;
#define JIM_TYPE_NONE 0
#define JIM_TYPE_REFERENCES 1
typedef struct Jim_CallFrame {
unsigned long id;
int level;
struct Jim_HashTable vars;
struct Jim_HashTable *staticVars;
struct Jim_CallFrame *parent;
Jim_Obj *const *argv;
int argc;
Jim_Obj *procArgsObjPtr;
Jim_Obj *procBodyObjPtr;
struct Jim_CallFrame *next;
Jim_Obj *nsObj;
Jim_Obj *unused_fileNameObj;
int unused_line;
Jim_Stack *localCommands;
struct Jim_Obj *tailcallObj;
struct Jim_Cmd *tailcallCmd;
} Jim_CallFrame;
typedef struct Jim_EvalFrame {
Jim_CallFrame *framePtr;
int level;
int procLevel;
struct Jim_Cmd *cmd;
struct Jim_EvalFrame *parent;
Jim_Obj *const *argv;
int argc;
Jim_Obj *scriptObj;
} Jim_EvalFrame;
typedef struct Jim_VarVal {
Jim_Obj *objPtr;
struct Jim_CallFrame *linkFramePtr;
int refCount;
} Jim_VarVal;
typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
Jim_Obj *const *argv);
typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);
typedef struct Jim_Dict {
struct JimDictHashEntry {
int offset;
unsigned hash;
} *ht;
unsigned int size;
unsigned int sizemask;
unsigned int uniq;
Jim_Obj **table;
int len;
int maxLen;
unsigned int dummy;
} Jim_Dict;
typedef struct Jim_Cmd {
int inUse;
int isproc;
struct Jim_Cmd *prevCmd;
Jim_Obj *cmdNameObj;
union {
struct {
Jim_CmdProc *cmdProc;
Jim_DelCmdProc *delProc;
void *privData;
} native;
struct {
Jim_Obj *argListObjPtr;
Jim_Obj *bodyObjPtr;
Jim_HashTable *staticVars;
int argListLen;
int reqArity;
int optArity;
int argsPos;
int upcall;
struct Jim_ProcArg {
Jim_Obj *nameObjPtr;
Jim_Obj *defaultObjPtr;
} *arglist;
Jim_Obj *nsObj;
} proc;
} u;
} Jim_Cmd;
typedef struct Jim_PrngState {
unsigned char sbox[256];
unsigned int i, j;
} Jim_PrngState;
typedef struct Jim_Interp {
Jim_Obj *result;
int unused_errorLine;
Jim_Obj *currentFilenameObj;
int break_level;
int maxCallFrameDepth;
int maxEvalDepth;
int evalDepth;
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 long procEpoch; /* Incremented every time the result
of procedures names lookup caching
may no longer be valid. */
unsigned long 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;
int quitting;
int safeexpr;
Jim_Obj *liveList;
Jim_Obj *freeList;
Jim_Obj *unused_currentScriptObj;
Jim_EvalFrame topEvalFrame;
Jim_EvalFrame *evalFrame;
int procLevel;
Jim_Obj * const *unused_argv;
Jim_Obj *nullScriptObj;
Jim_Obj *emptyObj;
Jim_Obj *trueObj;
Jim_Obj *falseObj;
unsigned long referenceNextId;
struct Jim_HashTable references;
unsigned long lastCollectId; /* reference max Id of the last GC
execution. It's set to ~0 while the collection
is running as sentinel to avoid to recursive
calls via the [collect] command inside
finalizers. */
jim_wide lastCollectTime;
Jim_Obj *stackTrace;
Jim_Obj *errorProc;
Jim_Obj *unknown;
Jim_Obj *defer;
Jim_Obj *traceCmdObj;
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(). */
Jim_Cmd *oldCmdCache;
int oldCmdCacheSize;
struct Jim_CallFrame *freeFramesList;
struct Jim_HashTable assocData;
Jim_PrngState *prngState;
struct Jim_HashTable packages;
Jim_Stack *loadHandles;
} Jim_Interp;
#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))
#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_SetResult(i,o) do { \
Jim_Obj *_resultObjPtr_ = (o); \
Jim_IncrRefCount(_resultObjPtr_); \
Jim_DecrRefCount(i,(i)->result); \
(i)->result = _resultObjPtr_; \
} while(0)
#define Jim_GetId(i) (++(i)->id)
#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;
#define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
#define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
#define JIM_EXPORT extern
JIM_EXPORT void *(*Jim_Allocator)(void *ptr, size_t size);
#define Jim_Free(P) Jim_Allocator((P), 0)
#define Jim_Realloc(P, S) Jim_Allocator((P), (S))
#define Jim_Alloc(S) Jim_Allocator(NULL, (S))
JIM_EXPORT char * Jim_StrDup (const char *s);
JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
JIM_EXPORT char **Jim_GetEnviron(void);
JIM_EXPORT void Jim_SetEnviron(char **env);
JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file);
#ifndef CLOCK_REALTIME
# define CLOCK_REALTIME 0
#endif
#ifndef CLOCK_MONOTONIC
# define CLOCK_MONOTONIC 1
#endif
#ifndef CLOCK_MONOTONIC_RAW
# define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
#endif
JIM_EXPORT jim_wide Jim_GetTimeUsec(unsigned type);
JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
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_EvalObjList(Jim_Interp *interp, Jim_Obj *listObj);
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_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
Jim_Obj **resObjPtrPtr, int flags);
JIM_EXPORT Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
int *lineptr);
JIM_EXPORT void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
Jim_Obj *fileNameObj, int lineNumber);
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));
JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht,
const Jim_HashTableType *type, void *privdata);
JIM_EXPORT void 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 Jim_HashTableIterator *Jim_GetHashTableIterator
(Jim_HashTable *ht);
JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
(Jim_HashTableIterator *iter);
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 Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp,
Jim_Obj *objPtr);
JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr,
int *lenPtr);
JIM_EXPORT const char *Jim_String(Jim_Obj *objPtr);
JIM_EXPORT int Jim_Length(Jim_Obj *objPtr);
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);
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);
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, ...);
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,
Jim_Obj *cmdNameObj);
JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
Jim_Obj *oldNameObj, Jim_Obj *newNameObj);
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_MakeGlobalNamespaceName(Jim_Interp *interp,
Jim_Obj *nameObjPtr);
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);
JIM_EXPORT Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp,
Jim_Obj *levelObjPtr);
JIM_EXPORT int Jim_Collect (Jim_Interp *interp);
JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp);
JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr,
int *indexPtr);
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 Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx);
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);
JIM_EXPORT Jim_Obj *Jim_ListJoin(Jim_Interp *interp,
Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen);
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 Jim_Obj **Jim_DictPairs(Jim_Interp *interp,
Jim_Obj *dictPtr, int *len);
JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
#define JIM_DICTMATCH_KEYS 0x0001
#define JIM_DICTMATCH_VALUES 0x002
JIM_EXPORT int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types);
JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr);
JIM_EXPORT int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr);
JIM_EXPORT Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv);
JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
int *intPtr);
JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
Jim_Obj *exprObjPtr);
JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
Jim_Obj *exprObjPtr, int *boolPtr);
JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
int *booleanPtr);
JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
jim_wide *widePtr);
JIM_EXPORT int Jim_GetWideExpr(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);
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);
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_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr,
const char *const *tablePtr);
JIM_EXPORT int Jim_ScriptIsComplete(Jim_Interp *interp,
Jim_Obj *scriptObj, char *stateCharPtr);
JIM_EXPORT int Jim_FindByName(const char *name, const char * const array[], size_t len);
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);
JIM_EXPORT int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version);
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);
#define Jim_PackageProvideCheck(INTERP, NAME) \
if (Jim_CheckAbiVersion(INTERP, JIM_ABI_VERSION) == JIM_ERR || Jim_PackageProvide(INTERP, NAME, "1.0", JIM_ERRMSG)) \
return JIM_ERR
JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
JIM_EXPORT void Jim_HistoryLoad(const char *filename);
JIM_EXPORT void Jim_HistorySave(const char *filename);
JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt);
JIM_EXPORT void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj);
JIM_EXPORT void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj);
JIM_EXPORT void Jim_HistoryAdd(const char *line);
JIM_EXPORT void Jim_HistoryShow(void);
JIM_EXPORT void Jim_HistorySetMaxLen(int length);
JIM_EXPORT int Jim_HistoryGetMaxLen(void);
JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
JIM_EXPORT int Jim_IsBigEndian(void);
#define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
JIM_EXPORT void Jim_SignalSetIgnored(jim_wide mask);
JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
JIM_EXPORT int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
#ifdef __cplusplus
}
#endif
#endif
#ifndef JIM_SUBCMD_H
#define JIM_SUBCMD_H
#ifdef __cplusplus
extern "C" {
#endif
#define JIM_MODFLAG_HIDDEN 0x0001
#define JIM_MODFLAG_FULLARGV 0x0002
typedef int jim_subcmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
typedef struct {
const char *cmd;
const char *args;
jim_subcmd_function *function;
short minargs;
short maxargs;
unsigned short flags;
} jim_subcmd_type;
#define JIM_DEF_SUBCMD(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs }
#define JIM_DEF_SUBCMD_HIDDEN(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs, JIM_MODFLAG_HIDDEN }
const jim_subcmd_type *
Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type *ct, Jim_Obj *subcmd);
#ifdef __cplusplus
}
#endif
#endif
#ifndef JIMREGEXP_H
#define JIMREGEXP_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
typedef struct {
int rm_so;
int rm_eo;
} regmatch_t;
typedef struct regexp {
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
#define REG_NEWLINE 1
#define REG_ICASE 2
#define REG_NOTBOL 16
enum {
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,
REG_ERR_UNMATCHED_PAREN,
REG_ERR_UNMATCHED_BRACES,
REG_ERR_BAD_COUNT,
REG_ERR_JUNK_ON_END,
REG_ERR_OPERAND_COULD_BE_EMPTY,
REG_ERR_NESTED_COUNT,
REG_ERR_INTERNAL,
REG_ERR_COUNT_FOLLOWS_NOTHING,
REG_ERR_INVALID_ESCAPE,
REG_ERR_CORRUPTED,
REG_ERR_NULL_CHAR,
REG_ERR_UNMATCHED_BRACKET,
REG_ERR_NUM
};
int jim_regcomp(regex_t *preg, const char *regex, int cflags);
int jim_regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
void jim_regfree(regex_t *preg);
#ifdef __cplusplus
}
#endif
#endif
#ifndef JIM_SIGNAL_H
#define JIM_SIGNAL_H
#ifdef __cplusplus
extern "C" {
#endif
const char *Jim_SignalId(int sig);
#ifdef __cplusplus
}
#endif
#endif
#ifndef JIMIOCOMPAT_H
#define JIMIOCOMPAT_H
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
void Jim_SetResultErrno(Jim_Interp *interp, const char *msg);
int Jim_OpenForWrite(const char *filename, int append);
int Jim_OpenForRead(const char *filename);
#if defined(__MINGW32__) || defined(_WIN32)
#ifndef STRICT
#define STRICT
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#include <process.h>
typedef HANDLE phandle_t;
#define JIM_BAD_PHANDLE INVALID_HANDLE_VALUE
#define WIFEXITED(STATUS) (((STATUS) & 0xff00) == 0)
#define WEXITSTATUS(STATUS) ((STATUS) & 0x00ff)
#define WIFSIGNALED(STATUS) (((STATUS) & 0xff00) != 0)
#define WTERMSIG(STATUS) (((STATUS) >> 8) & 0xff)
#define WNOHANG 1
int Jim_Errno(void);
long waitpid(phandle_t phandle, int *status, int nohang);
phandle_t JimWaitPid(long processid, int *status, int nohang);
long JimProcessPid(phandle_t phandle);
#define HAVE_PIPE
#define pipe(P) _pipe((P), 0, O_NOINHERIT)
typedef struct __stat64 jim_stat_t;
#define Jim_Stat _stat64
#define Jim_FileStat _fstat64
#define Jim_Lseek _lseeki64
#define O_TEXT _O_TEXT
#define O_BINARY _O_BINARY
#define Jim_SetMode _setmode
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#else
#if defined(HAVE_STAT64)
typedef struct stat64 jim_stat_t;
#define Jim_Stat stat64
#if defined(HAVE_FSTAT64)
#define Jim_FileStat fstat64
#endif
#if defined(HAVE_LSTAT64)
#define Jim_LinkStat lstat64
#endif
#else
typedef struct stat jim_stat_t;
#define Jim_Stat stat
#if defined(HAVE_FSTAT)
#define Jim_FileStat fstat
#endif
#if defined(HAVE_LSTAT)
#define Jim_LinkStat lstat
#endif
#endif
#if defined(HAVE_LSEEK64)
#define Jim_Lseek lseek64
#else
#define Jim_Lseek lseek
#endif
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
typedef int phandle_t;
#define Jim_Errno() errno
#define JIM_BAD_PHANDLE -1
#define JimProcessPid(PIDTYPE) (PIDTYPE)
#define JimWaitPid waitpid
#ifndef HAVE_EXECVPE
#define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
#endif
#endif
#ifndef O_TEXT
#define O_TEXT 0
#endif
#endif
# ifndef MAXPATHLEN
# ifdef PATH_MAX
# define MAXPATHLEN PATH_MAX
# else
# define MAXPATHLEN JIM_PATH_LEN
# endif
# endif
int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb);
#endif
int Jim_bootstrapInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
return JIM_ERR;
return Jim_EvalSource(interp, "bootstrap.tcl", 1,
"\n"
"proc package {cmd args} {\n"
" if {$cmd eq \"require\"} {\n"
" foreach path $::auto_path {\n"
" lassign $args pkg\n"
" set pkgpath $path/$pkg.tcl\n"
" if {$path eq \".\"} {\n"
" set pkgpath $pkg.tcl\n"
" }\n"
" if {[file exists $pkgpath]} {\n"
" tailcall uplevel #0 [list source $pkgpath]\n"
" }\n"
" }\n"
" }\n"
"}\n"
"set tcl_platform(bootstrap) 1\n"
);
}
int Jim_initjimshInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
return JIM_ERR;
return Jim_EvalSource(interp, "initjimsh.tcl", 1,
"\n"
"\n"
"\n"
"proc _jimsh_init {} {\n"
" rename _jimsh_init {}\n"
" global jim::exe jim::argv0 tcl_interactive auto_path tcl_platform\n"
"\n"
"\n"
" if {[exists jim::argv0]} {\n"
" if {[string match \"*/*\" $jim::argv0]} {\n"
" set jim::exe [file join [pwd] $jim::argv0]\n"
" } else {\n"
" set jim::argv0 [file tail $jim::argv0]\n"
" set path [split [env PATH \"\"] $tcl_platform(pathSeparator)]\n"
" if {$tcl_platform(platform) eq \"windows\"} {\n"
"\n"
" set path [lmap p [list \"\" {*}$path] { string map {\\\\ /} $p }]\n"
" }\n"
" foreach p $path {\n"
" set exec [file join [pwd] $p $jim::argv0]\n"
" if {[file executable $exec]} {\n"
" set jim::exe $exec\n"
" break\n"
" }\n"
" }\n"
" }\n"
" }\n"
"\n"
"\n"
" lappend p {*}[split [env JIMLIB {}] $tcl_platform(pathSeparator)]\n"
" if {[exists jim::exe]} {\n"
" lappend p [file dirname $jim::exe]\n"
" }\n"
" lappend p {*}$auto_path\n"
" set auto_path $p\n"
"\n"
" if {$tcl_interactive && [env HOME {}] ne \"\"} {\n"
" foreach src {.jimrc jimrc.tcl} {\n"
" if {[file exists [env HOME]/$src]} {\n"
" uplevel #0 source [env HOME]/$src\n"
" break\n"
" }\n"
" }\n"
" }\n"
" return \"\"\n"
"}\n"
"\n"
"if {$tcl_platform(platform) eq \"windows\"} {\n"
" set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
"}\n"
"\n"
"\n"
"set tcl::autocomplete_commands {array clock debug dict file history info namespace package signal socket string tcl::prefix zlib}\n"
"\n"
"\n"
"\n"
"proc tcl::autocomplete {prefix} {\n"
" if {[set space [string first \" \" $prefix]] != -1} {\n"
" set cmd [string range $prefix 0 $space-1]\n"
" if {$cmd in $::tcl::autocomplete_commands || [info channel $cmd] ne \"\"} {\n"
" set arg [string range $prefix $space+1 end]\n"
"\n"
" return [lmap p [$cmd -commands] {\n"
" if {![string match \"${arg}*\" $p]} continue\n"
" function \"$cmd $p\"\n"
" }]\n"
" }\n"
" }\n"
"\n"
" if {[string match \"source *\" $prefix]} {\n"
" set path [string range $prefix 7 end]\n"
" return [lmap p [glob -nocomplain \"${path}*\"] {\n"
" function \"source $p\"\n"
" }]\n"
" }\n"
"\n"
" return [lmap p [lsort [info commands $prefix*]] {\n"
" if {[string match \"* *\" $p]} {\n"
" continue\n"
" }\n"
" function $p\n"
" }]\n"
"}\n"
"\n"
"\n"
"set tcl::stdhint_commands {array clock debug dict file history info namespace package signal string zlib}\n"
"\n"
"set tcl::stdhint_cols {\n"
" none {0}\n"
" black {30}\n"
" red {31}\n"
" green {32}\n"
" yellow {33}\n"
" blue {34}\n"
" purple {35}\n"
" cyan {36}\n"
" normal {37}\n"
" grey {30 1}\n"
" gray {30 1}\n"
" lred {31 1}\n"
" lgreen {32 1}\n"
" lyellow {33 1}\n"
" lblue {34 1}\n"
" lpurple {35 1}\n"
" lcyan {36 1}\n"
" white {37 1}\n"
"}\n"
"\n"
"\n"
"set tcl::stdhint_col $tcl::stdhint_cols(lcyan)\n"
"\n"
"\n"
"proc tcl::stdhint {string} {\n"
" set result \"\"\n"
" if {[llength $string] >= 2} {\n"
" lassign $string cmd arg\n"
" if {$cmd in $::tcl::stdhint_commands || [info channel $cmd] ne \"\"} {\n"
" catch {\n"
" set help [$cmd -help $arg]\n"
" if {[string match \"Usage: $cmd *\" $help]} {\n"
" set n [llength $string]\n"
" set subcmd [lindex $help $n]\n"
" incr n\n"
" set hint [join [lrange $help $n end]]\n"
" set prefix \"\"\n"
" if {![string match \"* \" $string]} {\n"
" if {$n == 3 && $subcmd ne $arg} {\n"
"\n"
" set prefix \"[string range $subcmd [string length $arg] end] \"\n"
" } else {\n"
" set prefix \" \"\n"
" }\n"
" }\n"
" set result [list $prefix$hint {*}$::tcl::stdhint_col]\n"
" }\n"
" }\n"
" }\n"
" }\n"
" return $result\n"
"}\n"
"\n"
"_jimsh_init\n"
);
}
int Jim_globInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
return JIM_ERR;
return Jim_EvalSource(interp, "glob.tcl", 1,
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"package require readdir\n"
"\n"
"\n"
"proc glob.globdir {dir pattern} {\n"
" if {[file exists $dir/$pattern]} {\n"
"\n"
" return [list $pattern]\n"
" }\n"
"\n"
" set result {}\n"
" set files [readdir $dir]\n"
" lappend files . ..\n"
"\n"
" foreach name $files {\n"
" if {[string match $pattern $name]} {\n"
"\n"
" if {[string index $name 0] eq \".\" && [string index $pattern 0] ne \".\"} {\n"
" continue\n"
" }\n"
" lappend result $name\n"
" }\n"
" }\n"
"\n"
" return $result\n"
"}\n"
"\n"
"\n"
"\n"
"\n"
"proc glob.explode {pattern} {\n"
" set oldexp {}\n"
" set newexp {\"\"}\n"
"\n"
" while 1 {\n"
" set oldexp $newexp\n"
" set newexp {}\n"
" set ob [string first \\{ $pattern]\n"
" set cb [string first \\} $pattern]\n"
"\n"
" if {$ob < $cb && $ob != -1} {\n"
" set mid [string range $pattern 0 $ob-1]\n"
" set subexp [lassign [glob.explode [string range $pattern $ob+1 end]] pattern]\n"
" if {$pattern eq \"\"} {\n"
" error \"unmatched open brace in glob pattern\"\n"
" }\n"
" set pattern [string range $pattern 1 end]\n"
"\n"
" foreach subs $subexp {\n"
" foreach sub [split $subs ,] {\n"
" foreach old $oldexp {\n"
" lappend newexp $old$mid$sub\n"
" }\n"
" }\n"
" }\n"
" } elseif {$cb != -1} {\n"
" set suf [string range $pattern 0 $cb-1]\n"
" set rest [string range $pattern $cb end]\n"
" break\n"
" } else {\n"
" set suf $pattern\n"
" set rest \"\"\n"
" break\n"
" }\n"
" }\n"
"\n"
" foreach old $oldexp {\n"
" lappend newexp $old$suf\n"
" }\n"
" list $rest {*}$newexp\n"
"}\n"
"\n"
"\n"
"\n"
"proc glob.glob {base pattern} {\n"
" set dir [file dirname $pattern]\n"
" if {$pattern eq $dir || $pattern eq \"\"} {\n"
" return [list [file join $base $dir] $pattern]\n"
" } elseif {$pattern eq [file tail $pattern]} {\n"
" set dir \"\"\n"
" }\n"
"\n"
"\n"
" set dirlist [glob.glob $base $dir]\n"
" set pattern [file tail $pattern]\n"
"\n"
"\n"
" set result {}\n"
" foreach {realdir dir} $dirlist {\n"
" if {![file isdir $realdir]} {\n"
" continue\n"
" }\n"
" if {[string index $dir end] ne \"/\" && $dir ne \"\"} {\n"
" append dir /\n"
" }\n"
" foreach name [glob.globdir $realdir $pattern] {\n"
" lappend result [file join $realdir $name] $dir$name\n"
" }\n"
" }\n"
" return $result\n"
"}\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"proc glob {args} {\n"
" set nocomplain 0\n"
" set base \"\"\n"
" set tails 0\n"
"\n"
" set n 0\n"
" foreach arg $args {\n"
" if {[info exists param]} {\n"
" set $param $arg\n"
" unset param\n"
" incr n\n"
" continue\n"
" }\n"
" switch -glob -- $arg {\n"
" -d* {\n"
" set switch $arg\n"
" set param base\n"
" }\n"
" -n* {\n"
" set nocomplain 1\n"
" }\n"
" -ta* {\n"
" set tails 1\n"
" }\n"
" -- {\n"
" incr n\n"
" break\n"
" }\n"
" -* {\n"
" return -code error \"bad option \\\"$arg\\\": must be -directory, -nocomplain, -tails, or --\"\n"
" }\n"
" * {\n"
" break\n"
" }\n"
" }\n"
" incr n\n"
" }\n"
" if {[info exists param]} {\n"
" return -code error \"missing argument to \\\"$switch\\\"\"\n"
" }\n"
" if {[llength $args] <= $n} {\n"
" return -code error \"wrong # args: should be \\\"glob ?options? pattern ?pattern ...?\\\"\"\n"
" }\n"
"\n"
" set args [lrange $args $n end]\n"
"\n"
" set result {}\n"
" foreach pattern $args {\n"
" set escpattern [string map {\n"
" \\\\\\\\ \\x01 \\\\\\{ \\x02 \\\\\\} \\x03 \\\\, \\x04\n"
" } $pattern]\n"
" set patexps [lassign [glob.explode $escpattern] rest]\n"
" if {$rest ne \"\"} {\n"
" return -code error \"unmatched close brace in glob pattern\"\n"
" }\n"
" foreach patexp $patexps {\n"
" set patexp [string map {\n"
" \\x01 \\\\\\\\ \\x02 \\{ \\x03 \\} \\x04 ,\n"
" } $patexp]\n"
" foreach {realname name} [glob.glob $base $patexp] {\n"
" incr n\n"
" if {$tails} {\n"
" lappend result $name\n"
" } else {\n"
" lappend result [file join $base $name]\n"
" }\n"
" }\n"
" }\n"
" }\n"
"\n"
" if {!$nocomplain && [llength $result] == 0} {\n"
" set s $(([llength $args] > 1) ? \"s\" : \"\")\n"
" return -code error \"no files matched glob pattern$s \\\"[join $args]\\\"\"\n"
" }\n"
"\n"
" return $result\n"
"}\n"
);
}
int Jim_stdlibInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "stdlib", "1.0", JIM_ERRMSG))
return JIM_ERR;
return Jim_EvalSource(interp, "stdlib.tcl", 1,
"\n"
"\n"
"if {![exists -command ref]} {\n"
"\n"
" proc ref {args} {{count 0}} {\n"
" format %08x [incr count]\n"
" }\n"
"}\n"
"\n"
"\n"
"proc lambda {arglist args} {\n"
" tailcall proc [ref {} function lambda.finalizer] $arglist {*}$args\n"
"}\n"
"\n"
"proc lambda.finalizer {name val} {\n"
" rename $name {}\n"
"}\n"
"\n"
"\n"
"proc curry {args} {\n"
" alias [ref {} function lambda.finalizer] {*}$args\n"
"}\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"proc function {value} {\n"
" return $value\n"
"}\n"
"\n"
"\n"
"proc stackdump {stacktrace} {\n"
" set lines {}\n"
" lappend lines \"Traceback (most recent call last):\"\n"
" foreach {cmd l f p} [lreverse $stacktrace] {\n"
" set line {}\n"
" if {$f ne \"\"} {\n"
" append line \" File \\\"$f\\\", line $l\"\n"
" }\n"
" if {$p ne \"\"} {\n"
" append line \", in $p\"\n"
" }\n"
" if {$line ne \"\"} {\n"
" lappend lines $line\n"
" if {$cmd ne \"\"} {\n"
" set nl [string first \\n $cmd 1]\n"
" if {$nl >= 0} {\n"
" set cmd [string range $cmd 0 $nl-1]...\n"
" }\n"
" lappend lines \" $cmd\"\n"
" }\n"
" }\n"
" }\n"
" if {[llength $lines] > 1} {\n"
" return [join $lines \\n]\n"
" }\n"
"}\n"
"\n"
"\n"
"\n"
"proc defer {script} {\n"
" upvar jim::defer v\n"
" lappend v $script\n"
"}\n"
"\n"
"\n"
"\n"
"proc errorInfo {msg {stacktrace \"\"}} {\n"
" if {$stacktrace eq \"\"} {\n"
"\n"
" set stacktrace [info stacktrace]\n"
" }\n"
" lassign $stacktrace p f l cmd\n"
" if {$f ne \"\"} {\n"
" set result \"$f:$l: Error: \"\n"
" }\n"
" append result \"$msg\\n\"\n"
" append result [stackdump $stacktrace]\n"
"\n"
"\n"
" string trim $result\n"
"}\n"
"\n"
"\n"
"\n"
"proc {info nameofexecutable} {} {\n"
" if {[exists ::jim::exe]} {\n"
" return $::jim::exe\n"
" }\n"
"}\n"
"\n"
"\n"
"proc {dict update} {&varName args script} {\n"
" set keys {}\n"
" foreach {n v} $args {\n"
" upvar $v var_$v\n"
" if {[dict exists $varName $n]} {\n"
" set var_$v [dict get $varName $n]\n"
" }\n"
" }\n"
" catch {uplevel 1 $script} msg opts\n"
" if {[info exists varName]} {\n"
" foreach {n v} $args {\n"
" if {[info exists var_$v]} {\n"
" dict set varName $n [set var_$v]\n"
" } else {\n"
" dict unset varName $n\n"
" }\n"
" }\n"
" }\n"
" return {*}$opts $msg\n"
"}\n"
"\n"
"proc {dict replace} {dictionary {args {key value}}} {\n"
" if {[llength ${key value}] % 2} {\n"
" tailcall {dict replace}\n"
" }\n"
" tailcall dict merge $dictionary ${key value}\n"
"}\n"
"\n"
"\n"
"proc {dict lappend} {varName key {args value}} {\n"
" upvar $varName dict\n"
" if {[exists dict] && [dict exists $dict $key]} {\n"
" set list [dict get $dict $key]\n"
" }\n"
" lappend list {*}$value\n"
" dict set dict $key $list\n"
"}\n"
"\n"
"\n"
"proc {dict append} {varName key {args value}} {\n"
" upvar $varName dict\n"
" if {[exists dict] && [dict exists $dict $key]} {\n"
" set str [dict get $dict $key]\n"
" }\n"
" append str {*}$value\n"
" dict set dict $key $str\n"
"}\n"
"\n"
"\n"
"proc {dict incr} {varName key {increment 1}} {\n"
" upvar $varName dict\n"
" if {[exists dict] && [dict exists $dict $key]} {\n"
" set value [dict get $dict $key]\n"
" }\n"
" incr value $increment\n"
" dict set dict $key $value\n"
"}\n"
"\n"
"\n"
"proc {dict remove} {dictionary {args key}} {\n"
" foreach k $key {\n"
" dict unset dictionary $k\n"
" }\n"
" return $dictionary\n"
"}\n"
"\n"
"\n"
"proc {dict for} {vars dictionary script} {\n"
" if {[llength $vars] != 2} {\n"
" return -code error \"must have exactly two variable names\"\n"
" }\n"
" dict size $dictionary\n"
" tailcall foreach $vars $dictionary $script\n"
"}\n"
);
}
int Jim_tclcompatInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "tclcompat", "1.0", JIM_ERRMSG))
return JIM_ERR;
return Jim_EvalSource(interp, "tclcompat.tcl", 1,
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"set env [env]\n"
"\n"
"\n"
"if {[exists -command stdout]} {\n"
"\n"
" foreach p {gets flush close eof seek tell} {\n"
" proc $p {chan args} {p} {\n"
" tailcall $chan $p {*}$args\n"
" }\n"
" }\n"
" unset p\n"
"\n"
"\n"
"\n"
" proc puts {{-nonewline {}} {chan stdout} msg} {\n"
" if {${-nonewline} ni {-nonewline {}}} {\n"
" tailcall ${-nonewline} puts $msg\n"
" }\n"
" tailcall $chan puts {*}${-nonewline} $msg\n"
" }\n"
"\n"
"\n"
"\n"
"\n"
"\n"
" proc read {{-nonewline {}} chan} {\n"
" if {${-nonewline} ni {-nonewline {}}} {\n"
" tailcall ${-nonewline} read {*}${chan}\n"
" }\n"
" tailcall $chan read {*}${-nonewline}\n"
" }\n"
"\n"
" proc fconfigure {f args} {\n"
" foreach {n v} $args {\n"
" switch -glob -- $n {\n"
" -bl* {\n"
" $f ndelay $(!$v)\n"
" }\n"
" -bu* {\n"
" $f buffering $v\n"
" }\n"
" -tr* {\n"
" $f translation $v\n"
" }\n"
" default {\n"
" return -code error \"fconfigure: unknown option $n\"\n"
" }\n"
" }\n"
" }\n"
" }\n"
"}\n"
"\n"
"\n"
"proc fileevent {args} {\n"
" tailcall {*}$args\n"
"}\n"
"\n"
"\n"
"\n"
"proc parray {arrayname {pattern *} {puts puts}} {\n"
" upvar $arrayname a\n"
"\n"
" set max 0\n"
" foreach name [array names a $pattern]] {\n"
" if {[string length $name] > $max} {\n"
" set max [string length $name]\n"
" }\n"
" }\n"
" incr max [string length $arrayname]\n"
" incr max 2\n"
" foreach name [lsort [array names a $pattern]] {\n"
" $puts [format \"%-${max}s = %s\" $arrayname\\($name\\) $a($name)]\n"
" }\n"
"}\n"
"\n"
"\n"
"proc {file copy} {{force {}} source target} {\n"
" try {\n"
" if {$force ni {{} -force}} {\n"
" error \"bad option \\\"$force\\\": should be -force\"\n"
" }\n"
"\n"
" set in [open $source rb]\n"
"\n"
" if {[file exists $target]} {\n"
" if {$force eq \"\"} {\n"
" error \"error copying \\\"$source\\\" to \\\"$target\\\": file already exists\"\n"
" }\n"
"\n"
" if {$source eq $target} {\n"
" return\n"
" }\n"
"\n"
"\n"
" file stat $source ss\n"
" file stat $target ts\n"
" if {$ss(dev) == $ts(dev) && $ss(ino) == $ts(ino) && $ss(ino)} {\n"
" return\n"
" }\n"
" }\n"
" set out [open $target wb]\n"
" $in copyto $out\n"
" $out close\n"
" } on error {msg opts} {\n"
" incr opts(-level)\n"
" return {*}$opts $msg\n"
" } finally {\n"
" catch {$in close}\n"
" }\n"
"}\n"
"\n"
"\n"
"\n"
"proc popen {cmd {mode r}} {\n"
" lassign [pipe] r w\n"
" try {\n"
" if {[string match \"w*\" $mode]} {\n"
" lappend cmd <@$r &\n"
" set pids [exec {*}$cmd]\n"
" $r close\n"
" set f $w\n"
" } else {\n"
" lappend cmd >@$w &\n"
" set pids [exec {*}$cmd]\n"
" $w close\n"
" set f $r\n"
" }\n"
" lambda {cmd args} {f pids} {\n"
" if {$cmd eq \"pid\"} {\n"
" return $pids\n"
" }\n"
" if {$cmd eq \"close\"} {\n"
" $f close\n"
"\n"
" set retopts {}\n"
" foreach p $pids {\n"
" lassign [wait $p] status - rc\n"
" if {$status eq \"CHILDSTATUS\"} {\n"
" if {$rc == 0} {\n"
" continue\n"
" }\n"
" set msg \"child process exited abnormally\"\n"
" } else {\n"
" set msg \"child killed: received signal\"\n"
" }\n"
" set retopts [list -code error -errorcode [list $status $p $rc] $msg]\n"
" }\n"
" return {*}$retopts\n"
" }\n"
" tailcall $f $cmd {*}$args\n"
" }\n"
" } on error {error opts} {\n"
" $r close\n"
" $w close\n"
" error $error\n"
" }\n"
"}\n"
"\n"
"\n"
"local proc pid {{channelId {}}} {\n"
" if {$channelId eq \"\"} {\n"
" tailcall upcall pid\n"
" }\n"
" if {[catch {$channelId tell}]} {\n"
" return -code error \"can not find channel named \\\"$channelId\\\"\"\n"
" }\n"
" if {[catch {$channelId pid} pids]} {\n"
" return \"\"\n"
" }\n"
" return $pids\n"
"}\n"
"\n"
"\n"
"\n"
"proc throw {code {msg \"\"}} {\n"
" return -code $code $msg\n"
"}\n"
"\n"
"\n"
"proc {file delete force} {path} {\n"
" foreach e [readdir $path] {\n"
" file delete -force $path/$e\n"
" }\n"
" file delete $path\n"
"}\n"
);
}
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#include <sys/stat.h>
#endif
#ifdef HAVE_UTIL_H
#include <util.h>
#endif
#ifdef HAVE_PTY_H
#include <pty.h>
#endif
#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 <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#define HAVE_SOCKETS
#elif defined (__MINGW32__)
#endif
#if defined(JIM_SSL)
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
#ifdef HAVE_TERMIOS_H
#endif
#define AIO_CMD_LEN 32
#define AIO_DEFAULT_RBUF_LEN 256
#define AIO_DEFAULT_WBUF_LIMIT (64 * 1024)
#define AIO_KEEPOPEN 1
#define AIO_NODELETE 2
#define AIO_EOF 4
#define AIO_WBUF_NONE 8
#define AIO_NONBLOCK 16
#define AIO_ONEREAD 32
enum wbuftype {
WBUF_OPT_NONE,
WBUF_OPT_LINE,
WBUF_OPT_FULL,
};
#if defined(JIM_IPV6)
#define IPV6 1
#else
#define IPV6 0
#ifndef PF_INET6
#define PF_INET6 0
#endif
#endif
#if defined(HAVE_SYS_UN_H) && defined(PF_UNIX)
#define UNIX_SOCKETS 1
#else
#define UNIX_SOCKETS 0
#endif
static int JimReadableTimeout(int fd, long ms)
{
#ifdef HAVE_SELECT
int retval;
struct timeval tv;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms % 1000) * 1000;
retval = select(fd + 1, &rfds, NULL, NULL, ms == 0 ? NULL : &tv);
if (retval > 0) {
return JIM_OK;
}
return JIM_ERR;
#else
return JIM_OK;
#endif
}
struct AioFile;
typedef struct {
int (*writer)(struct AioFile *af, const char *buf, int len);
int (*reader)(struct AioFile *af, char *buf, int len, int pending);
int (*error)(const struct AioFile *af);
const char *(*strerror)(struct AioFile *af);
int (*verify)(struct AioFile *af);
} JimAioFopsType;
typedef struct AioFile
{
Jim_Obj *filename;
int wbuft;
int flags;
long timeout;
int fd;
int addr_family;
void *ssl;
const JimAioFopsType *fops;
Jim_Obj *readbuf;
Jim_Obj *writebuf;
char *rbuf;
size_t rbuf_len;
size_t wbuf_limit;
} AioFile;
static void aio_consume(Jim_Obj *objPtr, int n);
static int stdio_writer(struct AioFile *af, const char *buf, int len)
{
int ret = write(af->fd, buf, len);
if (ret < 0 && errno == EPIPE) {
aio_consume(af->writebuf, Jim_Length(af->writebuf));
}
return ret;
}
static int stdio_reader(struct AioFile *af, char *buf, int len, int nb)
{
if (nb || af->timeout == 0 || JimReadableTimeout(af->fd, af->timeout) == JIM_OK) {
int ret;
errno = 0;
ret = read(af->fd, buf, len);
if (ret <= 0 && errno != EAGAIN && errno != EINTR) {
af->flags |= AIO_EOF;
}
return ret;
}
errno = ETIMEDOUT;
return -1;
}
static int stdio_error(const AioFile *af)
{
if (af->flags & AIO_EOF) {
return JIM_OK;
}
switch (errno) {
case EAGAIN:
case EINTR:
case ETIMEDOUT:
#ifdef ECONNRESET
case ECONNRESET:
#endif
#ifdef ECONNABORTED
case ECONNABORTED:
#endif
return JIM_OK;
default:
return JIM_ERR;
}
}
static const char *stdio_strerror(struct AioFile *af)
{
return strerror(errno);
}
static const JimAioFopsType stdio_fops = {
stdio_writer,
stdio_reader,
stdio_error,
stdio_strerror,
NULL,
};
static void aio_set_nonblocking(AioFile *af, int nb)
{
#ifdef O_NDELAY
int old = !!(af->flags & AIO_NONBLOCK);
if (old != nb) {
int fmode = fcntl(af->fd, F_GETFL);
if (nb) {
fmode |= O_NDELAY;
af->flags |= AIO_NONBLOCK;
}
else {
fmode &= ~O_NDELAY;
af->flags &= ~AIO_NONBLOCK;
}
(void)fcntl(af->fd, F_SETFL, fmode);
}
#endif
}
static int aio_start_nonblocking(AioFile *af)
{
int old = !!(af->flags & AIO_NONBLOCK);
if (af->timeout) {
aio_set_nonblocking(af, 1);
}
return old;
}
static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
const char *hdlfmt, int family, int flags);
static const char *JimAioErrorString(AioFile *af)
{
if (af && af->fops)
return af->fops->strerror(af);
return strerror(errno);
}
static void JimAioSetError(Jim_Interp *interp, Jim_Obj *name)
{
AioFile *af = Jim_CmdPrivData(interp);
if (name) {
Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
}
else {
Jim_SetResultString(interp, JimAioErrorString(af), -1);
}
}
static int aio_eof(AioFile *af)
{
return af->flags & AIO_EOF;
}
static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
{
int ret = 0;
if (!aio_eof(af)) {
ret = af->fops->error(af);
if (ret) {
JimAioSetError(interp, af->filename);
}
}
return ret;
}
static void aio_consume(Jim_Obj *objPtr, int n)
{
assert(objPtr->bytes);
assert(n <= objPtr->length);
memmove(objPtr->bytes, objPtr->bytes + n, objPtr->length - n + 1);
objPtr->length -= n;
}
static int aio_flush(Jim_Interp *interp, AioFile *af);
#ifdef jim_ext_eventloop
static int aio_autoflush(Jim_Interp *interp, void *clientData, int mask)
{
AioFile *af = clientData;
aio_flush(interp, af);
if (Jim_Length(af->writebuf) == 0) {
return -1;
}
return 0;
}
#endif
static int aio_flush(Jim_Interp *interp, AioFile *af)
{
int len;
const char *pt = Jim_GetString(af->writebuf, &len);
if (len) {
int ret = af->fops->writer(af, pt, len);
if (ret > 0) {
aio_consume(af->writebuf, ret);
}
if (ret < 0) {
return JimCheckStreamError(interp, af);
}
if (Jim_Length(af->writebuf)) {
#ifdef jim_ext_eventloop
void *handler = Jim_FindFileHandler(interp, af->fd, JIM_EVENT_WRITABLE);
if (handler == NULL) {
Jim_CreateFileHandler(interp, af->fd, JIM_EVENT_WRITABLE, aio_autoflush, af, NULL);
return JIM_OK;
}
else if (handler == af) {
return JIM_OK;
}
#endif
Jim_SetResultString(interp, "send buffer is full", -1);
return JIM_ERR;
}
}
return JIM_OK;
}
static int aio_read_len(Jim_Interp *interp, AioFile *af, unsigned flags, int neededLen)
{
if (!af->readbuf) {
af->readbuf = Jim_NewStringObj(interp, NULL, 0);
}
if (neededLen >= 0) {
neededLen -= Jim_Length(af->readbuf);
if (neededLen <= 0) {
return JIM_OK;
}
}
while (neededLen && !aio_eof(af)) {
int retval;
int readlen;
if (neededLen == -1) {
readlen = af->rbuf_len;
}
else {
readlen = (neededLen > af->rbuf_len ? af->rbuf_len : neededLen);
}
if (!af->rbuf) {
af->rbuf = Jim_Alloc(af->rbuf_len);
}
retval = af->fops->reader(af, af->rbuf, readlen, flags & AIO_NONBLOCK);
if (retval > 0) {
if (retval) {
Jim_AppendString(interp, af->readbuf, af->rbuf, retval);
}
if (neededLen != -1) {
neededLen -= retval;
}
if (flags & AIO_ONEREAD) {
return JIM_OK;
}
continue;
}
if ((flags & AIO_ONEREAD) || JimCheckStreamError(interp, af)) {
return JIM_ERR;
}
break;
}
return JIM_OK;
}
static Jim_Obj *aio_read_consume(Jim_Interp *interp, AioFile *af, int neededLen)
{
Jim_Obj *objPtr = NULL;
if (neededLen < 0 || af->readbuf == NULL || Jim_Length(af->readbuf) <= neededLen) {
objPtr = af->readbuf;
af->readbuf = NULL;
}
else if (af->readbuf) {
int len;
const char *pt = Jim_GetString(af->readbuf, &len);
objPtr = Jim_NewStringObj(interp, pt, neededLen);
aio_consume(af->readbuf, neededLen);
}
return objPtr;
}
static void JimAioDelProc(Jim_Interp *interp, void *privData)
{
AioFile *af = privData;
JIM_NOTUSED(interp);
aio_flush(interp, af);
Jim_DecrRefCount(interp, af->writebuf);
#if UNIX_SOCKETS
if (af->addr_family == PF_UNIX && (af->flags & AIO_NODELETE) == 0) {
Jim_Obj *filenameObj = aio_sockname(interp, af->fd);
if (filenameObj) {
if (Jim_Length(filenameObj)) {
remove(Jim_String(filenameObj));
}
Jim_FreeNewObj(interp, filenameObj);
}
}
#endif
Jim_DecrRefCount(interp, af->filename);
#ifdef jim_ext_eventloop
Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
#endif
#if defined(JIM_SSL)
if (af->ssl != NULL) {
SSL_free(af->ssl);
}
#endif
if (!(af->flags & AIO_KEEPOPEN)) {
close(af->fd);
}
if (af->readbuf) {
Jim_FreeNewObj(interp, af->readbuf);
}
Jim_Free(af->rbuf);
Jim_Free(af);
}
static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
int nonewline = 0;
jim_wide neededLen = -1;
static const char * const options[] = { "-pending", "-nonewline", NULL };
enum { OPT_PENDING, OPT_NONEWLINE };
int option;
int nb;
Jim_Obj *objPtr;
if (argc) {
if (*Jim_String(argv[0]) == '-') {
if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
switch (option) {
case OPT_PENDING:
break;
case OPT_NONEWLINE:
nonewline++;
break;
}
}
else {
if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
return JIM_ERR;
if (neededLen < 0) {
Jim_SetResultString(interp, "invalid parameter: negative len", -1);
return JIM_ERR;
}
}
argc--;
argv++;
}
if (argc) {
return -1;
}
nb = aio_start_nonblocking(af);
if (aio_read_len(interp, af, nb ? AIO_NONBLOCK : 0, neededLen) != JIM_OK) {
aio_set_nonblocking(af, nb);
return JIM_ERR;
}
objPtr = aio_read_consume(interp, af, neededLen);
aio_set_nonblocking(af, nb);
if (objPtr) {
if (nonewline) {
int len;
const char *s = Jim_GetString(objPtr, &len);
if (len > 0 && s[len - 1] == '\n') {
objPtr->length--;
objPtr->bytes[objPtr->length] = '\0';
}
}
Jim_SetResult(interp, objPtr);
}
else {
Jim_SetEmptyResult(interp);
}
return JIM_OK;
}
int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
{
Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
return ((AioFile *) cmdPtr->u.native.privData)->fd;
}
Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
return -1;
}
static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
aio_flush(interp, af);
Jim_SetResultInt(interp, af->fd);
return JIM_OK;
}
static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
jim_wide count = 0;
jim_wide maxlen = JIM_WIDE_MAX;
int ok = 1;
Jim_Obj *objv[4];
if (argc == 2) {
if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
return JIM_ERR;
}
}
objv[0] = argv[0];
objv[1] = Jim_NewStringObj(interp, "flush", -1);
if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", argv[0]);
return JIM_ERR;
}
objv[0] = argv[0];
objv[1] = Jim_NewStringObj(interp, "puts", -1);
objv[2] = Jim_NewStringObj(interp, "-nonewline", -1);
Jim_IncrRefCount(objv[1]);
Jim_IncrRefCount(objv[2]);
while (count < maxlen) {
jim_wide len = maxlen - count;
if (len > af->rbuf_len) {
len = af->rbuf_len;
}
if (aio_read_len(interp, af, 0, len) != JIM_OK) {
ok = 0;
break;
}
objv[3] = aio_read_consume(interp, af, len);
count += Jim_Length(objv[3]);
if (Jim_EvalObjVector(interp, 4, objv) != JIM_OK) {
ok = 0;
break;
}
if (aio_eof(af)) {
break;
}
if (count >= 16384 && af->rbuf_len < 65536) {
af->rbuf_len = 65536;
af->rbuf = Jim_Realloc(af->rbuf, af->rbuf_len);
}
}
Jim_DecrRefCount(interp, objv[1]);
Jim_DecrRefCount(interp, objv[2]);
if (!ok) {
return JIM_ERR;
}
Jim_SetResultInt(interp, count);
return JIM_OK;
}
static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
Jim_Obj *objPtr = NULL;
int len;
int nb;
unsigned flags = AIO_ONEREAD;
char *nl = NULL;
int offset = 0;
errno = 0;
nb = aio_start_nonblocking(af);
if (nb) {
flags |= AIO_NONBLOCK;
}
while (!aio_eof(af)) {
if (af->readbuf) {
const char *pt = Jim_GetString(af->readbuf, &len);
nl = memchr(pt + offset, '\n', len - offset);
if (nl) {
objPtr = Jim_NewStringObj(interp, pt, nl - pt);
aio_consume(af->readbuf, nl - pt + 1);
break;
}
offset = len;
}
if (aio_read_len(interp, af, flags, -1) != JIM_OK) {
break;
}
}
aio_set_nonblocking(af, nb);
if (!nl && aio_eof(af) && af->readbuf) {
objPtr = af->readbuf;
af->readbuf = NULL;
}
else if (!objPtr) {
objPtr = Jim_NewStringObj(interp, NULL, 0);
}
if (argc) {
if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
Jim_FreeNewObj(interp, objPtr);
return JIM_ERR;
}
len = Jim_Length(objPtr);
if (!nl && len == 0) {
len = -1;
}
Jim_SetResultInt(interp, len);
}
else {
Jim_SetResult(interp, objPtr);
}
return JIM_OK;
}
static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
int wlen;
const char *wdata;
Jim_Obj *strObj;
int wnow = 0;
int nl = 1;
if (argc == 2) {
if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
return -1;
}
strObj = argv[1];
nl = 0;
}
else {
strObj = argv[0];
}
#ifdef JIM_MAINTAINER
if (Jim_IsShared(af->writebuf)) {
Jim_DecrRefCount(interp, af->writebuf);
af->writebuf = Jim_DuplicateObj(interp, af->writebuf);
Jim_IncrRefCount(af->writebuf);
}
#endif
Jim_AppendObj(interp, af->writebuf, strObj);
if (nl) {
Jim_AppendString(interp, af->writebuf, "\n", 1);
}
wdata = Jim_GetString(af->writebuf, &wlen);
switch (af->wbuft) {
case WBUF_OPT_NONE:
wnow = 1;
break;
case WBUF_OPT_LINE:
if (nl || memchr(wdata, '\n', wlen) != NULL) {
wnow = 1;
}
break;
case WBUF_OPT_FULL:
if (wlen >= af->wbuf_limit) {
wnow = 1;
}
break;
}
if (wnow) {
return aio_flush(interp, af);
}
return JIM_OK;
}
static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
#ifdef HAVE_ISATTY
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetResultInt(interp, isatty(af->fd));
#else
Jim_SetResultInt(interp, 0);
#endif
return JIM_OK;
}
static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
return aio_flush(interp, af);
}
static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetResultInt(interp, !!aio_eof(af));
return JIM_OK;
}
static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
if (argc == 3) {
int option = -1;
#if defined(HAVE_SOCKETS)
static const char * const options[] = { "r", "w", "-nodelete", NULL };
enum { OPT_R, OPT_W, OPT_NODELETE };
if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
#endif
switch (option) {
#if defined(HAVE_SHUTDOWN)
case OPT_R:
case OPT_W:
if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
return JIM_OK;
}
JimAioSetError(interp, NULL);
return JIM_ERR;
#endif
#if UNIX_SOCKETS
case OPT_NODELETE:
if (af->addr_family == PF_UNIX) {
af->flags |= AIO_NODELETE;
break;
}
#endif
default:
Jim_SetResultString(interp, "not supported", -1);
return JIM_ERR;
}
}
af->flags &= ~AIO_KEEPOPEN;
return Jim_DeleteCommand(interp, argv[0]);
}
static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
int orig = SEEK_SET;
jim_wide offset;
if (argc == 2) {
if (Jim_CompareStringImmediate(interp, argv[1], "start"))
orig = SEEK_SET;
else if (Jim_CompareStringImmediate(interp, argv[1], "current"))
orig = SEEK_CUR;
else if (Jim_CompareStringImmediate(interp, argv[1], "end"))
orig = SEEK_END;
else {
return -1;
}
}
if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
return JIM_ERR;
}
if (orig != SEEK_CUR || offset != 0) {
aio_flush(interp, af);
}
if (Jim_Lseek(af->fd, offset, orig) == -1) {
JimAioSetError(interp, af->filename);
return JIM_ERR;
}
if (af->readbuf) {
Jim_FreeNewObj(interp, af->readbuf);
af->readbuf = NULL;
}
af->flags &= ~AIO_EOF;
return JIM_OK;
}
static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetResultInt(interp, Jim_Lseek(af->fd, 0, SEEK_CUR));
return JIM_OK;
}
static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetResult(interp, af->filename);
return JIM_OK;
}
#ifdef O_NDELAY
static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
if (argc) {
long nb;
if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
return JIM_ERR;
}
aio_set_nonblocking(af, nb);
}
Jim_SetResultInt(interp, (af->flags & AIO_NONBLOCK) ? 1 : 0);
return JIM_OK;
}
#endif
#ifdef HAVE_FSYNC
static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
if (aio_flush(interp, af) != JIM_OK) {
return JIM_ERR;
}
fsync(af->fd);
return JIM_OK;
}
#endif
static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
Jim_Obj *resultObj;
static const char * const options[] = {
"none",
"line",
"full",
NULL
};
if (argc) {
if (Jim_GetEnum(interp, argv[0], options, &af->wbuft, NULL, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
if (af->wbuft == WBUF_OPT_FULL && argc == 2) {
long l;
if (Jim_GetLong(interp, argv[1], &l) != JIM_OK || l <= 0) {
return JIM_ERR;
}
af->wbuf_limit = l;
}
if (af->wbuft == WBUF_OPT_NONE) {
if (aio_flush(interp, af) != JIM_OK) {
return JIM_ERR;
}
}
}
resultObj = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, resultObj, Jim_NewStringObj(interp, options[af->wbuft], -1));
if (af->wbuft == WBUF_OPT_FULL) {
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, af->wbuf_limit));
}
Jim_SetResult(interp, resultObj);
return JIM_OK;
}
static int aio_cmd_translation(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
enum {OPT_BINARY, OPT_TEXT};
static const char * const options[] = {
"binary",
"text",
NULL
};
int opt;
if (Jim_GetEnum(interp, argv[0], options, &opt, NULL, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
#if defined(Jim_SetMode)
else {
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetMode(af->fd, opt == OPT_BINARY ? O_BINARY : O_TEXT);
}
#endif
return JIM_OK;
}
static int aio_cmd_readsize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
if (argc) {
long l;
if (Jim_GetLong(interp, argv[0], &l) != JIM_OK || l <= 0) {
return JIM_ERR;
}
af->rbuf_len = l;
if (af->rbuf) {
af->rbuf = Jim_Realloc(af->rbuf, af->rbuf_len);
}
}
Jim_SetResultInt(interp, af->rbuf_len);
return JIM_OK;
}
#ifdef jim_ext_eventloop
static int aio_cmd_timeout(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
#ifdef HAVE_SELECT
AioFile *af = Jim_CmdPrivData(interp);
if (argc == 1) {
if (Jim_GetLong(interp, argv[0], &af->timeout) != JIM_OK) {
return JIM_ERR;
}
}
Jim_SetResultInt(interp, af->timeout);
return JIM_OK;
#else
Jim_SetResultString(interp, "timeout not supported", -1);
return JIM_ERR;
#endif
}
static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask,
int argc, Jim_Obj * const *argv)
{
if (argc == 0) {
Jim_Obj *objPtr = Jim_FindFileHandler(interp, af->fd, mask);
if (objPtr) {
Jim_SetResult(interp, objPtr);
}
return JIM_OK;
}
Jim_DeleteFileHandler(interp, af->fd, mask);
if (Jim_Length(argv[0])) {
Jim_CreateScriptFileHandler(interp, af->fd, mask, argv[0]);
}
return JIM_OK;
}
static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
return aio_eventinfo(interp, af, JIM_EVENT_READABLE, argc, argv);
}
static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, argc, argv);
}
static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, argc, argv);
}
#endif
#if defined(jim_ext_file) && defined(Jim_FileStat)
static int aio_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
AioFile *af = Jim_CmdPrivData(interp);
if (Jim_FileStat(af->fd, &sb) == -1) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
return Jim_FileStoreStatData(interp, argc == 0 ? NULL : argv[0], &sb);
}
#endif
static const jim_subcmd_type aio_command_table[] = {
{ "read",
"?-nonewline|len?",
aio_cmd_read,
0,
2,
},
{ "copyto",
"handle ?size?",
aio_cmd_copy,
1,
2,
},
{ "getfd",
NULL,
aio_cmd_getfd,
0,
0,
},
{ "gets",
"?var?",
aio_cmd_gets,
0,
1,
},
{ "puts",
"?-nonewline? str",
aio_cmd_puts,
1,
2,
},
{ "isatty",
NULL,
aio_cmd_isatty,
0,
0,
},
{ "flush",
NULL,
aio_cmd_flush,
0,
0,
},
{ "eof",
NULL,
aio_cmd_eof,
0,
0,
},
{ "close",
"?r(ead)|w(rite)?",
aio_cmd_close,
0,
1,
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
{ "ndelay",
"?0|1?",
aio_cmd_ndelay,
0,
1,
},
#endif
#ifdef HAVE_FSYNC
{ "sync",
NULL,
aio_cmd_sync,
0,
0,
},
#endif
{ "buffering",
"?none|line|full? ?size?",
aio_cmd_buffering,
0,
2,
},
{ "translation",
"binary|text",
aio_cmd_translation,
1,
1,
},
{ "readsize",
"?size?",
aio_cmd_readsize,
0,
1,
},
#if defined(jim_ext_file) && defined(Jim_FileStat)
{ "stat",
"?var?",
aio_cmd_stat,
0,
1,
},
#endif
#ifdef jim_ext_eventloop
{ "readable",
"?readable-script?",
aio_cmd_readable,
0,
1,
},
{ "writable",
"?writable-script?",
aio_cmd_writable,
0,
1,
},
{ "onexception",
"?exception-script?",
aio_cmd_onexception,
0,
1,
},
{ "timeout",
"?ms?",
aio_cmd_timeout,
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 parse_posix_open_mode(Jim_Interp *interp, Jim_Obj *modeObj)
{
int i;
int flags = 0;
#ifndef O_NOCTTY
#define O_NOCTTY 0
#endif
static const char * const modetypes[] = {
"RDONLY", "WRONLY", "RDWR", "APPEND", "BINARY", "CREAT", "EXCL", "NOCTTY", "TRUNC", NULL
};
static const int modeflags[] = {
O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, 0, O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC,
};
for (i = 0; i < Jim_ListLength(interp, modeObj); i++) {
int opt;
Jim_Obj *objPtr = Jim_ListGetIndex(interp, modeObj, i);
if (Jim_GetEnum(interp, objPtr, modetypes, &opt, "access mode", JIM_ERRMSG) != JIM_OK) {
return -1;
}
flags |= modeflags[opt];
}
return flags;
}
static int parse_open_mode(Jim_Interp *interp, Jim_Obj *filenameObj, Jim_Obj *modeObj)
{
int flags;
const char *mode = Jim_String(modeObj);
if (*mode == 'R' || *mode == 'W') {
return parse_posix_open_mode(interp, modeObj);
}
if (*mode == 'r') {
flags = O_RDONLY;
}
else if (*mode == 'w') {
flags = O_WRONLY | O_CREAT | O_TRUNC;
}
else if (*mode == 'a') {
flags = O_WRONLY | O_CREAT | O_APPEND;
}
else {
Jim_SetResultFormatted(interp, "%s: invalid open mode '%s'", Jim_String(filenameObj), mode);
return -1;
}
mode++;
if (*mode == 'b') {
#ifdef O_BINARY
flags |= O_BINARY;
#endif
mode++;
}
if (*mode == 't') {
#ifdef O_TEXT
flags |= O_TEXT;
#endif
mode++;
}
if (*mode == '+') {
mode++;
flags &= ~(O_RDONLY | O_WRONLY);
flags |= O_RDWR;
}
if (*mode == 'x') {
mode++;
#ifdef O_EXCL
flags |= O_EXCL;
#endif
}
if (*mode == 'F') {
mode++;
#ifdef O_LARGEFILE
flags |= O_LARGEFILE;
#endif
}
if (*mode == 'e') {
mode++;
}
return flags;
}
static int JimAioOpenCommand(Jim_Interp *interp, int argc,
Jim_Obj *const *argv)
{
int openflags;
const char *filename;
int fd = -1;
int n = 0;
int flags = 0;
if (argc > 2 && Jim_CompareStringImmediate(interp, argv[2], "-noclose")) {
flags = AIO_KEEPOPEN;
n++;
}
if (argc < 2 || argc > 3 + n) {
Jim_WrongNumArgs(interp, 1, argv, "filename ?-noclose? ?mode?");
return JIM_ERR;
}
filename = Jim_String(argv[1]);
#ifdef jim_ext_tclcompat
{
if (*filename == '|') {
Jim_Obj *evalObj[3];
int i = 0;
evalObj[i++] = Jim_NewStringObj(interp, "::popen", -1);
evalObj[i++] = Jim_NewStringObj(interp, filename + 1, -1);
if (argc == 3 + n) {
evalObj[i++] = argv[2 + n];
}
return Jim_EvalObjVector(interp, i, evalObj);
}
}
#endif
if (argc == 3 + n) {
openflags = parse_open_mode(interp, argv[1], argv[2 + n]);
if (openflags == -1) {
return JIM_ERR;
}
}
else {
openflags = O_RDONLY;
}
fd = open(filename, openflags, 0666);
if (fd < 0) {
JimAioSetError(interp, argv[1]);
return JIM_ERR;
}
return JimMakeChannel(interp, fd, argv[1], "aio.handle%ld", 0, flags) ? JIM_OK : JIM_ERR;
}
static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
const char *hdlfmt, int family, int flags)
{
AioFile *af;
char buf[AIO_CMD_LEN];
Jim_Obj *cmdname;
snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
cmdname = Jim_NewStringObj(interp, buf, -1);
if (!filename) {
filename = cmdname;
}
Jim_IncrRefCount(filename);
af = Jim_Alloc(sizeof(*af));
memset(af, 0, sizeof(*af));
af->filename = filename;
af->fd = fd;
af->addr_family = family;
af->fops = &stdio_fops;
af->ssl = NULL;
if (flags & AIO_WBUF_NONE) {
af->wbuft = WBUF_OPT_NONE;
}
else {
#ifdef HAVE_ISATTY
af->wbuft = isatty(af->fd) ? WBUF_OPT_LINE : WBUF_OPT_FULL;
#else
af->wbuft = WBUF_OPT_FULL;
#endif
}
#ifdef FD_CLOEXEC
if ((flags & AIO_KEEPOPEN) == 0) {
(void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
}
#endif
aio_set_nonblocking(af, !!(flags & AIO_NONBLOCK));
af->flags |= flags;
af->writebuf = Jim_NewStringObj(interp, NULL, 0);
Jim_IncrRefCount(af->writebuf);
af->wbuf_limit = AIO_DEFAULT_WBUF_LIMIT;
af->rbuf_len = AIO_DEFAULT_RBUF_LEN;
Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, cmdname));
return af;
}
#if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS) || defined(HAVE_OPENPTY)
static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
const char *hdlfmt, int family, int flags)
{
if (JimMakeChannel(interp, p[0], filename, hdlfmt, family, flags)) {
Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
if (JimMakeChannel(interp, p[1], filename, hdlfmt, family, flags)) {
Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
}
close(p[0]);
close(p[1]);
JimAioSetError(interp, NULL);
return JIM_ERR;
}
#endif
#ifdef HAVE_PIPE
static int JimCreatePipe(Jim_Interp *interp, Jim_Obj *filenameObj, int flags)
{
int p[2];
if (pipe(p) != 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
return JimMakeChannelPair(interp, p, filenameObj, "aio.pipe%ld", 0, flags);
}
static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "");
return JIM_ERR;
}
return JimCreatePipe(interp, argv[0], 0);
}
#endif
#ifdef HAVE_OPENPTY
static int JimAioOpenPtyCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int p[2];
char path[MAXPATHLEN];
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "");
return JIM_ERR;
}
if (openpty(&p[0], &p[1], path, NULL, NULL) != 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0);
return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0);
}
#endif
int Jim_aioInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG))
return JIM_ERR;
#if defined(JIM_SSL)
Jim_CreateCommand(interp, "load_ssl_certs", JimAioLoadSSLCertsCommand, NULL, NULL);
#endif
Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
#ifdef HAVE_SOCKETS
Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
#endif
#ifdef HAVE_PIPE
Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
#endif
JimMakeChannel(interp, fileno(stdin), NULL, "stdin", 0, AIO_KEEPOPEN);
JimMakeChannel(interp, fileno(stdout), NULL, "stdout", 0, AIO_KEEPOPEN);
JimMakeChannel(interp, fileno(stderr), NULL, "stderr", 0, AIO_KEEPOPEN | AIO_WBUF_NONE);
return JIM_OK;
}
#include <errno.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *dirPath;
DIR *dirPtr;
struct dirent *entryPtr;
int nocomplain = 0;
if (argc == 3 && Jim_CompareStringImmediate(interp, argv[1], "-nocomplain")) {
nocomplain = 1;
}
if (argc != 2 && !nocomplain) {
Jim_WrongNumArgs(interp, 1, argv, "?-nocomplain? dirPath");
return JIM_ERR;
}
dirPath = Jim_String(argv[1 + nocomplain]);
dirPtr = opendir(dirPath);
if (dirPtr == NULL) {
if (nocomplain) {
return JIM_OK;
}
Jim_SetResultString(interp, strerror(errno), -1);
return JIM_ERR;
}
else {
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
while ((entryPtr = readdir(dirPtr)) != NULL) {
if (entryPtr->d_name[0] == '.') {
if (entryPtr->d_name[1] == '\0') {
continue;
}
if ((entryPtr->d_name[1] == '.') && (entryPtr->d_name[2] == '\0'))
continue;
}
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, entryPtr->d_name, -1));
}
closedir(dirPtr);
Jim_SetResult(interp, listObj);
return JIM_OK;
}
}
int Jim_readdirInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "readdir");
Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
return JIM_OK;
}
#include <stdlib.h>
#include <string.h>
#if defined(JIM_REGEXP)
#else
#include <regex.h>
#define jim_regcomp regcomp
#define jim_regexec regexec
#define jim_regerror regerror
#define jim_regfree regfree
#endif
static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
jim_regfree(objPtr->internalRep.ptrIntValue.ptr);
Jim_Free(objPtr->internalRep.ptrIntValue.ptr);
}
static const Jim_ObjType regexpObjType = {
"regexp",
FreeRegexpInternalRep,
NULL,
NULL,
JIM_TYPE_NONE
};
static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags)
{
regex_t *compre;
const char *pattern;
int ret;
if (objPtr->typePtr == &regexpObjType &&
objPtr->internalRep.ptrIntValue.ptr && objPtr->internalRep.ptrIntValue.int1 == flags) {
return objPtr->internalRep.ptrIntValue.ptr;
}
pattern = Jim_String(objPtr);
compre = Jim_Alloc(sizeof(regex_t));
if ((ret = jim_regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
char buf[100];
jim_regerror(ret, compre, buf, sizeof(buf));
Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
jim_regfree(compre);
Jim_Free(compre);
return NULL;
}
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &regexpObjType;
objPtr->internalRep.ptrIntValue.int1 = flags;
objPtr->internalRep.ptrIntValue.ptr = compre;
return compre;
}
int Jim_RegexpCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int opt_indices = 0;
int opt_all = 0;
int opt_inline = 0;
regex_t *regex;
int match, i, j;
int offset = 0;
regmatch_t *pmatch = NULL;
int source_len;
int result = JIM_OK;
const char *pattern;
const char *source_str;
int num_matches = 0;
int num_vars;
Jim_Obj *resultListObj = NULL;
int regcomp_flags = 0;
int eflags = 0;
int option;
enum {
OPT_INDICES, OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_INLINE, OPT_START, OPT_END
};
static const char * const options[] = {
"-indices", "-nocase", "-line", "-all", "-inline", "-start", "--", NULL
};
if (argc < 3) {
wrongNumArgs:
Jim_WrongNumArgs(interp, 1, argv,
"?-switch ...? exp string ?matchVar? ?subMatchVar ...?");
return JIM_ERR;
}
for (i = 1; i < argc; i++) {
const char *opt = Jim_String(argv[i]);
if (*opt != '-') {
break;
}
if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
return JIM_ERR;
}
if (option == OPT_END) {
i++;
break;
}
switch (option) {
case OPT_INDICES:
opt_indices = 1;
break;
case OPT_NOCASE:
regcomp_flags |= REG_ICASE;
break;
case OPT_LINE:
regcomp_flags |= REG_NEWLINE;
break;
case OPT_ALL:
opt_all = 1;
break;
case OPT_INLINE:
opt_inline = 1;
break;
case OPT_START:
if (++i == argc) {
goto wrongNumArgs;
}
if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
return JIM_ERR;
}
break;
}
}
if (argc - i < 2) {
goto wrongNumArgs;
}
regex = SetRegexpFromAny(interp, argv[i], regcomp_flags);
if (!regex) {
return JIM_ERR;
}
pattern = Jim_String(argv[i]);
source_str = Jim_GetString(argv[i + 1], &source_len);
num_vars = argc - i - 2;
if (opt_inline) {
if (num_vars) {
Jim_SetResultString(interp, "regexp match variables not allowed when using -inline",
-1);
result = JIM_ERR;
goto done;
}
num_vars = regex->re_nsub + 1;
}
pmatch = Jim_Alloc((num_vars + 1) * sizeof(*pmatch));
if (offset) {
if (offset < 0) {
offset += source_len + 1;
}
if (offset > source_len) {
source_str += source_len;
}
else if (offset > 0) {
source_str += utf8_index(source_str, offset);
}
eflags |= REG_NOTBOL;
}
if (opt_inline) {
resultListObj = Jim_NewListObj(interp, NULL, 0);
}
next_match:
match = jim_regexec(regex, source_str, num_vars + 1, pmatch, eflags);
if (match >= REG_BADPAT) {
char buf[100];
jim_regerror(match, regex, buf, sizeof(buf));
Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
result = JIM_ERR;
goto done;
}
if (match == REG_NOMATCH) {
goto done;
}
num_matches++;
if (opt_all && !opt_inline) {
goto try_next_match;
}
j = 0;
for (i += 2; opt_inline ? j < num_vars : i < argc; i++, j++) {
Jim_Obj *resultObj;
if (opt_indices) {
resultObj = Jim_NewListObj(interp, NULL, 0);
}
else {
resultObj = Jim_NewStringObj(interp, "", 0);
}
if (pmatch[j].rm_so == -1) {
if (opt_indices) {
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
}
}
else {
if (opt_indices) {
int so = utf8_strlen(source_str, pmatch[j].rm_so);
int eo = utf8_strlen(source_str, pmatch[j].rm_eo);
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + so));
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + eo - 1));
}
else {
Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so);
}
}
if (opt_inline) {
Jim_ListAppendElement(interp, resultListObj, resultObj);
}
else {
result = Jim_SetVariable(interp, argv[i], resultObj);
if (result != JIM_OK) {
Jim_FreeObj(interp, resultObj);
break;
}
}
}
try_next_match:
if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
if (pmatch[0].rm_eo) {
offset += utf8_strlen(source_str, pmatch[0].rm_eo);
source_str += pmatch[0].rm_eo;
}
else {
source_str++;
offset++;
}
if (*source_str) {
eflags = REG_NOTBOL;
goto next_match;
}
}
done:
if (result == JIM_OK) {
if (opt_inline) {
Jim_SetResult(interp, resultListObj);
}
else {
Jim_SetResultInt(interp, num_matches);
}
}
Jim_Free(pmatch);
return result;
}
#define MAX_SUB_MATCHES 50
int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int regcomp_flags = 0;
int regexec_flags = 0;
int opt_all = 0;
int opt_command = 0;
int offset = 0;
regex_t *regex;
const char *p;
int result = JIM_OK;
regmatch_t pmatch[MAX_SUB_MATCHES + 1];
int num_matches = 0;
int i, j, n;
Jim_Obj *varname;
Jim_Obj *resultObj;
Jim_Obj *cmd_prefix = NULL;
Jim_Obj *regcomp_obj = NULL;
const char *source_str;
int source_len;
const char *replace_str = NULL;
int replace_len;
const char *pattern;
int option;
enum {
OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_COMMAND, OPT_END
};
static const char * const options[] = {
"-nocase", "-line", "-all", "-start", "-command", "--", NULL
};
if (argc < 4) {
wrongNumArgs:
Jim_WrongNumArgs(interp, 1, argv,
"?-switch ...? exp string subSpec ?varName?");
return JIM_ERR;
}
for (i = 1; i < argc; i++) {
const char *opt = Jim_String(argv[i]);
if (*opt != '-') {
break;
}
if (Jim_GetEnum(interp, argv[i], options, &option, "switch", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
return JIM_ERR;
}
if (option == OPT_END) {
i++;
break;
}
switch (option) {
case OPT_NOCASE:
regcomp_flags |= REG_ICASE;
break;
case OPT_LINE:
regcomp_flags |= REG_NEWLINE;
break;
case OPT_ALL:
opt_all = 1;
break;
case OPT_START:
if (++i == argc) {
goto wrongNumArgs;
}
if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
return JIM_ERR;
}
break;
case OPT_COMMAND:
opt_command = 1;
break;
}
}
if (argc - i != 3 && argc - i != 4) {
goto wrongNumArgs;
}
regcomp_obj = Jim_DuplicateObj(interp, argv[i]);
Jim_IncrRefCount(regcomp_obj);
regex = SetRegexpFromAny(interp, regcomp_obj, regcomp_flags);
if (!regex) {
Jim_DecrRefCount(interp, regcomp_obj);
return JIM_ERR;
}
pattern = Jim_String(argv[i]);
source_str = Jim_GetString(argv[i + 1], &source_len);
if (opt_command) {
cmd_prefix = argv[i + 2];
if (Jim_ListLength(interp, cmd_prefix) == 0) {
Jim_SetResultString(interp, "command prefix must be a list of at least one element", -1);
Jim_DecrRefCount(interp, regcomp_obj);
return JIM_ERR;
}
Jim_IncrRefCount(cmd_prefix);
}
else {
replace_str = Jim_GetString(argv[i + 2], &replace_len);
}
varname = argv[i + 3];
resultObj = Jim_NewStringObj(interp, "", 0);
if (offset) {
if (offset < 0) {
offset += source_len + 1;
}
if (offset > source_len) {
offset = source_len;
}
else if (offset < 0) {
offset = 0;
}
}
offset = utf8_index(source_str, offset);
Jim_AppendString(interp, resultObj, source_str, offset);
n = source_len - offset;
p = source_str + offset;
do {
int match = jim_regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
if (match >= REG_BADPAT) {
char buf[100];
jim_regerror(match, regex, buf, sizeof(buf));
Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
return JIM_ERR;
}
if (match == REG_NOMATCH) {
break;
}
num_matches++;
Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
if (opt_command) {
Jim_Obj *cmdListObj = Jim_DuplicateObj(interp, cmd_prefix);
for (j = 0; j < MAX_SUB_MATCHES; j++) {
if (pmatch[j].rm_so == -1) {
break;
}
else {
Jim_Obj *srcObj = Jim_NewStringObj(interp, p + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so);
Jim_ListAppendElement(interp, cmdListObj, srcObj);
}
}
Jim_IncrRefCount(cmdListObj);
result = Jim_EvalObj(interp, cmdListObj);
Jim_DecrRefCount(interp, cmdListObj);
if (result != JIM_OK) {
goto cmd_error;
}
Jim_AppendString(interp, resultObj, Jim_String(Jim_GetResult(interp)), -1);
}
else {
for (j = 0; j < replace_len; j++) {
int idx;
int c = replace_str[j];
if (c == '&') {
idx = 0;
}
else if (c == '\\' && j < replace_len) {
c = replace_str[++j];
if ((c >= '0') && (c <= '9')) {
idx = c - '0';
}
else if ((c == '\\') || (c == '&')) {
Jim_AppendString(interp, resultObj, replace_str + j, 1);
continue;
}
else {
Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2);
continue;
}
}
else {
Jim_AppendString(interp, resultObj, replace_str + j, 1);
continue;
}
if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
pmatch[idx].rm_eo - pmatch[idx].rm_so);
}
}
}
p += pmatch[0].rm_eo;
n -= pmatch[0].rm_eo;
if (!opt_all || n == 0) {
break;
}
if ((regcomp_flags & REG_NEWLINE) == 0 && pattern[0] == '^') {
break;
}
if (pattern[0] == '\0' && n) {
Jim_AppendString(interp, resultObj, p, 1);
p++;
n--;
}
if (pmatch[0].rm_eo == pmatch[0].rm_so) {
regexec_flags = REG_NOTBOL;
}
else {
regexec_flags = 0;
}
} while (n);
Jim_AppendString(interp, resultObj, p, -1);
cmd_error:
if (result == JIM_OK) {
if (argc - i == 4) {
result = Jim_SetVariable(interp, varname, resultObj);
if (result == JIM_OK) {
Jim_SetResultInt(interp, num_matches);
}
else {
Jim_FreeObj(interp, resultObj);
}
}
else {
Jim_SetResult(interp, resultObj);
result = JIM_OK;
}
}
else {
Jim_FreeObj(interp, resultObj);
}
if (opt_command) {
Jim_DecrRefCount(interp, cmd_prefix);
}
Jim_DecrRefCount(interp, regcomp_obj);
return result;
}
int Jim_regexpInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "regexp");
Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
return JIM_OK;
}
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.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
#if defined(__MINGW32__) || defined(__MSYS__) || defined(_MSC_VER)
#define ISWINDOWS 1
#undef HAVE_SYMLINK
#else
#define ISWINDOWS 0
#endif
#if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
#define STAT_MTIME_US(STAT) ((STAT).st_mtimespec.tv_sec * 1000000ll + (STAT).st_mtimespec.tv_nsec / 1000)
#elif defined(HAVE_STRUCT_STAT_ST_MTIM)
#define STAT_MTIME_US(STAT) ((STAT).st_mtim.tv_sec * 1000000ll + (STAT).st_mtim.tv_nsec / 1000)
#endif
static void JimFixPath(char *path)
{
if (ISWINDOWS) {
char *p = path;
while ((p = strchr(p, '\\')) != NULL) {
*p++ = '/';
}
}
}
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";
}
static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
{
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
}
int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb)
{
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
AppendStatElement(interp, listObj, "dev", sb->st_dev);
AppendStatElement(interp, listObj, "ino", sb->st_ino);
AppendStatElement(interp, listObj, "mode", sb->st_mode);
AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
AppendStatElement(interp, listObj, "uid", sb->st_uid);
AppendStatElement(interp, listObj, "gid", sb->st_gid);
AppendStatElement(interp, listObj, "size", sb->st_size);
AppendStatElement(interp, listObj, "atime", sb->st_atime);
AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
#ifdef STAT_MTIME_US
AppendStatElement(interp, listObj, "mtimeus", STAT_MTIME_US(*sb));
#endif
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));
if (varName) {
Jim_Obj *objPtr;
objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
if (objPtr) {
Jim_Obj *objv[2];
objv[0] = objPtr;
objv[1] = listObj;
objPtr = Jim_DictMerge(interp, 2, objv);
if (objPtr == NULL) {
Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
Jim_FreeNewObj(interp, listObj);
return JIM_ERR;
}
Jim_InvalidateStringRep(objPtr);
Jim_FreeNewObj(interp, listObj);
listObj = objPtr;
}
Jim_SetVariable(interp, varName, listObj);
}
Jim_SetResult(interp, listObj);
return JIM_OK;
}
static int JimPathLenNoTrailingSlashes(const char *path, int len)
{
int i;
for (i = len; i > 1 && path[i - 1] == '/'; i--) {
if (ISWINDOWS && path[i - 2] == ':') {
break;
}
}
return i;
}
static Jim_Obj *JimStripTrailingSlashes(Jim_Interp *interp, Jim_Obj *objPtr)
{
int len = Jim_Length(objPtr);
const char *path = Jim_String(objPtr);
int i = JimPathLenNoTrailingSlashes(path, len);
if (i != len) {
objPtr = Jim_NewStringObj(interp, path, i);
}
Jim_IncrRefCount(objPtr);
return objPtr;
}
static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
const char *path = Jim_String(objPtr);
const char *p = strrchr(path, '/');
if (!p) {
Jim_SetResultString(interp, ".", -1);
}
else if (p[1] == 0) {
Jim_SetResult(interp, objPtr);
}
else if (p == path) {
Jim_SetResultString(interp, "/", -1);
}
else if (ISWINDOWS && p[-1] == ':') {
Jim_SetResultString(interp, path, p - path + 1);
}
else {
int len = JimPathLenNoTrailingSlashes(path, p - path);
Jim_SetResultString(interp, path, len);
}
Jim_DecrRefCount(interp, objPtr);
return JIM_OK;
}
static int file_cmd_split(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
const char *path = Jim_String(argv[0]);
if (*path == '/') {
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "/", 1));
}
while (1) {
while (*path == '/') {
path++;
}
if (*path) {
const char *pt = strchr(path, '/');
if (pt) {
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, pt - path));
path = pt;
continue;
}
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, -1));
}
break;
}
Jim_SetResult(interp, listObj);
return JIM_OK;
}
static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *path = Jim_String(argv[0]);
const char *lastSlash = strrchr(path, '/');
const char *p = strrchr(path, '.');
if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
Jim_SetResult(interp, argv[0]);
}
else {
Jim_SetResultString(interp, path, p - path);
}
return JIM_OK;
}
static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
const char *path = Jim_String(objPtr);
const char *lastSlash = strrchr(path, '/');
const char *p = strrchr(path, '.');
if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
p = "";
}
Jim_SetResultString(interp, p, -1);
Jim_DecrRefCount(interp, objPtr);
return JIM_OK;
}
static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
const char *path = Jim_String(objPtr);
const char *lastSlash = strrchr(path, '/');
if (lastSlash) {
Jim_SetResultString(interp, lastSlash + 1, -1);
}
else {
Jim_SetResult(interp, objPtr);
}
Jim_DecrRefCount(interp, objPtr);
return JIM_OK;
}
#ifndef HAVE_RESTRICT
#define restrict
#endif
static char *JimRealPath(const char *restrict path, char *restrict resolved_path, size_t len)
{
#if defined(HAVE__FULLPATH)
return _fullpath(resolved_path, path, len);
#elif defined(HAVE_REALPATH)
return realpath(path, resolved_path);
#else
return NULL;
#endif
}
static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *path = Jim_String(argv[0]);
char *newname = Jim_Alloc(MAXPATHLEN);
if (JimRealPath(path, newname, MAXPATHLEN)) {
JimFixPath(newname);
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
return JIM_OK;
}
Jim_Free(newname);
Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
return JIM_ERR;
}
static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int i;
char *newname = Jim_Alloc(MAXPATHLEN + 1);
char *last = newname;
*newname = 0;
for (i = 0; i < argc; i++) {
int len;
const char *part = Jim_GetString(argv[i], &len);
if (*part == '/') {
last = newname;
}
else if (ISWINDOWS && strchr(part, ':')) {
last = newname;
}
else if (part[0] == '.') {
if (part[1] == '/') {
part += 2;
len -= 2;
}
else if (part[1] == 0 && last != newname) {
continue;
}
}
if (last != newname && last[-1] != '/') {
*last++ = '/';
}
if (len) {
if (last + len - newname >= MAXPATHLEN) {
Jim_Free(newname);
Jim_SetResultString(interp, "Path too long", -1);
return JIM_ERR;
}
memcpy(last, part, len);
last += len;
}
if (last > newname + 1 && last[-1] == '/') {
if (!ISWINDOWS || !(last > newname + 2 && last[-2] == ':')) {
*--last = 0;
}
}
}
*last = 0;
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
return JIM_OK;
}
static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
{
Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1);
return JIM_OK;
}
static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return file_access(interp, argv[0], R_OK);
}
static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
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);
}
static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
argc--;
argv++;
}
while (argc--) {
const char *path = Jim_String(argv[0]);
if (unlink(path) == -1 && errno != ENOENT) {
if (rmdir(path) == -1) {
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;
}
}
}
argv++;
}
return JIM_OK;
}
#ifdef HAVE_MKDIR_ONE_ARG
#define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
#else
#define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
#endif
static int mkdir_all(char *path)
{
int ok = 1;
goto first;
while (ok--) {
{
char *slash = strrchr(path, '/');
if (slash && slash != path) {
*slash = 0;
if (mkdir_all(path) != 0) {
return -1;
}
*slash = '/';
}
}
first:
if (MKDIR_DEFAULT(path) == 0) {
return 0;
}
if (errno == ENOENT) {
continue;
}
if (errno == EEXIST) {
jim_stat_t sb;
if (Jim_Stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
return 0;
}
errno = EEXIST;
}
break;
}
return -1;
}
static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
while (argc--) {
char *path = Jim_StrDup(Jim_String(argv[0]));
int rc = mkdir_all(path);
Jim_Free(path);
if (rc != 0) {
Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
strerror(errno));
return JIM_ERR;
}
argv++;
}
return JIM_OK;
}
static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL, 0);
if (fd < 0) {
return JIM_ERR;
}
close(fd);
return JIM_OK;
}
static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *source;
const char *dest;
int force = 0;
if (argc == 3) {
if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
return -1;
}
force++;
argv++;
argc--;
}
source = Jim_String(argv[0]);
dest = Jim_String(argv[1]);
if (!force && access(dest, F_OK) == 0) {
Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
argv[1]);
return JIM_ERR;
}
#if ISWINDOWS
if (access(dest, F_OK) == 0) {
remove(dest);
}
#endif
if (rename(source, dest) != 0) {
Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
strerror(errno));
return JIM_ERR;
}
return JIM_OK;
}
#if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int ret;
const char *source;
const char *dest;
static const char * const options[] = { "-hard", "-symbolic", NULL };
enum { OPT_HARD, OPT_SYMBOLIC, };
int option = OPT_HARD;
if (argc == 3) {
if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
argv++;
argc--;
}
dest = Jim_String(argv[0]);
source = Jim_String(argv[1]);
if (option == OPT_HARD) {
ret = link(source, dest);
}
else {
ret = symlink(source, dest);
}
if (ret != 0) {
Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
strerror(errno));
return JIM_ERR;
}
return JIM_OK;
}
#endif
static int file_stat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb)
{
const char *path = Jim_String(filename);
if (Jim_Stat(path, sb) == -1) {
Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
return JIM_ERR;
}
return JIM_OK;
}
#ifdef Jim_LinkStat
static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb)
{
const char *path = Jim_String(filename);
if (Jim_LinkStat(path, sb) == -1) {
Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
return JIM_ERR;
}
return JIM_OK;
}
#else
#define file_lstat file_stat
#endif
static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResultInt(interp, sb.st_atime);
return JIM_OK;
}
static int JimSetFileTimes(Jim_Interp *interp, const char *filename, jim_wide us)
{
#ifdef HAVE_UTIMES
struct timeval times[2];
times[1].tv_sec = times[0].tv_sec = us / 1000000;
times[1].tv_usec = times[0].tv_usec = us % 1000000;
if (utimes(filename, times) != 0) {
Jim_SetResultFormatted(interp, "can't set time on \"%s\": %s", filename, strerror(errno));
return JIM_ERR;
}
return JIM_OK;
#else
Jim_SetResultString(interp, "Not implemented", -1);
return JIM_ERR;
#endif
}
static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (argc == 2) {
jim_wide secs;
if (Jim_GetWide(interp, argv[1], &secs) != JIM_OK) {
return JIM_ERR;
}
return JimSetFileTimes(interp, Jim_String(argv[0]), secs * 1000000);
}
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResultInt(interp, sb.st_mtime);
return JIM_OK;
}
#ifdef STAT_MTIME_US
static int file_cmd_mtimeus(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (argc == 2) {
jim_wide us;
if (Jim_GetWide(interp, argv[1], &us) != JIM_OK) {
return JIM_ERR;
}
return JimSetFileTimes(interp, Jim_String(argv[0]), us);
}
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResultInt(interp, STAT_MTIME_US(sb));
return JIM_OK;
}
#endif
static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return Jim_EvalPrefix(interp, "file copy", argc, argv);
}
static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResultInt(interp, sb.st_size);
return JIM_OK;
}
static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
int ret = 0;
if (file_stat(interp, argv[0], &sb) == JIM_OK) {
ret = S_ISDIR(sb.st_mode);
}
Jim_SetResultInt(interp, ret);
return JIM_OK;
}
static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
int ret = 0;
if (file_stat(interp, argv[0], &sb) == JIM_OK) {
ret = S_ISREG(sb.st_mode);
}
Jim_SetResultInt(interp, ret);
return JIM_OK;
}
#ifdef HAVE_GETEUID
static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
int ret = 0;
if (file_stat(interp, argv[0], &sb) == JIM_OK) {
ret = (geteuid() == sb.st_uid);
}
Jim_SetResultInt(interp, ret);
return JIM_OK;
}
#endif
#if defined(HAVE_READLINK)
static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *path = Jim_String(argv[0]);
char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
int linkLength = readlink(path, linkValue, MAXPATHLEN);
if (linkLength == -1) {
Jim_Free(linkValue);
Jim_SetResultFormatted(interp, "could not read link \"%#s\": %s", argv[0], strerror(errno));
return JIM_ERR;
}
linkValue[linkLength] = 0;
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
return JIM_OK;
}
#endif
static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
return JIM_OK;
}
#ifdef Jim_LinkStat
static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
}
#else
#define file_cmd_lstat file_cmd_stat
#endif
static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
}
static const jim_subcmd_type file_command_table[] = {
{ "atime",
"name",
file_cmd_atime,
1,
1,
},
{ "mtime",
"name ?time?",
file_cmd_mtime,
1,
2,
},
#ifdef STAT_MTIME_US
{ "mtimeus",
"name ?time?",
file_cmd_mtimeus,
1,
2,
},
#endif
{ "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,
},
{ "split",
"name",
file_cmd_split,
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,
},
{ "tempfile",
"?template?",
file_cmd_tempfile,
0,
1,
},
{ "rename",
"?-force? source dest",
file_cmd_rename,
2,
3,
},
#if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
{ "link",
"?-symbolic|-hard? newname target",
file_cmd_link,
2,
3,
},
#endif
#if defined(HAVE_READLINK)
{ "readlink",
"name",
file_cmd_readlink,
1,
1,
},
#endif
{ "size",
"name",
file_cmd_size,
1,
1,
},
{ "stat",
"name ?var?",
file_cmd_stat,
1,
2,
},
{ "lstat",
"name ?var?",
file_cmd_lstat,
1,
2,
},
{ "type",
"name",
file_cmd_type,
1,
1,
},
#ifdef HAVE_GETEUID
{ "owned",
"name",
file_cmd_owned,
1,
1,
},
#endif
{ "isdirectory",
"name",
file_cmd_isdirectory,
1,
1,
},
{ "isfile",
"name",
file_cmd_isfile,
1,
1,
},
{
NULL
}
};
static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *path;
if (argc != 2) {
Jim_WrongNumArgs(interp, 1, argv, "dirname");
return JIM_ERR;
}
path = Jim_String(argv[1]);
if (chdir(path) != 0) {
Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
strerror(errno));
return JIM_ERR;
}
return JIM_OK;
}
static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
char *cwd = Jim_Alloc(MAXPATHLEN);
if (getcwd(cwd, MAXPATHLEN) == NULL) {
Jim_SetResultString(interp, "Failed to get pwd", -1);
Jim_Free(cwd);
return JIM_ERR;
}
JimFixPath(cwd);
Jim_SetResultString(interp, cwd, -1);
Jim_Free(cwd);
return JIM_OK;
}
int Jim_fileInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "file");
Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
return JIM_OK;
}
#include <string.h>
#include <ctype.h>
#if (!(defined(HAVE_VFORK) || defined(HAVE_FORK)) || !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)
{
Jim_PackageProvideCheck(interp, "exec");
Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
return JIM_OK;
}
#else
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
struct WaitInfoTable;
static char **JimOriginalEnviron(void);
static char **JimSaveEnv(char **env);
static void JimRestoreEnv(char **env);
static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
phandle_t **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr);
static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj);
static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
#if defined(__MINGW32__)
static phandle_t JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId);
#endif
static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
{
int len;
const char *s = Jim_GetString(objPtr, &len);
if (len > 0 && s[len - 1] == '\n') {
objPtr->length--;
objPtr->bytes[objPtr->length] = '\0';
}
}
static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj)
{
char buf[256];
int ret = 0;
while (1) {
int retval = read(fd, buf, sizeof(buf));
if (retval > 0) {
ret = 1;
Jim_AppendString(interp, strObj, buf, retval);
}
if (retval <= 0) {
break;
}
}
close(fd);
return ret;
}
static char **JimBuildEnv(Jim_Interp *interp)
{
int i;
int size;
int num;
int n;
char **envptr;
char *envdata;
Jim_Obj *objPtr = Jim_GetGlobalVariableStr(interp, "env", JIM_NONE);
if (!objPtr) {
return JimOriginalEnviron();
}
num = Jim_ListLength(interp, objPtr);
if (num % 2) {
num--;
}
size = Jim_Length(objPtr) + 2;
envptr = Jim_Alloc(sizeof(*envptr) * (num / 2 + 1) + size);
envdata = (char *)&envptr[num / 2 + 1];
n = 0;
for (i = 0; i < num; i += 2) {
const char *s1, *s2;
Jim_Obj *elemObj;
Jim_ListIndex(interp, objPtr, i, &elemObj, JIM_NONE);
s1 = Jim_String(elemObj);
Jim_ListIndex(interp, objPtr, i + 1, &elemObj, JIM_NONE);
s2 = Jim_String(elemObj);
envptr[n] = envdata;
envdata += sprintf(envdata, "%s=%s", s1, s2);
envdata++;
n++;
}
envptr[n] = NULL;
*envdata = 0;
return envptr;
}
static void JimFreeEnv(char **env, char **original_environ)
{
if (env != original_environ) {
Jim_Free(env);
}
}
static Jim_Obj *JimMakeErrorCode(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj)
{
Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
if (pid <= 0) {
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, -1));
}
else if (WIFEXITED(waitStatus)) {
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
}
else {
const char *type;
const char *action;
const char *signame;
if (WIFSIGNALED(waitStatus)) {
type = "CHILDKILLED";
action = "killed";
signame = Jim_SignalId(WTERMSIG(waitStatus));
}
else {
type = "CHILDSUSP";
action = "suspended";
signame = "none";
}
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
if (errStrObj) {
Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
}
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, signame, -1));
}
return errorCode;
}
static int JimCheckWaitStatus(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj)
{
if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
return JIM_OK;
}
Jim_SetGlobalVariableStr(interp, "errorCode", JimMakeErrorCode(interp, pid, waitStatus, errStrObj));
return JIM_ERR;
}
struct WaitInfo
{
phandle_t phandle;
int status;
int flags;
};
struct WaitInfoTable {
struct WaitInfo *info;
int size;
int used;
int refcount;
};
#define WI_DETACHED 2
#define WAIT_TABLE_GROW_BY 4
static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
{
struct WaitInfoTable *table = privData;
if (--table->refcount == 0) {
Jim_Free(table->info);
Jim_Free(table);
}
}
static struct WaitInfoTable *JimAllocWaitInfoTable(void)
{
struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
table->info = NULL;
table->size = table->used = 0;
table->refcount = 1;
return table;
}
static int JimWaitRemove(struct WaitInfoTable *table, phandle_t phandle)
{
int i;
for (i = 0; i < table->used; i++) {
if (phandle == table->info[i].phandle) {
if (i != table->used - 1) {
table->info[i] = table->info[table->used - 1];
}
table->used--;
return 0;
}
}
return -1;
}
static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int outputId;
int errorId;
phandle_t *pidPtr;
int numPids, result;
int child_siginfo = 1;
Jim_Obj *childErrObj;
Jim_Obj *errStrObj;
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
Jim_Obj *listObj;
int i;
argc--;
numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
if (numPids < 0) {
return JIM_ERR;
}
listObj = Jim_NewListObj(interp, NULL, 0);
for (i = 0; i < numPids; i++) {
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, JimProcessPid(pidPtr[i])));
}
Jim_SetResult(interp, listObj);
JimDetachPids(table, numPids, pidPtr);
Jim_Free(pidPtr);
return JIM_OK;
}
numPids =
JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, &outputId, &errorId);
if (numPids < 0) {
return JIM_ERR;
}
result = JIM_OK;
errStrObj = Jim_NewStringObj(interp, "", 0);
if (outputId != -1) {
if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) {
result = JIM_ERR;
Jim_SetResultErrno(interp, "error reading from output pipe");
}
}
childErrObj = Jim_NewStringObj(interp, "", 0);
Jim_IncrRefCount(childErrObj);
if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
result = JIM_ERR;
}
if (errorId != -1) {
int ret;
Jim_Lseek(errorId, 0, SEEK_SET);
ret = JimAppendStreamToString(interp, errorId, errStrObj);
if (ret < 0) {
Jim_SetResultErrno(interp, "error reading from error pipe");
result = JIM_ERR;
}
else if (ret > 0) {
child_siginfo = 0;
}
}
if (child_siginfo) {
Jim_AppendObj(interp, errStrObj, childErrObj);
}
Jim_DecrRefCount(interp, childErrObj);
Jim_RemoveTrailingNewline(errStrObj);
Jim_SetResult(interp, errStrObj);
return result;
}
static long JimWaitForProcess(struct WaitInfoTable *table, phandle_t phandle, int *statusPtr)
{
if (JimWaitRemove(table, phandle) == 0) {
return waitpid(phandle, statusPtr, 0);
}
return -1;
}
static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr)
{
int j;
for (j = 0; j < numPids; j++) {
int i;
for (i = 0; i < table->used; i++) {
if (pidPtr[j] == table->info[i].phandle) {
table->info[i].flags |= WI_DETACHED;
break;
}
}
}
}
static int JimGetChannelFd(Jim_Interp *interp, const char *name)
{
Jim_Obj *objv[2];
objv[0] = Jim_NewStringObj(interp, name, -1);
objv[1] = Jim_NewStringObj(interp, "getfd", -1);
if (Jim_EvalObjVector(interp, 2, objv) == JIM_OK) {
jim_wide fd;
if (Jim_GetWide(interp, Jim_GetResult(interp), &fd) == JIM_OK) {
return fd;
}
}
return -1;
}
static void JimReapDetachedPids(struct WaitInfoTable *table)
{
struct WaitInfo *waitPtr;
int count;
int dest;
if (!table) {
return;
}
waitPtr = table->info;
dest = 0;
for (count = table->used; count > 0; waitPtr++, count--) {
if (waitPtr->flags & WI_DETACHED) {
int status;
long pid = waitpid(waitPtr->phandle, &status, WNOHANG);
if (pid > 0) {
table->used--;
continue;
}
}
if (waitPtr != &table->info[dest]) {
table->info[dest] = *waitPtr;
}
dest++;
}
}
static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
int nohang = 0;
long pid;
phandle_t phandle;
int status;
Jim_Obj *errCodeObj;
if (argc == 1) {
JimReapDetachedPids(table);
return JIM_OK;
}
if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) {
nohang = 1;
}
if (argc != nohang + 2) {
Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?");
return JIM_ERR;
}
if (Jim_GetLong(interp, argv[nohang + 1], &pid) != JIM_OK) {
return JIM_ERR;
}
phandle = JimWaitPid(pid, &status, nohang ? WNOHANG : 0);
if (phandle == JIM_BAD_PHANDLE) {
pid = -1;
}
#ifndef __MINGW32__
else if (pid < 0) {
pid = phandle;
}
#endif
errCodeObj = JimMakeErrorCode(interp, pid, status, NULL);
if (phandle != JIM_BAD_PHANDLE && (WIFEXITED(status) || WIFSIGNALED(status))) {
JimWaitRemove(table, phandle);
}
Jim_SetResult(interp, errCodeObj);
return JIM_OK;
}
static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "");
return JIM_ERR;
}
Jim_SetResultInt(interp, (jim_wide)getpid());
return JIM_OK;
}
static int
JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr,
int *inPipePtr, int *outPipePtr, int *errFilePtr)
{
phandle_t *pidPtr = NULL; /* Points to alloc-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. */
int input_len = 0;
#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. */
int outputFile = FILE_NAME; /* 0 means output is the name of output file.
* 1 means output is the name of output file, and append.
* 2 means output is filehandle name.
* All this is ignored if output is NULL
*/
int errorFile = FILE_NAME; /* 0 means error is the name of error file.
* 1 means error is the name of error file, and append.
* 2 means error is filehandle name.
* All this is ignored if error is NULL
*/
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;
int outputId = -1;
int errorId = -1;
int lastOutputId = -1;
int pipeIds[2];
int firstArg, lastArg; /* Indexes of first and last arguments in
* current command. */
int lastBar;
int i;
phandle_t phandle;
char **save_environ;
#if defined(HAVE_EXECVPE) && !defined(__MINGW32__)
char **child_environ;
#endif
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
int arg_count = 0;
if (inPipePtr != NULL) {
*inPipePtr = -1;
}
if (outPipePtr != NULL) {
*outPipePtr = -1;
}
if (errFilePtr != NULL) {
*errFilePtr = -1;
}
pipeIds[0] = pipeIds[1] = -1;
cmdCount = 1;
lastBar = -1;
for (i = 0; i < argc; i++) {
const char *arg = Jim_String(argv[i]);
if (arg[0] == '<') {
inputFile = FILE_NAME;
input = arg + 1;
if (*input == '<') {
inputFile = FILE_TEXT;
input_len = Jim_Length(argv[i]) - 2;
input++;
}
else if (*input == '@') {
inputFile = FILE_HANDLE;
input++;
}
if (!*input && ++i < argc) {
input = Jim_GetString(argv[i], &input_len);
}
}
else if (arg[0] == '>') {
int dup_error = 0;
outputFile = FILE_NAME;
output = arg + 1;
if (*output == '>') {
outputFile = FILE_APPEND;
output++;
}
if (*output == '&') {
output++;
dup_error = 1;
}
if (*output == '@') {
outputFile = FILE_HANDLE;
output++;
}
if (!*output && ++i < argc) {
output = Jim_String(argv[i]);
}
if (dup_error) {
errorFile = outputFile;
error = output;
}
}
else if (arg[0] == '2' && arg[1] == '>') {
error = arg + 2;
errorFile = FILE_NAME;
if (*error == '@') {
errorFile = FILE_HANDLE;
error++;
}
else if (*error == '>') {
errorFile = FILE_APPEND;
error++;
}
if (!*error && ++i < argc) {
error = Jim_String(argv[i]);
}
}
else {
if (strcmp(arg, "|") == 0 || strcmp(arg, "|&") == 0) {
if (i == lastBar + 1 || i == argc - 1) {
Jim_SetResultString(interp, "illegal use of | or |& in command", -1);
goto badargs;
}
lastBar = i;
cmdCount++;
}
arg_array[arg_count++] = (char *)arg;
continue;
}
if (i >= argc) {
Jim_SetResultFormatted(interp, "can't specify \"%s\" as last word in command", arg);
goto badargs;
}
}
if (arg_count == 0) {
Jim_SetResultString(interp, "didn't specify command to execute", -1);
badargs:
Jim_Free(arg_array);
return -1;
}
save_environ = JimSaveEnv(JimBuildEnv(interp));
if (input != NULL) {
if (inputFile == FILE_TEXT) {
inputId = Jim_MakeTempFile(interp, NULL, 1);
if (inputId == -1) {
goto error;
}
if (write(inputId, input, input_len) != input_len) {
Jim_SetResultErrno(interp, "couldn't write temp file");
close(inputId);
goto error;
}
Jim_Lseek(inputId, 0L, SEEK_SET);
}
else if (inputFile == FILE_HANDLE) {
int fd = JimGetChannelFd(interp, input);
if (fd < 0) {
goto error;
}
inputId = dup(fd);
}
else {
inputId = Jim_OpenForRead(input);
if (inputId == -1) {
Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, strerror(Jim_Errno()));
goto error;
}
}
}
else if (inPipePtr != NULL) {
if (pipe(pipeIds) != 0) {
Jim_SetResultErrno(interp, "couldn't create input pipe for command");
goto error;
}
inputId = pipeIds[0];
*inPipePtr = pipeIds[1];
pipeIds[0] = pipeIds[1] = -1;
}
if (output != NULL) {
if (outputFile == FILE_HANDLE) {
int fd = JimGetChannelFd(interp, output);
if (fd < 0) {
goto error;
}
lastOutputId = dup(fd);
}
else {
lastOutputId = Jim_OpenForWrite(output, outputFile == FILE_APPEND);
if (lastOutputId == -1) {
Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, strerror(Jim_Errno()));
goto error;
}
}
}
else if (outPipePtr != NULL) {
if (pipe(pipeIds) != 0) {
Jim_SetResultErrno(interp, "couldn't create output pipe");
goto error;
}
lastOutputId = pipeIds[1];
*outPipePtr = pipeIds[0];
pipeIds[0] = pipeIds[1] = -1;
}
if (error != NULL) {
if (errorFile == FILE_HANDLE) {
if (strcmp(error, "1") == 0) {
if (lastOutputId != -1) {
errorId = dup(lastOutputId);
}
else {
error = "stdout";
}
}
if (errorId == -1) {
int fd = JimGetChannelFd(interp, error);
if (fd < 0) {
goto error;
}
errorId = dup(fd);
}
}
else {
errorId = Jim_OpenForWrite(error, errorFile == FILE_APPEND);
if (errorId == -1) {
Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, strerror(Jim_Errno()));
goto error;
}
}
}
else if (errFilePtr != NULL) {
errorId = Jim_MakeTempFile(interp, NULL, 1);
if (errorId == -1) {
goto error;
}
*errFilePtr = dup(errorId);
}
pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
int pipe_dup_err = 0;
int origErrorId = errorId;
for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
if (strcmp(arg_array[lastArg], "|") == 0) {
break;
}
if (strcmp(arg_array[lastArg], "|&") == 0) {
pipe_dup_err = 1;
break;
}
}
if (lastArg == firstArg) {
Jim_SetResultString(interp, "missing command to exec", -1);
goto error;
}
arg_array[lastArg] = NULL;
if (lastArg == arg_count) {
outputId = lastOutputId;
lastOutputId = -1;
}
else {
if (pipe(pipeIds) != 0) {
Jim_SetResultErrno(interp, "couldn't create pipe");
goto error;
}
outputId = pipeIds[1];
}
if (pipe_dup_err) {
errorId = outputId;
}
#ifdef __MINGW32__
phandle = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId);
if (phandle == JIM_BAD_PHANDLE) {
Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
goto error;
}
#else
i = strlen(arg_array[firstArg]);
#ifdef HAVE_EXECVPE
child_environ = Jim_GetEnviron();
#endif
#ifdef HAVE_VFORK
phandle = vfork();
#else
phandle = fork();
#endif
if (phandle < 0) {
Jim_SetResultErrno(interp, "couldn't fork child process");
goto error;
}
if (phandle == 0) {
if (inputId != -1 && inputId != fileno(stdin)) {
dup2(inputId, fileno(stdin));
close(inputId);
}
if (outputId != -1 && outputId != fileno(stdout)) {
dup2(outputId, fileno(stdout));
if (outputId != errorId) {
close(outputId);
}
}
if (errorId != -1 && errorId != fileno(stderr)) {
dup2(errorId, fileno(stderr));
close(errorId);
}
if (outPipePtr && *outPipePtr != -1) {
close(*outPipePtr);
}
if (errFilePtr && *errFilePtr != -1) {
close(*errFilePtr);
}
if (pipeIds[0] != -1) {
close(pipeIds[0]);
}
if (lastOutputId != -1) {
close(lastOutputId);
}
execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ);
if (write(fileno(stderr), "couldn't exec \"", 15) &&
write(fileno(stderr), arg_array[firstArg], i) &&
write(fileno(stderr), "\"\n", 2)) {
}
#ifdef JIM_MAINTAINER
{
static char *const false_argv[2] = {"false", NULL};
execvp(false_argv[0],false_argv);
}
#endif
_exit(127);
}
#endif
if (table->used == table->size) {
table->size += WAIT_TABLE_GROW_BY;
table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
}
table->info[table->used].phandle = phandle;
table->info[table->used].flags = 0;
table->used++;
pidPtr[numPids] = phandle;
errorId = origErrorId;
if (inputId != -1) {
close(inputId);
}
if (outputId != -1) {
close(outputId);
}
inputId = pipeIds[0];
pipeIds[0] = pipeIds[1] = -1;
}
*pidArrayPtr = pidPtr;
cleanup:
if (inputId != -1) {
close(inputId);
}
if (lastOutputId != -1) {
close(lastOutputId);
}
if (errorId != -1) {
close(errorId);
}
Jim_Free(arg_array);
JimRestoreEnv(save_environ);
return numPids;
error:
if ((inPipePtr != NULL) && (*inPipePtr != -1)) {
close(*inPipePtr);
*inPipePtr = -1;
}
if ((outPipePtr != NULL) && (*outPipePtr != -1)) {
close(*outPipePtr);
*outPipePtr = -1;
}
if ((errFilePtr != NULL) && (*errFilePtr != -1)) {
close(*errFilePtr);
*errFilePtr = -1;
}
if (pipeIds[0] != -1) {
close(pipeIds[0]);
}
if (pipeIds[1] != -1) {
close(pipeIds[1]);
}
if (pidPtr != NULL) {
for (i = 0; i < numPids; i++) {
if (pidPtr[i] != JIM_BAD_PHANDLE) {
JimDetachPids(table, 1, &pidPtr[i]);
}
}
Jim_Free(pidPtr);
}
numPids = -1;
goto cleanup;
}
static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj)
{
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
int result = JIM_OK;
int i;
for (i = 0; i < numPids; i++) {
int waitStatus = 0;
long pid = JimWaitForProcess(table, pidPtr[i], &waitStatus);
if (pid > 0) {
if (JimCheckWaitStatus(interp, pid, waitStatus, errStrObj) != JIM_OK) {
result = JIM_ERR;
}
}
}
Jim_Free(pidPtr);
return result;
}
int Jim_execInit(Jim_Interp *interp)
{
struct WaitInfoTable *waitinfo;
Jim_PackageProvideCheck(interp, "exec");
waitinfo = JimAllocWaitInfoTable();
Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable);
waitinfo->refcount++;
Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable);
Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0);
return JIM_OK;
}
#if defined(__MINGW32__)
static 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++) {
snprintf(fullPath, MAX_PATH, "%s%s", originalName, 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, Jim_GetEnviron());
}
static char **JimOriginalEnviron(void)
{
return 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 phandle_t
JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId)
{
STARTUPINFO startInfo;
PROCESS_INFORMATION procInfo;
HANDLE hProcess;
char execPath[MAX_PATH];
phandle_t phandle = INVALID_HANDLE_VALUE;
Jim_Obj *cmdLineObj;
char *winenv;
if (JimWinFindExecutable(argv[0], execPath) < 0) {
return phandle;
}
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 == -1) {
inputId = _fileno(stdin);
}
DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(inputId), hProcess, &startInfo.hStdInput,
0, TRUE, DUPLICATE_SAME_ACCESS);
if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
goto end;
}
if (outputId == -1) {
outputId = _fileno(stdout);
}
DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(outputId), hProcess, &startInfo.hStdOutput,
0, TRUE, DUPLICATE_SAME_ACCESS);
if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
goto end;
}
if (errorId == -1) {
errorId = _fileno(stderr);
}
DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(errorId), hProcess, &startInfo.hStdError,
0, TRUE, DUPLICATE_SAME_ACCESS);
if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
goto end;
}
if (env == NULL) {
winenv = NULL;
}
else if (env[0] == NULL) {
winenv = (char *)"\0";
}
else {
winenv = env[0];
}
if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE,
0, winenv, NULL, &startInfo, &procInfo)) {
goto end;
}
WaitForInputIdle(procInfo.hProcess, 5000);
CloseHandle(procInfo.hThread);
phandle = procInfo.hProcess;
end:
Jim_FreeNewObj(interp, cmdLineObj);
if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdInput);
}
if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdOutput);
}
if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdError);
}
return phandle;
}
#else
static char **JimOriginalEnviron(void)
{
return Jim_GetEnviron();
}
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
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
struct clock_options {
int gmt;
const char *format;
};
static int parse_clock_options(Jim_Interp *interp, int argc, Jim_Obj *const *argv, struct clock_options *opts)
{
static const char * const options[] = { "-gmt", "-format", NULL };
enum { OPT_GMT, OPT_FORMAT, };
int i;
for (i = 0; i < argc; i += 2) {
int option;
if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
return JIM_ERR;
}
switch (option) {
case OPT_GMT:
if (Jim_GetBoolean(interp, argv[i + 1], &opts->gmt) != JIM_OK) {
return JIM_ERR;
}
break;
case OPT_FORMAT:
opts->format = Jim_String(argv[i + 1]);
break;
}
}
return JIM_OK;
}
static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
char buf[100];
time_t t;
jim_wide seconds;
struct clock_options options = { 0, "%a %b %d %H:%M:%S %Z %Y" };
struct tm *tm;
if (Jim_GetWide(interp, argv[0], &seconds) != JIM_OK) {
return JIM_ERR;
}
if (argc % 2 == 0) {
return -1;
}
if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
return JIM_ERR;
}
t = seconds;
tm = options.gmt ? gmtime(&t) : localtime(&t);
if (tm == NULL || strftime(buf, sizeof(buf), options.format, tm) == 0) {
Jim_SetResultString(interp, "format string too long or invalid time", -1);
return JIM_ERR;
}
Jim_SetResultString(interp, buf, -1);
return JIM_OK;
}
#ifdef HAVE_STRPTIME
static time_t jim_timegm(const struct tm *tm)
{
int m = tm->tm_mon + 1;
int y = 1900 + tm->tm_year - (m <= 2);
int era = (y >= 0 ? y : y - 399) / 400;
unsigned yoe = (unsigned)(y - era * 400);
unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + tm->tm_mday - 1;
unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
long days = (era * 146097 + (int)doe - 719468);
int secs = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
return days * 24 * 60 * 60 + secs;
}
static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
char *pt;
struct tm tm;
time_t now = time(NULL);
struct clock_options options = { 0, NULL };
if (argc % 2 == 0) {
return -1;
}
if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
return JIM_ERR;
}
if (options.format == NULL) {
return -1;
}
localtime_r(&now, &tm);
pt = strptime(Jim_String(argv[0]), options.format, &tm);
if (pt == 0 || *pt != 0) {
Jim_SetResultString(interp, "Failed to parse time according to format", -1);
return JIM_ERR;
}
tm.tm_isdst = options.gmt ? 0 : -1;
Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm));
return JIM_OK;
}
#endif
static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000000);
return JIM_OK;
}
static int clock_cmd_clicks(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW));
return JIM_OK;
}
static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME));
return JIM_OK;
}
static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000);
return JIM_OK;
}
static const jim_subcmd_type clock_command_table[] = {
{ "clicks",
NULL,
clock_cmd_clicks,
0,
0,
},
{ "format",
"seconds ?-format string? ?-gmt boolean?",
clock_cmd_format,
1,
5,
},
{ "microseconds",
NULL,
clock_cmd_micros,
0,
0,
},
{ "milliseconds",
NULL,
clock_cmd_millis,
0,
0,
},
#ifdef HAVE_STRPTIME
{ "scan",
"str -format format ?-gmt boolean?",
clock_cmd_scan,
3,
5,
},
#endif
{ "seconds",
NULL,
clock_cmd_seconds,
0,
0,
},
{ NULL }
};
int Jim_clockInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "clock");
Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
return JIM_OK;
}
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static int array_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
Jim_SetResultInt(interp, dictObj && Jim_DictSize(interp, dictObj) != -1);
return JIM_OK;
}
static int array_cmd_get(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
Jim_Obj *patternObj;
if (!objPtr) {
return JIM_OK;
}
patternObj = (argc == 1) ? NULL : argv[1];
if (patternObj == NULL || Jim_CompareStringImmediate(interp, patternObj, "*")) {
if (Jim_IsList(objPtr) && Jim_ListLength(interp, objPtr) % 2 == 0) {
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
}
return Jim_DictMatchTypes(interp, objPtr, patternObj, JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS | JIM_DICTMATCH_VALUES);
}
static int array_cmd_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
if (!objPtr) {
return JIM_OK;
}
return Jim_DictMatchTypes(interp, objPtr, argc == 1 ? NULL : argv[1], JIM_DICTMATCH_KEYS, JIM_DICTMATCH_KEYS);
}
static int array_cmd_unset(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int i;
int len;
Jim_Obj *resultObj;
Jim_Obj *objPtr;
Jim_Obj **dictValuesObj;
if (argc == 1 || Jim_CompareStringImmediate(interp, argv[1], "*")) {
Jim_UnsetVariable(interp, argv[0], JIM_NONE);
return JIM_OK;
}
objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
if (objPtr == NULL) {
return JIM_OK;
}
dictValuesObj = Jim_DictPairs(interp, objPtr, &len);
if (dictValuesObj == NULL) {
Jim_SetResultString(interp, "", -1);
return JIM_OK;
}
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]);
}
}
Jim_SetVariable(interp, argv[0], resultObj);
return JIM_OK;
}
static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
int len = 0;
objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
if (objPtr) {
len = Jim_DictSize(interp, objPtr);
if (len < 0) {
Jim_SetResultInt(interp, 0);
return JIM_OK;
}
}
Jim_SetResultInt(interp, len);
return JIM_OK;
}
static int array_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
if (objPtr) {
return Jim_DictInfo(interp, objPtr);
}
Jim_SetResultFormatted(interp, "\"%#s\" isn't an array", argv[0], NULL);
return JIM_ERR;
}
static int array_cmd_set(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int i;
int len;
Jim_Obj *listObj = argv[1];
Jim_Obj *dictObj;
len = Jim_ListLength(interp, listObj);
if (len % 2) {
Jim_SetResultString(interp, "list must have an even number of elements", -1);
return JIM_ERR;
}
dictObj = Jim_GetVariable(interp, argv[0], JIM_UNSHARED);
if (!dictObj) {
return Jim_SetVariable(interp, argv[0], listObj);
}
else if (Jim_DictSize(interp, dictObj) < 0) {
return JIM_ERR;
}
if (Jim_IsShared(dictObj)) {
dictObj = Jim_DuplicateObj(interp, dictObj);
}
for (i = 0; i < len; i += 2) {
Jim_Obj *nameObj;
Jim_Obj *valueObj;
Jim_ListIndex(interp, listObj, i, &nameObj, JIM_NONE);
Jim_ListIndex(interp, listObj, i + 1, &valueObj, JIM_NONE);
Jim_DictAddElement(interp, dictObj, nameObj, valueObj);
}
return Jim_SetVariable(interp, argv[0], dictObj);
}
static const jim_subcmd_type array_command_table[] = {
{ "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,
},
{ "size",
"arrayName",
array_cmd_size,
1,
1,
},
{ "stat",
"arrayName",
array_cmd_stat,
1,
1,
},
{ "unset",
"arrayName ?pattern?",
array_cmd_unset,
1,
2,
},
{ NULL
}
};
int Jim_arrayInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "array");
Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
return JIM_OK;
}
int Jim_InitStaticExtensions(Jim_Interp *interp)
{
extern int Jim_bootstrapInit(Jim_Interp *);
extern int Jim_aioInit(Jim_Interp *);
extern int Jim_readdirInit(Jim_Interp *);
extern int Jim_regexpInit(Jim_Interp *);
extern int Jim_fileInit(Jim_Interp *);
extern int Jim_globInit(Jim_Interp *);
extern int Jim_execInit(Jim_Interp *);
extern int Jim_clockInit(Jim_Interp *);
extern int Jim_arrayInit(Jim_Interp *);
extern int Jim_stdlibInit(Jim_Interp *);
extern int Jim_tclcompatInit(Jim_Interp *);
Jim_bootstrapInit(interp);
Jim_aioInit(interp);
Jim_readdirInit(interp);
Jim_regexpInit(interp);
Jim_fileInit(interp);
Jim_globInit(interp);
Jim_execInit(interp);
Jim_clockInit(interp);
Jim_arrayInit(interp);
Jim_stdlibInit(interp);
Jim_tclcompatInit(interp);
return JIM_OK;
}
#ifndef JIM_TINY
#define JIM_OPTIMIZATION
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <limits.h>
#include <assert.h>
#include <errno.h>
#include <time.h>
#include <setjmp.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
#ifdef HAVE_CRT_EXTERNS_H
#include <crt_externs.h>
#endif
#include <math.h>
#ifndef TCL_LIBRARY
#define TCL_LIBRARY "."
#endif
#ifndef TCL_PLATFORM_OS
#define TCL_PLATFORM_OS "unknown"
#endif
#ifndef TCL_PLATFORM_PLATFORM
#define TCL_PLATFORM_PLATFORM "unknown"
#endif
#ifndef TCL_PLATFORM_PATH_SEPARATOR
#define TCL_PLATFORM_PATH_SEPARATOR ":"
#endif
#ifdef JIM_MAINTAINER
#define JIM_DEBUG_COMMAND
#define JIM_DEBUG_PANIC
#endif
#define JIM_INTEGER_SPACE 24
#if defined(DEBUG_SHOW_SCRIPT) || defined(DEBUG_SHOW_SCRIPT_TOKENS) || defined(JIM_DEBUG_COMMAND) || defined(DEBUG_SHOW_SUBST)
static const char *jim_tt_name(int type);
#endif
#ifdef JIM_DEBUG_PANIC
static void JimPanicDump(int fail_condition, const char *fmt, ...);
#define JimPanic(X) JimPanicDump X
#else
#define JimPanic(X)
#endif
#ifdef JIM_OPTIMIZATION
static int JimIsWide(Jim_Obj *objPtr);
#define JIM_IF_OPTIM(X) X
#else
#define JIM_IF_OPTIM(X)
#endif
static char JimEmptyStringRep[] = "";
static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
int flags);
static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *const *indexv, int indexc,
Jim_Obj **resultObj, int flags);
static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
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 int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
static int JimSign(jim_wide w);
static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv);
static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr);
static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
#define JIM_DICT_SUGAR 100
#define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
#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) {
*uc = utf8_upper(*uc);
}
return l;
}
static Jim_Obj *JimPushInterpObjImpl(Jim_Obj **iop, Jim_Obj *no)
{
Jim_Obj *io = *iop;
Jim_IncrRefCount(no);
*iop = no;
return io;
}
#define JimPushInterpObj(IO, NO) JimPushInterpObjImpl(&(IO), NO)
#define JimPopInterpObj(I, IO, SO) do { Jim_DecrRefCount(I, IO); IO = SO; } while (0)
#define JIM_CHARSET_SCAN 2
#define JIM_CHARSET_GLOB 0
static const char *JimCharsetMatch(const char *pattern, int plen, int c, int flags)
{
int not = 0;
int pchar;
int match = 0;
int nocase = 0;
int n;
if (flags & JIM_NOCASE) {
nocase++;
c = utf8_upper(c);
}
if (flags & JIM_CHARSET_SCAN) {
if (*pattern == '^') {
not++;
pattern++;
plen--;
}
if (*pattern == ']') {
goto first;
}
}
while (plen && *pattern != ']') {
if (pattern[0] == '\\') {
first:
n = utf8_tounicode_case(pattern, &pchar, nocase);
pattern += n;
plen -= n;
}
else {
int start;
int end;
n = utf8_tounicode_case(pattern, &start, nocase);
pattern += n;
plen -= n;
if (pattern[0] == '-' && plen > 1) {
n = 1 + utf8_tounicode_case(pattern + 1, &end, nocase);
pattern += n;
plen -= n;
if ((c >= start && c <= end) || (c >= end && c <= start)) {
match = 1;
}
continue;
}
pchar = start;
}
if (pchar == c) {
match = 1;
}
}
if (not) {
match = !match;
}
return match ? pattern : NULL;
}
static int JimGlobMatch(const char *pattern, int plen, const char *string, int slen, int nocase)
{
int c;
int pchar;
int n;
const char *p;
while (plen) {
switch (pattern[0]) {
case '*':
while (pattern[1] == '*' && plen) {
pattern++;
plen--;
}
pattern++;
plen--;
if (!plen) {
return 1;
}
while (slen) {
if (JimGlobMatch(pattern, plen, string, slen, nocase))
return 1;
n = utf8_tounicode(string, &c);
string += n;
slen -= n;
}
return 0;
case '?':
n = utf8_tounicode(string, &c);
string += n;
slen -= n;
break;
case '[': {
n = utf8_tounicode(string, &c);
string += n;
slen -= n;
p = JimCharsetMatch(pattern + 1, plen - 1, c, nocase ? JIM_NOCASE : 0);
if (!p) {
return 0;
}
plen -= p - pattern;
pattern = p;
if (!plen) {
continue;
}
break;
}
case '\\':
if (pattern[1]) {
pattern++;
plen--;
}
default:
n = utf8_tounicode_case(string, &c, nocase);
string += n;
slen -= n;
utf8_tounicode_case(pattern, &pchar, nocase);
if (pchar != c) {
return 0;
}
break;
}
n = utf8_tounicode_case(pattern, &pchar, nocase);
pattern += n;
plen -= n;
if (!slen) {
while (*pattern == '*' && plen) {
pattern++;
plen--;
}
break;
}
}
if (!plen && !slen) {
return 1;
}
return 0;
}
static int JimStringCompareUtf8(const char *s1, int l1, const char *s2, int l2, int nocase)
{
int minlen = l1;
if (l2 < l1) {
minlen = l2;
}
while (minlen) {
int c1, c2;
s1 += utf8_tounicode_case(s1, &c1, nocase);
s2 += utf8_tounicode_case(s2, &c2, nocase);
if (c1 != c2) {
return JimSign(c1 - c2);
}
minlen--;
}
if (l1 < l2) {
return -1;
}
if (l1 > l2) {
return 1;
}
return 0;
}
static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
{
int i;
int l1bytelen;
if (!l1 || !l2 || l1 > l2) {
return -1;
}
if (idx < 0)
idx = 0;
s2 += utf8_index(s2, idx);
l1bytelen = utf8_index(s1, l1);
for (i = idx; i <= l2 - l1; i++) {
int c;
if (memcmp(s2, s1, l1bytelen) == 0) {
return i;
}
s2 += utf8_tounicode(s2, &c);
}
return -1;
}
static int JimStringLast(const char *s1, int l1, const char *s2, int l2)
{
const char *p;
if (!l1 || !l2 || l1 > l2)
return -1;
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
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);
}
return n;
}
#endif
static int JimCheckConversion(const char *str, const char *endptr)
{
if (str[0] == '\0' || str == endptr) {
return JIM_ERR;
}
if (endptr[0] != '\0') {
while (*endptr) {
if (!isspace(UCHAR(*endptr))) {
return JIM_ERR;
}
endptr++;
}
}
return JIM_OK;
}
static int JimNumberBase(const char *str, int *base, int *sign)
{
int i = 0;
*base = 0;
while (isspace(UCHAR(str[i]))) {
i++;
}
if (str[i] == '-') {
*sign = -1;
i++;
}
else {
if (str[i] == '+') {
i++;
}
*sign = 1;
}
if (str[i] != '0') {
return 0;
}
switch (str[i + 1]) {
case 'x': case 'X': *base = 16; break;
case 'o': case 'O': *base = 8; break;
case 'b': case 'B': *base = 2; break;
case 'd': case 'D': *base = 10; break;
default: return 0;
}
i += 2;
if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
return i;
}
*base = 0;
return 0;
}
static long jim_strtol(const char *str, char **endptr)
{
int sign;
int base;
int i = JimNumberBase(str, &base, &sign);
if (base != 0) {
long value = strtol(str + i, endptr, base);
if (endptr == NULL || *endptr != str + i) {
return value * sign;
}
}
return strtol(str, endptr, 10);
}
static jim_wide jim_strtoull(const char *str, char **endptr)
{
#ifdef HAVE_LONG_LONG
int sign;
int base;
int i = JimNumberBase(str, &base, &sign);
if (base != 0) {
jim_wide value = strtoull(str + i, endptr, base);
if (endptr == NULL || *endptr != str + i) {
return value * sign;
}
}
return strtoull(str, endptr, 10);
#else
return (unsigned long)jim_strtol(str, endptr);
#endif
}
int Jim_StringToWide(const char *str, jim_wide * widePtr, int base)
{
char *endptr;
if (base) {
*widePtr = strtoull(str, &endptr, base);
}
else {
*widePtr = jim_strtoull(str, &endptr);
}
return JimCheckConversion(str, endptr);
}
int Jim_StringToDouble(const char *str, double *doublePtr)
{
char *endptr;
errno = 0;
*doublePtr = strtod(str, &endptr);
return JimCheckConversion(str, endptr);
}
static jim_wide JimPowWide(jim_wide b, jim_wide e)
{
jim_wide res = 1;
if (b == 1) {
return 1;
}
if (e < 0) {
if (b != -1) {
return 0;
}
e = -e;
}
while (e)
{
if (e & 1) {
res *= b;
}
e >>= 1;
b *= b;
}
return res;
}
#ifdef JIM_DEBUG_PANIC
static void JimPanicDump(int condition, const char *fmt, ...)
{
va_list ap;
if (!condition) {
return;
}
va_start(ap, fmt);
fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n\n");
va_end(ap);
#if defined(HAVE_BACKTRACE)
{
void *array[40];
int size, i;
char **strings;
size = backtrace(array, 40);
strings = backtrace_symbols(array, size);
for (i = 0; i < size; i++)
fprintf(stderr, "[backtrace] %s\n", strings[i]);
fprintf(stderr, "[backtrace] Include the above lines and the output\n");
fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n");
}
#endif
exit(1);
}
#endif
void *JimDefaultAllocator(void *ptr, size_t size)
{
if (size == 0) {
free(ptr);
return NULL;
}
else {
void *p = realloc(ptr, size);
if( p==0 ){
fprintf(stderr,"Out of memory\n");
exit(1);
}
return p;
}
}
void *(*Jim_Allocator)(void *ptr, size_t size) = JimDefaultAllocator;
char *Jim_StrDup(const char *s)
{
return Jim_StrDupLen(s, strlen(s));
}
char *Jim_StrDupLen(const char *s, int l)
{
char *copy = Jim_Alloc(l + 1);
memcpy(copy, s, l);
copy[l] = 0;
return copy;
}
jim_wide Jim_GetTimeUsec(unsigned type)
{
long long now;
struct timeval tv;
#if defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
if (clock_gettime(type, &ts) == 0) {
now = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
}
else
#endif
{
gettimeofday(&tv, NULL);
now = tv.tv_sec * 1000000LL + tv.tv_usec;
}
return now;
}
static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
static unsigned int JimHashTableNextPower(unsigned int size);
static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
unsigned int Jim_IntHashFunction(unsigned int key)
{
key += ~(key << 15);
key ^= (key >> 10);
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
unsigned int Jim_GenHashFunction(const unsigned char *string, int length)
{
unsigned result = 0;
string += length;
while (length--) {
result += (result << 3) + (unsigned char)(*--string);
}
return result;
}
static void JimResetHashTable(Jim_HashTable *ht)
{
ht->table = NULL;
ht->size = 0;
ht->sizemask = 0;
ht->used = 0;
ht->collisions = 0;
#ifdef JIM_RANDOMISE_HASH
ht->uniq = (rand() ^ time(NULL) ^ clock());
#else
ht->uniq = 0;
#endif
}
static void JimInitHashTableIterator(Jim_HashTable *ht, Jim_HashTableIterator *iter)
{
iter->ht = ht;
iter->index = -1;
iter->entry = NULL;
iter->nextEntry = NULL;
}
int Jim_InitHashTable(Jim_HashTable *ht, const Jim_HashTableType *type, void *privDataPtr)
{
JimResetHashTable(ht);
ht->type = type;
ht->privdata = privDataPtr;
return JIM_OK;
}
void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
{
Jim_HashTable n;
unsigned int realsize = JimHashTableNextPower(size), i;
if (size <= ht->used)
return;
Jim_InitHashTable(&n, ht->type, ht->privdata);
n.size = realsize;
n.sizemask = realsize - 1;
n.table = Jim_Alloc(realsize * sizeof(Jim_HashEntry *));
n.uniq = ht->uniq;
memset(n.table, 0, realsize * sizeof(Jim_HashEntry *));
n.used = ht->used;
for (i = 0; ht->used > 0; i++) {
Jim_HashEntry *he, *nextHe;
if (ht->table[i] == NULL)
continue;
he = ht->table[i];
while (he) {
unsigned int h;
nextHe = he->next;
h = Jim_HashKey(ht, he->key) & n.sizemask;
he->next = n.table[h];
n.table[h] = he;
ht->used--;
he = nextHe;
}
}
assert(ht->used == 0);
Jim_Free(ht->table);
*ht = n;
}
int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
{
Jim_HashEntry *entry = JimInsertHashEntry(ht, key, 0);;
if (entry == NULL)
return JIM_ERR;
Jim_SetHashKey(ht, entry, key);
Jim_SetHashVal(ht, entry, val);
return JIM_OK;
}
int Jim_ReplaceHashEntry(Jim_HashTable *ht, const void *key, void *val)
{
int existed;
Jim_HashEntry *entry;
entry = JimInsertHashEntry(ht, key, 1);
if (entry->key) {
if (ht->type->valDestructor && ht->type->valDup) {
void *newval = ht->type->valDup(ht->privdata, val);
ht->type->valDestructor(ht->privdata, entry->u.val);
entry->u.val = newval;
}
else {
Jim_FreeEntryVal(ht, entry);
Jim_SetHashVal(ht, entry, val);
}
existed = 1;
}
else {
Jim_SetHashKey(ht, entry, key);
Jim_SetHashVal(ht, entry, val);
existed = 0;
}
return existed;
}
int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
{
if (ht->used) {
unsigned int h = Jim_HashKey(ht, key) & ht->sizemask;
Jim_HashEntry *prevHe = NULL;
Jim_HashEntry *he = ht->table[h];
while (he) {
if (Jim_CompareHashKeys(ht, key, he->key)) {
if (prevHe)
prevHe->next = he->next;
else
ht->table[h] = he->next;
ht->used--;
Jim_FreeEntryKey(ht, he);
Jim_FreeEntryVal(ht, he);
Jim_Free(he);
return JIM_OK;
}
prevHe = he;
he = he->next;
}
}
return JIM_ERR;
}
void Jim_ClearHashTable(Jim_HashTable *ht)
{
unsigned int i;
for (i = 0; ht->used > 0; i++) {
Jim_HashEntry *he, *nextHe;
he = ht->table[i];
while (he) {
nextHe = he->next;
Jim_FreeEntryKey(ht, he);
Jim_FreeEntryVal(ht, he);
Jim_Free(he);
ht->used--;
he = nextHe;
}
ht->table[i] = NULL;
}
}
int Jim_FreeHashTable(Jim_HashTable *ht)
{
Jim_ClearHashTable(ht);
Jim_Free(ht->table);
JimResetHashTable(ht);
return JIM_OK;
}
Jim_HashEntry *Jim_FindHashEntry(Jim_HashTable *ht, const void *key)
{
Jim_HashEntry *he;
unsigned int h;
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))
return he;
he = he->next;
}
return NULL;
}
Jim_HashTableIterator *Jim_GetHashTableIterator(Jim_HashTable *ht)
{
Jim_HashTableIterator *iter = Jim_Alloc(sizeof(*iter));
JimInitHashTableIterator(ht, iter);
return iter;
}
Jim_HashEntry *Jim_NextHashEntry(Jim_HashTableIterator *iter)
{
while (1) {
if (iter->entry == NULL) {
iter->index++;
if (iter->index >= (signed)iter->ht->size)
break;
iter->entry = iter->ht->table[iter->index];
}
else {
iter->entry = iter->nextEntry;
}
if (iter->entry) {
iter->nextEntry = iter->entry->next;
return iter->entry;
}
}
return NULL;
}
static void JimExpandHashTableIfNeeded(Jim_HashTable *ht)
{
if (ht->size == 0)
Jim_ExpandHashTable(ht, JIM_HT_INITIAL_SIZE);
if (ht->size == ht->used)
Jim_ExpandHashTable(ht, ht->size * 2);
}
static unsigned int JimHashTableNextPower(unsigned int size)
{
unsigned int i = JIM_HT_INITIAL_SIZE;
if (size >= 2147483648U)
return 2147483648U;
while (1) {
if (i >= size)
return i;
i *= 2;
}
}
static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace)
{
unsigned int h;
Jim_HashEntry *he;
JimExpandHashTableIfNeeded(ht);
h = Jim_HashKey(ht, key) & ht->sizemask;
he = ht->table[h];
while (he) {
if (Jim_CompareHashKeys(ht, key, he->key))
return replace ? he : NULL;
he = he->next;
}
he = Jim_Alloc(sizeof(*he));
he->next = ht->table[h];
ht->table[h] = he;
ht->used++;
he->key = NULL;
return he;
}
static unsigned int JimStringCopyHTHashFunction(const void *key)
{
return Jim_GenHashFunction(key, strlen(key));
}
static void *JimStringCopyHTDup(void *privdata, const void *key)
{
return Jim_StrDup(key);
}
static int JimStringCopyHTKeyCompare(void *privdata, const void *key1, const void *key2)
{
return strcmp(key1, key2) == 0;
}
static void JimStringCopyHTKeyDestructor(void *privdata, void *key)
{
Jim_Free(key);
}
static const Jim_HashTableType JimPackageHashTableType = {
JimStringCopyHTHashFunction,
JimStringCopyHTDup,
NULL,
JimStringCopyHTKeyCompare,
JimStringCopyHTKeyDestructor,
NULL
};
typedef struct AssocDataValue
{
Jim_InterpDeleteProc *delProc;
void *data;
} AssocDataValue;
static void JimAssocDataHashTableValueDestructor(void *privdata, void *data)
{
AssocDataValue *assocPtr = (AssocDataValue *) data;
if (assocPtr->delProc != NULL)
assocPtr->delProc((Jim_Interp *)privdata, assocPtr->data);
Jim_Free(data);
}
static const Jim_HashTableType JimAssocDataHashTableType = {
JimStringCopyHTHashFunction,
JimStringCopyHTDup,
NULL,
JimStringCopyHTKeyCompare,
JimStringCopyHTKeyDestructor,
JimAssocDataHashTableValueDestructor
};
void Jim_InitStack(Jim_Stack *stack)
{
stack->len = 0;
stack->maxlen = 0;
stack->vector = NULL;
}
void Jim_FreeStack(Jim_Stack *stack)
{
Jim_Free(stack->vector);
}
int Jim_StackLen(Jim_Stack *stack)
{
return stack->len;
}
void Jim_StackPush(Jim_Stack *stack, void *element)
{
int neededLen = stack->len + 1;
if (neededLen > stack->maxlen) {
stack->maxlen = neededLen < 20 ? 20 : neededLen * 2;
stack->vector = Jim_Realloc(stack->vector, sizeof(void *) * stack->maxlen);
}
stack->vector[stack->len] = element;
stack->len++;
}
void *Jim_StackPop(Jim_Stack *stack)
{
if (stack->len == 0)
return NULL;
stack->len--;
return stack->vector[stack->len];
}
void *Jim_StackPeek(Jim_Stack *stack)
{
if (stack->len == 0)
return NULL;
return stack->vector[stack->len - 1];
}
void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc) (void *ptr))
{
int i;
for (i = 0; i < stack->len; i++)
freeFunc(stack->vector[i]);
}
#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
#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_SUBEXPR_COMMA 13
#define JIM_TT_EXPR_INT 14
#define JIM_TT_EXPR_DOUBLE 15
#define JIM_TT_EXPR_BOOLEAN 16
#define JIM_TT_EXPRSUGAR 17
#define JIM_TT_EXPR_OP 20
#define TOKEN_IS_SEP(type) (type >= JIM_TT_SEP && type <= JIM_TT_EOF)
#define TOKEN_IS_EXPR_START(type) (type == JIM_TT_NONE || type == JIM_TT_SUBEXPR_START || type == JIM_TT_SUBEXPR_COMMA)
#define TOKEN_IS_EXPR_OP(type) (type >= JIM_TT_EXPR_OP)
struct JimParseMissing {
int ch;
int line;
};
struct JimParserCtx
{
const char *p;
int len;
int linenr;
const char *tstart;
const char *tend;
int tline;
int tt;
int eof;
int inquote;
int comment;
struct JimParseMissing missing;
const char *errmsg;
};
static int JimParseScript(struct JimParserCtx *pc);
static int JimParseSep(struct JimParserCtx *pc);
static int JimParseEol(struct JimParserCtx *pc);
static int JimParseCmd(struct JimParserCtx *pc);
static int JimParseQuote(struct JimParserCtx *pc);
static int JimParseVar(struct JimParserCtx *pc);
static int JimParseBrace(struct JimParserCtx *pc);
static int JimParseStr(struct JimParserCtx *pc);
static int JimParseComment(struct JimParserCtx *pc);
static void JimParseSubCmd(struct JimParserCtx *pc);
static int JimParseSubQuote(struct JimParserCtx *pc);
static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc);
static void JimParserInit(struct JimParserCtx *pc, const char *prg, int len, int linenr)
{
pc->p = prg;
pc->len = len;
pc->tstart = NULL;
pc->tend = NULL;
pc->tline = 0;
pc->tt = JIM_TT_NONE;
pc->eof = 0;
pc->inquote = 0;
pc->linenr = linenr;
pc->comment = 1;
pc->missing.ch = ' ';
pc->missing.line = linenr;
}
static int JimParseScript(struct JimParserCtx *pc)
{
while (1) {
if (!pc->len) {
pc->tstart = pc->p;
pc->tend = pc->p - 1;
pc->tline = pc->linenr;
pc->tt = JIM_TT_EOL;
if (pc->inquote) {
pc->missing.ch = '"';
}
pc->eof = 1;
return JIM_OK;
}
switch (*(pc->p)) {
case '\\':
if (*(pc->p + 1) == '\n' && !pc->inquote) {
return JimParseSep(pc);
}
pc->comment = 0;
return JimParseStr(pc);
case ' ':
case '\t':
case '\r':
case '\f':
if (!pc->inquote)
return JimParseSep(pc);
pc->comment = 0;
return JimParseStr(pc);
case '\n':
case ';':
pc->comment = 1;
if (!pc->inquote)
return JimParseEol(pc);
return JimParseStr(pc);
case '[':
pc->comment = 0;
return JimParseCmd(pc);
case '$':
pc->comment = 0;
if (JimParseVar(pc) == JIM_ERR) {
pc->tstart = pc->tend = pc->p++;
pc->len--;
pc->tt = JIM_TT_ESC;
}
return JIM_OK;
case '#':
if (pc->comment) {
JimParseComment(pc);
continue;
}
return JimParseStr(pc);
default:
pc->comment = 0;
return JimParseStr(pc);
}
return JIM_OK;
}
}
static int JimParseSep(struct JimParserCtx *pc)
{
pc->tstart = pc->p;
pc->tline = pc->linenr;
while (isspace(UCHAR(*pc->p)) || (*pc->p == '\\' && *(pc->p + 1) == '\n')) {
if (*pc->p == '\n') {
break;
}
if (*pc->p == '\\') {
pc->p++;
pc->len--;
pc->linenr++;
}
pc->p++;
pc->len--;
}
pc->tend = pc->p - 1;
pc->tt = JIM_TT_SEP;
return JIM_OK;
}
static int JimParseEol(struct JimParserCtx *pc)
{
pc->tstart = pc->p;
pc->tline = pc->linenr;
while (isspace(UCHAR(*pc->p)) || *pc->p == ';') {
if (*pc->p == '\n')
pc->linenr++;
pc->p++;
pc->len--;
}
pc->tend = pc->p - 1;
pc->tt = JIM_TT_EOL;
return JIM_OK;
}
static void JimParseSubBrace(struct JimParserCtx *pc)
{
int level = 1;
pc->p++;
pc->len--;
while (pc->len) {
switch (*pc->p) {
case '\\':
if (pc->len > 1) {
if (*++pc->p == '\n') {
pc->linenr++;
}
pc->len--;
}
break;
case '{':
level++;
break;
case '}':
if (--level == 0) {
pc->tend = pc->p - 1;
pc->p++;
pc->len--;
return;
}
break;
case '\n':
pc->linenr++;
break;
}
pc->p++;
pc->len--;
}
pc->missing.ch = '{';
pc->missing.line = pc->tline;
pc->tend = pc->p - 1;
}
static int JimParseSubQuote(struct JimParserCtx *pc)
{
int tt = JIM_TT_STR;
int line = pc->tline;
pc->p++;
pc->len--;
while (pc->len) {
switch (*pc->p) {
case '\\':
if (pc->len > 1) {
if (*++pc->p == '\n') {
pc->linenr++;
}
pc->len--;
tt = JIM_TT_ESC;
}
break;
case '"':
pc->tend = pc->p - 1;
pc->p++;
pc->len--;
return tt;
case '[':
JimParseSubCmd(pc);
tt = JIM_TT_ESC;
continue;
case '\n':
pc->linenr++;
break;
case '$':
tt = JIM_TT_ESC;
break;
}
pc->p++;
pc->len--;
}
pc->missing.ch = '"';
pc->missing.line = line;
pc->tend = pc->p - 1;
return tt;
}
static void JimParseSubCmd(struct JimParserCtx *pc)
{
int level = 1;
int startofword = 1;
int line = pc->tline;
pc->p++;
pc->len--;
while (pc->len) {
switch (*pc->p) {
case '\\':
if (pc->len > 1) {
if (*++pc->p == '\n') {
pc->linenr++;
}
pc->len--;
}
break;
case '[':
level++;
break;
case ']':
if (--level == 0) {
pc->tend = pc->p - 1;
pc->p++;
pc->len--;
return;
}
break;
case '"':
if (startofword) {
JimParseSubQuote(pc);
if (pc->missing.ch == '"') {
return;
}
continue;
}
break;
case '{':
JimParseSubBrace(pc);
startofword = 0;
continue;
case '\n':
pc->linenr++;
break;
}
startofword = isspace(UCHAR(*pc->p));
pc->p++;
pc->len--;
}
pc->missing.ch = '[';
pc->missing.line = line;
pc->tend = pc->p - 1;
}
static int JimParseBrace(struct JimParserCtx *pc)
{
pc->tstart = pc->p + 1;
pc->tline = pc->linenr;
pc->tt = JIM_TT_STR;
JimParseSubBrace(pc);
return JIM_OK;
}
static int JimParseCmd(struct JimParserCtx *pc)
{
pc->tstart = pc->p + 1;
pc->tline = pc->linenr;
pc->tt = JIM_TT_CMD;
JimParseSubCmd(pc);
return JIM_OK;
}
static int JimParseQuote(struct JimParserCtx *pc)
{
pc->tstart = pc->p + 1;
pc->tline = pc->linenr;
pc->tt = JimParseSubQuote(pc);
return JIM_OK;
}
static int JimParseVar(struct JimParserCtx *pc)
{
pc->p++;
pc->len--;
#ifdef EXPRSUGAR_BRACKET
if (*pc->p == '[') {
JimParseCmd(pc);
pc->tt = JIM_TT_EXPRSUGAR;
return JIM_OK;
}
#endif
pc->tstart = pc->p;
pc->tt = JIM_TT_VAR;
pc->tline = pc->linenr;
if (*pc->p == '{') {
pc->tstart = ++pc->p;
pc->len--;
while (pc->len && *pc->p != '}') {
if (*pc->p == '\n') {
pc->linenr++;
}
pc->p++;
pc->len--;
}
pc->tend = pc->p - 1;
if (pc->len) {
pc->p++;
pc->len--;
}
}
else {
while (1) {
if (pc->p[0] == ':' && pc->p[1] == ':') {
while (*pc->p == ':') {
pc->p++;
pc->len--;
}
continue;
}
if (isalnum(UCHAR(*pc->p)) || *pc->p == '_' || UCHAR(*pc->p) >= 0x80) {
pc->p++;
pc->len--;
continue;
}
break;
}
if (*pc->p == '(') {
int count = 1;
const char *paren = NULL;
pc->tt = JIM_TT_DICTSUGAR;
while (count && pc->len) {
pc->p++;
pc->len--;
if (*pc->p == '\\' && pc->len >= 1) {
pc->p++;
pc->len--;
}
else if (*pc->p == '(') {
count++;
}
else if (*pc->p == ')') {
paren = pc->p;
count--;
}
}
if (count == 0) {
pc->p++;
pc->len--;
}
else if (paren) {
paren++;
pc->len += (pc->p - paren);
pc->p = paren;
}
#ifndef EXPRSUGAR_BRACKET
if (*pc->tstart == '(') {
pc->tt = JIM_TT_EXPRSUGAR;
}
#endif
}
pc->tend = pc->p - 1;
}
if (pc->tstart == pc->p) {
pc->p--;
pc->len++;
return JIM_ERR;
}
return JIM_OK;
}
static int JimParseStr(struct JimParserCtx *pc)
{
if (pc->tt == JIM_TT_SEP || pc->tt == JIM_TT_EOL ||
pc->tt == JIM_TT_NONE || pc->tt == JIM_TT_STR) {
if (*pc->p == '{') {
return JimParseBrace(pc);
}
if (*pc->p == '"') {
pc->inquote = 1;
pc->p++;
pc->len--;
pc->missing.line = pc->tline;
}
}
pc->tstart = pc->p;
pc->tline = pc->linenr;
while (1) {
if (pc->len == 0) {
if (pc->inquote) {
pc->missing.ch = '"';
}
pc->tend = pc->p - 1;
pc->tt = JIM_TT_ESC;
return JIM_OK;
}
switch (*pc->p) {
case '\\':
if (!pc->inquote && *(pc->p + 1) == '\n') {
pc->tend = pc->p - 1;
pc->tt = JIM_TT_ESC;
return JIM_OK;
}
if (pc->len >= 2) {
if (*(pc->p + 1) == '\n') {
pc->linenr++;
}
pc->p++;
pc->len--;
}
else if (pc->len == 1) {
pc->missing.ch = '\\';
}
break;
case '(':
if (pc->len > 1 && pc->p[1] != '$') {
break;
}
case ')':
if (*pc->p == '(' || pc->tt == JIM_TT_VAR) {
if (pc->p == pc->tstart) {
pc->p++;
pc->len--;
}
pc->tend = pc->p - 1;
pc->tt = JIM_TT_ESC;
return JIM_OK;
}
break;
case '$':
case '[':
pc->tend = pc->p - 1;
pc->tt = JIM_TT_ESC;
return JIM_OK;
case ' ':
case '\t':
case '\n':
case '\r':
case '\f':
case ';':
if (!pc->inquote) {
pc->tend = pc->p - 1;
pc->tt = JIM_TT_ESC;
return JIM_OK;
}
else if (*pc->p == '\n') {
pc->linenr++;
}
break;
case '"':
if (pc->inquote) {
pc->tend = pc->p - 1;
pc->tt = JIM_TT_ESC;
pc->p++;
pc->len--;
pc->inquote = 0;
return JIM_OK;
}
break;
}
pc->p++;
pc->len--;
}
return JIM_OK;
}
static int JimParseComment(struct JimParserCtx *pc)
{
while (*pc->p) {
if (*pc->p == '\\') {
pc->p++;
pc->len--;
if (pc->len == 0) {
pc->missing.ch = '\\';
return JIM_OK;
}
if (*pc->p == '\n') {
pc->linenr++;
}
}
else if (*pc->p == '\n') {
pc->p++;
pc->len--;
pc->linenr++;
break;
}
pc->p++;
pc->len--;
}
return JIM_OK;
}
static int xdigitval(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 odigitval(int c)
{
if (c >= '0' && c <= '7')
return c - '0';
return -1;
}
static int JimEscape(char *dest, const char *s, int slen)
{
char *p = dest;
int i, len;
for (i = 0; i < slen; i++) {
switch (s[i]) {
case '\\':
switch (s[i + 1]) {
case 'a':
*p++ = 0x7;
i++;
break;
case 'b':
*p++ = 0x8;
i++;
break;
case 'f':
*p++ = 0xc;
i++;
break;
case 'n':
*p++ = 0xa;
i++;
break;
case 'r':
*p++ = 0xd;
i++;
break;
case 't':
*p++ = 0x9;
i++;
break;
case 'u':
case 'U':
case 'x':
{
unsigned val = 0;
int k;
int maxchars = 2;
i++;
if (s[i] == 'U') {
maxchars = 8;
}
else if (s[i] == 'u') {
if (s[i + 1] == '{') {
maxchars = 6;
i++;
}
else {
maxchars = 4;
}
}
for (k = 0; k < maxchars; k++) {
int c = xdigitval(s[i + k + 1]);
if (c == -1) {
break;
}
val = (val << 4) | c;
}
if (s[i] == '{') {
if (k == 0 || val > 0x1fffff || s[i + k + 1] != '}') {
i--;
k = 0;
}
else {
k++;
}
}
if (k) {
if (s[i] == 'x') {
*p++ = val;
}
else {
p += utf8_fromunicode(p, val);
}
i += k;
break;
}
*p++ = s[i];
}
break;
case 'v':
*p++ = 0xb;
i++;
break;
case '\0':
*p++ = '\\';
i++;
break;
case '\n':
*p++ = ' ';
do {
i++;
} while (s[i + 1] == ' ' || s[i + 1] == '\t');
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
{
int val = 0;
int c = odigitval(s[i + 1]);
val = c;
c = odigitval(s[i + 2]);
if (c == -1) {
*p++ = val;
i++;
break;
}
val = (val * 8) + c;
c = odigitval(s[i + 3]);
if (c == -1) {
*p++ = val;
i += 2;
break;
}
val = (val * 8) + c;
*p++ = val;
i += 3;
}
break;
default:
*p++ = s[i + 1];
i++;
break;
}
break;
default:
*p++ = s[i];
break;
}
}
len = p - dest;
*p = '\0';
return len;
}
static Jim_Obj *JimParserGetTokenObj(Jim_Interp *interp, struct JimParserCtx *pc)
{
const char *start, *end;
char *token;
int len;
start = pc->tstart;
end = pc->tend;
len = (end - start) + 1;
if (len < 0) {
len = 0;
}
token = Jim_Alloc(len + 1);
if (pc->tt != JIM_TT_ESC) {
memcpy(token, start, len);
token[len] = '\0';
}
else {
len = JimEscape(token, start, len);
}
return Jim_NewStringObjNoAlloc(interp, token, len);
}
static int JimParseListSep(struct JimParserCtx *pc);
static int JimParseListStr(struct JimParserCtx *pc);
static int JimParseListQuote(struct JimParserCtx *pc);
static int JimParseList(struct JimParserCtx *pc)
{
if (isspace(UCHAR(*pc->p))) {
return JimParseListSep(pc);
}
switch (*pc->p) {
case '"':
return JimParseListQuote(pc);
case '{':
return JimParseBrace(pc);
default:
if (pc->len) {
return JimParseListStr(pc);
}
break;
}
pc->tstart = pc->tend = pc->p;
pc->tline = pc->linenr;
pc->tt = JIM_TT_EOL;
pc->eof = 1;
return JIM_OK;
}
static int JimParseListSep(struct JimParserCtx *pc)
{
pc->tstart = pc->p;
pc->tline = pc->linenr;
while (isspace(UCHAR(*pc->p))) {
if (*pc->p == '\n') {
pc->linenr++;
}
pc->p++;
pc->len--;
}
pc->tend = pc->p - 1;
pc->tt = JIM_TT_SEP;
return JIM_OK;
}
static int JimParseListQuote(struct JimParserCtx *pc)
{
pc->p++;
pc->len--;
pc->tstart = pc->p;
pc->tline = pc->linenr;
pc->tt = JIM_TT_STR;
while (pc->len) {
switch (*pc->p) {
case '\\':
pc->tt = JIM_TT_ESC;
if (--pc->len == 0) {
pc->tend = pc->p;
return JIM_OK;
}
pc->p++;
break;
case '\n':
pc->linenr++;
break;
case '"':
pc->tend = pc->p - 1;
pc->p++;
pc->len--;
return JIM_OK;
}
pc->p++;
pc->len--;
}
pc->tend = pc->p - 1;
return JIM_OK;
}
static int JimParseListStr(struct JimParserCtx *pc)
{
pc->tstart = pc->p;
pc->tline = pc->linenr;
pc->tt = JIM_TT_STR;
while (pc->len) {
if (isspace(UCHAR(*pc->p))) {
pc->tend = pc->p - 1;
return JIM_OK;
}
if (*pc->p == '\\') {
if (--pc->len == 0) {
pc->tend = pc->p;
return JIM_OK;
}
pc->tt = JIM_TT_ESC;
pc->p++;
}
pc->p++;
pc->len--;
}
pc->tend = pc->p - 1;
return JIM_OK;
}
Jim_Obj *Jim_NewObj(Jim_Interp *interp)
{
Jim_Obj *objPtr;
if (interp->freeList != NULL) {
objPtr = interp->freeList;
interp->freeList = objPtr->nextObjPtr;
}
else {
objPtr = Jim_Alloc(sizeof(*objPtr));
}
objPtr->refCount = 0;
objPtr->prevObjPtr = NULL;
objPtr->nextObjPtr = interp->liveList;
if (interp->liveList)
interp->liveList->prevObjPtr = objPtr;
interp->liveList = objPtr;
return objPtr;
}
void Jim_FreeObj(Jim_Interp *interp, Jim_Obj *objPtr)
{
JimPanic((objPtr->refCount != 0, "!!!Object %p freed with bad refcount %d, type=%s", objPtr,
objPtr->refCount, objPtr->typePtr ? objPtr->typePtr->name : "<none>"));
Jim_FreeIntRep(interp, objPtr);
if (objPtr->bytes != NULL) {
if (objPtr->bytes != JimEmptyStringRep)
Jim_Free(objPtr->bytes);
}
if (objPtr->prevObjPtr)
objPtr->prevObjPtr->nextObjPtr = objPtr->nextObjPtr;
if (objPtr->nextObjPtr)
objPtr->nextObjPtr->prevObjPtr = objPtr->prevObjPtr;
if (interp->liveList == objPtr)
interp->liveList = objPtr->nextObjPtr;
#ifdef JIM_DISABLE_OBJECT_POOL
Jim_Free(objPtr);
#else
objPtr->prevObjPtr = NULL;
objPtr->nextObjPtr = interp->freeList;
if (interp->freeList)
interp->freeList->prevObjPtr = objPtr;
interp->freeList = objPtr;
objPtr->refCount = -1;
#endif
}
void Jim_InvalidateStringRep(Jim_Obj *objPtr)
{
if (objPtr->bytes != NULL) {
if (objPtr->bytes != JimEmptyStringRep)
Jim_Free(objPtr->bytes);
}
objPtr->bytes = NULL;
}
Jim_Obj *Jim_DuplicateObj(Jim_Interp *interp, Jim_Obj *objPtr)
{
Jim_Obj *dupPtr;
dupPtr = Jim_NewObj(interp);
if (objPtr->bytes == NULL) {
dupPtr->bytes = NULL;
}
else if (objPtr->length == 0) {
dupPtr->bytes = JimEmptyStringRep;
dupPtr->length = 0;
dupPtr->typePtr = NULL;
return dupPtr;
}
else {
dupPtr->bytes = Jim_Alloc(objPtr->length + 1);
dupPtr->length = objPtr->length;
memcpy(dupPtr->bytes, objPtr->bytes, objPtr->length + 1);
}
dupPtr->typePtr = objPtr->typePtr;
if (objPtr->typePtr != NULL) {
if (objPtr->typePtr->dupIntRepProc == NULL) {
dupPtr->internalRep = objPtr->internalRep;
}
else {
objPtr->typePtr->dupIntRepProc(interp, objPtr, dupPtr);
}
}
return dupPtr;
}
const char *Jim_GetString(Jim_Obj *objPtr, int *lenPtr)
{
if (objPtr->bytes == NULL) {
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;
}
int Jim_Length(Jim_Obj *objPtr)
{
if (objPtr->bytes == NULL) {
Jim_GetString(objPtr, NULL);
}
return objPtr->length;
}
const char *Jim_String(Jim_Obj *objPtr)
{
if (objPtr->bytes == NULL) {
Jim_GetString(objPtr, NULL);
}
return objPtr->bytes;
}
static void JimSetStringBytes(Jim_Obj *objPtr, const char *str)
{
objPtr->bytes = Jim_StrDup(str);
objPtr->length = strlen(str);
}
static void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static const Jim_ObjType dictSubstObjType = {
"dict-substitution",
FreeDictSubstInternalRep,
DupDictSubstInternalRep,
NULL,
JIM_TYPE_NONE,
};
static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static const Jim_ObjType interpolatedObjType = {
"interpolated",
FreeInterpolatedInternalRep,
DupInterpolatedInternalRep,
NULL,
JIM_TYPE_NONE,
};
static void FreeInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
}
static void DupInterpolatedInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
dupPtr->internalRep = srcPtr->internalRep;
Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
}
static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static int SetStringFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
static const Jim_ObjType stringObjType = {
"string",
NULL,
DupStringInternalRep,
NULL,
JIM_TYPE_REFERENCES,
};
static void DupStringInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
JIM_NOTUSED(interp);
dupPtr->internalRep.strValue.maxLength = srcPtr->length;
dupPtr->internalRep.strValue.charLength = srcPtr->internalRep.strValue.charLength;
}
static int SetStringFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
if (objPtr->typePtr != &stringObjType) {
if (objPtr->bytes == NULL) {
JimPanic((objPtr->typePtr->updateStringProc == NULL, "UpdateStringProc called against '%s' type.", objPtr->typePtr->name));
objPtr->typePtr->updateStringProc(objPtr);
}
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &stringObjType;
objPtr->internalRep.strValue.maxLength = objPtr->length;
objPtr->internalRep.strValue.charLength = -1;
}
return JIM_OK;
}
int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr)
{
#ifdef JIM_UTF8
SetStringFromAny(interp, objPtr);
if (objPtr->internalRep.strValue.charLength < 0) {
objPtr->internalRep.strValue.charLength = utf8_strlen(objPtr->bytes, objPtr->length);
}
return objPtr->internalRep.strValue.charLength;
#else
return Jim_Length(objPtr);
#endif
}
Jim_Obj *Jim_NewStringObj(Jim_Interp *interp, const char *s, int len)
{
Jim_Obj *objPtr = Jim_NewObj(interp);
if (len == -1)
len = strlen(s);
if (len == 0) {
objPtr->bytes = JimEmptyStringRep;
}
else {
objPtr->bytes = Jim_StrDupLen(s, len);
}
objPtr->length = len;
objPtr->typePtr = NULL;
return objPtr;
}
Jim_Obj *Jim_NewStringObjUtf8(Jim_Interp *interp, const char *s, int charlen)
{
#ifdef JIM_UTF8
int bytelen = utf8_index(s, charlen);
Jim_Obj *objPtr = Jim_NewStringObj(interp, s, bytelen);
objPtr->typePtr = &stringObjType;
objPtr->internalRep.strValue.maxLength = bytelen;
objPtr->internalRep.strValue.charLength = charlen;
return objPtr;
#else
return Jim_NewStringObj(interp, s, charlen);
#endif
}
Jim_Obj *Jim_NewStringObjNoAlloc(Jim_Interp *interp, char *s, int len)
{
Jim_Obj *objPtr = Jim_NewObj(interp);
objPtr->bytes = s;
objPtr->length = (len == -1) ? strlen(s) : len;
objPtr->typePtr = NULL;
return objPtr;
}
static void StringAppendString(Jim_Obj *objPtr, const char *str, int len)
{
int needlen;
if (len == -1)
len = strlen(str);
needlen = objPtr->length + len;
if (objPtr->internalRep.strValue.maxLength < needlen ||
objPtr->internalRep.strValue.maxLength == 0) {
needlen *= 2;
if (needlen < 7) {
needlen = 7;
}
if (objPtr->bytes == JimEmptyStringRep) {
objPtr->bytes = Jim_Alloc(needlen + 1);
}
else {
objPtr->bytes = Jim_Realloc(objPtr->bytes, needlen + 1);
}
objPtr->internalRep.strValue.maxLength = needlen;
}
memcpy(objPtr->bytes + objPtr->length, str, len);
objPtr->bytes[objPtr->length + len] = '\0';
if (objPtr->internalRep.strValue.charLength >= 0) {
objPtr->internalRep.strValue.charLength += utf8_strlen(objPtr->bytes + objPtr->length, len);
}
objPtr->length += len;
}
void Jim_AppendString(Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len)
{
JimPanic((Jim_IsShared(objPtr), "Jim_AppendString called with shared object"));
SetStringFromAny(interp, objPtr);
StringAppendString(objPtr, str, len);
}
void Jim_AppendObj(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr)
{
int len;
const char *str = Jim_GetString(appendObjPtr, &len);
Jim_AppendString(interp, objPtr, str, len);
}
void Jim_AppendStrings(Jim_Interp *interp, Jim_Obj *objPtr, ...)
{
va_list ap;
SetStringFromAny(interp, objPtr);
va_start(ap, objPtr);
while (1) {
const char *s = va_arg(ap, const char *);
if (s == NULL)
break;
Jim_AppendString(interp, objPtr, s, -1);
}
va_end(ap);
}
int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
{
if (aObjPtr == bObjPtr) {
return 1;
}
else {
int Alen, Blen;
const char *sA = Jim_GetString(aObjPtr, &Alen);
const char *sB = Jim_GetString(bObjPtr, &Blen);
return Alen == Blen && memcmp(sA, sB, Alen) == 0;
}
}
int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
{
int plen, slen;
const char *pattern = Jim_GetString(patternObjPtr, &plen);
const char *string = Jim_GetString(objPtr, &slen);
return JimGlobMatch(pattern, plen, string, slen, nocase);
}
int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
{
const char *s1 = Jim_String(firstObjPtr);
int l1 = Jim_Utf8Length(interp, firstObjPtr);
const char *s2 = Jim_String(secondObjPtr);
int l2 = Jim_Utf8Length(interp, secondObjPtr);
return JimStringCompareUtf8(s1, l1, s2, l2, nocase);
}
static int JimRelToAbsIndex(int len, int idx)
{
if (idx < 0 && idx > -INT_MAX)
return len + idx;
return idx;
}
static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
{
int rangeLen;
if (*firstPtr > *lastPtr) {
rangeLen = 0;
}
else {
rangeLen = *lastPtr - *firstPtr + 1;
if (rangeLen) {
if (*firstPtr < 0) {
rangeLen += *firstPtr;
*firstPtr = 0;
}
if (*lastPtr >= len) {
rangeLen -= (*lastPtr - (len - 1));
*lastPtr = len - 1;
}
}
}
if (rangeLen < 0)
rangeLen = 0;
*rangeLenPtr = rangeLen;
}
static int JimStringGetRange(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr,
int len, int *first, int *last, int *range)
{
if (Jim_GetIndex(interp, firstObjPtr, first) != JIM_OK) {
return JIM_ERR;
}
if (Jim_GetIndex(interp, lastObjPtr, last) != JIM_OK) {
return JIM_ERR;
}
*first = JimRelToAbsIndex(len, *first);
*last = JimRelToAbsIndex(len, *last);
JimRelToAbsRange(len, first, last, range);
return JIM_OK;
}
Jim_Obj *Jim_StringByteRangeObj(Jim_Interp *interp,
Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
{
int first, last;
const char *str;
int rangeLen;
int bytelen;
str = Jim_GetString(strObjPtr, &bytelen);
if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, bytelen, &first, &last, &rangeLen) != JIM_OK) {
return NULL;
}
if (first == 0 && rangeLen == bytelen) {
return strObjPtr;
}
return Jim_NewStringObj(interp, str + first, rangeLen);
}
Jim_Obj *Jim_StringRangeObj(Jim_Interp *interp,
Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr)
{
#ifdef JIM_UTF8
int first, last;
const char *str;
int len, rangeLen;
int bytelen;
str = Jim_GetString(strObjPtr, &bytelen);
len = Jim_Utf8Length(interp, strObjPtr);
if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
return NULL;
}
if (first == 0 && rangeLen == len) {
return strObjPtr;
}
if (len == bytelen) {
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);
#endif
}
Jim_Obj *JimStringReplaceObj(Jim_Interp *interp,
Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr, Jim_Obj *newStrObj)
{
int first, last;
const char *str;
int len, rangeLen;
Jim_Obj *objPtr;
len = Jim_Utf8Length(interp, strObjPtr);
if (JimStringGetRange(interp, firstObjPtr, lastObjPtr, len, &first, &last, &rangeLen) != JIM_OK) {
return NULL;
}
if (last < first) {
return strObjPtr;
}
str = Jim_String(strObjPtr);
objPtr = Jim_NewStringObjUtf8(interp, str, first);
if (newStrObj) {
Jim_AppendObj(interp, objPtr, newStrObj);
}
Jim_AppendString(interp, objPtr, str + utf8_index(str, last + 1), len - last - 1);
return objPtr;
}
static void JimStrCopyUpperLower(char *dest, const char *str, int uc)
{
while (*str) {
int c;
str += utf8_tounicode(str, &c);
dest += utf8_getchars(dest, uc ? utf8_upper(c) : utf8_lower(c));
}
*dest = 0;
}
static Jim_Obj *JimStringToLower(Jim_Interp *interp, Jim_Obj *strObjPtr)
{
char *buf;
int len;
const char *str;
str = Jim_GetString(strObjPtr, &len);
#ifdef JIM_UTF8
len *= 2;
#endif
buf = Jim_Alloc(len + 1);
JimStrCopyUpperLower(buf, str, 0);
return Jim_NewStringObjNoAlloc(interp, buf, -1);
}
static Jim_Obj *JimStringToUpper(Jim_Interp *interp, Jim_Obj *strObjPtr)
{
char *buf;
const char *str;
int len;
str = Jim_GetString(strObjPtr, &len);
#ifdef JIM_UTF8
len *= 2;
#endif
buf = Jim_Alloc(len + 1);
JimStrCopyUpperLower(buf, str, 1);
return Jim_NewStringObjNoAlloc(interp, buf, -1);
}
static Jim_Obj *JimStringToTitle(Jim_Interp *interp, Jim_Obj *strObjPtr)
{
char *buf, *p;
int len;
int c;
const char *str;
str = Jim_GetString(strObjPtr, &len);
#ifdef JIM_UTF8
len *= 2;
#endif
buf = p = Jim_Alloc(len + 1);
str += utf8_tounicode(str, &c);
p += utf8_getchars(p, utf8_title(c));
JimStrCopyUpperLower(p, str, 0);
return Jim_NewStringObjNoAlloc(interp, buf, -1);
}
static const char *utf8_memchr(const char *str, int len, int c)
{
#ifdef JIM_UTF8
while (len) {
int sc;
int n = utf8_tounicode(str, &sc);
if (sc == c) {
return str;
}
str += n;
len -= n;
}
return NULL;
#else
return memchr(str, c, len);
#endif
}
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) {
break;
}
str += n;
len -= n;
}
return str;
}
static const char *JimFindTrimRight(const char *str, int len, const char *trimchars, int trimlen)
{
str += len;
while (len) {
int c;
int n = utf8_prev_len(str, len);
len -= n;
str -= n;
n = utf8_tounicode(str, &c);
if (utf8_memchr(trimchars, trimlen, c) == NULL) {
return str + n;
}
}
return NULL;
}
static const char default_trim_chars[] = " \t\n\r";
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;
const char *str = Jim_GetString(strObjPtr, &len);
const char *trimchars = default_trim_chars;
int trimcharslen = default_trim_chars_len;
const char *newstr;
if (trimcharsObjPtr) {
trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
}
newstr = JimFindTrimLeft(str, len, trimchars, trimcharslen);
if (newstr == str) {
return strObjPtr;
}
return Jim_NewStringObj(interp, newstr, len - (newstr - str));
}
static Jim_Obj *JimStringTrimRight(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *trimcharsObjPtr)
{
int len;
const char *trimchars = default_trim_chars;
int trimcharslen = default_trim_chars_len;
const char *nontrim;
if (trimcharsObjPtr) {
trimchars = Jim_GetString(trimcharsObjPtr, &trimcharslen);
}
SetStringFromAny(interp, strObjPtr);
len = Jim_Length(strObjPtr);
nontrim = JimFindTrimRight(strObjPtr->bytes, len, trimchars, trimcharslen);
if (nontrim == NULL) {
return Jim_NewEmptyStringObj(interp);
}
if (nontrim == strObjPtr->bytes + len) {
return strObjPtr;
}
if (Jim_IsShared(strObjPtr)) {
strObjPtr = Jim_NewStringObj(interp, strObjPtr->bytes, (nontrim - strObjPtr->bytes));
}
else {
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)
{
Jim_Obj *objPtr = JimStringTrimLeft(interp, strObjPtr, trimcharsObjPtr);
strObjPtr = JimStringTrimRight(interp, objPtr, trimcharsObjPtr);
if (objPtr != strObjPtr && objPtr->refCount == 0) {
Jim_FreeNewObj(interp, objPtr);
}
return strObjPtr;
}
#ifdef HAVE_ISASCII
#define jim_isascii isascii
#else
static int jim_isascii(int c)
{
return !(c & ~0x7f);
}
#endif
static int JimStringIs(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *strClass, int strict)
{
static const char * const strclassnames[] = {
"integer", "alpha", "alnum", "ascii", "digit",
"double", "lower", "upper", "space", "xdigit",
"control", "print", "graph", "punct", "boolean",
NULL
};
enum {
STR_IS_INTEGER, STR_IS_ALPHA, STR_IS_ALNUM, STR_IS_ASCII, STR_IS_DIGIT,
STR_IS_DOUBLE, STR_IS_LOWER, STR_IS_UPPER, STR_IS_SPACE, STR_IS_XDIGIT,
STR_IS_CONTROL, STR_IS_PRINT, STR_IS_GRAPH, STR_IS_PUNCT, STR_IS_BOOLEAN,
};
int strclass;
int len;
int i;
const char *str;
int (*isclassfunc)(int c) = NULL;
if (Jim_GetEnum(interp, strClass, strclassnames, &strclass, "class", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
return JIM_ERR;
}
str = Jim_GetString(strObjPtr, &len);
if (len == 0) {
Jim_SetResultBool(interp, !strict);
return JIM_OK;
}
switch (strclass) {
case STR_IS_INTEGER:
{
jim_wide w;
Jim_SetResultBool(interp, JimGetWideNoErr(interp, strObjPtr, &w) == JIM_OK);
return JIM_OK;
}
case STR_IS_DOUBLE:
{
double d;
Jim_SetResultBool(interp, Jim_GetDouble(interp, strObjPtr, &d) == JIM_OK && errno != ERANGE);
return JIM_OK;
}
case STR_IS_BOOLEAN:
{
int b;
Jim_SetResultBool(interp, Jim_GetBoolean(interp, strObjPtr, &b) == JIM_OK);
return JIM_OK;
}
case STR_IS_ALPHA: isclassfunc = isalpha; break;
case STR_IS_ALNUM: isclassfunc = isalnum; break;
case STR_IS_ASCII: isclassfunc = jim_isascii; break;
case STR_IS_DIGIT: isclassfunc = isdigit; break;
case STR_IS_LOWER: isclassfunc = islower; break;
case STR_IS_UPPER: isclassfunc = isupper; break;
case STR_IS_SPACE: isclassfunc = isspace; break;
case STR_IS_XDIGIT: isclassfunc = isxdigit; break;
case STR_IS_CONTROL: isclassfunc = iscntrl; break;
case STR_IS_PRINT: isclassfunc = isprint; break;
case STR_IS_GRAPH: isclassfunc = isgraph; break;
case STR_IS_PUNCT: isclassfunc = ispunct; break;
default:
return JIM_ERR;
}
for (i = 0; i < len; i++) {
if (!isclassfunc(UCHAR(str[i]))) {
Jim_SetResultBool(interp, 0);
return JIM_OK;
}
}
Jim_SetResultBool(interp, 1);
return JIM_OK;
}
static const Jim_ObjType comparedStringObjType = {
"compared-string",
NULL,
NULL,
NULL,
JIM_TYPE_REFERENCES,
};
int Jim_CompareStringImmediate(Jim_Interp *interp, Jim_Obj *objPtr, const char *str)
{
if (objPtr->typePtr == &comparedStringObjType && objPtr->internalRep.ptr == str) {
return 1;
}
else {
if (strcmp(str, Jim_String(objPtr)) != 0)
return 0;
if (objPtr->typePtr != &comparedStringObjType) {
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &comparedStringObjType;
}
objPtr->internalRep.ptr = (char *)str;
return 1;
}
}
static int qsortCompareStringPointers(const void *a, const void *b)
{
char *const *sa = (char *const *)a;
char *const *sb = (char *const *)b;
return strcmp(*sa, *sb);
}
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 = {
"source",
FreeSourceInternalRep,
DupSourceInternalRep,
NULL,
JIM_TYPE_REFERENCES,
};
void FreeSourceInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
Jim_DecrRefCount(interp, objPtr->internalRep.sourceValue.fileNameObj);
}
void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
}
static const Jim_ObjType scriptLineObjType = {
"scriptline",
NULL,
NULL,
NULL,
JIM_NONE,
};
static Jim_Obj *JimNewScriptLineObj(Jim_Interp *interp, int argc, int line)
{
Jim_Obj *objPtr;
#ifdef DEBUG_SHOW_SCRIPT
char buf[100];
snprintf(buf, sizeof(buf), "line=%d, argc=%d", line, argc);
objPtr = Jim_NewStringObj(interp, buf, -1);
#else
objPtr = Jim_NewEmptyStringObj(interp);
#endif
objPtr->typePtr = &scriptLineObjType;
objPtr->internalRep.scriptLineValue.argc = argc;
objPtr->internalRep.scriptLineValue.line = line;
return objPtr;
}
static void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static const Jim_ObjType scriptObjType = {
"script",
FreeScriptInternalRep,
DupScriptInternalRep,
NULL,
JIM_TYPE_NONE,
};
typedef struct ScriptToken
{
Jim_Obj *objPtr;
int type;
} ScriptToken;
typedef struct ScriptObj
{
ScriptToken *token;
Jim_Obj *fileNameObj;
int len;
int substFlags;
int inUse; /* Used to share a ScriptObj. Currently
only used by Jim_EvalObj() as protection against
shimmering of the currently evaluated object. */
int firstline;
int linenr;
int missing;
} ScriptObj;
static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
static int JimParseCheckMissing(Jim_Interp *interp, int ch);
static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script);
void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
int i;
struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
if (--script->inUse != 0)
return;
for (i = 0; i < script->len; i++) {
Jim_DecrRefCount(interp, script->token[i].objPtr);
}
Jim_Free(script->token);
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);
dupPtr->typePtr = NULL;
}
typedef struct
{
const char *token;
int len;
int type;
int line;
} ParseToken;
typedef struct
{
ParseToken *list;
int size;
int count;
ParseToken static_list[20];
} ParseTokenList;
static void ScriptTokenListInit(ParseTokenList *tokenlist)
{
tokenlist->list = tokenlist->static_list;
tokenlist->size = sizeof(tokenlist->static_list) / sizeof(ParseToken);
tokenlist->count = 0;
}
static void ScriptTokenListFree(ParseTokenList *tokenlist)
{
if (tokenlist->list != tokenlist->static_list) {
Jim_Free(tokenlist->list);
}
}
static void ScriptAddToken(ParseTokenList *tokenlist, const char *token, int len, int type,
int line)
{
ParseToken *t;
if (tokenlist->count == tokenlist->size) {
tokenlist->size *= 2;
if (tokenlist->list != tokenlist->static_list) {
tokenlist->list =
Jim_Realloc(tokenlist->list, tokenlist->size * sizeof(*tokenlist->list));
}
else {
tokenlist->list = Jim_Alloc(tokenlist->size * sizeof(*tokenlist->list));
memcpy(tokenlist->list, tokenlist->static_list,
tokenlist->count * sizeof(*tokenlist->list));
}
}
t = &tokenlist->list[tokenlist->count++];
t->token = token;
t->len = len;
t->type = type;
t->line = line;
}
static int JimCountWordTokens(struct ScriptObj *script, ParseToken *t)
{
int expand = 1;
int count = 0;
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)) {
expand = -1;
t++;
}
else {
if (script->missing == ' ') {
script->missing = '}';
script->linenr = t[1].line;
}
}
}
while (!TOKEN_IS_SEP(t->type)) {
t++;
count++;
}
return count * expand;
}
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) {
int len = t->len;
char *str = Jim_Alloc(len + 1);
len = JimEscape(str, t->token, len);
objPtr = Jim_NewStringObjNoAlloc(interp, str, len);
}
else {
objPtr = Jim_NewStringObj(interp, t->token, t->len);
}
return objPtr;
}
static void ScriptObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
ParseTokenList *tokenlist)
{
int i;
struct ScriptToken *token;
int lineargs = 0;
ScriptToken *linefirst;
int count;
int linenr;
#ifdef DEBUG_SHOW_SCRIPT_TOKENS
printf("==== Tokens ====\n");
for (i = 0; i < tokenlist->count; i++) {
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
count = tokenlist->count;
for (i = 0; i < tokenlist->count; i++) {
if (tokenlist->list[i].type == JIM_TT_EOL) {
count++;
}
}
linenr = script->firstline = tokenlist->list[0].line;
token = script->token = Jim_Alloc(sizeof(ScriptToken) * count);
linefirst = token++;
for (i = 0; i < tokenlist->count; ) {
int wordtokens;
while (tokenlist->list[i].type == JIM_TT_SEP) {
i++;
}
wordtokens = JimCountWordTokens(script, tokenlist->list + i);
if (wordtokens == 0) {
if (lineargs) {
linefirst->type = JIM_TT_LINE;
linefirst->objPtr = JimNewScriptLineObj(interp, lineargs, linenr);
Jim_IncrRefCount(linefirst->objPtr);
lineargs = 0;
linefirst = token++;
}
i++;
continue;
}
else if (wordtokens != 1) {
token->type = JIM_TT_WORD;
token->objPtr = Jim_NewIntObj(interp, wordtokens);
Jim_IncrRefCount(token->objPtr);
token++;
if (wordtokens < 0) {
i++;
wordtokens = -wordtokens - 1;
lineargs--;
}
}
if (lineargs == 0) {
linenr = tokenlist->list[i].line;
}
lineargs++;
while (wordtokens--) {
const ParseToken *t = &tokenlist->list[i++];
token->type = t->type;
token->objPtr = JimMakeScriptObj(interp, t);
Jim_IncrRefCount(token->objPtr);
Jim_SetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
token++;
}
}
if (lineargs == 0) {
token--;
}
script->len = token - script->token;
JimPanic((script->len >= count, "allocated script array is too short"));
#ifdef DEBUG_SHOW_SCRIPT
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
}
int Jim_ScriptIsComplete(Jim_Interp *interp, Jim_Obj *scriptObj, char *stateCharPtr)
{
ScriptObj *script = JimGetScript(interp, scriptObj);
if (stateCharPtr) {
*stateCharPtr = script->missing;
}
return script->missing == ' ' || script->missing == '}';
}
static int JimParseCheckMissing(Jim_Interp *interp, int ch)
{
const char *msg;
switch (ch) {
case '\\':
case ' ':
return JIM_OK;
case '[':
msg = "unmatched \"[\"";
break;
case '{':
msg = "missing close-brace";
break;
case '}':
msg = "extra characters after close-brace";
break;
case '"':
default:
msg = "missing quote";
break;
}
Jim_SetResultString(interp, msg, -1);
return JIM_ERR;
}
Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, int *lineptr)
{
int line;
Jim_Obj *fileNameObj;
if (objPtr->typePtr == &sourceObjType) {
fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
line = objPtr->internalRep.sourceValue.lineNumber;
}
else if (objPtr->typePtr == &scriptObjType) {
ScriptObj *script = JimGetScript(interp, objPtr);
fileNameObj = script->fileNameObj;
line = script->firstline;
}
else {
fileNameObj = interp->emptyObj;
line = 1;
}
*lineptr = line;
return fileNameObj;
}
void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
Jim_Obj *fileNameObj, int lineNumber)
{
JimPanic((Jim_IsShared(objPtr), "Jim_SetSourceInfo called with shared object"));
Jim_FreeIntRep(interp, objPtr);
Jim_IncrRefCount(fileNameObj);
objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
objPtr->internalRep.sourceValue.lineNumber = lineNumber;
objPtr->typePtr = &sourceObjType;
}
static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
ParseTokenList *tokenlist)
{
int i;
struct ScriptToken *token;
token = script->token = Jim_Alloc(sizeof(ScriptToken) * tokenlist->count);
for (i = 0; i < tokenlist->count; i++) {
const ParseToken *t = &tokenlist->list[i];
token->type = t->type;
token->objPtr = JimMakeScriptObj(interp, t);
Jim_IncrRefCount(token->objPtr);
token++;
}
script->len = i;
}
static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
int scriptTextLen;
const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
struct JimParserCtx parser;
struct ScriptObj *script;
ParseTokenList tokenlist;
Jim_Obj *fileNameObj;
int line;
fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
ScriptTokenListInit(&tokenlist);
JimParserInit(&parser, scriptText, scriptTextLen, line);
while (!parser.eof) {
JimParseScript(&parser);
ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
parser.tline);
}
ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
script = Jim_Alloc(sizeof(*script));
memset(script, 0, sizeof(*script));
script->inUse = 1;
script->fileNameObj = fileNameObj;
Jim_IncrRefCount(script->fileNameObj);
script->missing = parser.missing.ch;
script->linenr = parser.missing.line;
ScriptObjAddTokens(interp, script, &tokenlist);
ScriptTokenListFree(&tokenlist);
Jim_FreeIntRep(interp, objPtr);
Jim_SetIntRepPtr(objPtr, script);
objPtr->typePtr = &scriptObjType;
}
static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
{
if (objPtr == interp->emptyObj) {
objPtr = interp->nullScriptObj;
}
if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
JimSetScriptFromAny(interp, objPtr);
}
return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
}
void Jim_InterpIncrProcEpoch(Jim_Interp *interp)
{
interp->procEpoch++;
while (interp->oldCmdCache) {
Jim_Cmd *next = interp->oldCmdCache->prevCmd;
Jim_Free(interp->oldCmdCache);
interp->oldCmdCache = next;
}
interp->oldCmdCacheSize = 0;
}
static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
{
cmdPtr->inUse++;
}
static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
{
if (--cmdPtr->inUse == 0) {
if (cmdPtr->isproc) {
Jim_DecrRefCount(interp, cmdPtr->u.proc.argListObjPtr);
Jim_DecrRefCount(interp, cmdPtr->u.proc.bodyObjPtr);
Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
if (cmdPtr->u.proc.staticVars) {
Jim_FreeHashTable(cmdPtr->u.proc.staticVars);
Jim_Free(cmdPtr->u.proc.staticVars);
}
}
else {
if (cmdPtr->u.native.delProc) {
cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
}
}
if (cmdPtr->prevCmd) {
JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
}
cmdPtr->prevCmd = interp->oldCmdCache;
interp->oldCmdCache = cmdPtr;
if (!interp->quitting && ++interp->oldCmdCacheSize >= 1000) {
Jim_InterpIncrProcEpoch(interp);
}
}
}
static void JimIncrVarRef(Jim_VarVal *vv)
{
vv->refCount++;
}
static void JimDecrVarRef(Jim_Interp *interp, Jim_VarVal *vv)
{
assert(vv->refCount > 0);
if (--vv->refCount == 0) {
if (vv->objPtr) {
Jim_DecrRefCount(interp, vv->objPtr);
}
Jim_Free(vv);
}
}
static void JimVariablesHTValDestructor(void *interp, void *val)
{
JimDecrVarRef(interp, val);
}
static unsigned int JimObjectHTHashFunction(const void *key)
{
Jim_Obj *keyObj = (Jim_Obj *)key;
int length;
const char *string;
#ifdef JIM_OPTIMIZATION
if (JimIsWide(keyObj) && keyObj->bytes == NULL) {
jim_wide objValue = JimWideValue(keyObj);
if (objValue > INT_MIN && objValue < INT_MAX) {
unsigned result = 0;
unsigned value = (unsigned)objValue;
if (objValue < 0) {
value = (unsigned)-objValue;
}
do {
result += (result << 3) + (value % 10 + '0');
value /= 10;
} while (value);
if (objValue < 0) {
result += (result << 3) + '-';
}
return result;
}
}
#endif
string = Jim_GetString(keyObj, &length);
return Jim_GenHashFunction((const unsigned char *)string, length);
}
static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
{
return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
}
static void *JimObjectHTKeyValDup(void *privdata, const void *val)
{
Jim_IncrRefCount((Jim_Obj *)val);
return (void *)val;
}
static void JimObjectHTKeyValDestructor(void *interp, void *val)
{
Jim_DecrRefCount(interp, (Jim_Obj *)val);
}
static void *JimVariablesHTValDup(void *privdata, const void *val)
{
JimIncrVarRef((Jim_VarVal *)val);
return (void *)val;
}
static const Jim_HashTableType JimVariablesHashTableType = {
JimObjectHTHashFunction,
JimObjectHTKeyValDup,
JimVariablesHTValDup,
JimObjectHTKeyCompare,
JimObjectHTKeyValDestructor,
JimVariablesHTValDestructor
};
static const char *Jim_GetStringNoQualifier(Jim_Obj *objPtr, int *length)
{
int len;
const char *str = Jim_GetString(objPtr, &len);
if (len >= 2 && str[0] == ':' && str[1] == ':') {
while (len && *str == ':') {
len--;
str++;
}
}
*length = len;
return str;
}
static unsigned int JimCommandsHT_HashFunction(const void *key)
{
int len;
const char *str = Jim_GetStringNoQualifier((Jim_Obj *)key, &len);
return Jim_GenHashFunction((const unsigned char *)str, len);
}
static int JimCommandsHT_KeyCompare(void *privdata, const void *key1, const void *key2)
{
int len1, len2;
const char *str1 = Jim_GetStringNoQualifier((Jim_Obj *)key1, &len1);
const char *str2 = Jim_GetStringNoQualifier((Jim_Obj *)key2, &len2);
return len1 == len2 && memcmp(str1, str2, len1) == 0;
}
static void JimCommandsHT_ValDestructor(void *interp, void *val)
{
JimDecrCmdRefCount(interp, val);
}
static const Jim_HashTableType JimCommandsHashTableType = {
JimCommandsHT_HashFunction,
JimObjectHTKeyValDup,
NULL,
JimCommandsHT_KeyCompare,
JimObjectHTKeyValDestructor,
JimCommandsHT_ValDestructor
};
Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
{
#ifdef jim_ext_namespace
Jim_Obj *resultObj;
const char *name = Jim_String(nameObjPtr);
if (name[0] == ':' && name[1] == ':') {
return nameObjPtr;
}
Jim_IncrRefCount(nameObjPtr);
resultObj = Jim_NewStringObj(interp, "::", -1);
Jim_AppendObj(interp, resultObj, nameObjPtr);
Jim_DecrRefCount(interp, nameObjPtr);
return resultObj;
#else
return nameObjPtr;
#endif
}
static Jim_Obj *JimQualifyName(Jim_Interp *interp, Jim_Obj *objPtr)
{
#ifdef jim_ext_namespace
if (Jim_Length(interp->framePtr->nsObj)) {
int len;
const char *name = Jim_GetString(objPtr, &len);
if (len < 2 || name[0] != ':' || name[1] != ':') {
objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
Jim_AppendStrings(interp, objPtr, "::", name, NULL);
}
}
#endif
Jim_IncrRefCount(objPtr);
return objPtr;
}
static void JimCreateCommand(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Cmd *cmd)
{
JimPanic((nameObjPtr->refCount == 0, "JimCreateCommand called with zero ref count name"));
if (interp->local) {
Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, nameObjPtr);
if (he) {
cmd->prevCmd = Jim_GetHashEntryVal(he);
Jim_SetHashVal(&interp->commands, he, cmd);
Jim_InterpIncrProcEpoch(interp);
return;
}
}
Jim_ReplaceHashEntry(&interp->commands, nameObjPtr, cmd);
}
int Jim_CreateCommandObj(Jim_Interp *interp, Jim_Obj *cmdNameObj,
Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
{
Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
memset(cmdPtr, 0, sizeof(*cmdPtr));
cmdPtr->inUse = 1;
cmdPtr->u.native.delProc = delProc;
cmdPtr->u.native.cmdProc = cmdProc;
cmdPtr->u.native.privData = privData;
Jim_IncrRefCount(cmdNameObj);
JimCreateCommand(interp, cmdNameObj, cmdPtr);
Jim_DecrRefCount(interp, cmdNameObj);
return JIM_OK;
}
int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
{
return Jim_CreateCommandObj(interp, Jim_NewStringObj(interp, cmdNameStr, -1), cmdProc, privData, delProc);
}
static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
{
int len, i;
len = Jim_ListLength(interp, staticsListObjPtr);
if (len == 0) {
return JIM_OK;
}
cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
for (i = 0; i < len; i++) {
Jim_Obj *initObjPtr = NULL;
Jim_Obj *nameObjPtr;
Jim_VarVal *vv = NULL;
Jim_Obj *objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
int subLen = Jim_ListLength(interp, objPtr);
int byref = 0;
if (subLen != 1 && subLen != 2) {
Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
objPtr);
return JIM_ERR;
}
nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
if (subLen == 1) {
int len;
const char *pt = Jim_GetString(nameObjPtr, &len);
if (*pt == '&') {
nameObjPtr = Jim_NewStringObj(interp, pt + 1, len - 1);
byref = 1;
}
}
Jim_IncrRefCount(nameObjPtr);
if (subLen == 1) {
switch (SetVariableFromAny(interp, nameObjPtr)) {
case JIM_DICT_SUGAR:
if (byref) {
Jim_SetResultFormatted(interp, "Can't link to array element \"%#s\"", nameObjPtr);
}
else {
Jim_SetResultFormatted(interp, "Can't initialise array element \"%#s\"", nameObjPtr);
}
Jim_DecrRefCount(interp, nameObjPtr);
return JIM_ERR;
case JIM_OK:
if (byref) {
vv = nameObjPtr->internalRep.varValue.vv;
}
else {
initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
}
break;
case JIM_ERR:
Jim_SetResultFormatted(interp,
"variable for initialization of static \"%#s\" not found in the local context",
nameObjPtr);
Jim_DecrRefCount(interp, nameObjPtr);
return JIM_ERR;
}
}
else {
initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
}
if (vv == NULL) {
vv = Jim_Alloc(sizeof(*vv));
vv->objPtr = initObjPtr;
Jim_IncrRefCount(vv->objPtr);
vv->linkFramePtr = NULL;
vv->refCount = 0;
}
if (JimSetNewVariable(cmdPtr->u.proc.staticVars, nameObjPtr, vv) != JIM_OK) {
Jim_SetResultFormatted(interp,
"static variable name \"%#s\" duplicated in statics list", nameObjPtr);
JimIncrVarRef(vv);
JimDecrVarRef(interp, vv);
Jim_DecrRefCount(interp, nameObjPtr);
return JIM_ERR;
}
Jim_DecrRefCount(interp, nameObjPtr);
}
return JIM_OK;
}
#ifdef jim_ext_namespace
static const char *Jim_memrchr(const char *p, int c, int len)
{
int i;
for (i = len; i > 0; i--) {
if (p[i] == c) {
return p + i;
}
}
return NULL;
}
#endif
static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *nameObjPtr)
{
#ifdef jim_ext_namespace
if (cmdPtr->isproc) {
int len;
const char *cmdname = Jim_GetStringNoQualifier(nameObjPtr, &len);
const char *pt = Jim_memrchr(cmdname, ':', len);
if (pt && pt != cmdname && pt[-1] == ':') {
pt++;
Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 2);
Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
Jim_Obj *tempObj = Jim_NewStringObj(interp, pt, len - (pt - cmdname));
if (Jim_FindHashEntry(&interp->commands, tempObj)) {
Jim_InterpIncrProcEpoch(interp);
}
Jim_FreeNewObj(interp, tempObj);
}
}
#endif
}
static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
{
Jim_Cmd *cmdPtr;
int argListLen;
int i;
argListLen = Jim_ListLength(interp, argListObjPtr);
cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
assert(cmdPtr);
memset(cmdPtr, 0, sizeof(*cmdPtr));
cmdPtr->inUse = 1;
cmdPtr->isproc = 1;
cmdPtr->u.proc.argListObjPtr = argListObjPtr;
cmdPtr->u.proc.argListLen = argListLen;
cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
cmdPtr->u.proc.argsPos = -1;
cmdPtr->u.proc.arglist = (struct Jim_ProcArg *)(cmdPtr + 1);
cmdPtr->u.proc.nsObj = nsObj ? nsObj : interp->emptyObj;
Jim_IncrRefCount(argListObjPtr);
Jim_IncrRefCount(bodyObjPtr);
Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
if (staticsListObjPtr && JimCreateProcedureStatics(interp, cmdPtr, staticsListObjPtr) != JIM_OK) {
goto err;
}
for (i = 0; i < argListLen; i++) {
Jim_Obj *argPtr;
Jim_Obj *nameObjPtr;
Jim_Obj *defaultObjPtr;
int len;
argPtr = Jim_ListGetIndex(interp, argListObjPtr, i);
len = Jim_ListLength(interp, argPtr);
if (len == 0) {
Jim_SetResultString(interp, "argument with no name", -1);
err:
JimDecrCmdRefCount(interp, cmdPtr);
return NULL;
}
if (len > 2) {
Jim_SetResultFormatted(interp, "too many fields in argument specifier \"%#s\"", argPtr);
goto err;
}
if (len == 2) {
nameObjPtr = Jim_ListGetIndex(interp, argPtr, 0);
defaultObjPtr = Jim_ListGetIndex(interp, argPtr, 1);
}
else {
nameObjPtr = argPtr;
defaultObjPtr = NULL;
}
if (Jim_CompareStringImmediate(interp, nameObjPtr, "args")) {
if (cmdPtr->u.proc.argsPos >= 0) {
Jim_SetResultString(interp, "'args' specified more than once", -1);
goto err;
}
cmdPtr->u.proc.argsPos = i;
}
else {
if (len == 2) {
cmdPtr->u.proc.optArity++;
}
else {
cmdPtr->u.proc.reqArity++;
}
}
cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
}
return cmdPtr;
}
int Jim_DeleteCommand(Jim_Interp *interp, Jim_Obj *nameObj)
{
int ret = JIM_OK;
nameObj = JimQualifyName(interp, nameObj);
if (Jim_DeleteHashEntry(&interp->commands, nameObj) == JIM_ERR) {
Jim_SetResultFormatted(interp, "can't delete \"%#s\": command doesn't exist", nameObj);
ret = JIM_ERR;
}
Jim_DecrRefCount(interp, nameObj);
return ret;
}
int Jim_RenameCommand(Jim_Interp *interp, Jim_Obj *oldNameObj, Jim_Obj *newNameObj)
{
int ret = JIM_ERR;
Jim_HashEntry *he;
Jim_Cmd *cmdPtr;
if (Jim_Length(newNameObj) == 0) {
return Jim_DeleteCommand(interp, oldNameObj);
}
oldNameObj = JimQualifyName(interp, oldNameObj);
newNameObj = JimQualifyName(interp, newNameObj);
he = Jim_FindHashEntry(&interp->commands, oldNameObj);
if (he == NULL) {
Jim_SetResultFormatted(interp, "can't rename \"%#s\": command doesn't exist", oldNameObj);
}
else if (Jim_FindHashEntry(&interp->commands, newNameObj)) {
Jim_SetResultFormatted(interp, "can't rename to \"%#s\": command already exists", newNameObj);
}
else {
cmdPtr = Jim_GetHashEntryVal(he);
if (cmdPtr->prevCmd) {
Jim_SetResultFormatted(interp, "can't rename local command \"%#s\"", oldNameObj);
}
else {
JimIncrCmdRefCount(cmdPtr);
JimUpdateProcNamespace(interp, cmdPtr, newNameObj);
Jim_AddHashEntry(&interp->commands, newNameObj, cmdPtr);
Jim_DeleteHashEntry(&interp->commands, oldNameObj);
Jim_InterpIncrProcEpoch(interp);
ret = JIM_OK;
}
}
Jim_DecrRefCount(interp, oldNameObj);
Jim_DecrRefCount(interp, newNameObj);
return ret;
}
static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
Jim_DecrRefCount(interp, objPtr->internalRep.cmdValue.nsObj);
}
static void DupCommandInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
dupPtr->internalRep.cmdValue = srcPtr->internalRep.cmdValue;
dupPtr->typePtr = srcPtr->typePtr;
Jim_IncrRefCount(dupPtr->internalRep.cmdValue.nsObj);
}
static const Jim_ObjType commandObjType = {
"command",
FreeCommandInternalRep,
DupCommandInternalRep,
NULL,
JIM_TYPE_REFERENCES,
};
Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
Jim_Cmd *cmd;
if (objPtr->typePtr == &commandObjType
&& objPtr->internalRep.cmdValue.procEpoch == interp->procEpoch
#ifdef jim_ext_namespace
&& Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
#endif
&& objPtr->internalRep.cmdValue.cmdPtr->inUse) {
cmd = objPtr->internalRep.cmdValue.cmdPtr;
}
else {
Jim_Obj *qualifiedNameObj = JimQualifyName(interp, objPtr);
Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, qualifiedNameObj);
#ifdef jim_ext_namespace
if (he == NULL && Jim_Length(interp->framePtr->nsObj)) {
he = Jim_FindHashEntry(&interp->commands, objPtr);
}
#endif
if (he == NULL) {
if (flags & JIM_ERRMSG) {
Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
}
Jim_DecrRefCount(interp, qualifiedNameObj);
return NULL;
}
cmd = Jim_GetHashEntryVal(he);
cmd->cmdNameObj = Jim_GetHashEntryKey(he);
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &commandObjType;
objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
objPtr->internalRep.cmdValue.cmdPtr = cmd;
objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
Jim_IncrRefCount(interp->framePtr->nsObj);
Jim_DecrRefCount(interp, qualifiedNameObj);
}
while (cmd->u.proc.upcall) {
cmd = cmd->prevCmd;
}
return cmd;
}
static const Jim_ObjType variableObjType = {
"variable",
NULL,
NULL,
NULL,
JIM_TYPE_REFERENCES,
};
static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
const char *varName;
Jim_CallFrame *framePtr;
int global;
int len;
Jim_VarVal *vv;
if (objPtr->typePtr == &variableObjType) {
framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
return JIM_OK;
}
}
else if (objPtr->typePtr == &dictSubstObjType) {
return JIM_DICT_SUGAR;
}
varName = Jim_GetString(objPtr, &len);
if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
return JIM_DICT_SUGAR;
}
if (varName[0] == ':' && varName[1] == ':') {
while (*varName == ':') {
varName++;
len--;
}
global = 1;
framePtr = interp->topFramePtr;
Jim_Obj *tempObj = Jim_NewStringObj(interp, varName, len);
vv = JimFindVariable(&framePtr->vars, tempObj);
Jim_FreeNewObj(interp, tempObj);
}
else {
global = 0;
framePtr = interp->framePtr;
vv = JimFindVariable(&framePtr->vars, objPtr);
if (vv == NULL && framePtr->staticVars) {
vv = JimFindVariable(framePtr->staticVars, objPtr);
}
}
if (vv == NULL) {
return JIM_ERR;
}
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &variableObjType;
objPtr->internalRep.varValue.callFrameId = framePtr->id;
objPtr->internalRep.varValue.vv = vv;
objPtr->internalRep.varValue.global = global;
return JIM_OK;
}
static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv)
{
return Jim_AddHashEntry(ht, nameObjPtr, vv);
}
static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
{
Jim_HashEntry *he = Jim_FindHashEntry(ht, nameObjPtr);
if (he) {
return (Jim_VarVal *)Jim_GetHashEntryVal(he);
}
return NULL;
}
static int JimUnsetVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
{
return Jim_DeleteHashEntry(ht, nameObjPtr);
}
static Jim_VarVal *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
{
const char *name;
Jim_CallFrame *framePtr;
int global;
int len;
Jim_VarVal *vv = Jim_Alloc(sizeof(*vv));
vv->objPtr = valObjPtr;
Jim_IncrRefCount(valObjPtr);
vv->linkFramePtr = NULL;
vv->refCount = 0;
name = Jim_GetString(nameObjPtr, &len);
if (name[0] == ':' && name[1] == ':') {
while (*name == ':') {
name++;
len--;
}
framePtr = interp->topFramePtr;
global = 1;
JimSetNewVariable(&framePtr->vars, Jim_NewStringObj(interp, name, len), vv);
}
else {
framePtr = interp->framePtr;
global = 0;
JimSetNewVariable(&framePtr->vars, nameObjPtr, vv);
}
Jim_FreeIntRep(interp, nameObjPtr);
nameObjPtr->typePtr = &variableObjType;
nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
nameObjPtr->internalRep.varValue.vv = vv;
nameObjPtr->internalRep.varValue.global = global;
return vv;
}
int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
{
int err;
Jim_VarVal *vv;
switch (SetVariableFromAny(interp, nameObjPtr)) {
case JIM_DICT_SUGAR:
return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
case JIM_ERR:
JimCreateVariable(interp, nameObjPtr, valObjPtr);
break;
case JIM_OK:
vv = nameObjPtr->internalRep.varValue.vv;
if (vv->linkFramePtr == NULL) {
Jim_IncrRefCount(valObjPtr);
Jim_DecrRefCount(interp, vv->objPtr);
vv->objPtr = valObjPtr;
}
else {
Jim_CallFrame *savedCallFrame;
savedCallFrame = interp->framePtr;
interp->framePtr = vv->linkFramePtr;
err = Jim_SetVariable(interp, vv->objPtr, valObjPtr);
interp->framePtr = savedCallFrame;
if (err != JIM_OK)
return err;
}
}
return JIM_OK;
}
int Jim_SetVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
{
Jim_Obj *nameObjPtr;
int result;
nameObjPtr = Jim_NewStringObj(interp, name, -1);
Jim_IncrRefCount(nameObjPtr);
result = Jim_SetVariable(interp, nameObjPtr, objPtr);
Jim_DecrRefCount(interp, nameObjPtr);
return result;
}
int Jim_SetGlobalVariableStr(Jim_Interp *interp, const char *name, Jim_Obj *objPtr)
{
Jim_CallFrame *savedFramePtr;
int result;
savedFramePtr = interp->framePtr;
interp->framePtr = interp->topFramePtr;
result = Jim_SetVariableStr(interp, name, objPtr);
interp->framePtr = savedFramePtr;
return result;
}
int Jim_SetVariableStrWithStr(Jim_Interp *interp, const char *name, const char *val)
{
Jim_Obj *valObjPtr;
int result;
valObjPtr = Jim_NewStringObj(interp, val, -1);
Jim_IncrRefCount(valObjPtr);
result = Jim_SetVariableStr(interp, name, valObjPtr);
Jim_DecrRefCount(interp, valObjPtr);
return result;
}
int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
{
const char *varName;
const char *targetName;
Jim_CallFrame *framePtr;
Jim_VarVal *vv;
int len;
int varnamelen;
switch (SetVariableFromAny(interp, nameObjPtr)) {
case JIM_DICT_SUGAR:
Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
return JIM_ERR;
case JIM_OK:
vv = nameObjPtr->internalRep.varValue.vv;
if (vv->linkFramePtr == NULL) {
Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
return JIM_ERR;
}
vv->linkFramePtr = NULL;
break;
}
varName = Jim_GetString(nameObjPtr, &varnamelen);
if (varName[0] == ':' && varName[1] == ':') {
while (*varName == ':') {
varName++;
varnamelen--;
}
framePtr = interp->topFramePtr;
}
else {
framePtr = interp->framePtr;
}
targetName = Jim_GetString(targetNameObjPtr, &len);
if (targetName[0] == ':' && targetName[1] == ':') {
while (*targetName == ':') {
targetName++;
len--;
}
targetNameObjPtr = Jim_NewStringObj(interp, targetName, len);
targetCallFrame = interp->topFramePtr;
}
Jim_IncrRefCount(targetNameObjPtr);
if (framePtr->level < targetCallFrame->level) {
Jim_SetResultFormatted(interp,
"bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
nameObjPtr);
Jim_DecrRefCount(interp, targetNameObjPtr);
return JIM_ERR;
}
if (framePtr == targetCallFrame) {
Jim_Obj *objPtr = targetNameObjPtr;
while (1) {
if (Jim_Length(objPtr) == varnamelen && memcmp(Jim_String(objPtr), varName, varnamelen) == 0) {
Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
Jim_DecrRefCount(interp, targetNameObjPtr);
return JIM_ERR;
}
if (SetVariableFromAny(interp, objPtr) != JIM_OK)
break;
vv = objPtr->internalRep.varValue.vv;
if (vv->linkFramePtr != targetCallFrame)
break;
objPtr = vv->objPtr;
}
}
Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
nameObjPtr->internalRep.varValue.vv->linkFramePtr = targetCallFrame;
Jim_DecrRefCount(interp, targetNameObjPtr);
return JIM_OK;
}
Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
{
if (interp->safeexpr) {
return nameObjPtr;
}
switch (SetVariableFromAny(interp, nameObjPtr)) {
case JIM_OK:{
Jim_VarVal *vv = nameObjPtr->internalRep.varValue.vv;
if (vv->linkFramePtr == NULL) {
return vv->objPtr;
}
else {
Jim_Obj *objPtr;
Jim_CallFrame *savedCallFrame = interp->framePtr;
interp->framePtr = vv->linkFramePtr;
objPtr = Jim_GetVariable(interp, vv->objPtr, flags);
interp->framePtr = savedCallFrame;
if (objPtr) {
return objPtr;
}
}
}
break;
case JIM_DICT_SUGAR:
return JimDictSugarGet(interp, nameObjPtr, flags);
}
if (flags & JIM_ERRMSG) {
Jim_SetResultFormatted(interp, "can't read \"%#s\": no such variable", nameObjPtr);
}
return NULL;
}
Jim_Obj *Jim_GetGlobalVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
{
Jim_CallFrame *savedFramePtr;
Jim_Obj *objPtr;
savedFramePtr = interp->framePtr;
interp->framePtr = interp->topFramePtr;
objPtr = Jim_GetVariable(interp, nameObjPtr, flags);
interp->framePtr = savedFramePtr;
return objPtr;
}
Jim_Obj *Jim_GetVariableStr(Jim_Interp *interp, const char *name, int flags)
{
Jim_Obj *nameObjPtr, *varObjPtr;
nameObjPtr = Jim_NewStringObj(interp, name, -1);
Jim_IncrRefCount(nameObjPtr);
varObjPtr = Jim_GetVariable(interp, nameObjPtr, flags);
Jim_DecrRefCount(interp, nameObjPtr);
return varObjPtr;
}
Jim_Obj *Jim_GetGlobalVariableStr(Jim_Interp *interp, const char *name, int flags)
{
Jim_CallFrame *savedFramePtr;
Jim_Obj *objPtr;
savedFramePtr = interp->framePtr;
interp->framePtr = interp->topFramePtr;
objPtr = Jim_GetVariableStr(interp, name, flags);
interp->framePtr = savedFramePtr;
return objPtr;
}
int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
{
Jim_VarVal *vv;
int retval;
Jim_CallFrame *framePtr;
retval = SetVariableFromAny(interp, nameObjPtr);
if (retval == JIM_DICT_SUGAR) {
return JimDictSugarSet(interp, nameObjPtr, NULL);
}
else if (retval == JIM_OK) {
vv = nameObjPtr->internalRep.varValue.vv;
if (vv->linkFramePtr) {
framePtr = interp->framePtr;
interp->framePtr = vv->linkFramePtr;
retval = Jim_UnsetVariable(interp, vv->objPtr, JIM_NONE);
interp->framePtr = framePtr;
}
else {
if (nameObjPtr->internalRep.varValue.global) {
int len;
const char *name = Jim_GetString(nameObjPtr, &len);
while (*name == ':') {
name++;
len--;
}
framePtr = interp->topFramePtr;
Jim_Obj *tempObj = Jim_NewStringObj(interp, name, len);
retval = JimUnsetVariable(&framePtr->vars, tempObj);
Jim_FreeNewObj(interp, tempObj);
}
else {
framePtr = interp->framePtr;
retval = JimUnsetVariable(&framePtr->vars, nameObjPtr);
}
if (retval == JIM_OK) {
framePtr->id = interp->callFrameEpoch++;
}
}
}
if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
Jim_SetResultFormatted(interp, "can't unset \"%#s\": no such variable", nameObjPtr);
}
return retval;
}
static void JimDictSugarParseVarKey(Jim_Interp *interp, Jim_Obj *objPtr,
Jim_Obj **varPtrPtr, Jim_Obj **keyPtrPtr)
{
const char *str, *p;
int len, keyLen;
Jim_Obj *varObjPtr, *keyObjPtr;
str = Jim_GetString(objPtr, &len);
p = strchr(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--;
}
keyObjPtr = Jim_NewStringObj(interp, p, keyLen);
Jim_IncrRefCount(varObjPtr);
Jim_IncrRefCount(keyObjPtr);
*varPtrPtr = varObjPtr;
*keyPtrPtr = keyObjPtr;
}
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, JIM_MUSTEXIST);
if (err == JIM_OK) {
Jim_SetEmptyResult(interp);
}
else {
if (!valObjPtr) {
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;
}
}
Jim_SetResultFormatted(interp, "can't %s \"%#s\": variable isn't array",
(valObjPtr ? "set" : "unset"), objPtr);
}
return err;
}
static Jim_Obj *JimDictExpandArrayVariable(Jim_Interp *interp, Jim_Obj *varObjPtr,
Jim_Obj *keyObjPtr, int flags)
{
Jim_Obj *dictObjPtr;
Jim_Obj *resObjPtr = NULL;
int ret;
dictObjPtr = Jim_GetVariable(interp, varObjPtr, JIM_ERRMSG);
if (!dictObjPtr) {
return NULL;
}
ret = Jim_DictKey(interp, dictObjPtr, keyObjPtr, &resObjPtr, JIM_NONE);
if (ret != JIM_OK) {
Jim_SetResultFormatted(interp,
"can't read \"%#s(%#s)\": %s array", varObjPtr, keyObjPtr,
ret < 0 ? "variable isn't" : "no such element in");
}
else if ((flags & JIM_UNSHARED) && Jim_IsShared(dictObjPtr)) {
Jim_SetVariable(interp, varObjPtr, Jim_DuplicateObj(interp, dictObjPtr));
}
return resObjPtr;
}
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);
}
void FreeDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr);
Jim_DecrRefCount(interp, objPtr->internalRep.dictSubstValue.indexObjPtr);
}
static void DupDictSubstInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
dupPtr->internalRep = srcPtr->internalRep;
Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.varNameObjPtr);
Jim_IncrRefCount(dupPtr->internalRep.dictSubstValue.indexObjPtr);
}
static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
if (objPtr->typePtr != &dictSubstObjType) {
Jim_Obj *varObjPtr, *keyObjPtr;
if (objPtr->typePtr == &interpolatedObjType) {
varObjPtr = objPtr->internalRep.dictSubstValue.varNameObjPtr;
keyObjPtr = objPtr->internalRep.dictSubstValue.indexObjPtr;
Jim_IncrRefCount(varObjPtr);
Jim_IncrRefCount(keyObjPtr);
}
else {
JimDictSugarParseVarKey(interp, objPtr, &varObjPtr, &keyObjPtr);
}
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &dictSubstObjType;
objPtr->internalRep.dictSubstValue.varNameObjPtr = varObjPtr;
objPtr->internalRep.dictSubstValue.indexObjPtr = keyObjPtr;
}
}
static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
{
Jim_Obj *resObjPtr = NULL;
Jim_Obj *substKeyObjPtr = NULL;
if (interp->safeexpr) {
return objPtr;
}
SetDictSubstFromAny(interp, objPtr);
if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
&substKeyObjPtr, JIM_NONE)
!= JIM_OK) {
return NULL;
}
Jim_IncrRefCount(substKeyObjPtr);
resObjPtr =
JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
substKeyObjPtr, 0);
Jim_DecrRefCount(interp, substKeyObjPtr);
return resObjPtr;
}
static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
{
Jim_CallFrame *cf;
if (interp->freeFramesList) {
cf = interp->freeFramesList;
interp->freeFramesList = cf->next;
cf->argv = NULL;
cf->argc = 0;
cf->procArgsObjPtr = NULL;
cf->procBodyObjPtr = NULL;
cf->next = NULL;
cf->staticVars = NULL;
cf->localCommands = NULL;
cf->tailcallObj = NULL;
cf->tailcallCmd = NULL;
}
else {
cf = Jim_Alloc(sizeof(*cf));
memset(cf, 0, sizeof(*cf));
Jim_InitHashTable(&cf->vars, &JimVariablesHashTableType, interp);
}
cf->id = interp->callFrameEpoch++;
cf->parent = parent;
cf->level = parent ? parent->level + 1 : 0;
cf->nsObj = nsObj;
Jim_IncrRefCount(nsObj);
return cf;
}
static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
{
if (localCommands) {
Jim_Obj *cmdNameObj;
while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
Jim_HashTable *ht = &interp->commands;
Jim_HashEntry *he = Jim_FindHashEntry(ht, cmdNameObj);
if (he) {
Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
if (cmd->prevCmd) {
Jim_Cmd *prevCmd = cmd->prevCmd;
cmd->prevCmd = NULL;
JimDecrCmdRefCount(interp, cmd);
Jim_SetHashVal(ht, he, prevCmd);
}
else {
Jim_DeleteHashEntry(ht, cmdNameObj);
}
}
Jim_DecrRefCount(interp, cmdNameObj);
}
Jim_FreeStack(localCommands);
Jim_Free(localCommands);
}
return JIM_OK;
}
static int JimInvokeDefer(Jim_Interp *interp, int retcode)
{
Jim_Obj *objPtr;
if (JimFindVariable(&interp->framePtr->vars, interp->defer) == NULL) {
return retcode;
}
objPtr = Jim_GetVariable(interp, interp->defer, JIM_NONE);
if (objPtr) {
int ret = JIM_OK;
int i;
int listLen = Jim_ListLength(interp, objPtr);
Jim_Obj *resultObjPtr;
Jim_IncrRefCount(objPtr);
resultObjPtr = Jim_GetResult(interp);
Jim_IncrRefCount(resultObjPtr);
Jim_SetEmptyResult(interp);
for (i = listLen; i > 0; i--) {
Jim_Obj *scriptObjPtr = Jim_ListGetIndex(interp, objPtr, i - 1);
ret = Jim_EvalObj(interp, scriptObjPtr);
if (ret != JIM_OK) {
break;
}
}
if (ret == JIM_OK || retcode == JIM_ERR) {
Jim_SetResult(interp, resultObjPtr);
}
else {
retcode = ret;
}
Jim_DecrRefCount(interp, resultObjPtr);
Jim_DecrRefCount(interp, objPtr);
}
return retcode;
}
#define JIM_FCF_FULL 0
#define JIM_FCF_REUSE 1
static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action)
{
JimDeleteLocalProcs(interp, cf->localCommands);
if (cf->procArgsObjPtr)
Jim_DecrRefCount(interp, cf->procArgsObjPtr);
if (cf->procBodyObjPtr)
Jim_DecrRefCount(interp, cf->procBodyObjPtr);
Jim_DecrRefCount(interp, cf->nsObj);
if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
Jim_FreeHashTable(&cf->vars);
else {
Jim_ClearHashTable(&cf->vars);
}
cf->next = interp->freeFramesList;
interp->freeFramesList = cf;
}
int Jim_IsBigEndian(void)
{
union {
unsigned short s;
unsigned char c[2];
} uval = {0x0102};
return uval.c[0] == 1;
}
Jim_Interp *Jim_CreateInterp(void)
{
Jim_Interp *i = Jim_Alloc(sizeof(*i));
memset(i, 0, sizeof(*i));
i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
i->lastCollectTime = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
#ifdef JIM_REFERENCES
Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
#endif
Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
i->emptyObj = Jim_NewEmptyStringObj(i);
i->trueObj = Jim_NewIntObj(i, 1);
i->falseObj = Jim_NewIntObj(i, 0);
i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
i->result = i->emptyObj;
i->stackTrace = Jim_NewListObj(i, NULL, 0);
i->unknown = Jim_NewStringObj(i, "unknown", -1);
i->defer = Jim_NewStringObj(i, "jim::defer", -1);
i->errorProc = i->emptyObj;
i->nullScriptObj = Jim_NewEmptyStringObj(i);
i->evalFrame = &i->topEvalFrame;
i->currentFilenameObj = Jim_NewEmptyStringObj(i);
Jim_IncrRefCount(i->emptyObj);
Jim_IncrRefCount(i->result);
Jim_IncrRefCount(i->stackTrace);
Jim_IncrRefCount(i->unknown);
Jim_IncrRefCount(i->defer);
Jim_IncrRefCount(i->nullScriptObj);
Jim_IncrRefCount(i->errorProc);
Jim_IncrRefCount(i->trueObj);
Jim_IncrRefCount(i->falseObj);
Jim_IncrRefCount(i->currentFilenameObj);
Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
Jim_SetVariableStrWithStr(i, "tcl_platform(bootstrap)", "0");
Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
Jim_SetVariableStr(i, "tcl_platform(stackFormat)", Jim_NewIntObj(i, 4));
return i;
}
void Jim_FreeInterp(Jim_Interp *i)
{
Jim_CallFrame *cf, *cfx;
Jim_Obj *objPtr, *nextObjPtr;
i->quitting = 1;
for (cf = i->framePtr; cf; cf = cfx) {
JimInvokeDefer(i, JIM_OK);
cfx = cf->parent;
JimFreeCallFrame(i, cf, JIM_FCF_FULL);
}
Jim_FreeHashTable(&i->commands);
Jim_DecrRefCount(i, i->emptyObj);
Jim_DecrRefCount(i, i->trueObj);
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_DecrRefCount(i, i->defer);
Jim_DecrRefCount(i, i->nullScriptObj);
Jim_DecrRefCount(i, i->currentFilenameObj);
Jim_InterpIncrProcEpoch(i);
#ifdef JIM_REFERENCES
Jim_FreeHashTable(&i->references);
#endif
Jim_FreeHashTable(&i->packages);
Jim_Free(i->prngState);
Jim_FreeHashTable(&i->assocData);
if (i->traceCmdObj) {
Jim_DecrRefCount(i, i->traceCmdObj);
}
#ifdef JIM_MAINTAINER
if (i->liveList != NULL) {
objPtr = i->liveList;
printf("\n-------------------------------------\n");
printf("Objects still in the free list:\n");
while (objPtr) {
const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
Jim_String(objPtr);
if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
printf("%p (%d) %-10s: '%.20s...'\n",
(void *)objPtr, objPtr->refCount, type, objPtr->bytes);
}
else {
printf("%p (%d) %-10s: '%s'\n",
(void *)objPtr, objPtr->refCount, type, objPtr->bytes ? objPtr->bytes : "(null)");
}
if (objPtr->typePtr == &sourceObjType) {
printf("FILE %s LINE %d\n",
Jim_String(objPtr->internalRep.sourceValue.fileNameObj),
objPtr->internalRep.sourceValue.lineNumber);
}
objPtr = objPtr->nextObjPtr;
}
printf("-------------------------------------\n\n");
JimPanic((1, "Live list non empty freeing the interpreter! Leak?"));
}
#endif
objPtr = i->freeList;
while (objPtr) {
nextObjPtr = objPtr->nextObjPtr;
Jim_Free(objPtr);
objPtr = nextObjPtr;
}
for (cf = i->freeFramesList; cf; cf = cfx) {
cfx = cf->next;
if (cf->vars.table)
Jim_FreeHashTable(&cf->vars);
Jim_Free(cf);
}
Jim_Free(i);
}
Jim_CallFrame *Jim_GetCallFrameByLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr)
{
long level;
const char *str;
Jim_CallFrame *framePtr;
if (levelObjPtr) {
str = Jim_String(levelObjPtr);
if (str[0] == '#') {
char *endptr;
level = jim_strtol(str + 1, &endptr);
if (str[1] == '\0' || endptr[0] != '\0') {
level = -1;
}
}
else {
if (Jim_GetLong(interp, levelObjPtr, &level) != JIM_OK || level < 0) {
level = -1;
}
else {
level = interp->framePtr->level - level;
}
}
}
else {
str = "1";
level = interp->framePtr->level - 1;
}
if (level == 0) {
return interp->topFramePtr;
}
if (level > 0) {
for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
if (framePtr->level == level) {
return framePtr;
}
}
}
Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
return NULL;
}
static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, long level)
{
Jim_CallFrame *framePtr;
if (level == 0) {
return interp->framePtr;
}
if (level < 0) {
level = interp->framePtr->level + level;
}
if (level > 0) {
for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
if (framePtr->level == level) {
return framePtr;
}
}
}
return NULL;
}
static Jim_EvalFrame *JimGetEvalFrameByProcLevel(Jim_Interp *interp, int proclevel)
{
Jim_EvalFrame *evalFrame;
if (proclevel == 0) {
return interp->evalFrame;
}
if (proclevel < 0) {
proclevel = interp->procLevel + proclevel;
}
if (proclevel >= 0) {
for (evalFrame = interp->evalFrame; evalFrame; evalFrame = evalFrame->parent) {
if (evalFrame->procLevel == proclevel) {
return evalFrame;
}
}
}
return NULL;
}
static Jim_Obj *JimProcForEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame)
{
if (frame == interp->evalFrame || (frame->cmd && frame->cmd->cmdNameObj)) {
Jim_EvalFrame *e;
for (e = frame->parent; e; e = e->parent) {
if (e->cmd && e->cmd->isproc && e->cmd->cmdNameObj) {
break;
}
}
if (e && e->cmd && e->cmd->cmdNameObj) {
return e->cmd->cmdNameObj;
}
}
return NULL;
}
static void JimAddStackFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *listObj)
{
Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
Jim_Obj *fileNameObj = interp->emptyObj;
int linenr = 1;
if (frame->scriptObj) {
ScriptObj *script = JimGetScript(interp, frame->scriptObj);
fileNameObj = script->fileNameObj;
linenr = script->linenr;
}
Jim_ListAppendElement(interp, listObj, procNameObj ? procNameObj : interp->emptyObj);
Jim_ListAppendElement(interp, listObj, fileNameObj);
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, linenr));
Jim_ListAppendElement(interp, listObj, Jim_NewListObj(interp, frame->argv, frame->argc));
}
static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
{
Jim_IncrRefCount(stackTraceObj);
Jim_DecrRefCount(interp, interp->stackTrace);
interp->stackTrace = stackTraceObj;
interp->errorFlag = 1;
}
static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script)
{
if (!interp->errorFlag) {
int i;
Jim_Obj *stackTrace = Jim_NewListObj(interp, NULL, 0);
if (interp->procLevel == 0 && script) {
Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
Jim_ListAppendElement(interp, stackTrace, script->fileNameObj);
Jim_ListAppendElement(interp, stackTrace, Jim_NewIntObj(interp, script->linenr));
Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
}
else {
for (i = 0; i <= interp->procLevel; i++) {
Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
if (frame) {
JimAddStackFrame(interp, frame, stackTrace);
}
}
}
JimSetStackTrace(interp, stackTrace);
}
}
int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
void *data)
{
AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
assocEntryPtr->delProc = delProc;
assocEntryPtr->data = data;
return Jim_AddHashEntry(&interp->assocData, key, assocEntryPtr);
}
void *Jim_GetAssocData(Jim_Interp *interp, const char *key)
{
Jim_HashEntry *entryPtr = Jim_FindHashEntry(&interp->assocData, key);
if (entryPtr != NULL) {
AssocDataValue *assocEntryPtr = Jim_GetHashEntryVal(entryPtr);
return assocEntryPtr->data;
}
return NULL;
}
int Jim_DeleteAssocData(Jim_Interp *interp, const char *key)
{
return Jim_DeleteHashEntry(&interp->assocData, key);
}
int Jim_GetExitCode(Jim_Interp *interp)
{
return interp->exitCode;
}
static void UpdateStringOfInt(struct Jim_Obj *objPtr);
static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
static const Jim_ObjType intObjType = {
"int",
NULL,
NULL,
UpdateStringOfInt,
JIM_TYPE_NONE,
};
static const Jim_ObjType coercedDoubleObjType = {
"coerced-double",
NULL,
NULL,
UpdateStringOfInt,
JIM_TYPE_NONE,
};
static void UpdateStringOfInt(struct Jim_Obj *objPtr)
{
char buf[JIM_INTEGER_SPACE + 1];
jim_wide wideValue = JimWideValue(objPtr);
int pos = 0;
if (wideValue == 0) {
buf[pos++] = '0';
}
else {
char tmp[JIM_INTEGER_SPACE];
int num = 0;
int i;
if (wideValue < 0) {
buf[pos++] = '-';
i = wideValue % 10;
tmp[num++] = (i > 0) ? (10 - i) : -i;
wideValue /= -10;
}
while (wideValue) {
tmp[num++] = wideValue % 10;
wideValue /= 10;
}
for (i = 0; i < num; i++) {
buf[pos++] = '0' + tmp[num - i - 1];
}
}
buf[pos] = 0;
JimSetStringBytes(objPtr, buf);
}
static int SetIntFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
jim_wide wideValue;
const char *str;
if (objPtr->typePtr == &coercedDoubleObjType) {
objPtr->typePtr = &intObjType;
return JIM_OK;
}
str = Jim_String(objPtr);
if (Jim_StringToWide(str, &wideValue, 0) != JIM_OK) {
if (flags & JIM_ERRMSG) {
Jim_SetResultFormatted(interp, "expected integer but got \"%#s\"", objPtr);
}
return JIM_ERR;
}
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;
}
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &intObjType;
objPtr->internalRep.wideValue = wideValue;
return JIM_OK;
}
#ifdef JIM_OPTIMIZATION
static int JimIsWide(Jim_Obj *objPtr)
{
return objPtr->typePtr == &intObjType;
}
#endif
int Jim_GetWide(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
{
if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
return JIM_ERR;
*widePtr = JimWideValue(objPtr);
return JIM_OK;
}
int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
{
int ret = JIM_OK;
if (objPtr->typePtr == &sourceObjType || objPtr->typePtr == NULL) {
SetIntFromAny(interp, objPtr, 0);
}
if (objPtr->typePtr == &intObjType) {
*widePtr = JimWideValue(objPtr);
}
else {
JimPanic((interp->safeexpr, "interp->safeexpr is set"));
interp->safeexpr++;
ret = Jim_EvalExpression(interp, objPtr);
interp->safeexpr--;
if (ret == JIM_OK) {
ret = Jim_GetWide(interp, Jim_GetResult(interp), widePtr);
}
if (ret != JIM_OK) {
Jim_SetResultFormatted(interp, "expected integer expression but got \"%#s\"", objPtr);
}
}
return ret;
}
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);
return JIM_OK;
}
int Jim_GetLong(Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr)
{
jim_wide wideValue;
int retval;
retval = Jim_GetWide(interp, objPtr, &wideValue);
if (retval == JIM_OK) {
*longPtr = (long)wideValue;
return JIM_OK;
}
return JIM_ERR;
}
Jim_Obj *Jim_NewIntObj(Jim_Interp *interp, jim_wide wideValue)
{
Jim_Obj *objPtr;
objPtr = Jim_NewObj(interp);
objPtr->typePtr = &intObjType;
objPtr->bytes = NULL;
objPtr->internalRep.wideValue = wideValue;
return objPtr;
}
#define JIM_DOUBLE_SPACE 30
static void UpdateStringOfDouble(struct Jim_Obj *objPtr);
static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
static const Jim_ObjType doubleObjType = {
"double",
NULL,
NULL,
UpdateStringOfDouble,
JIM_TYPE_NONE,
};
#if !HAVE_DECL_ISNAN
#undef isnan
#define isnan(X) ((X) != (X))
#endif
#if !HAVE_DECL_ISINF
#undef isinf
#define isinf(X) (1.0 / (X) == 0.0)
#endif
static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
{
double value = objPtr->internalRep.doubleValue;
if (isnan(value)) {
JimSetStringBytes(objPtr, "NaN");
return;
}
if (isinf(value)) {
if (value < 0) {
JimSetStringBytes(objPtr, "-Inf");
}
else {
JimSetStringBytes(objPtr, "Inf");
}
return;
}
{
char buf[JIM_DOUBLE_SPACE + 1];
int i;
int len = sprintf(buf, "%.12g", value);
for (i = 0; i < len; i++) {
if (buf[i] == '.' || buf[i] == 'e') {
#if defined(JIM_SPRINTF_DOUBLE_NEEDS_FIX)
char *e = strchr(buf, 'e');
if (e && (e[1] == '-' || e[1] == '+') && e[2] == '0') {
e += 2;
memmove(e, e + 1, len - (e - buf));
}
#endif
break;
}
}
if (buf[i] == '\0') {
buf[i++] = '.';
buf[i++] = '0';
buf[i] = '\0';
}
JimSetStringBytes(objPtr, buf);
}
}
static int SetDoubleFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
double doubleValue;
jim_wide wideValue;
const char *str;
#ifdef HAVE_LONG_LONG
#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) {
objPtr->typePtr = &coercedDoubleObjType;
return JIM_OK;
}
#endif
str = Jim_String(objPtr);
if (Jim_StringToWide(str, &wideValue, 10) == JIM_OK) {
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &coercedDoubleObjType;
objPtr->internalRep.wideValue = wideValue;
return JIM_OK;
}
else {
if (Jim_StringToDouble(str, &doubleValue) != JIM_OK) {
Jim_SetResultFormatted(interp, "expected floating-point number but got \"%#s\"", objPtr);
return JIM_ERR;
}
Jim_FreeIntRep(interp, objPtr);
}
objPtr->typePtr = &doubleObjType;
objPtr->internalRep.doubleValue = doubleValue;
return JIM_OK;
}
int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr)
{
if (objPtr->typePtr == &coercedDoubleObjType) {
*doublePtr = JimWideValue(objPtr);
return JIM_OK;
}
if (objPtr->typePtr != &doubleObjType && SetDoubleFromAny(interp, objPtr) == JIM_ERR)
return JIM_ERR;
if (objPtr->typePtr == &coercedDoubleObjType) {
*doublePtr = JimWideValue(objPtr);
}
else {
*doublePtr = objPtr->internalRep.doubleValue;
}
return JIM_OK;
}
Jim_Obj *Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue)
{
Jim_Obj *objPtr;
objPtr = Jim_NewObj(interp);
objPtr->typePtr = &doubleObjType;
objPtr->bytes = NULL;
objPtr->internalRep.doubleValue = doubleValue;
return objPtr;
}
static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags);
int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
{
if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
return JIM_ERR;
*booleanPtr = (int) JimWideValue(objPtr);
return JIM_OK;
}
static const char * const jim_true_false_strings[8] = {
"1", "true", "yes", "on",
"0", "false", "no", "off"
};
static const int jim_true_false_lens[8] = {
1, 4, 3, 2,
1, 5, 2, 3,
};
static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
int index = Jim_FindByName(Jim_String(objPtr), jim_true_false_strings,
sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings));
if (index < 0) {
if (flags & JIM_ERRMSG) {
Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr);
}
return JIM_ERR;
}
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &intObjType;
objPtr->internalRep.wideValue = index < 4 ? 1 : 0;
return JIM_OK;
}
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);
static const Jim_ObjType listObjType = {
"list",
FreeListInternalRep,
DupListInternalRep,
UpdateStringOfList,
JIM_TYPE_NONE,
};
void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
int i;
for (i = 0; i < objPtr->internalRep.listValue.len; i++) {
Jim_DecrRefCount(interp, objPtr->internalRep.listValue.ele[i]);
}
Jim_Free(objPtr->internalRep.listValue.ele);
}
void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
int i;
JIM_NOTUSED(interp);
dupPtr->internalRep.listValue.len = srcPtr->internalRep.listValue.len;
dupPtr->internalRep.listValue.maxLen = srcPtr->internalRep.listValue.maxLen;
dupPtr->internalRep.listValue.ele =
Jim_Alloc(sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.maxLen);
memcpy(dupPtr->internalRep.listValue.ele, srcPtr->internalRep.listValue.ele,
sizeof(Jim_Obj *) * srcPtr->internalRep.listValue.len);
for (i = 0; i < dupPtr->internalRep.listValue.len; i++) {
Jim_IncrRefCount(dupPtr->internalRep.listValue.ele[i]);
}
dupPtr->typePtr = &listObjType;
}
#define JIM_ELESTR_SIMPLE 0
#define JIM_ELESTR_BRACE 1
#define JIM_ELESTR_QUOTE 2
static unsigned char ListElementQuotingType(const char *s, int len)
{
int i, level, blevel, trySimple = 1;
if (len == 0)
return JIM_ELESTR_BRACE;
if (s[0] == '"' || s[0] == '{') {
trySimple = 0;
goto testbrace;
}
for (i = 0; i < len; i++) {
switch (s[i]) {
case ' ':
case '$':
case '"':
case '[':
case ']':
case ';':
case '\\':
case '\r':
case '\n':
case '\t':
case '\f':
case '\v':
trySimple = 0;
case '{':
case '}':
goto testbrace;
}
}
return JIM_ELESTR_SIMPLE;
testbrace:
if (s[len - 1] == '\\')
return JIM_ELESTR_QUOTE;
level = 0;
blevel = 0;
for (i = 0; i < len; i++) {
switch (s[i]) {
case '{':
level++;
break;
case '}':
level--;
if (level < 0)
return JIM_ELESTR_QUOTE;
break;
case '[':
blevel++;
break;
case ']':
blevel--;
break;
case '\\':
if (s[i + 1] == '\n')
return JIM_ELESTR_QUOTE;
else if (s[i + 1] != '\0')
i++;
break;
}
}
if (blevel < 0) {
return JIM_ELESTR_QUOTE;
}
if (level == 0) {
if (!trySimple)
return JIM_ELESTR_BRACE;
for (i = 0; i < len; i++) {
switch (s[i]) {
case ' ':
case '$':
case '"':
case '[':
case ']':
case ';':
case '\\':
case '\r':
case '\n':
case '\t':
case '\f':
case '\v':
return JIM_ELESTR_BRACE;
break;
}
}
return JIM_ELESTR_SIMPLE;
}
return JIM_ELESTR_QUOTE;
}
static int BackslashQuoteString(const char *s, int len, char *q)
{
char *p = q;
while (len--) {
switch (*s) {
case ' ':
case '$':
case '"':
case '[':
case ']':
case '{':
case '}':
case ';':
case '\\':
*p++ = '\\';
*p++ = *s++;
break;
case '\n':
*p++ = '\\';
*p++ = 'n';
s++;
break;
case '\r':
*p++ = '\\';
*p++ = 'r';
s++;
break;
case '\t':
*p++ = '\\';
*p++ = 't';
s++;
break;
case '\f':
*p++ = '\\';
*p++ = 'f';
s++;
break;
case '\v':
*p++ = '\\';
*p++ = 'v';
s++;
break;
default:
*p++ = *s++;
break;
}
}
*p = '\0';
return p - q;
}
static void JimMakeListStringRep(Jim_Obj *objPtr, Jim_Obj **objv, int objc)
{
#define STATIC_QUOTING_LEN 32
int i, bufLen, realLength;
const char *strRep;
char *p;
unsigned char *quotingType, staticQuoting[STATIC_QUOTING_LEN];
if (objc > STATIC_QUOTING_LEN) {
quotingType = Jim_Alloc(objc);
}
else {
quotingType = staticQuoting;
}
bufLen = 0;
for (i = 0; i < objc; i++) {
int len;
strRep = Jim_GetString(objv[i], &len);
quotingType[i] = ListElementQuotingType(strRep, len);
switch (quotingType[i]) {
case JIM_ELESTR_SIMPLE:
if (i != 0 || strRep[0] != '#') {
bufLen += len;
break;
}
quotingType[i] = JIM_ELESTR_BRACE;
case JIM_ELESTR_BRACE:
bufLen += len + 2;
break;
case JIM_ELESTR_QUOTE:
bufLen += len * 2;
break;
}
bufLen++;
}
bufLen++;
p = objPtr->bytes = Jim_Alloc(bufLen + 1);
realLength = 0;
for (i = 0; i < objc; i++) {
int len, qlen;
strRep = Jim_GetString(objv[i], &len);
switch (quotingType[i]) {
case JIM_ELESTR_SIMPLE:
memcpy(p, strRep, len);
p += len;
realLength += len;
break;
case JIM_ELESTR_BRACE:
*p++ = '{';
memcpy(p, strRep, len);
p += len;
*p++ = '}';
realLength += len + 2;
break;
case JIM_ELESTR_QUOTE:
if (i == 0 && strRep[0] == '#') {
*p++ = '\\';
realLength++;
}
qlen = BackslashQuoteString(strRep, len, p);
p += qlen;
realLength += qlen;
break;
}
if (i + 1 != objc) {
*p++ = ' ';
realLength++;
}
}
*p = '\0';
objPtr->length = realLength;
if (quotingType != staticQuoting) {
Jim_Free(quotingType);
}
}
static void UpdateStringOfList(struct Jim_Obj *objPtr)
{
JimMakeListStringRep(objPtr, objPtr->internalRep.listValue.ele, objPtr->internalRep.listValue.len);
}
static int SetListFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
struct JimParserCtx parser;
const char *str;
int strLen;
Jim_Obj *fileNameObj;
int linenr;
if (objPtr->typePtr == &listObjType) {
return JIM_OK;
}
if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
Jim_Dict *dict = objPtr->internalRep.dictValue;
objPtr->typePtr = &listObjType;
objPtr->internalRep.listValue.len = dict->len;
objPtr->internalRep.listValue.maxLen = dict->maxLen;
objPtr->internalRep.listValue.ele = dict->table;
Jim_Free(dict->ht);
Jim_Free(dict);
return JIM_OK;
}
fileNameObj = Jim_GetSourceInfo(interp, objPtr, &linenr);
Jim_IncrRefCount(fileNameObj);
str = Jim_GetString(objPtr, &strLen);
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &listObjType;
objPtr->internalRep.listValue.len = 0;
objPtr->internalRep.listValue.maxLen = 0;
objPtr->internalRep.listValue.ele = NULL;
if (strLen) {
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);
Jim_SetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
ListAppendElement(objPtr, elementPtr);
}
}
Jim_DecrRefCount(interp, fileNameObj);
return JIM_OK;
}
Jim_Obj *Jim_NewListObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
{
Jim_Obj *objPtr;
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;
if (len) {
ListInsertElements(objPtr, 0, len, elements);
}
return objPtr;
}
static void JimListGetElements(Jim_Interp *interp, Jim_Obj *listObj, int *listLen,
Jim_Obj ***listVec)
{
*listLen = Jim_ListLength(interp, listObj);
*listVec = listObj->internalRep.listValue.ele;
}
static int JimSign(jim_wide w)
{
if (w == 0) {
return 0;
}
else if (w < 0) {
return -1;
}
return 1;
}
struct lsort_info {
jmp_buf jmpbuf;
Jim_Obj *command;
Jim_Interp *interp;
enum {
JIM_LSORT_ASCII,
JIM_LSORT_NOCASE,
JIM_LSORT_INTEGER,
JIM_LSORT_REAL,
JIM_LSORT_COMMAND,
JIM_LSORT_DICT
} type;
int order;
Jim_Obj **indexv;
int indexc;
int unique;
int (*subfn)(Jim_Obj **, Jim_Obj **);
};
static struct lsort_info *sort_info;
static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
Jim_Obj *lObj, *rObj;
if (Jim_ListIndices(sort_info->interp, *lhsObj, sort_info->indexv, sort_info->indexc, &lObj, JIM_ERRMSG) != JIM_OK ||
Jim_ListIndices(sort_info->interp, *rhsObj, sort_info->indexv, sort_info->indexc, &rObj, JIM_ERRMSG) != JIM_OK) {
longjmp(sort_info->jmpbuf, JIM_ERR);
}
return sort_info->subfn(&lObj, &rObj);
}
static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
}
static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
}
static int ListSortDict(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
const char *left = Jim_String(*lhsObj);
const char *right = Jim_String(*rhsObj);
while (1) {
if (isdigit(UCHAR(*left)) && isdigit(UCHAR(*right))) {
jim_wide lint, rint;
char *lend, *rend;
lint = jim_strtoull(left, &lend);
rint = jim_strtoull(right, &rend);
if (lint != rint) {
return JimSign(lint - rint) * sort_info->order;
}
if (lend -left != rend - right) {
return JimSign((lend - left) - (rend - right)) * sort_info->order;
}
left = lend;
right = rend;
}
else {
int cl, cr;
left += utf8_tounicode_case(left, &cl, 1);
right += utf8_tounicode_case(right, &cr, 1);
if (cl != cr) {
return JimSign(cl - cr) * sort_info->order;
}
if (cl == 0) {
return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
}
}
}
}
static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
jim_wide lhs = 0, rhs = 0;
if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
longjmp(sort_info->jmpbuf, JIM_ERR);
}
return JimSign(lhs - rhs) * sort_info->order;
}
static int ListSortReal(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
double lhs = 0, rhs = 0;
if (Jim_GetDouble(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
Jim_GetDouble(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
longjmp(sort_info->jmpbuf, JIM_ERR);
}
if (lhs == rhs) {
return 0;
}
if (lhs > rhs) {
return sort_info->order;
}
return -sort_info->order;
}
static int ListSortCommand(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
Jim_Obj *compare_script;
int rc;
jim_wide ret = 0;
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);
if (rc != JIM_OK || Jim_GetWide(sort_info->interp, Jim_GetResult(sort_info->interp), &ret) != JIM_OK) {
longjmp(sort_info->jmpbuf, rc);
}
return JimSign(ret) * sort_info->order;
}
static void ListRemoveDuplicates(Jim_Obj *listObjPtr, int (*comp)(Jim_Obj **lhs, Jim_Obj **rhs))
{
int src;
int dst = 0;
Jim_Obj **ele = listObjPtr->internalRep.listValue.ele;
for (src = 1; src < listObjPtr->internalRep.listValue.len; src++) {
if (comp(&ele[dst], &ele[src]) == 0) {
Jim_DecrRefCount(sort_info->interp, ele[dst]);
}
else {
dst++;
}
ele[dst] = ele[src];
}
dst++;
if (dst < listObjPtr->internalRep.listValue.len) {
ele[dst] = ele[src];
}
listObjPtr->internalRep.listValue.len = dst;
}
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 *);
int (*fn) (Jim_Obj **, Jim_Obj **);
Jim_Obj **vector;
int len;
int rc;
JimPanic((Jim_IsShared(listObjPtr), "ListSortElements called with shared object"));
SetListFromAny(interp, listObjPtr);
prev_info = sort_info;
sort_info = info;
vector = listObjPtr->internalRep.listValue.ele;
len = listObjPtr->internalRep.listValue.len;
switch (info->type) {
case JIM_LSORT_ASCII:
fn = ListSortString;
break;
case JIM_LSORT_NOCASE:
fn = ListSortStringNoCase;
break;
case JIM_LSORT_INTEGER:
fn = ListSortInteger;
break;
case JIM_LSORT_REAL:
fn = ListSortReal;
break;
case JIM_LSORT_COMMAND:
fn = ListSortCommand;
break;
case JIM_LSORT_DICT:
fn = ListSortDict;
break;
default:
fn = NULL;
JimPanic((1, "ListSort called with invalid sort type"));
return -1;
}
if (info->indexc) {
info->subfn = fn;
fn = ListSortIndexHelper;
}
if ((rc = setjmp(info->jmpbuf)) == 0) {
qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
if (info->unique && len > 1) {
ListRemoveDuplicates(listObjPtr, fn);
}
Jim_InvalidateStringRep(listObjPtr);
}
sort_info = prev_info;
return rc;
}
static void ListEnsureLength(Jim_Obj *listPtr, int idx)
{
assert(idx >= 0);
if (idx >= listPtr->internalRep.listValue.maxLen) {
if (idx < 4) {
idx = 4;
}
listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
sizeof(Jim_Obj *) * idx);
listPtr->internalRep.listValue.maxLen = idx;
}
}
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 (elemc == 0) {
return;
}
if (requiredLen > listPtr->internalRep.listValue.maxLen) {
if (currentLen) {
requiredLen *= 2;
}
ListEnsureLength(listPtr, requiredLen);
}
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];
Jim_IncrRefCount(point[i]);
}
listPtr->internalRep.listValue.len += elemc;
}
static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr)
{
ListInsertElements(listPtr, -1, 1, &objPtr);
}
static void ListAppendList(Jim_Obj *listPtr, Jim_Obj *appendListPtr)
{
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), "Jim_ListAppendElement called with shared object"));
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), "Jim_ListAppendList called with shared object"));
SetListFromAny(interp, listPtr);
SetListFromAny(interp, appendListPtr);
Jim_InvalidateStringRep(listPtr);
ListAppendList(listPtr, appendListPtr);
}
int Jim_ListLength(Jim_Interp *interp, Jim_Obj *objPtr)
{
SetListFromAny(interp, objPtr);
return objPtr->internalRep.listValue.len;
}
void Jim_ListInsertElements(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
int objc, Jim_Obj *const *objVec)
{
JimPanic((Jim_IsShared(listPtr), "Jim_ListInsertElement called with shared object"));
SetListFromAny(interp, listPtr);
if (idx >= 0 && idx > listPtr->internalRep.listValue.len)
idx = listPtr->internalRep.listValue.len;
else if (idx < 0)
idx = 0;
Jim_InvalidateStringRep(listPtr);
ListInsertElements(listPtr, idx, objc, objVec);
}
Jim_Obj *Jim_ListGetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx)
{
SetListFromAny(interp, listPtr);
if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
(idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
return NULL;
}
if (idx < 0)
idx = listPtr->internalRep.listValue.len + idx;
return listPtr->internalRep.listValue.ele[idx];
}
int Jim_ListIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx, Jim_Obj **objPtrPtr, int flags)
{
*objPtrPtr = Jim_ListGetIndex(interp, listPtr, idx);
if (*objPtrPtr == NULL) {
if (flags & JIM_ERRMSG) {
Jim_SetResultString(interp, "list index out of range", -1);
}
return JIM_ERR;
}
return JIM_OK;
}
static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr,
Jim_Obj *const *indexv, int indexc, Jim_Obj **resultObj, int flags)
{
int i;
int static_idxes[5];
int *idxes = static_idxes;
int ret = JIM_OK;
if (indexc > sizeof(static_idxes) / sizeof(*static_idxes)) {
idxes = Jim_Alloc(indexc * sizeof(*idxes));
}
for (i = 0; i < indexc; i++) {
ret = Jim_GetIndex(interp, indexv[i], &idxes[i]);
if (ret != JIM_OK) {
goto err;
}
}
for (i = 0; i < indexc; i++) {
Jim_Obj *objPtr = Jim_ListGetIndex(interp, listPtr, idxes[i]);
if (!objPtr) {
if (flags & JIM_ERRMSG) {
if (idxes[i] < 0 || idxes[i] > Jim_ListLength(interp, listPtr)) {
Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
}
else {
Jim_SetResultFormatted(interp, "element %#s missing from sublist \"%#s\"", indexv[i], listPtr);
}
}
return -1;
}
listPtr = objPtr;
}
*resultObj = listPtr;
err:
if (idxes != static_idxes)
Jim_Free(idxes);
return ret;
}
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
Jim_Obj *newObjPtr, int flags)
{
SetListFromAny(interp, listPtr);
if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
(idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
if (flags & JIM_ERRMSG) {
Jim_SetResultString(interp, "list index out of range", -1);
}
return JIM_ERR;
}
if (idx < 0)
idx = listPtr->internalRep.listValue.len + idx;
Jim_DecrRefCount(interp, listPtr->internalRep.listValue.ele[idx]);
listPtr->internalRep.listValue.ele[idx] = newObjPtr;
Jim_IncrRefCount(newObjPtr);
return JIM_OK;
}
int Jim_ListSetIndex(Jim_Interp *interp, Jim_Obj *varNamePtr,
Jim_Obj *const *indexv, int indexc, Jim_Obj *newObjPtr)
{
Jim_Obj *varObjPtr, *objPtr, *listObjPtr;
int shared, i, idx;
varObjPtr = objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG | JIM_UNSHARED);
if (objPtr == NULL)
return JIM_ERR;
if ((shared = Jim_IsShared(objPtr)))
varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
for (i = 0; i < indexc - 1; i++) {
listObjPtr = objPtr;
if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
goto err;
objPtr = Jim_ListGetIndex(interp, listObjPtr, idx);
if (objPtr == NULL) {
Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
goto err;
}
if (Jim_IsShared(objPtr)) {
objPtr = Jim_DuplicateObj(interp, objPtr);
ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
}
Jim_InvalidateStringRep(listObjPtr);
}
if (Jim_GetIndex(interp, indexv[indexc - 1], &idx) != JIM_OK)
goto err;
if (ListSetIndex(interp, objPtr, idx, newObjPtr, JIM_ERRMSG) == JIM_ERR)
goto err;
Jim_InvalidateStringRep(objPtr);
Jim_InvalidateStringRep(varObjPtr);
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;
}
Jim_Obj *Jim_ListJoin(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *joinStr, int joinStrLen)
{
int i;
int listLen = Jim_ListLength(interp, listObjPtr);
Jim_Obj *resObjPtr = Jim_NewEmptyStringObj(interp);
for (i = 0; i < listLen; ) {
Jim_AppendObj(interp, resObjPtr, Jim_ListGetIndex(interp, listObjPtr, i));
if (++i != listLen) {
Jim_AppendString(interp, resObjPtr, joinStr, joinStrLen);
}
}
return resObjPtr;
}
Jim_Obj *Jim_ConcatObj(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
int i;
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++)
ListAppendList(objPtr, objv[i]);
return objPtr;
}
else {
int len = 0, objLen;
char *bytes, *p;
for (i = 0; i < objc; i++) {
len += Jim_Length(objv[i]);
}
if (objc)
len += objc - 1;
p = bytes = Jim_Alloc(len + 1);
for (i = 0; i < objc; i++) {
const char *s = Jim_GetString(objv[i], &objLen);
while (objLen && isspace(UCHAR(*s))) {
s++;
objLen--;
len--;
}
while (objLen && isspace(UCHAR(s[objLen - 1]))) {
if (objLen > 1 && s[objLen - 2] == '\\') {
break;
}
objLen--;
len--;
}
memcpy(p, s, objLen);
p += objLen;
if (i + 1 != objc) {
if (objLen)
*p++ = ' ';
else {
len--;
}
}
}
*p = '\0';
return Jim_NewStringObjNoAlloc(interp, bytes, len);
}
}
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);
first = JimRelToAbsIndex(len, first);
last = JimRelToAbsIndex(len, last);
JimRelToAbsRange(len, &first, &last, &rangeLen);
if (first == 0 && last == len) {
return listObjPtr;
}
return Jim_NewListObj(interp, listObjPtr->internalRep.listValue.ele + first, rangeLen);
}
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);
static const Jim_ObjType dictObjType = {
"dict",
FreeDictInternalRep,
DupDictInternalRep,
UpdateStringOfDict,
JIM_TYPE_NONE,
};
static void JimFreeDict(Jim_Interp *interp, Jim_Dict *dict)
{
int i;
for (i = 0; i < dict->len; i++) {
Jim_DecrRefCount(interp, dict->table[i]);
}
Jim_Free(dict->table);
Jim_Free(dict->ht);
Jim_Free(dict);
}
enum {
DICT_HASH_FIND = -1,
DICT_HASH_REMOVE = -2,
DICT_HASH_ADD = -3,
};
static int JimDictHashFind(Jim_Dict *dict, Jim_Obj *keyObjPtr, int op_tvoffset)
{
unsigned h = (JimObjectHTHashFunction(keyObjPtr) + dict->uniq);
unsigned idx = h & dict->sizemask;
int tvoffset = 0;
unsigned peturb = h;
unsigned first_removed = ~0;
if (dict->len) {
while ((tvoffset = dict->ht[idx].offset)) {
if (tvoffset == -1) {
if (first_removed == ~0) {
first_removed = idx;
}
}
else if (dict->ht[idx].hash == h) {
if (Jim_StringEqObj(keyObjPtr, dict->table[tvoffset - 1])) {
break;
}
}
peturb >>= 5;
idx = (5 * idx + 1 + peturb) & dict->sizemask;
}
}
switch (op_tvoffset) {
case DICT_HASH_FIND:
break;
case DICT_HASH_REMOVE:
if (tvoffset) {
dict->ht[idx].offset = -1;
dict->dummy++;
}
break;
case DICT_HASH_ADD:
if (tvoffset == 0) {
if (first_removed != ~0) {
idx = first_removed;
dict->dummy--;
}
dict->ht[idx].offset = dict->len + 1;
dict->ht[idx].hash = h;
}
break;
default:
assert(tvoffset);
dict->ht[idx].offset = op_tvoffset;
break;
}
return tvoffset;
}
static void JimDictExpandHashTable(Jim_Dict *dict, unsigned int size)
{
int i;
struct JimDictHashEntry *prevht = dict->ht;
int prevsize = dict->size;
dict->size = JimHashTableNextPower(size);
dict->sizemask = dict->size - 1;
dict->ht = Jim_Alloc(dict->size * sizeof(*dict->ht));
memset(dict->ht, 0, dict->size * sizeof(*dict->ht));
for (i = 0; i < prevsize; i++) {
if (prevht[i].offset > 0) {
unsigned h = prevht[i].hash;
unsigned idx = h & dict->sizemask;
unsigned peturb = h;
while (dict->ht[idx].offset) {
peturb >>= 5;
idx = (5 * idx + 1 + peturb) & dict->sizemask;
}
dict->ht[idx].offset = prevht[i].offset;
dict->ht[idx].hash = h;
}
}
Jim_Free(prevht);
}
static int JimDictAdd(Jim_Dict *dict, Jim_Obj *keyObjPtr)
{
if (dict->size <= dict->len + dict->dummy) {
JimDictExpandHashTable(dict, dict->size ? dict->size * 2 : 8);
}
return JimDictHashFind(dict, keyObjPtr, DICT_HASH_ADD);
}
static Jim_Dict *JimDictNew(Jim_Interp *interp, int table_size, int ht_size)
{
Jim_Dict *dict = Jim_Alloc(sizeof(*dict));
memset(dict, 0, sizeof(*dict));
if (ht_size) {
JimDictExpandHashTable(dict, ht_size);
}
if (table_size) {
dict->table = Jim_Alloc(table_size * sizeof(*dict->table));
dict->maxLen = table_size;
}
#ifdef JIM_RANDOMISE_HASH
dict->uniq = (rand() ^ time(NULL) ^ clock());
#endif
return dict;
}
static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
JimFreeDict(interp, objPtr->internalRep.dictValue);
}
static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
Jim_Dict *oldDict = srcPtr->internalRep.dictValue;
int i;
Jim_Dict *newDict = JimDictNew(interp, oldDict->maxLen, oldDict->size);
for (i = 0; i < oldDict->len; i++) {
newDict->table[i] = oldDict->table[i];
Jim_IncrRefCount(newDict->table[i]);
}
newDict->len = oldDict->len;
newDict->uniq = oldDict->uniq;
memcpy(newDict->ht, oldDict->ht, sizeof(*oldDict->ht) * oldDict->size);
dupPtr->internalRep.dictValue = newDict;
dupPtr->typePtr = &dictObjType;
}
static void UpdateStringOfDict(struct Jim_Obj *objPtr)
{
JimMakeListStringRep(objPtr, objPtr->internalRep.dictValue->table, objPtr->internalRep.dictValue->len);
}
static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
int listlen;
if (objPtr->typePtr == &dictObjType) {
return JIM_OK;
}
if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
Jim_String(objPtr);
}
listlen = Jim_ListLength(interp, objPtr);
if (listlen % 2) {
Jim_SetResultString(interp, "missing value to go with key", -1);
return JIM_ERR;
}
else {
Jim_Dict *dict = JimDictNew(interp, 0, listlen);
int i;
dict->table = objPtr->internalRep.listValue.ele;
dict->maxLen = objPtr->internalRep.listValue.maxLen;
for (i = 0; i < listlen; i += 2) {
int tvoffset = JimDictAdd(dict, dict->table[i]);
if (tvoffset) {
Jim_DecrRefCount(interp, dict->table[tvoffset]);
dict->table[tvoffset] = dict->table[i + 1];
Jim_DecrRefCount(interp, dict->table[i]);
}
else {
if (dict->len != i) {
dict->table[dict->len++] = dict->table[i];
dict->table[dict->len++] = dict->table[i + 1];
}
else {
dict->len += 2;
}
}
}
objPtr->typePtr = &dictObjType;
objPtr->internalRep.dictValue = dict;
return JIM_OK;
}
}
static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
{
Jim_Dict *dict = objPtr->internalRep.dictValue;
if (valueObjPtr == NULL) {
int tvoffset = JimDictHashFind(dict, keyObjPtr, DICT_HASH_REMOVE);
if (tvoffset) {
Jim_DecrRefCount(interp, dict->table[tvoffset - 1]);
Jim_DecrRefCount(interp, dict->table[tvoffset]);
dict->len -= 2;
if (tvoffset != dict->len + 1) {
dict->table[tvoffset - 1] = dict->table[dict->len];
dict->table[tvoffset] = dict->table[dict->len + 1];
JimDictHashFind(dict, dict->table[tvoffset - 1], tvoffset);
}
return JIM_OK;
}
return JIM_ERR;
}
else {
int tvoffset = JimDictAdd(dict, keyObjPtr);
if (tvoffset) {
Jim_IncrRefCount(valueObjPtr);
Jim_DecrRefCount(interp, dict->table[tvoffset]);
dict->table[tvoffset] = valueObjPtr;
}
else {
if (dict->maxLen == dict->len) {
if (dict->maxLen < 4) {
dict->maxLen = 4;
}
else {
dict->maxLen *= 2;
}
dict->table = Jim_Realloc(dict->table, dict->maxLen * sizeof(*dict->table));
}
Jim_IncrRefCount(keyObjPtr);
Jim_IncrRefCount(valueObjPtr);
dict->table[dict->len++] = keyObjPtr;
dict->table[dict->len++] = valueObjPtr;
}
return JIM_OK;
}
}
int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
{
JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
return JIM_ERR;
}
Jim_InvalidateStringRep(objPtr);
return DictAddElement(interp, objPtr, keyObjPtr, valueObjPtr);
}
Jim_Obj *Jim_NewDictObj(Jim_Interp *interp, Jim_Obj *const *elements, int len)
{
Jim_Obj *objPtr;
int i;
JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
objPtr = Jim_NewObj(interp);
objPtr->typePtr = &dictObjType;
objPtr->bytes = NULL;
objPtr->internalRep.dictValue = JimDictNew(interp, len, len);
for (i = 0; i < len; i += 2)
DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
return objPtr;
}
int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
Jim_Obj **objPtrPtr, int flags)
{
int tvoffset;
Jim_Dict *dict;
if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
return -1;
}
dict = dictPtr->internalRep.dictValue;
tvoffset = JimDictHashFind(dict, keyPtr, DICT_HASH_FIND);
if (tvoffset == 0) {
if (flags & JIM_ERRMSG) {
Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
}
return JIM_ERR;
}
*objPtrPtr = dict->table[tvoffset];
return JIM_OK;
}
Jim_Obj **Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, int *len)
{
if (Jim_IsList(dictPtr)) {
Jim_Obj **table;
JimListGetElements(interp, dictPtr, len, &table);
if (*len % 2 == 0) {
return table;
}
}
if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
*len = 1;
return NULL;
}
*len = dictPtr->internalRep.dictValue->len;
return dictPtr->internalRep.dictValue->table;
}
int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
{
int i;
if (keyc == 0) {
*objPtrPtr = dictPtr;
return JIM_OK;
}
for (i = 0; i < keyc; i++) {
Jim_Obj *objPtr;
int rc = Jim_DictKey(interp, dictPtr, keyv[i], &objPtr, flags);
if (rc != JIM_OK) {
return rc;
}
dictPtr = objPtr;
}
*objPtrPtr = dictPtr;
return JIM_OK;
}
int Jim_SetDictKeysVector(Jim_Interp *interp, Jim_Obj *varNamePtr,
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, flags);
if (objPtr == NULL) {
if (newObjPtr == NULL && (flags & JIM_MUSTEXIST)) {
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;
}
}
if ((shared = Jim_IsShared(objPtr)))
varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
for (i = 0; i < keyc; i++) {
dictObjPtr = objPtr;
if (SetDictFromAny(interp, dictObjPtr) != JIM_OK) {
goto err;
}
if (i == keyc - 1) {
if (Jim_DictAddElement(interp, objPtr, keyv[keyc - 1], newObjPtr) != JIM_OK) {
if (newObjPtr || (flags & JIM_MUSTEXIST)) {
goto err;
}
}
break;
}
Jim_InvalidateStringRep(dictObjPtr);
if (Jim_DictKey(interp, dictObjPtr, keyv[i], &objPtr,
newObjPtr ? JIM_NONE : JIM_ERRMSG) == JIM_OK) {
if (Jim_IsShared(objPtr)) {
objPtr = Jim_DuplicateObj(interp, objPtr);
DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
}
}
else {
if (newObjPtr == NULL) {
goto err;
}
objPtr = Jim_NewDictObj(interp, NULL, 0);
DictAddElement(interp, dictObjPtr, keyv[i], objPtr);
}
}
Jim_InvalidateStringRep(objPtr);
Jim_InvalidateStringRep(varObjPtr);
if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
goto err;
}
if (!(flags & JIM_NORESULT)) {
Jim_SetResult(interp, varObjPtr);
}
return JIM_OK;
err:
if (shared) {
Jim_FreeNewObj(interp, varObjPtr);
}
return JIM_ERR;
}
static void UpdateStringOfIndex(struct Jim_Obj *objPtr);
static int SetIndexFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
static const Jim_ObjType indexObjType = {
"index",
NULL,
NULL,
UpdateStringOfIndex,
JIM_TYPE_NONE,
};
static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
{
if (objPtr->internalRep.intValue == -1) {
JimSetStringBytes(objPtr, "end");
}
else {
char buf[JIM_INTEGER_SPACE + 1];
if (objPtr->internalRep.intValue >= 0 || objPtr->internalRep.intValue == -INT_MAX) {
sprintf(buf, "%d", objPtr->internalRep.intValue);
}
else {
sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
}
JimSetStringBytes(objPtr, buf);
}
}
static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
jim_wide idx;
int end = 0;
const char *str;
Jim_Obj *exprObj = objPtr;
JimPanic((objPtr->refCount == 0, "SetIndexFromAny() called with zero refcount object"));
str = Jim_String(objPtr);
if (strncmp(str, "end", 3) == 0) {
end = 1;
str += 3;
idx = 0;
switch (*str) {
case '\0':
exprObj = NULL;
break;
case '-':
case '+':
exprObj = Jim_NewStringObj(interp, str, -1);
break;
default:
goto badindex;
}
}
if (exprObj) {
int ret;
Jim_IncrRefCount(exprObj);
ret = Jim_GetWideExpr(interp, exprObj, &idx);
Jim_DecrRefCount(interp, exprObj);
if (ret != JIM_OK) {
goto badindex;
}
}
if (end) {
if (idx > 0) {
idx = INT_MAX;
}
else {
idx--;
}
}
else if (idx < 0) {
idx = -INT_MAX;
}
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &indexObjType;
objPtr->internalRep.intValue = idx;
return JIM_OK;
badindex:
Jim_SetResultFormatted(interp,
"bad index \"%#s\": must be intexpr or end?[+-]intexpr?", objPtr);
return JIM_ERR;
}
int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
{
if (objPtr->typePtr == &intObjType) {
jim_wide val = JimWideValue(objPtr);
if (val < 0)
*indexPtr = -INT_MAX;
else if (val > INT_MAX)
*indexPtr = INT_MAX;
else
*indexPtr = (int)val;
return JIM_OK;
}
if (objPtr->typePtr != &indexObjType && SetIndexFromAny(interp, objPtr) == JIM_ERR)
return JIM_ERR;
*indexPtr = objPtr->internalRep.intValue;
return JIM_OK;
}
static const char * const jimReturnCodes[] = {
"ok",
"error",
"return",
"break",
"continue",
"signal",
"exit",
"eval",
NULL
};
#define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes) - 1)
static const Jim_ObjType returnCodeObjType = {
"return-code",
NULL,
NULL,
NULL,
JIM_TYPE_NONE,
};
const char *Jim_ReturnCode(int code)
{
if (code < 0 || code >= (int)jimReturnCodesSize) {
return "?";
}
else {
return jimReturnCodes[code];
}
}
static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
int returnCode;
jim_wide wideValue;
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;
}
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &returnCodeObjType;
objPtr->internalRep.intValue = returnCode;
return JIM_OK;
}
int Jim_GetReturnCode(Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr)
{
if (objPtr->typePtr != &returnCodeObjType && SetReturnCodeFromAny(interp, objPtr) == JIM_ERR)
return JIM_ERR;
*intPtr = objPtr->internalRep.intValue;
return JIM_OK;
}
static int JimParseExprOperator(struct JimParserCtx *pc);
static int JimParseExprNumber(struct JimParserCtx *pc);
static int JimParseExprIrrational(struct JimParserCtx *pc);
static int JimParseExprBoolean(struct JimParserCtx *pc);
enum
{
JIM_EXPROP_MUL = JIM_TT_EXPR_OP,
JIM_EXPROP_DIV,
JIM_EXPROP_MOD,
JIM_EXPROP_SUB,
JIM_EXPROP_ADD,
JIM_EXPROP_LSHIFT,
JIM_EXPROP_RSHIFT,
JIM_EXPROP_ROTL,
JIM_EXPROP_ROTR,
JIM_EXPROP_LT,
JIM_EXPROP_GT,
JIM_EXPROP_LTE,
JIM_EXPROP_GTE,
JIM_EXPROP_NUMEQ,
JIM_EXPROP_NUMNE,
JIM_EXPROP_BITAND,
JIM_EXPROP_BITXOR,
JIM_EXPROP_BITOR,
JIM_EXPROP_LOGICAND,
JIM_EXPROP_LOGICOR,
JIM_EXPROP_TERNARY,
JIM_EXPROP_COLON,
JIM_EXPROP_POW,
JIM_EXPROP_STREQ,
JIM_EXPROP_STRNE,
JIM_EXPROP_STRIN,
JIM_EXPROP_STRNI,
JIM_EXPROP_STRLT,
JIM_EXPROP_STRGT,
JIM_EXPROP_STRLE,
JIM_EXPROP_STRGE,
JIM_EXPROP_NOT,
JIM_EXPROP_BITNOT,
JIM_EXPROP_UNARYMINUS,
JIM_EXPROP_UNARYPLUS,
JIM_EXPROP_FUNC_INT,
JIM_EXPROP_FUNC_WIDE,
JIM_EXPROP_FUNC_ABS,
JIM_EXPROP_FUNC_DOUBLE,
JIM_EXPROP_FUNC_ROUND,
JIM_EXPROP_FUNC_RAND,
JIM_EXPROP_FUNC_SRAND,
JIM_EXPROP_FUNC_SIN,
JIM_EXPROP_FUNC_COS,
JIM_EXPROP_FUNC_TAN,
JIM_EXPROP_FUNC_ASIN,
JIM_EXPROP_FUNC_ACOS,
JIM_EXPROP_FUNC_ATAN,
JIM_EXPROP_FUNC_ATAN2,
JIM_EXPROP_FUNC_SINH,
JIM_EXPROP_FUNC_COSH,
JIM_EXPROP_FUNC_TANH,
JIM_EXPROP_FUNC_CEIL,
JIM_EXPROP_FUNC_FLOOR,
JIM_EXPROP_FUNC_EXP,
JIM_EXPROP_FUNC_LOG,
JIM_EXPROP_FUNC_LOG10,
JIM_EXPROP_FUNC_SQRT,
JIM_EXPROP_FUNC_POW,
JIM_EXPROP_FUNC_HYPOT,
JIM_EXPROP_FUNC_FMOD,
};
struct JimExprNode {
int type;
struct Jim_Obj *objPtr;
struct JimExprNode *left;
struct JimExprNode *right;
struct JimExprNode *ternary;
};
typedef struct Jim_ExprOperator
{
const char *name;
int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode);
unsigned char precedence;
unsigned char arity;
unsigned char attr;
unsigned char namelen;
} Jim_ExprOperator;
static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr);
static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);
static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
{
int intresult = 1;
int rc, bA = 0;
double dA, dC = 0;
jim_wide wA, wC = 0;
Jim_Obj *A;
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
return rc;
}
if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
switch (node->type) {
case JIM_EXPROP_FUNC_INT:
case JIM_EXPROP_FUNC_WIDE:
case JIM_EXPROP_FUNC_ROUND:
case JIM_EXPROP_UNARYPLUS:
wC = wA;
break;
case JIM_EXPROP_FUNC_DOUBLE:
dC = wA;
intresult = 0;
break;
case JIM_EXPROP_FUNC_ABS:
wC = wA >= 0 ? wA : -wA;
break;
case JIM_EXPROP_UNARYMINUS:
wC = -wA;
break;
case JIM_EXPROP_NOT:
wC = !wA;
break;
default:
abort();
}
}
else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
switch (node->type) {
case JIM_EXPROP_FUNC_INT:
case JIM_EXPROP_FUNC_WIDE:
wC = dA;
break;
case JIM_EXPROP_FUNC_ROUND:
wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
break;
case JIM_EXPROP_FUNC_DOUBLE:
case JIM_EXPROP_UNARYPLUS:
dC = dA;
intresult = 0;
break;
case JIM_EXPROP_FUNC_ABS:
#ifdef JIM_MATH_FUNCTIONS
dC = fabs(dA);
#else
dC = dA >= 0 ? dA : -dA;
#endif
intresult = 0;
break;
case JIM_EXPROP_UNARYMINUS:
dC = -dA;
intresult = 0;
break;
case JIM_EXPROP_NOT:
wC = !dA;
break;
default:
abort();
}
}
else if ((rc = Jim_GetBoolean(interp, A, &bA)) == JIM_OK) {
switch (node->type) {
case JIM_EXPROP_NOT:
wC = !bA;
break;
default:
abort();
}
}
if (rc == JIM_OK) {
if (intresult) {
Jim_SetResultInt(interp, wC);
}
else {
Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
}
}
Jim_DecrRefCount(interp, A);
return rc;
}
static double JimRandDouble(Jim_Interp *interp)
{
unsigned long x;
JimRandomBytes(interp, &x, sizeof(x));
return (double)x / (double)~0UL;
}
static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node)
{
jim_wide wA;
Jim_Obj *A;
int rc;
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
return rc;
}
rc = Jim_GetWide(interp, A, &wA);
if (rc == JIM_OK) {
switch (node->type) {
case JIM_EXPROP_BITNOT:
Jim_SetResultInt(interp, ~wA);
break;
case JIM_EXPROP_FUNC_SRAND:
JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
break;
default:
abort();
}
}
Jim_DecrRefCount(interp, A);
return rc;
}
static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node)
{
JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));
Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
return JIM_OK;
}
#ifdef JIM_MATH_FUNCTIONS
static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node)
{
int rc;
double dA, dC;
Jim_Obj *A;
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
return rc;
}
rc = Jim_GetDouble(interp, A, &dA);
if (rc == JIM_OK) {
switch (node->type) {
case JIM_EXPROP_FUNC_SIN:
dC = sin(dA);
break;
case JIM_EXPROP_FUNC_COS:
dC = cos(dA);
break;
case JIM_EXPROP_FUNC_TAN:
dC = tan(dA);
break;
case JIM_EXPROP_FUNC_ASIN:
dC = asin(dA);
break;
case JIM_EXPROP_FUNC_ACOS:
dC = acos(dA);
break;
case JIM_EXPROP_FUNC_ATAN:
dC = atan(dA);
break;
case JIM_EXPROP_FUNC_SINH:
dC = sinh(dA);
break;
case JIM_EXPROP_FUNC_COSH:
dC = cosh(dA);
break;
case JIM_EXPROP_FUNC_TANH:
dC = tanh(dA);
break;
case JIM_EXPROP_FUNC_CEIL:
dC = ceil(dA);
break;
case JIM_EXPROP_FUNC_FLOOR:
dC = floor(dA);
break;
case JIM_EXPROP_FUNC_EXP:
dC = exp(dA);
break;
case JIM_EXPROP_FUNC_LOG:
dC = log(dA);
break;
case JIM_EXPROP_FUNC_LOG10:
dC = log10(dA);
break;
case JIM_EXPROP_FUNC_SQRT:
dC = sqrt(dA);
break;
default:
abort();
}
Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
}
Jim_DecrRefCount(interp, A);
return rc;
}
#endif
static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node)
{
jim_wide wA, wB;
int rc;
Jim_Obj *A, *B;
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
return rc;
}
if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
Jim_DecrRefCount(interp, A);
return rc;
}
rc = JIM_ERR;
if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
jim_wide wC;
rc = JIM_OK;
switch (node->type) {
case JIM_EXPROP_LSHIFT:
wC = wA << wB;
break;
case JIM_EXPROP_RSHIFT:
wC = wA >> wB;
break;
case JIM_EXPROP_BITAND:
wC = wA & wB;
break;
case JIM_EXPROP_BITXOR:
wC = wA ^ wB;
break;
case JIM_EXPROP_BITOR:
wC = wA | wB;
break;
case JIM_EXPROP_MOD:
if (wB == 0) {
wC = 0;
Jim_SetResultString(interp, "Division by zero", -1);
rc = JIM_ERR;
}
else {
int negative = 0;
if (wB < 0) {
wB = -wB;
wA = -wA;
negative = 1;
}
wC = wA % wB;
if (wC < 0) {
wC += wB;
}
if (negative) {
wC = -wC;
}
}
break;
case JIM_EXPROP_ROTL:
case JIM_EXPROP_ROTR:{
unsigned long uA = (unsigned long)wA;
unsigned long uB = (unsigned long)wB;
const unsigned int S = sizeof(unsigned long) * 8;
uB %= S;
if (node->type == JIM_EXPROP_ROTR) {
uB = S - uB;
}
wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
break;
}
default:
abort();
}
Jim_SetResultInt(interp, wC);
}
Jim_DecrRefCount(interp, A);
Jim_DecrRefCount(interp, B);
return rc;
}
static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node)
{
int rc = JIM_OK;
double dA, dB, dC = 0;
jim_wide wA, wB, wC = 0;
Jim_Obj *A, *B;
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
return rc;
}
if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
Jim_DecrRefCount(interp, A);
return rc;
}
if ((A->typePtr != &doubleObjType || A->bytes) &&
(B->typePtr != &doubleObjType || B->bytes) &&
JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {
switch (node->type) {
case JIM_EXPROP_POW:
case JIM_EXPROP_FUNC_POW:
if (wA == 0 && wB < 0) {
Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
rc = JIM_ERR;
goto done;
}
wC = JimPowWide(wA, wB);
goto intresult;
case JIM_EXPROP_ADD:
wC = wA + wB;
goto intresult;
case JIM_EXPROP_SUB:
wC = wA - wB;
goto intresult;
case JIM_EXPROP_MUL:
wC = wA * wB;
goto intresult;
case JIM_EXPROP_DIV:
if (wB == 0) {
Jim_SetResultString(interp, "Division by zero", -1);
rc = JIM_ERR;
goto done;
}
else {
if (wB < 0) {
wB = -wB;
wA = -wA;
}
wC = wA / wB;
if (wA % wB < 0) {
wC--;
}
goto intresult;
}
case JIM_EXPROP_LT:
wC = wA < wB;
goto intresult;
case JIM_EXPROP_GT:
wC = wA > wB;
goto intresult;
case JIM_EXPROP_LTE:
wC = wA <= wB;
goto intresult;
case JIM_EXPROP_GTE:
wC = wA >= wB;
goto intresult;
case JIM_EXPROP_NUMEQ:
wC = wA == wB;
goto intresult;
case JIM_EXPROP_NUMNE:
wC = wA != wB;
goto intresult;
}
}
if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
switch (node->type) {
#ifndef JIM_MATH_FUNCTIONS
case JIM_EXPROP_POW:
case JIM_EXPROP_FUNC_POW:
case JIM_EXPROP_FUNC_ATAN2:
case JIM_EXPROP_FUNC_HYPOT:
case JIM_EXPROP_FUNC_FMOD:
Jim_SetResultString(interp, "unsupported", -1);
rc = JIM_ERR;
goto done;
#else
case JIM_EXPROP_POW:
case JIM_EXPROP_FUNC_POW:
dC = pow(dA, dB);
goto doubleresult;
case JIM_EXPROP_FUNC_ATAN2:
dC = atan2(dA, dB);
goto doubleresult;
case JIM_EXPROP_FUNC_HYPOT:
dC = hypot(dA, dB);
goto doubleresult;
case JIM_EXPROP_FUNC_FMOD:
dC = fmod(dA, dB);
goto doubleresult;
#endif
case JIM_EXPROP_ADD:
dC = dA + dB;
goto doubleresult;
case JIM_EXPROP_SUB:
dC = dA - dB;
goto doubleresult;
case JIM_EXPROP_MUL:
dC = dA * dB;
goto doubleresult;
case JIM_EXPROP_DIV:
if (dB == 0) {
#ifdef INFINITY
dC = dA < 0 ? -INFINITY : INFINITY;
#else
dC = (dA < 0 ? -1.0 : 1.0) * strtod("Inf", NULL);
#endif
}
else {
dC = dA / dB;
}
goto doubleresult;
case JIM_EXPROP_LT:
wC = dA < dB;
goto intresult;
case JIM_EXPROP_GT:
wC = dA > dB;
goto intresult;
case JIM_EXPROP_LTE:
wC = dA <= dB;
goto intresult;
case JIM_EXPROP_GTE:
wC = dA >= dB;
goto intresult;
case JIM_EXPROP_NUMEQ:
wC = dA == dB;
goto intresult;
case JIM_EXPROP_NUMNE:
wC = dA != dB;
goto intresult;
}
}
else {
int i = Jim_StringCompareObj(interp, A, B, 0);
switch (node->type) {
case JIM_EXPROP_LT:
wC = i < 0;
goto intresult;
case JIM_EXPROP_GT:
wC = i > 0;
goto intresult;
case JIM_EXPROP_LTE:
wC = i <= 0;
goto intresult;
case JIM_EXPROP_GTE:
wC = i >= 0;
goto intresult;
case JIM_EXPROP_NUMEQ:
wC = i == 0;
goto intresult;
case JIM_EXPROP_NUMNE:
wC = i != 0;
goto intresult;
}
}
rc = JIM_ERR;
done:
Jim_DecrRefCount(interp, A);
Jim_DecrRefCount(interp, B);
return rc;
intresult:
Jim_SetResultInt(interp, wC);
goto done;
doubleresult:
Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
goto done;
}
static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
{
int listlen;
int i;
listlen = Jim_ListLength(interp, listObjPtr);
for (i = 0; i < listlen; i++) {
if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
return 1;
}
}
return 0;
}
static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
{
Jim_Obj *A, *B;
jim_wide wC;
int comp, rc;
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
return rc;
}
if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
Jim_DecrRefCount(interp, A);
return rc;
}
switch (node->type) {
case JIM_EXPROP_STREQ:
case JIM_EXPROP_STRNE:
wC = Jim_StringEqObj(A, B);
if (node->type == JIM_EXPROP_STRNE) {
wC = !wC;
}
break;
case JIM_EXPROP_STRLT:
case JIM_EXPROP_STRGT:
case JIM_EXPROP_STRLE:
case JIM_EXPROP_STRGE:
comp = Jim_StringCompareObj(interp, A, B, 0);
if (node->type == JIM_EXPROP_STRLT) {
wC = comp == -1;
} else if (node->type == JIM_EXPROP_STRGT) {
wC = comp == 1;
} else if (node->type == JIM_EXPROP_STRLE) {
wC = comp == -1 || comp == 0;
} else {
wC = comp == 0 || comp == 1;
}
break;
case JIM_EXPROP_STRIN:
wC = JimSearchList(interp, B, A);
break;
case JIM_EXPROP_STRNI:
wC = !JimSearchList(interp, B, A);
break;
default:
abort();
}
Jim_SetResultInt(interp, wC);
Jim_DecrRefCount(interp, A);
Jim_DecrRefCount(interp, B);
return rc;
}
static int ExprBool(Jim_Interp *interp, Jim_Obj *obj)
{
long l;
double d;
int b;
int ret = -1;
Jim_IncrRefCount(obj);
if (Jim_GetLong(interp, obj, &l) == JIM_OK) {
ret = (l != 0);
}
else if (Jim_GetDouble(interp, obj, &d) == JIM_OK) {
ret = (d != 0);
}
else if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
ret = (b != 0);
}
Jim_DecrRefCount(interp, obj);
return ret;
}
static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node)
{
int result = JimExprGetTermBoolean(interp, node->left);
if (result == 1) {
result = JimExprGetTermBoolean(interp, node->right);
}
if (result == -1) {
return JIM_ERR;
}
Jim_SetResultInt(interp, result);
return JIM_OK;
}
static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node)
{
int result = JimExprGetTermBoolean(interp, node->left);
if (result == 0) {
result = JimExprGetTermBoolean(interp, node->right);
}
if (result == -1) {
return JIM_ERR;
}
Jim_SetResultInt(interp, result);
return JIM_OK;
}
static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node)
{
int result = JimExprGetTermBoolean(interp, node->left);
if (result == 1) {
return JimExprEvalTermNode(interp, node->right);
}
else if (result == 0) {
return JimExprEvalTermNode(interp, node->ternary);
}
return JIM_ERR;
}
enum
{
OP_FUNC = 0x0001,
OP_RIGHT_ASSOC = 0x0002,
};
#define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
#define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0)
static const struct Jim_ExprOperator Jim_ExprOperators[] = {
OPRINIT("*", 110, 2, JimExprOpBin),
OPRINIT("/", 110, 2, JimExprOpBin),
OPRINIT("%", 110, 2, JimExprOpIntBin),
OPRINIT("-", 100, 2, JimExprOpBin),
OPRINIT("+", 100, 2, JimExprOpBin),
OPRINIT("<<", 90, 2, JimExprOpIntBin),
OPRINIT(">>", 90, 2, JimExprOpIntBin),
OPRINIT("<<<", 90, 2, JimExprOpIntBin),
OPRINIT(">>>", 90, 2, JimExprOpIntBin),
OPRINIT("<", 80, 2, JimExprOpBin),
OPRINIT(">", 80, 2, JimExprOpBin),
OPRINIT("<=", 80, 2, JimExprOpBin),
OPRINIT(">=", 80, 2, JimExprOpBin),
OPRINIT("==", 70, 2, JimExprOpBin),
OPRINIT("!=", 70, 2, JimExprOpBin),
OPRINIT("&", 50, 2, JimExprOpIntBin),
OPRINIT("^", 49, 2, JimExprOpIntBin),
OPRINIT("|", 48, 2, JimExprOpIntBin),
OPRINIT("&&", 10, 2, JimExprOpAnd),
OPRINIT("||", 9, 2, JimExprOpOr),
OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC),
OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC),
OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),
OPRINIT("eq", 60, 2, JimExprOpStrBin),
OPRINIT("ne", 60, 2, JimExprOpStrBin),
OPRINIT("in", 55, 2, JimExprOpStrBin),
OPRINIT("ni", 55, 2, JimExprOpStrBin),
OPRINIT("lt", 75, 2, JimExprOpStrBin),
OPRINIT("gt", 75, 2, JimExprOpStrBin),
OPRINIT("le", 75, 2, JimExprOpStrBin),
OPRINIT("ge", 75, 2, JimExprOpStrBin),
OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC),
OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC),
OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC),
OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC),
OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC),
OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC),
OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC),
#ifdef JIM_MATH_FUNCTIONS
OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC),
OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC),
OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC),
OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC),
#endif
};
#undef OPRINIT
#undef OPRINIT_ATTR
#define JIM_EXPR_OPERATORS_NUM \
(sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
static int JimParseExpression(struct JimParserCtx *pc)
{
pc->errmsg = NULL;
while (1) {
while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
if (*pc->p == '\n') {
pc->linenr++;
}
pc->p++;
pc->len--;
}
if (*pc->p == '#') {
JimParseComment(pc);
continue;
}
break;
}
pc->tline = pc->linenr;
pc->tstart = pc->p;
if (pc->len == 0) {
pc->tend = pc->p;
pc->tt = JIM_TT_EOL;
pc->eof = 1;
return JIM_OK;
}
switch (*(pc->p)) {
case '(':
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->tend = pc->p;
pc->p++;
pc->len--;
break;
case '[':
return JimParseCmd(pc);
case '$':
if (JimParseVar(pc) == JIM_ERR)
return JimParseExprOperator(pc);
else {
if (pc->tt == JIM_TT_EXPRSUGAR) {
pc->errmsg = "nesting expr in expr is not allowed";
return JIM_ERR;
}
return JIM_OK;
}
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.':
return JimParseExprNumber(pc);
case '"':
return JimParseQuote(pc);
case '{':
return JimParseBrace(pc);
case 'N':
case 'I':
case 'n':
case 'i':
if (JimParseExprIrrational(pc) == JIM_ERR)
if (JimParseExprBoolean(pc) == JIM_ERR)
return JimParseExprOperator(pc);
break;
case 't':
case 'f':
case 'o':
case 'y':
if (JimParseExprBoolean(pc) == JIM_ERR)
return JimParseExprOperator(pc);
break;
default:
return JimParseExprOperator(pc);
break;
}
return JIM_OK;
}
static int JimParseExprNumber(struct JimParserCtx *pc)
{
char *end;
pc->tt = JIM_TT_EXPR_INT;
jim_strtoull(pc->p, (char **)&pc->p);
if (strchr("eENnIi.", *pc->p) || pc->p == pc->tstart) {
if (strtod(pc->tstart, &end)) { }
if (end == pc->tstart)
return JIM_ERR;
if (end > pc->p) {
pc->tt = JIM_TT_EXPR_DOUBLE;
pc->p = end;
}
}
pc->tend = pc->p - 1;
pc->len -= (pc->p - pc->tstart);
return JIM_OK;
}
static int JimParseExprIrrational(struct JimParserCtx *pc)
{
const char *irrationals[] = { "NaN", "nan", "NAN", "Inf", "inf", "INF", NULL };
int i;
for (i = 0; irrationals[i]; i++) {
const char *irr = irrationals[i];
if (strncmp(irr, pc->p, 3) == 0) {
pc->p += 3;
pc->len -= 3;
pc->tend = pc->p - 1;
pc->tt = JIM_TT_EXPR_DOUBLE;
return JIM_OK;
}
}
return JIM_ERR;
}
static int JimParseExprBoolean(struct JimParserCtx *pc)
{
int i;
for (i = 0; i < sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings); i++) {
if (strncmp(pc->p, jim_true_false_strings[i], jim_true_false_lens[i]) == 0) {
pc->p += jim_true_false_lens[i];
pc->len -= jim_true_false_lens[i];
pc->tend = pc->p - 1;
pc->tt = JIM_TT_EXPR_BOOLEAN;
return JIM_OK;
}
}
return JIM_ERR;
}
static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
{
static Jim_ExprOperator dummy_op;
if (opcode < JIM_TT_EXPR_OP) {
return &dummy_op;
}
return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
}
static int JimParseExprOperator(struct JimParserCtx *pc)
{
int i;
const struct Jim_ExprOperator *bestOp = NULL;
int bestLen = 0;
for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
const struct Jim_ExprOperator *op = &Jim_ExprOperators[i];
if (op->name[0] != pc->p[0]) {
continue;
}
if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) {
bestOp = op;
bestLen = op->namelen;
}
}
if (bestOp == NULL) {
return JIM_ERR;
}
if (bestOp->attr & OP_FUNC) {
const char *p = pc->p + bestLen;
int len = pc->len - bestLen;
while (len && isspace(UCHAR(*p))) {
len--;
p++;
}
if (*p != '(') {
pc->errmsg = "function requires parentheses";
return JIM_ERR;
}
}
pc->tend = pc->p + bestLen - 1;
pc->p += bestLen;
pc->len -= bestLen;
pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
return JIM_OK;
}
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 = {
"expression",
FreeExprInternalRep,
DupExprInternalRep,
NULL,
JIM_TYPE_NONE,
};
struct ExprTree
{
struct JimExprNode *expr;
struct JimExprNode *nodes;
int len;
int inUse;
};
static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num)
{
int i;
for (i = 0; i < num; i++) {
if (nodes[i].objPtr) {
Jim_DecrRefCount(interp, nodes[i].objPtr);
}
}
Jim_Free(nodes);
}
static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr)
{
ExprTreeFreeNodes(interp, expr->nodes, expr->len);
Jim_Free(expr);
}
static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
struct ExprTree *expr = (void *)objPtr->internalRep.ptr;
if (expr) {
if (--expr->inUse != 0) {
return;
}
ExprTreeFree(interp, expr);
}
}
static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
JIM_NOTUSED(interp);
JIM_NOTUSED(srcPtr);
dupPtr->typePtr = NULL;
}
struct ExprBuilder {
int parencount;
int level;
ParseToken *token;
ParseToken *first_token;
Jim_Stack stack;
Jim_Obj *exprObjPtr;
Jim_Obj *fileNameObj;
struct JimExprNode *nodes;
struct JimExprNode *next;
};
#ifdef DEBUG_SHOW_EXPR
static void JimShowExprNode(struct JimExprNode *node, int level)
{
int i;
for (i = 0; i < level; i++) {
printf(" ");
}
if (TOKEN_IS_EXPR_OP(node->type)) {
printf("%s\n", jim_tt_name(node->type));
if (node->left) {
JimShowExprNode(node->left, level + 1);
}
if (node->right) {
JimShowExprNode(node->right, level + 1);
}
if (node->ternary) {
JimShowExprNode(node->ternary, level + 1);
}
}
else {
printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr));
}
}
#endif
#define EXPR_UNTIL_CLOSE 0x0001
#define EXPR_FUNC_ARGS 0x0002
#define EXPR_TERNARY 0x0004
static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms) {
int rc;
struct JimExprNode *node;
int exp_stacklen = builder->stack.len + exp_numterms;
if (builder->level++ > 200) {
Jim_SetResultString(interp, "Expression too complex", -1);
return JIM_ERR;
}
while (builder->token->type != JIM_TT_EOL) {
ParseToken *t = builder->token++;
int prevtt;
if (t == builder->first_token) {
prevtt = JIM_TT_NONE;
}
else {
prevtt = t[-1].type;
}
if (t->type == JIM_TT_SUBEXPR_START) {
if (builder->stack.len == exp_stacklen) {
Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr);
return JIM_ERR;
}
builder->parencount++;
rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1);
if (rc != JIM_OK) {
return rc;
}
}
else if (t->type == JIM_TT_SUBEXPR_END) {
if (!(flags & EXPR_UNTIL_CLOSE)) {
if (builder->stack.len == exp_stacklen && builder->level > 1) {
builder->token--;
builder->level--;
return JIM_OK;
}
Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr);
return JIM_ERR;
}
builder->parencount--;
if (builder->stack.len == exp_stacklen) {
break;
}
}
else if (t->type == JIM_TT_SUBEXPR_COMMA) {
if (!(flags & EXPR_FUNC_ARGS)) {
if (builder->stack.len == exp_stacklen) {
builder->token--;
builder->level--;
return JIM_OK;
}
Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr);
return JIM_ERR;
}
else {
if (builder->stack.len > exp_stacklen) {
Jim_SetResultFormatted(interp, "too many arguments to math function");
return JIM_ERR;
}
}
}
else if (t->type == JIM_EXPROP_COLON) {
if (!(flags & EXPR_TERNARY)) {
if (builder->level != 1) {
builder->token--;
builder->level--;
return JIM_OK;
}
Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr);
return JIM_ERR;
}
if (builder->stack.len == exp_stacklen) {
builder->token--;
builder->level--;
return JIM_OK;
}
}
else if (TOKEN_IS_EXPR_OP(t->type)) {
const struct Jim_ExprOperator *op;
if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) {
if (t->type == JIM_EXPROP_SUB) {
t->type = JIM_EXPROP_UNARYMINUS;
}
else if (t->type == JIM_EXPROP_ADD) {
t->type = JIM_EXPROP_UNARYPLUS;
}
}
op = JimExprOperatorInfoByOpcode(t->type);
if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) {
builder->token--;
break;
}
if (op->attr & OP_FUNC) {
if (builder->token->type != JIM_TT_SUBEXPR_START) {
Jim_SetResultString(interp, "missing arguments for math function", -1);
return JIM_ERR;
}
builder->token++;
if (op->arity == 0) {
if (builder->token->type != JIM_TT_SUBEXPR_END) {
Jim_SetResultString(interp, "too many arguments for math function", -1);
return JIM_ERR;
}
builder->token++;
goto noargs;
}
builder->parencount++;
rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity);
}
else if (t->type == JIM_EXPROP_TERNARY) {
rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2);
}
else {
rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1);
}
if (rc != JIM_OK) {
return rc;
}
noargs:
node = builder->next++;
node->type = t->type;
if (op->arity >= 3) {
node->ternary = Jim_StackPop(&builder->stack);
if (node->ternary == NULL) {
goto missingoperand;
}
}
if (op->arity >= 2) {
node->right = Jim_StackPop(&builder->stack);
if (node->right == NULL) {
goto missingoperand;
}
}
if (op->arity >= 1) {
node->left = Jim_StackPop(&builder->stack);
if (node->left == NULL) {
missingoperand:
Jim_SetResultFormatted(interp, "missing operand to %s in expression: \"%#s\"", op->name, builder->exprObjPtr);
builder->next--;
return JIM_ERR;
}
}
Jim_StackPush(&builder->stack, node);
}
else {
Jim_Obj *objPtr = NULL;
if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr);
return JIM_ERR;
}
if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
char *endptr;
if (t->type == JIM_TT_EXPR_INT) {
objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
}
else {
objPtr = Jim_NewDoubleObj(interp, strtod(t->token, &endptr));
}
if (endptr != t->token + t->len) {
Jim_FreeNewObj(interp, objPtr);
objPtr = NULL;
}
}
if (!objPtr) {
objPtr = Jim_NewStringObj(interp, t->token, t->len);
if (t->type == JIM_TT_CMD) {
Jim_SetSourceInfo(interp, objPtr, builder->fileNameObj, t->line);
}
}
node = builder->next++;
node->objPtr = objPtr;
Jim_IncrRefCount(node->objPtr);
node->type = t->type;
Jim_StackPush(&builder->stack, node);
}
}
if (builder->stack.len == exp_stacklen) {
builder->level--;
return JIM_OK;
}
if ((flags & EXPR_FUNC_ARGS)) {
Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many");
}
else {
if (builder->stack.len < exp_stacklen) {
if (builder->level == 0) {
Jim_SetResultFormatted(interp, "empty expression");
}
else {
Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr);
}
}
else {
Jim_SetResultFormatted(interp, "extra terms after expression");
}
}
return JIM_ERR;
}
static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
{
struct ExprTree *expr;
struct ExprBuilder builder;
int rc;
struct JimExprNode *top = NULL;
builder.parencount = 0;
builder.level = 0;
builder.token = builder.first_token = tokenlist->list;
builder.exprObjPtr = exprObjPtr;
builder.fileNameObj = fileNameObj;
builder.nodes = Jim_Alloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
builder.next = builder.nodes;
Jim_InitStack(&builder.stack);
rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);
if (rc == JIM_OK) {
top = Jim_StackPop(&builder.stack);
if (builder.parencount) {
Jim_SetResultString(interp, "missing close parenthesis", -1);
rc = JIM_ERR;
}
}
Jim_FreeStack(&builder.stack);
if (rc != JIM_OK) {
ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes);
return NULL;
}
expr = Jim_Alloc(sizeof(*expr));
expr->inUse = 1;
expr->expr = top;
expr->nodes = builder.nodes;
expr->len = builder.next - builder.nodes;
assert(expr->len <= tokenlist->count - 1);
return expr;
}
static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
int exprTextLen;
const char *exprText;
struct JimParserCtx parser;
struct ExprTree *expr;
ParseTokenList tokenlist;
int line;
Jim_Obj *fileNameObj;
int rc = JIM_ERR;
fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
Jim_IncrRefCount(fileNameObj);
exprText = Jim_GetString(objPtr, &exprTextLen);
ScriptTokenListInit(&tokenlist);
JimParserInit(&parser, exprText, exprTextLen, line);
while (!parser.eof) {
if (JimParseExpression(&parser) != JIM_OK) {
ScriptTokenListFree(&tokenlist);
Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
if (parser.errmsg) {
Jim_AppendStrings(interp, Jim_GetResult(interp), ": ", parser.errmsg, NULL);
}
expr = NULL;
goto err;
}
ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
parser.tline);
}
#ifdef DEBUG_SHOW_EXPR_TOKENS
{
int i;
printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
for (i = 0; i < tokenlist.count; i++) {
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
if (tokenlist.count <= 1) {
Jim_SetResultString(interp, "empty expression", -1);
rc = JIM_ERR;
}
else {
rc = JimParseCheckMissing(interp, parser.missing.ch);
}
if (rc != JIM_OK) {
ScriptTokenListFree(&tokenlist);
Jim_DecrRefCount(interp, fileNameObj);
return rc;
}
expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj);
ScriptTokenListFree(&tokenlist);
if (!expr) {
goto err;
}
#ifdef DEBUG_SHOW_EXPR
printf("==== Expr ====\n");
JimShowExprNode(expr->expr, 0);
#endif
rc = JIM_OK;
err:
Jim_DecrRefCount(interp, fileNameObj);
Jim_FreeIntRep(interp, objPtr);
Jim_SetIntRepPtr(objPtr, expr);
objPtr->typePtr = &exprObjType;
return rc;
}
static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
{
if (objPtr->typePtr != &exprObjType) {
if (SetExprFromAny(interp, objPtr) != JIM_OK) {
return NULL;
}
}
return (struct ExprTree *) Jim_GetIntRepPtr(objPtr);
}
#ifdef JIM_OPTIMIZATION
static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node)
{
if (node->type == JIM_TT_EXPR_INT)
return node->objPtr;
else if (node->type == JIM_TT_VAR)
return Jim_GetVariable(interp, node->objPtr, JIM_NONE);
else if (node->type == JIM_TT_DICTSUGAR)
return JimExpandDictSugar(interp, node->objPtr);
else
return NULL;
}
#endif
static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node)
{
if (TOKEN_IS_EXPR_OP(node->type)) {
const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type);
return op->funcop(interp, node);
}
else {
Jim_Obj *objPtr;
switch (node->type) {
case JIM_TT_EXPR_INT:
case JIM_TT_EXPR_DOUBLE:
case JIM_TT_EXPR_BOOLEAN:
case JIM_TT_STR:
Jim_SetResult(interp, node->objPtr);
return JIM_OK;
case JIM_TT_VAR:
objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG);
if (objPtr) {
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
return JIM_ERR;
case JIM_TT_DICTSUGAR:
objPtr = JimExpandDictSugar(interp, node->objPtr);
if (objPtr) {
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
return JIM_ERR;
case JIM_TT_ESC:
if (interp->safeexpr) {
return JIM_ERR;
}
if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
return JIM_ERR;
case JIM_TT_CMD:
if (interp->safeexpr) {
return JIM_ERR;
}
return Jim_EvalObj(interp, node->objPtr);
default:
return JIM_ERR;
}
}
}
static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr)
{
int rc = JimExprEvalTermNode(interp, node);
if (rc == JIM_OK) {
*objPtrPtr = Jim_GetResult(interp);
Jim_IncrRefCount(*objPtrPtr);
}
return rc;
}
static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node)
{
if (JimExprEvalTermNode(interp, node) == JIM_OK) {
return ExprBool(interp, Jim_GetResult(interp));
}
return -1;
}
int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
{
struct ExprTree *expr;
int retcode = JIM_OK;
Jim_IncrRefCount(exprObjPtr);
expr = JimGetExpression(interp, exprObjPtr);
if (!expr) {
retcode = JIM_ERR;
goto done;
}
#ifdef JIM_OPTIMIZATION
if (!interp->safeexpr) {
Jim_Obj *objPtr;
switch (expr->len) {
case 1:
objPtr = JimExprIntValOrVar(interp, expr->expr);
if (objPtr) {
Jim_SetResult(interp, objPtr);
goto done;
}
break;
case 2:
if (expr->expr->type == JIM_EXPROP_NOT) {
objPtr = JimExprIntValOrVar(interp, expr->expr->left);
if (objPtr && JimIsWide(objPtr)) {
Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj);
goto done;
}
}
break;
case 3:
objPtr = JimExprIntValOrVar(interp, expr->expr->left);
if (objPtr && JimIsWide(objPtr)) {
Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right);
if (objPtr2 && JimIsWide(objPtr2)) {
jim_wide wideValueA = JimWideValue(objPtr);
jim_wide wideValueB = JimWideValue(objPtr2);
int cmpRes;
switch (expr->expr->type) {
case JIM_EXPROP_LT:
cmpRes = wideValueA < wideValueB;
break;
case JIM_EXPROP_LTE:
cmpRes = wideValueA <= wideValueB;
break;
case JIM_EXPROP_GT:
cmpRes = wideValueA > wideValueB;
break;
case JIM_EXPROP_GTE:
cmpRes = wideValueA >= wideValueB;
break;
case JIM_EXPROP_NUMEQ:
cmpRes = wideValueA == wideValueB;
break;
case JIM_EXPROP_NUMNE:
cmpRes = wideValueA != wideValueB;
break;
default:
goto noopt;
}
Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);
goto done;
}
}
break;
}
}
noopt:
#endif
expr->inUse++;
retcode = JimExprEvalTermNode(interp, expr->expr);
Jim_FreeIntRep(interp, exprObjPtr);
exprObjPtr->typePtr = &exprObjType;
Jim_SetIntRepPtr(exprObjPtr, expr);
done:
Jim_DecrRefCount(interp, exprObjPtr);
return retcode;
}
int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
{
int retcode = Jim_EvalExpression(interp, exprObjPtr);
if (retcode == JIM_OK) {
switch (ExprBool(interp, Jim_GetResult(interp))) {
case 0:
*boolPtr = 0;
break;
case 1:
*boolPtr = 1;
break;
case -1:
retcode = JIM_ERR;
break;
}
}
return retcode;
}
typedef struct ScanFmtPartDescr
{
const char *arg;
const char *prefix;
size_t width;
int pos;
char type;
char modifier;
} ScanFmtPartDescr;
typedef struct ScanFmtStringObj
{
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);
static void UpdateStringOfScanFmt(Jim_Obj *objPtr);
static const Jim_ObjType scanFmtStringObjType = {
"scanformatstring",
FreeScanFmtInternalRep,
DupScanFmtInternalRep,
UpdateStringOfScanFmt,
JIM_TYPE_NONE,
};
void FreeScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
JIM_NOTUSED(interp);
Jim_Free((char *)objPtr->internalRep.ptr);
objPtr->internalRep.ptr = 0;
}
void DupScanFmtInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
size_t size = (size_t) ((ScanFmtStringObj *) srcPtr->internalRep.ptr)->size;
ScanFmtStringObj *newVec = (ScanFmtStringObj *) Jim_Alloc(size);
JIM_NOTUSED(interp);
memcpy(newVec, srcPtr->internalRep.ptr, size);
dupPtr->internalRep.ptr = newVec;
dupPtr->typePtr = &scanFmtStringObjType;
}
static void UpdateStringOfScanFmt(Jim_Obj *objPtr)
{
JimSetStringBytes(objPtr, ((ScanFmtStringObj *) objPtr->internalRep.ptr)->stringRep);
}
static int SetScanFmtFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
ScanFmtStringObj *fmtObj;
char *buffer;
int maxCount, i, approxSize, lastPos = -1;
const char *fmt = Jim_String(objPtr);
int maxFmtLen = Jim_Length(objPtr);
const char *fmtEnd = fmt + maxFmtLen;
int curr;
Jim_FreeIntRep(interp, objPtr);
for (i = 0, maxCount = 0; i < maxFmtLen; ++i)
if (fmt[i] == '%')
++maxCount;
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];
fmtObj->stringRep = fmtObj->scratch + maxFmtLen + 3 + 1;
memcpy(fmtObj->stringRep, fmt, maxFmtLen);
buffer = fmtObj->stringRep + maxFmtLen + 1;
objPtr->internalRep.ptr = fmtObj;
objPtr->typePtr = &scanFmtStringObjType;
for (i = 0, curr = 0; fmt < fmtEnd; ++fmt) {
int width = 0, skip;
ScanFmtPartDescr *descr = &fmtObj->descr[curr];
fmtObj->count++;
descr->width = 0;
if (*fmt != '%' || fmt[1] == '%') {
descr->type = 0;
descr->prefix = &buffer[i];
for (; fmt < fmtEnd; ++fmt) {
if (*fmt == '%') {
if (fmt[1] != '%')
break;
++fmt;
}
buffer[i++] = *fmt;
}
buffer[i++] = 0;
}
++fmt;
if (fmt >= fmtEnd)
goto done;
descr->pos = 0;
if (*fmt == '*') {
descr->pos = -1;
++fmt;
}
else
fmtObj->convCount++;
if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
fmt += skip;
if (descr->pos != -1 && *fmt == '$') {
int prev;
++fmt;
descr->pos = width;
width = 0;
if ((lastPos == 0 && descr->pos > 0)
|| (lastPos > 0 && descr->pos == 0)) {
fmtObj->error = "cannot mix \"%\" and \"%n$\" conversion specifiers";
return JIM_ERR;
}
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;
}
}
if (descr->pos < 0) {
fmtObj->error =
"\"%n$\" conversion specifier is negative";
return JIM_ERR;
}
if (sscanf(fmt, "%d%n", &width, &skip) == 1) {
descr->width = width;
fmt += skip;
}
if (descr->pos > 0 && (size_t) descr->pos > fmtObj->maxPos)
fmtObj->maxPos = descr->pos;
}
else {
descr->width = width;
}
}
if (lastPos == -1)
lastPos = descr->pos;
if (*fmt == '[') {
int swapped = 1, beg = i, end, j;
descr->type = '[';
descr->arg = &buffer[i];
++fmt;
if (*fmt == '^')
buffer[i++] = *fmt++;
if (*fmt == ']')
buffer[i++] = *fmt++;
while (*fmt && *fmt != ']')
buffer[i++] = *fmt++;
if (*fmt != ']') {
fmtObj->error = "unmatched [ in format string";
return JIM_ERR;
}
end = i;
buffer[i++] = 0;
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];
buffer[j - 1] = buffer[j + 1];
buffer[j + 1] = tmp;
swapped = 1;
}
}
}
}
else {
if (fmt < fmtEnd && strchr("hlL", *fmt))
descr->modifier = tolower((int)*fmt++);
if (fmt >= fmtEnd) {
fmtObj->error = "missing scan conversion character";
return JIM_ERR;
}
descr->type = *fmt;
if (strchr("efgcsndoxui", *fmt) == 0) {
fmtObj->error = "bad scan conversion character";
return JIM_ERR;
}
else if (*fmt == 'c' && descr->width != 0) {
fmtObj->error = "field width may not be specified in %c " "conversion";
return JIM_ERR;
}
else if (*fmt == 'u' && descr->modifier == 'l') {
fmtObj->error = "unsigned wide not supported";
return JIM_ERR;
}
}
curr++;
}
done:
return JIM_OK;
}
#define FormatGetCnvCount(_fo_) \
((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->convCount
#define FormatGetMaxPos(_fo_) \
((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->maxPos
#define FormatGetError(_fo_) \
((ScanFmtStringObj*)((_fo_)->internalRep.ptr))->error
static Jim_Obj *JimScanAString(Jim_Interp *interp, const char *sdescr, const char *str)
{
char *buffer = Jim_StrDup(str);
char *p = buffer;
while (*str) {
int c;
int n;
if (!sdescr && isspace(UCHAR(*str)))
break;
n = utf8_tounicode(str, &c);
if (sdescr && !JimCharsetMatch(sdescr, strlen(sdescr), c, JIM_CHARSET_SCAN))
break;
while (n--)
*p++ = *str++;
}
*p = 0;
return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
}
static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int str_bytelen,
ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
{
const char *tok;
const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
size_t scanned = 0;
size_t anchor = pos;
int i;
Jim_Obj *tmpObj = NULL;
*valObjPtr = 0;
if (descr->prefix) {
for (i = 0; pos < str_bytelen && descr->prefix[i]; ++i) {
if (isspace(UCHAR(descr->prefix[i])))
while (pos < str_bytelen && isspace(UCHAR(str[pos])))
++pos;
else if (descr->prefix[i] != str[pos])
break;
else
++pos;
}
if (pos >= str_bytelen) {
return -1;
}
else if (descr->prefix[i] != 0)
return 0;
}
if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
while (isspace(UCHAR(str[pos])))
++pos;
scanned = pos - anchor;
if (descr->type == 'n') {
*valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
}
else if (pos >= str_bytelen) {
return -1;
}
else if (descr->type == 'c') {
int c;
scanned += utf8_tounicode(&str[pos], &c);
*valObjPtr = Jim_NewIntObj(interp, c);
return scanned;
}
else {
if (descr->width > 0) {
size_t sLen = utf8_strlen(&str[pos], str_bytelen - pos);
size_t tLen = descr->width > sLen ? sLen : descr->width;
tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
tok = tmpObj->bytes;
}
else {
tok = &str[pos];
}
switch (descr->type) {
case 'd':
case 'o':
case 'x':
case 'u':
case 'i':{
char *endp;
jim_wide w;
int base = descr->type == 'o' ? 8
: descr->type == 'x' ? 16 : descr->type == 'i' ? 0 : 10;
if (base == 0) {
w = jim_strtoull(tok, &endp);
}
else {
w = strtoull(tok, &endp, base);
}
if (endp != tok) {
*valObjPtr = Jim_NewIntObj(interp, w);
scanned += endp - tok;
}
else {
scanned = *tok ? 0 : -1;
}
break;
}
case 's':
case '[':{
*valObjPtr = JimScanAString(interp, descr->arg, tok);
scanned += Jim_Length(*valObjPtr);
break;
}
case 'e':
case 'f':
case 'g':{
char *endp;
double value = strtod(tok, &endp);
if (endp != tok) {
*valObjPtr = Jim_NewDoubleObj(interp, value);
scanned += endp - tok;
}
else {
scanned = *tok ? 0 : -1;
}
break;
}
}
if (tmpObj) {
Jim_FreeNewObj(interp, tmpObj);
}
}
return scanned;
}
Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
{
size_t i, pos;
int scanned = 1;
const char *str = Jim_String(strObjPtr);
int str_bytelen = Jim_Length(strObjPtr);
Jim_Obj *resultList = 0;
Jim_Obj **resultVec = 0;
int resultc;
Jim_Obj *emptyStr = 0;
ScanFmtStringObj *fmtObj;
JimPanic((fmtObjPtr->typePtr != &scanFmtStringObjType, "Jim_ScanString() for non-scan format"));
fmtObj = (ScanFmtStringObj *) fmtObjPtr->internalRep.ptr;
if (fmtObj->error != 0) {
if (flags & JIM_ERRMSG)
Jim_SetResultString(interp, fmtObj->error, -1);
return 0;
}
emptyStr = Jim_NewEmptyStringObj(interp);
Jim_IncrRefCount(emptyStr);
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);
}
for (i = 0, pos = 0; i < fmtObj->count; ++i) {
ScanFmtPartDescr *descr = &(fmtObj->descr[i]);
Jim_Obj *value = 0;
if (descr->type == 0)
continue;
if (scanned > 0)
scanned = ScanOneEntry(interp, str, pos, str_bytelen, fmtObj, i, &value);
if (scanned == -1 && i == 0)
goto eof;
pos += scanned;
if (value == 0)
value = Jim_NewEmptyStringObj(interp);
if (descr->pos == -1) {
Jim_FreeNewObj(interp, value);
}
else if (descr->pos == 0)
Jim_ListAppendElement(interp, resultList, value);
else if (resultVec[descr->pos - 1] == emptyStr) {
Jim_DecrRefCount(interp, resultVec[descr->pos - 1]);
Jim_IncrRefCount(value);
resultVec[descr->pos - 1] = value;
}
else {
Jim_FreeNewObj(interp, value);
goto err;
}
}
Jim_DecrRefCount(interp, emptyStr);
return resultList;
eof:
Jim_DecrRefCount(interp, emptyStr);
Jim_FreeNewObj(interp, resultList);
return (Jim_Obj *)EOF;
err:
Jim_DecrRefCount(interp, emptyStr);
Jim_FreeNewObj(interp, resultList);
return 0;
}
static void JimPrngInit(Jim_Interp *interp)
{
#define PRNG_SEED_SIZE 256
int i;
unsigned int *seed;
time_t t = time(NULL);
interp->prngState = Jim_Alloc(sizeof(Jim_PrngState));
seed = Jim_Alloc(PRNG_SEED_SIZE * sizeof(*seed));
for (i = 0; i < PRNG_SEED_SIZE; i++) {
seed[i] = (rand() ^ t ^ clock());
}
JimPrngSeed(interp, (unsigned char *)seed, PRNG_SEED_SIZE * sizeof(*seed));
Jim_Free(seed);
}
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;
if (interp->prngState == NULL)
JimPrngInit(interp);
prng = interp->prngState;
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];
prng->sbox[prng->i] = sj;
prng->sbox[prng->j] = si;
*destByte++ = prng->sbox[(si + sj) & 0xff];
}
}
static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen)
{
int i;
Jim_PrngState *prng;
if (interp->prngState == NULL)
JimPrngInit(interp);
prng = interp->prngState;
for (i = 0; i < 256; i++)
prng->sbox[i] = i;
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;
for (i = 0; i < 256; i += seedLen) {
JimRandomBytes(interp, seed, seedLen);
}
}
static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_wide wideValue, increment = 1;
Jim_Obj *intObjPtr;
if (argc != 2 && argc != 3) {
Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
return JIM_ERR;
}
if (argc == 3) {
if (Jim_GetWideExpr(interp, argv[2], &increment) != JIM_OK)
return JIM_ERR;
}
intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
if (!intObjPtr) {
wideValue = 0;
}
else if (Jim_GetWide(interp, intObjPtr, &wideValue) != JIM_OK) {
return JIM_ERR;
}
if (!intObjPtr || Jim_IsShared(intObjPtr)) {
intObjPtr = Jim_NewIntObj(interp, wideValue + increment);
if (Jim_SetVariable(interp, argv[1], intObjPtr) != JIM_OK) {
Jim_FreeNewObj(interp, intObjPtr);
return JIM_ERR;
}
}
else {
Jim_InvalidateStringRep(intObjPtr);
JimWideValue(intObjPtr) = wideValue + increment;
if (argv[1]->typePtr != &variableObjType) {
Jim_SetVariable(interp, argv[1], intObjPtr);
}
}
Jim_SetResult(interp, intObjPtr);
return JIM_OK;
}
#define JIM_EVAL_SARGV_LEN 8
#define JIM_EVAL_SINTV_LEN 8
static int JimTraceCallback(Jim_Interp *interp, const char *type, int argc, Jim_Obj *const *argv)
{
JimPanic((interp->traceCmdObj == NULL, "xtrace invoked with no object"));
int ret;
Jim_Obj *nargv[7];
Jim_Obj *traceCmdObj = interp->traceCmdObj;
Jim_Obj *resultObj = Jim_GetResult(interp);
ScriptObj *script = NULL;
if (interp->evalFrame->scriptObj) {
script = JimGetScript(interp, interp->evalFrame->scriptObj);
}
nargv[0] = traceCmdObj;
nargv[1] = Jim_NewStringObj(interp, type, -1);
nargv[2] = script ? script->fileNameObj : interp->emptyObj;
nargv[3] = Jim_NewIntObj(interp, script ? script->linenr : 1);
nargv[4] = resultObj;
nargv[5] = argv[0];
nargv[6] = Jim_NewListObj(interp, argv + 1, argc - 1);
interp->traceCmdObj = NULL;
Jim_IncrRefCount(resultObj);
ret = Jim_EvalObjVector(interp, 7, nargv);
Jim_DecrRefCount(interp, resultObj);
if (ret == JIM_OK || ret == JIM_RETURN) {
interp->traceCmdObj = traceCmdObj;
Jim_SetEmptyResult(interp);
ret = JIM_OK;
}
else {
Jim_DecrRefCount(interp, traceCmdObj);
}
return ret;
}
static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retcode;
if (interp->unknown_called > 50) {
return JIM_ERR;
}
if (Jim_GetCommand(interp, interp->unknown, JIM_NONE) == NULL)
return JIM_ERR;
interp->unknown_called++;
retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
interp->unknown_called--;
return retcode;
}
static void JimPushEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *scriptObj)
{
memset(frame, 0, sizeof(*frame));
frame->parent = interp->evalFrame;
frame->level = frame->parent->level + 1;
frame->procLevel = interp->procLevel;
frame->framePtr = interp->framePtr;
if (scriptObj) {
frame->scriptObj = scriptObj;
}
else {
frame->scriptObj = frame->parent->scriptObj;
}
interp->evalFrame = frame;
#if 0
if (frame->scriptObj) {
printf("script: %.*s\n", 20, Jim_String(frame->scriptObj));
}
#endif
}
static void JimPopEvalFrame(Jim_Interp *interp)
{
interp->evalFrame = interp->evalFrame->parent;
}
static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
int retcode;
Jim_Cmd *cmdPtr;
void *prevPrivData;
Jim_Obj *tailcallObj = NULL;
#if 0
printf("invoke");
int j;
for (j = 0; j < objc; j++) {
printf(" '%s'", Jim_String(objv[j]));
}
printf("\n");
#endif
cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
if (cmdPtr == NULL) {
return JimUnknown(interp, objc, objv);
}
JimIncrCmdRefCount(cmdPtr);
if (interp->evalDepth == interp->maxEvalDepth) {
Jim_SetResultString(interp, "Infinite eval recursion", -1);
retcode = JIM_ERR;
goto out;
}
interp->evalDepth++;
prevPrivData = interp->cmdPrivData;
tailcall:
interp->evalFrame->argc = objc;
interp->evalFrame->argv = objv;
interp->evalFrame->cmd = cmdPtr;
if (!interp->traceCmdObj ||
(retcode = JimTraceCallback(interp, "cmd", objc, objv)) == JIM_OK) {
Jim_SetEmptyResult(interp);
if (cmdPtr->isproc) {
retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
}
else {
interp->cmdPrivData = cmdPtr->u.native.privData;
retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
}
if (retcode == JIM_ERR) {
JimSetErrorStack(interp, NULL);
}
}
if (tailcallObj) {
Jim_DecrRefCount(interp, tailcallObj);
tailcallObj = NULL;
}
interp->evalFrame->argc = 0;
interp->evalFrame->argv = NULL;
if (retcode == JIM_EVAL && interp->framePtr->tailcallObj) {
JimDecrCmdRefCount(interp, cmdPtr);
cmdPtr = interp->framePtr->tailcallCmd;
interp->framePtr->tailcallCmd = NULL;
tailcallObj = interp->framePtr->tailcallObj;
interp->framePtr->tailcallObj = NULL;
objc = tailcallObj->internalRep.listValue.len;
objv = tailcallObj->internalRep.listValue.ele;
goto tailcall;
}
interp->cmdPrivData = prevPrivData;
interp->evalDepth--;
out:
JimDecrCmdRefCount(interp, cmdPtr);
if (retcode == JIM_ERR) {
JimSetErrorStack(interp, NULL);
}
if (interp->framePtr->tailcallObj) {
JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
Jim_DecrRefCount(interp, interp->framePtr->tailcallObj);
interp->framePtr->tailcallCmd = NULL;
interp->framePtr->tailcallObj = NULL;
}
return retcode;
}
int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
int i, retcode;
Jim_EvalFrame frame;
for (i = 0; i < objc; i++)
Jim_IncrRefCount(objv[i]);
JimPushEvalFrame(interp, &frame, NULL);
retcode = JimInvokeCommand(interp, objc, objv);
JimPopEvalFrame(interp);
for (i = 0; i < objc; i++)
Jim_DecrRefCount(interp, objv[i]);
return retcode;
}
int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
{
int ret;
Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
nargv[0] = prefix;
memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
ret = Jim_EvalObjVector(interp, objc + 1, nargv);
Jim_Free(nargv);
return ret;
}
static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
{
Jim_Obj *objPtr;
int ret = JIM_ERR;
switch (token->type) {
case JIM_TT_STR:
case JIM_TT_ESC:
objPtr = token->objPtr;
break;
case JIM_TT_VAR:
objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
break;
case JIM_TT_DICTSUGAR:
objPtr = JimExpandDictSugar(interp, token->objPtr);
break;
case JIM_TT_EXPRSUGAR:
ret = Jim_EvalExpression(interp, token->objPtr);
if (ret == JIM_OK) {
objPtr = Jim_GetResult(interp);
}
else {
objPtr = NULL;
}
break;
case JIM_TT_CMD:
ret = Jim_EvalObj(interp, token->objPtr);
if (ret == JIM_OK || ret == JIM_RETURN) {
objPtr = interp->result;
} else {
objPtr = NULL;
}
break;
default:
JimPanic((1,
"default token type (%d) reached " "in Jim_SubstObj().", token->type));
objPtr = NULL;
break;
}
if (objPtr) {
*objPtrPtr = objPtr;
return JIM_OK;
}
return ret;
}
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];
Jim_Obj *objPtr;
char *s;
if (tokens <= JIM_EVAL_SINTV_LEN)
intv = sintv;
else
intv = Jim_Alloc(sizeof(Jim_Obj *) * tokens);
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) {
tokens = i;
continue;
}
case JIM_CONTINUE:
if (flags & JIM_SUBST_FLAG) {
intv[i] = NULL;
continue;
}
default:
while (i--) {
Jim_DecrRefCount(interp, intv[i]);
}
if (intv != sintv) {
Jim_Free(intv);
}
return NULL;
}
Jim_IncrRefCount(intv[i]);
Jim_String(intv[i]);
totlen += intv[i]->length;
}
if (tokens == 1 && intv[0] && intv == sintv) {
intv[0]->refCount--;
return intv[0];
}
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) {
objPtr->typePtr = &interpolatedObjType;
objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
Jim_IncrRefCount(intv[2]);
}
else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
int line;
Jim_Obj *fileNameObj = Jim_GetSourceInfo(interp, intv[0], &line);
Jim_SetSourceInfo(interp, objPtr, fileNameObj, line);
}
s = objPtr->bytes = Jim_Alloc(totlen + 1);
objPtr->length = totlen;
for (i = 0; i < tokens; i++) {
if (intv[i]) {
memcpy(s, intv[i]->bytes, intv[i]->length);
s += intv[i]->length;
Jim_DecrRefCount(interp, intv[i]);
}
}
objPtr->bytes[totlen] = '\0';
if (intv != sintv) {
Jim_Free(intv);
}
return objPtr;
}
static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
{
int retcode = JIM_OK;
Jim_EvalFrame frame;
JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
JimPushEvalFrame(interp, &frame, NULL);
if (listPtr->internalRep.listValue.len) {
Jim_IncrRefCount(listPtr);
retcode = JimInvokeCommand(interp,
listPtr->internalRep.listValue.len,
listPtr->internalRep.listValue.ele);
Jim_DecrRefCount(interp, listPtr);
}
JimPopEvalFrame(interp);
return retcode;
}
int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
{
SetListFromAny(interp, listPtr);
return JimEvalObjList(interp, listPtr);
}
int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
{
int i;
ScriptObj *script;
ScriptToken *token;
int retcode = JIM_OK;
Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
Jim_EvalFrame frame;
if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
return JimEvalObjList(interp, scriptObjPtr);
}
Jim_IncrRefCount(scriptObjPtr);
script = JimGetScript(interp, scriptObjPtr);
if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
JimSetErrorStack(interp, script);
Jim_DecrRefCount(interp, scriptObjPtr);
return JIM_ERR;
}
Jim_SetEmptyResult(interp);
token = script->token;
#ifdef JIM_OPTIMIZATION
if (script->len == 0) {
Jim_DecrRefCount(interp, scriptObjPtr);
return JIM_OK;
}
if (script->len == 3
&& token[1].objPtr->typePtr == &commandObjType
&& token[1].objPtr->internalRep.cmdValue.cmdPtr->isproc == 0
&& token[1].objPtr->internalRep.cmdValue.cmdPtr->u.native.cmdProc == Jim_IncrCoreCommand
&& token[2].objPtr->typePtr == &variableObjType) {
Jim_Obj *objPtr = Jim_GetVariable(interp, token[2].objPtr, JIM_NONE);
if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
JimWideValue(objPtr)++;
Jim_InvalidateStringRep(objPtr);
Jim_DecrRefCount(interp, scriptObjPtr);
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
}
#endif
script->inUse++;
JimPushEvalFrame(interp, &frame, scriptObjPtr);
interp->errorFlag = 0;
argv = sargv;
for (i = 0; i < script->len && retcode == JIM_OK; ) {
int argc;
int j;
argc = token[i].objPtr->internalRep.scriptLineValue.argc;
script->linenr = token[i].objPtr->internalRep.scriptLineValue.line;
if (argc > JIM_EVAL_SARGV_LEN)
argv = Jim_Alloc(sizeof(Jim_Obj *) * argc);
i++;
for (j = 0; j < argc; j++) {
long wordtokens = 1;
int expand = 0;
Jim_Obj *wordObjPtr = NULL;
if (token[i].type == JIM_TT_WORD) {
wordtokens = JimWideValue(token[i++].objPtr);
if (wordtokens < 0) {
expand = 1;
wordtokens = -wordtokens;
}
}
if (wordtokens == 1) {
switch (token[i].type) {
case JIM_TT_ESC:
case JIM_TT_STR:
wordObjPtr = token[i].objPtr;
break;
case JIM_TT_VAR:
wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
break;
case JIM_TT_EXPRSUGAR:
retcode = Jim_EvalExpression(interp, token[i].objPtr);
if (retcode == JIM_OK) {
wordObjPtr = Jim_GetResult(interp);
}
else {
wordObjPtr = NULL;
}
break;
case JIM_TT_DICTSUGAR:
wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
break;
case JIM_TT_CMD:
retcode = Jim_EvalObj(interp, token[i].objPtr);
if (retcode == JIM_OK) {
wordObjPtr = Jim_GetResult(interp);
}
break;
default:
JimPanic((1, "default token type reached " "in Jim_EvalObj()."));
}
}
else {
wordObjPtr = JimInterpolateTokens(interp, token + i, wordtokens, JIM_NONE);
}
if (!wordObjPtr) {
if (retcode == JIM_OK) {
retcode = JIM_ERR;
}
break;
}
Jim_IncrRefCount(wordObjPtr);
i += wordtokens;
if (!expand) {
argv[j] = wordObjPtr;
}
else {
int len = Jim_ListLength(interp, wordObjPtr);
int newargc = argc + len - 1;
int k;
if (len > 1) {
if (argv == sargv) {
if (newargc > JIM_EVAL_SARGV_LEN) {
argv = Jim_Alloc(sizeof(*argv) * newargc);
memcpy(argv, sargv, sizeof(*argv) * j);
}
}
else {
argv = Jim_Realloc(argv, sizeof(*argv) * newargc);
}
}
for (k = 0; k < len; k++) {
argv[j++] = wordObjPtr->internalRep.listValue.ele[k];
Jim_IncrRefCount(wordObjPtr->internalRep.listValue.ele[k]);
}
Jim_DecrRefCount(interp, wordObjPtr);
j--;
argc += len - 1;
}
}
if (retcode == JIM_OK && argc) {
retcode = JimInvokeCommand(interp, argc, argv);
if (Jim_CheckSignal(interp)) {
retcode = JIM_SIGNAL;
}
}
while (j-- > 0) {
Jim_DecrRefCount(interp, argv[j]);
}
if (argv != sargv) {
Jim_Free(argv);
argv = sargv;
}
}
if (retcode == JIM_ERR) {
JimSetErrorStack(interp, NULL);
}
JimPopEvalFrame(interp);
Jim_FreeIntRep(interp, scriptObjPtr);
scriptObjPtr->typePtr = &scriptObjType;
Jim_SetIntRepPtr(scriptObjPtr, script);
Jim_DecrRefCount(interp, scriptObjPtr);
return retcode;
}
static int JimSetProcArg(Jim_Interp *interp, Jim_Obj *argNameObj, Jim_Obj *argValObj)
{
int retcode;
const char *varname = Jim_String(argNameObj);
if (*varname == '&') {
Jim_Obj *objPtr;
Jim_CallFrame *savedCallFrame = interp->framePtr;
interp->framePtr = interp->framePtr->parent;
objPtr = Jim_GetVariable(interp, argValObj, JIM_ERRMSG);
interp->framePtr = savedCallFrame;
if (!objPtr) {
return JIM_ERR;
}
objPtr = Jim_NewStringObj(interp, varname + 1, -1);
Jim_IncrRefCount(objPtr);
retcode = Jim_SetVariableLink(interp, objPtr, argValObj, interp->framePtr->parent);
Jim_DecrRefCount(interp, objPtr);
}
else {
retcode = Jim_SetVariable(interp, argNameObj, argValObj);
}
return retcode;
}
static void JimSetProcWrongArgs(Jim_Interp *interp, Jim_Obj *procNameObj, Jim_Cmd *cmd)
{
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) {
Jim_AppendString(interp, argmsg, "?", 1);
Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
Jim_AppendString(interp, argmsg, " ...?", -1);
}
else {
Jim_AppendString(interp, argmsg, "?arg ...?", -1);
}
}
else {
if (cmd->u.proc.arglist[i].defaultObjPtr) {
Jim_AppendString(interp, argmsg, "?", 1);
Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
Jim_AppendString(interp, argmsg, "?", 1);
}
else {
const char *arg = Jim_String(cmd->u.proc.arglist[i].nameObjPtr);
if (*arg == '&') {
arg++;
}
Jim_AppendString(interp, argmsg, arg, -1);
}
}
}
Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s%#s\"", procNameObj, argmsg);
}
#ifdef jim_ext_namespace
int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
{
Jim_CallFrame *callFramePtr;
int retcode;
callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
callFramePtr->argv = interp->evalFrame->argv;
callFramePtr->argc = interp->evalFrame->argc;
callFramePtr->procArgsObjPtr = NULL;
callFramePtr->procBodyObjPtr = scriptObj;
callFramePtr->staticVars = NULL;
Jim_IncrRefCount(scriptObj);
interp->framePtr = callFramePtr;
if (interp->framePtr->level == interp->maxCallFrameDepth) {
Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
retcode = JIM_ERR;
}
else {
retcode = Jim_EvalObj(interp, scriptObj);
}
interp->framePtr = interp->framePtr->parent;
JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
return retcode;
}
#endif
static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
{
Jim_CallFrame *callFramePtr;
int i, d, retcode, optargs;
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;
}
if (Jim_Length(cmd->u.proc.bodyObjPtr) == 0) {
return JIM_OK;
}
if (interp->framePtr->level == interp->maxCallFrameDepth) {
Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
return JIM_ERR;
}
callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
callFramePtr->argv = argv;
callFramePtr->argc = argc;
callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
callFramePtr->staticVars = cmd->u.proc.staticVars;
interp->procLevel++;
Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
interp->framePtr = callFramePtr;
optargs = (argc - 1 - cmd->u.proc.reqArity);
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) {
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);
if (cmd->u.proc.arglist[d].defaultObjPtr) {
nameObjPtr =cmd->u.proc.arglist[d].defaultObjPtr;
}
retcode = Jim_SetVariable(interp, nameObjPtr, listObjPtr);
if (retcode != JIM_OK) {
goto badargset;
}
i += argsLen;
continue;
}
if (cmd->u.proc.arglist[d].defaultObjPtr == NULL || optargs-- > 0) {
retcode = JimSetProcArg(interp, nameObjPtr, argv[i++]);
}
else {
retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
}
if (retcode != JIM_OK) {
goto badargset;
}
}
if (interp->traceCmdObj == NULL ||
(retcode = JimTraceCallback(interp, "proc", argc, argv)) == JIM_OK) {
retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
}
badargset:
retcode = JimInvokeDefer(interp, retcode);
interp->framePtr = interp->framePtr->parent;
JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
if (retcode == JIM_RETURN) {
if (--interp->returnLevel <= 0) {
retcode = interp->returnCode;
interp->returnCode = JIM_OK;
interp->returnLevel = 0;
}
}
interp->procLevel--;
return retcode;
}
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_SetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
}
retval = Jim_EvalObj(interp, scriptObjPtr);
Jim_DecrRefCount(interp, scriptObjPtr);
return retval;
}
int Jim_Eval(Jim_Interp *interp, const char *script)
{
return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
}
int Jim_EvalGlobal(Jim_Interp *interp, const char *script)
{
int retval;
Jim_CallFrame *savedFramePtr = interp->framePtr;
interp->framePtr = interp->topFramePtr;
retval = Jim_Eval(interp, script);
interp->framePtr = savedFramePtr;
return retval;
}
int Jim_EvalFileGlobal(Jim_Interp *interp, const char *filename)
{
int retval;
Jim_CallFrame *savedFramePtr = interp->framePtr;
interp->framePtr = interp->topFramePtr;
retval = Jim_EvalFile(interp, filename);
interp->framePtr = savedFramePtr;
return retval;
}
#include <sys/stat.h>
static Jim_Obj *JimReadTextFile(Jim_Interp *interp, const char *filename)
{
jim_stat_t sb;
int fd;
char *buf;
int readlen;
if (Jim_Stat(filename, &sb) == -1 || (fd = open(filename, O_RDONLY | O_TEXT, 0666)) < 0) {
Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
return NULL;
}
buf = Jim_Alloc(sb.st_size + 1);
readlen = read(fd, buf, sb.st_size);
close(fd);
if (readlen < 0) {
Jim_Free(buf);
Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
return NULL;
}
else {
Jim_Obj *objPtr;
buf[readlen] = 0;
objPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
return objPtr;
}
}
int Jim_EvalFile(Jim_Interp *interp, const char *filename)
{
Jim_Obj *filenameObj;
Jim_Obj *oldFilenameObj;
Jim_Obj *scriptObjPtr;
int retcode;
scriptObjPtr = JimReadTextFile(interp, filename);
if (!scriptObjPtr) {
return JIM_ERR;
}
filenameObj = Jim_NewStringObj(interp, filename, -1);
Jim_SetSourceInfo(interp, scriptObjPtr, filenameObj, 1);
oldFilenameObj = JimPushInterpObj(interp->currentFilenameObj, filenameObj);
retcode = Jim_EvalObj(interp, scriptObjPtr);
JimPopInterpObj(interp, interp->currentFilenameObj, oldFilenameObj);
if (retcode == JIM_RETURN) {
if (--interp->returnLevel <= 0) {
retcode = interp->returnCode;
interp->returnCode = JIM_OK;
interp->returnLevel = 0;
}
}
return retcode;
}
static void JimParseSubst(struct JimParserCtx *pc, int flags)
{
pc->tstart = pc->p;
pc->tline = pc->linenr;
if (pc->len == 0) {
pc->tend = pc->p;
pc->tt = JIM_TT_EOL;
pc->eof = 1;
return;
}
if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
JimParseCmd(pc);
return;
}
if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
if (JimParseVar(pc) == JIM_OK) {
return;
}
pc->tstart = pc->p;
pc->p++;
pc->len--;
}
while (pc->len) {
if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
break;
}
if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
break;
}
if (*pc->p == '\\' && pc->len > 1) {
pc->p++;
pc->len--;
}
pc->p++;
pc->len--;
}
pc->tend = pc->p - 1;
pc->tt = (flags & JIM_SUBST_NOESC) ? JIM_TT_STR : JIM_TT_ESC;
}
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;
ScriptTokenListInit(&tokenlist);
JimParserInit(&parser, scriptText, scriptTextLen, 1);
while (1) {
JimParseSubst(&parser, flags);
if (parser.eof) {
break;
}
ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
parser.tline);
}
script->inUse = 1;
script->substFlags = flags;
script->fileNameObj = interp->emptyObj;
Jim_IncrRefCount(script->fileNameObj);
SubstObjAddTokens(interp, script, &tokenlist);
ScriptTokenListFree(&tokenlist);
#ifdef DEBUG_SHOW_SUBST
{
int i;
printf("==== Subst ====\n");
for (i = 0; i < script->len; i++) {
printf("[%2d] %s '%s'\n", i, jim_tt_name(script->token[i].type),
Jim_String(script->token[i].objPtr));
}
}
#endif
Jim_FreeIntRep(interp, objPtr);
Jim_SetIntRepPtr(objPtr, script);
objPtr->typePtr = &scriptObjType;
return JIM_OK;
}
static ScriptObj *Jim_GetSubst(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
SetSubstFromAny(interp, objPtr, flags);
return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
}
int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
{
ScriptObj *script;
JimPanic((substObjPtr->refCount == 0, "Jim_SubstObj() called with zero refcount object"));
script = Jim_GetSubst(interp, substObjPtr, flags);
Jim_IncrRefCount(substObjPtr);
script->inUse++;
*resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
script->inUse--;
Jim_DecrRefCount(interp, substObjPtr);
if (*resObjPtrPtr == NULL) {
return JIM_ERR;
}
return JIM_OK;
}
void Jim_WrongNumArgs(Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg)
{
Jim_Obj *objPtr;
Jim_Obj *listObjPtr;
JimPanic((argc == 0, "Jim_WrongNumArgs() called with argc=0"));
listObjPtr = Jim_NewListObj(interp, argv, argc);
if (msg && *msg) {
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, msg, -1));
}
Jim_IncrRefCount(listObjPtr);
objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
Jim_DecrRefCount(interp, listObjPtr);
Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
}
typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
Jim_Obj *keyObjPtr, void *value, Jim_Obj *patternObjPtr, int type);
#define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL)
static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
JimHashtableIteratorCallbackType *callback, int type)
{
Jim_HashEntry *he;
Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
he = Jim_FindHashEntry(ht, patternObjPtr);
if (he) {
callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
patternObjPtr, type);
}
}
else {
Jim_HashTableIterator htiter;
JimInitHashTableIterator(ht, &htiter);
while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
patternObjPtr, type);
}
}
return listObjPtr;
}
#define JIM_CMDLIST_COMMANDS 0
#define JIM_CMDLIST_PROCS 1
#define JIM_CMDLIST_CHANNELS 2
static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
{
Jim_Cmd *cmdPtr = (Jim_Cmd *)value;
if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
return;
}
Jim_IncrRefCount(keyObj);
if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, keyObj) >= 0) {
int match = 1;
if (patternObj) {
int plen, slen;
const char *pattern = Jim_GetStringNoQualifier(patternObj, &plen);
const char *str = Jim_GetStringNoQualifier(keyObj, &slen);
#ifdef JIM_NO_INTROSPECTION
match = (JimStringCompareUtf8(pattern, plen, str, slen, 0) == 0);
#else
match = JimGlobMatch(pattern, plen, str, slen, 0);
#endif
}
if (match) {
Jim_ListAppendElement(interp, listObjPtr, keyObj);
}
}
Jim_DecrRefCount(interp, keyObj);
}
static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
{
return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
}
#define JIM_VARLIST_GLOBALS 0
#define JIM_VARLIST_LOCALS 1
#define JIM_VARLIST_VARS 2
#define JIM_VARLIST_MASK 0x000f
#define JIM_VARLIST_VALUES 0x1000
static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
{
Jim_VarVal *vv = (Jim_VarVal *)value;
if ((type & JIM_VARLIST_MASK) != JIM_VARLIST_LOCALS || vv->linkFramePtr == NULL) {
if (patternObj == NULL || Jim_StringMatchObj(interp, patternObj, keyObj, 0)) {
Jim_ListAppendElement(interp, listObjPtr, keyObj);
if (type & JIM_VARLIST_VALUES) {
Jim_ListAppendElement(interp, listObjPtr, vv->objPtr);
}
}
}
}
static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
{
if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
return interp->emptyObj;
}
else {
Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch,
mode);
}
}
static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
{
long level;
if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
Jim_CallFrame *targetCallFrame = JimGetCallFrameByInteger(interp, level);
if (targetCallFrame && targetCallFrame != interp->topFramePtr) {
#ifdef JIM_NO_INTROSPECTION
*objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, 1);
#else
*objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
#endif
return JIM_OK;
}
}
Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
return JIM_ERR;
}
static int JimInfoFrame(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
{
long level;
if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, level);
if (frame) {
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "source", -1));
if (frame->scriptObj) {
ScriptObj *script = JimGetScript(interp, frame->scriptObj);
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "line", -1));
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, script->linenr));
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "file", -1));
Jim_ListAppendElement(interp, listObj, script->fileNameObj);
}
#ifndef JIM_NO_INTROSPECTION
{
Jim_Obj *cmdObj = Jim_NewListObj(interp, frame->argv, frame->argc);
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "cmd", -1));
Jim_ListAppendElement(interp, listObj, cmdObj);
}
#endif
{
Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
if (procNameObj) {
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "proc", -1));
Jim_ListAppendElement(interp, listObj, procNameObj);
}
}
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "level", -1));
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, interp->framePtr->level - frame->framePtr->level));
*objPtrPtr = listObj;
return JIM_OK;
}
}
Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
return JIM_ERR;
}
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;
}
if (argc == 3) {
if (!Jim_CompareStringImmediate(interp, argv[1], "-nonewline")) {
Jim_SetResultString(interp, "The second argument must " "be -nonewline", -1);
return JIM_ERR;
}
else {
fputs(Jim_String(argv[2]), stdout);
}
}
else {
puts(Jim_String(argv[1]));
}
return JIM_OK;
}
static int JimAddMulHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int op)
{
jim_wide wideValue, res;
double doubleValue, doubleRes;
int i;
res = (op == JIM_EXPROP_ADD) ? 0 : 1;
for (i = 1; i < argc; i++) {
if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK)
goto trydouble;
if (op == JIM_EXPROP_ADD)
res += wideValue;
else
res *= wideValue;
}
Jim_SetResultInt(interp, res);
return JIM_OK;
trydouble:
doubleRes = (double)res;
for (; i < argc; i++) {
if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
return JIM_ERR;
if (op == JIM_EXPROP_ADD)
doubleRes += doubleValue;
else
doubleRes *= doubleValue;
}
Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
return JIM_OK;
}
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;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "number ?number ... number?");
return JIM_ERR;
}
else if (argc == 2) {
if (Jim_GetWide(interp, argv[1], &wideValue) != JIM_OK) {
if (Jim_GetDouble(interp, argv[1], &doubleValue) != JIM_OK) {
return JIM_ERR;
}
else {
if (op == JIM_EXPROP_SUB)
doubleRes = -doubleValue;
else
doubleRes = 1.0 / doubleValue;
Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
return JIM_OK;
}
}
if (op == JIM_EXPROP_SUB) {
res = -wideValue;
Jim_SetResultInt(interp, res);
}
else {
doubleRes = 1.0 / wideValue;
Jim_SetResult(interp, Jim_NewDoubleObj(interp, doubleRes));
}
return JIM_OK;
}
else {
if (Jim_GetWide(interp, argv[1], &res) != JIM_OK) {
if (Jim_GetDouble(interp, argv[1], &doubleRes)
!= JIM_OK) {
return JIM_ERR;
}
else {
goto trydouble;
}
}
}
for (i = 2; i < argc; i++) {
if (Jim_GetWide(interp, argv[i], &wideValue) != JIM_OK) {
doubleRes = (double)res;
goto trydouble;
}
if (op == JIM_EXPROP_SUB)
res -= wideValue;
else {
if (wideValue == 0) {
Jim_SetResultString(interp, "Division by zero", -1);
return JIM_ERR;
}
res /= wideValue;
}
}
Jim_SetResultInt(interp, res);
return JIM_OK;
trydouble:
for (; i < argc; i++) {
if (Jim_GetDouble(interp, argv[i], &doubleValue) != JIM_OK)
return JIM_ERR;
if (op == JIM_EXPROP_SUB)
doubleRes -= doubleValue;
else
doubleRes /= doubleValue;
}
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);
}
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;
}
if (argc == 2) {
Jim_Obj *objPtr;
objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
if (!objPtr)
return JIM_ERR;
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
return JIM_ERR;
Jim_SetResult(interp, argv[2]);
return JIM_OK;
}
static int Jim_UnsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int i = 1;
int complain = 1;
while (i < argc) {
if (Jim_CompareStringImmediate(interp, argv[i], "--")) {
i++;
break;
}
if (Jim_CompareStringImmediate(interp, argv[i], "-nocomplain")) {
complain = 0;
i++;
continue;
}
break;
}
while (i < argc) {
if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
&& complain) {
return JIM_ERR;
}
i++;
}
Jim_SetEmptyResult(interp);
return JIM_OK;
}
static int JimCheckLoopRetcode(Jim_Interp *interp, int retval)
{
if (retval == JIM_BREAK || retval == JIM_CONTINUE) {
if (--interp->break_level > 0) {
return 1;
}
}
return 0;
}
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;
}
while (1) {
int boolean = 0, retval;
if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
return retval;
if (!boolean)
break;
if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
if (JimCheckLoopRetcode(interp, retval)) {
return retval;
}
switch (retval) {
case JIM_BREAK:
goto out;
case JIM_CONTINUE:
continue;
default:
return retval;
}
}
}
out:
Jim_SetEmptyResult(interp);
return JIM_OK;
}
static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retval;
int boolean = 1;
int immediate = 0;
Jim_Obj *varNamePtr = NULL;
Jim_Obj *stopVarNamePtr = NULL;
if (argc != 5) {
Jim_WrongNumArgs(interp, 1, argv, "start test next body");
return JIM_ERR;
}
if ((retval = Jim_EvalObj(interp, argv[1])) != JIM_OK) {
return retval;
}
retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
#ifdef JIM_OPTIMIZATION
if (retval == JIM_OK && boolean) {
ScriptObj *incrScript;
struct ExprTree *expr;
jim_wide stop, currentVal;
Jim_Obj *objPtr;
int cmpOffset;
expr = JimGetExpression(interp, argv[2]);
incrScript = JimGetScript(interp, argv[3]);
if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
goto evalstart;
}
if (incrScript->token[1].type != JIM_TT_ESC) {
goto evalstart;
}
if (expr->expr->type == JIM_EXPROP_LT) {
cmpOffset = 0;
}
else if (expr->expr->type == JIM_EXPROP_LTE) {
cmpOffset = 1;
}
else {
goto evalstart;
}
if (expr->expr->left->type != JIM_TT_VAR) {
goto evalstart;
}
if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) {
goto evalstart;
}
if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
goto evalstart;
}
if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
goto evalstart;
}
if (expr->expr->right->type == JIM_TT_EXPR_INT) {
if (Jim_GetWideExpr(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) {
goto evalstart;
}
}
else {
stopVarNamePtr = expr->expr->right->objPtr;
Jim_IncrRefCount(stopVarNamePtr);
stop = 0;
}
varNamePtr = expr->expr->left->objPtr;
Jim_IncrRefCount(varNamePtr);
objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
goto testcond;
}
while (retval == JIM_OK) {
if (stopVarNamePtr) {
objPtr = Jim_GetVariable(interp, stopVarNamePtr, JIM_NONE);
if (objPtr == NULL || Jim_GetWide(interp, objPtr, &stop) != JIM_OK) {
goto testcond;
}
}
if (currentVal >= stop + cmpOffset) {
break;
}
retval = Jim_EvalObj(interp, argv[4]);
if (JimCheckLoopRetcode(interp, retval)) {
immediate++;
goto out;
}
if (retval == JIM_OK || retval == JIM_CONTINUE) {
retval = JIM_OK;
objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
if (objPtr == NULL) {
retval = JIM_ERR;
goto out;
}
if (!Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
currentVal = ++JimWideValue(objPtr);
Jim_InvalidateStringRep(objPtr);
}
else {
if (Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK ||
Jim_SetVariable(interp, varNamePtr, Jim_NewIntObj(interp,
++currentVal)) != JIM_OK) {
goto evalnext;
}
}
}
}
goto out;
}
evalstart:
#endif
while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
retval = Jim_EvalObj(interp, argv[4]);
if (JimCheckLoopRetcode(interp, retval)) {
immediate++;
break;
}
if (retval == JIM_OK || retval == JIM_CONTINUE) {
JIM_IF_OPTIM(evalnext:)
retval = Jim_EvalObj(interp, argv[3]);
if (retval == JIM_OK || retval == JIM_CONTINUE) {
JIM_IF_OPTIM(testcond:)
retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
}
}
}
JIM_IF_OPTIM(out:)
if (stopVarNamePtr) {
Jim_DecrRefCount(interp, stopVarNamePtr);
}
if (varNamePtr) {
Jim_DecrRefCount(interp, varNamePtr);
}
if (!immediate) {
if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
Jim_SetEmptyResult(interp);
return JIM_OK;
}
}
return retval;
}
static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retval;
jim_wide i;
jim_wide limit = 0;
jim_wide incr = 1;
Jim_Obj *bodyObjPtr;
if (argc < 4 || argc > 6) {
Jim_WrongNumArgs(interp, 1, argv, "var ?first? limit ?incr? body");
return JIM_ERR;
}
retval = Jim_GetWideExpr(interp, argv[2], &i);
if (argc > 4 && retval == JIM_OK) {
retval = Jim_GetWideExpr(interp, argv[3], &limit);
}
if (argc > 5 && retval == JIM_OK) {
Jim_GetWideExpr(interp, argv[4], &incr);
}
if (retval != JIM_OK) {
return retval;
}
if (argc == 4) {
limit = i;
i = 0;
}
bodyObjPtr = argv[argc - 1];
retval = Jim_SetVariable(interp, argv[1], Jim_NewIntObj(interp, i));
while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
retval = Jim_EvalObj(interp, bodyObjPtr);
if (JimCheckLoopRetcode(interp, retval)) {
return retval;
}
if (retval == JIM_OK || retval == JIM_CONTINUE) {
Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
retval = JIM_OK;
i += incr;
if (objPtr && !Jim_IsShared(objPtr) && objPtr->typePtr == &intObjType) {
if (argv[1]->typePtr != &variableObjType) {
if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
return JIM_ERR;
}
}
JimWideValue(objPtr) = i;
Jim_InvalidateStringRep(objPtr);
if (argv[1]->typePtr != &variableObjType) {
if (Jim_SetVariable(interp, argv[1], objPtr) != JIM_OK) {
retval = JIM_ERR;
break;
}
}
}
else {
objPtr = Jim_NewIntObj(interp, i);
retval = Jim_SetVariable(interp, argv[1], objPtr);
if (retval != JIM_OK) {
Jim_FreeNewObj(interp, objPtr);
}
}
}
}
if (retval == JIM_OK || retval == JIM_CONTINUE || retval == JIM_BREAK) {
Jim_SetEmptyResult(interp);
return JIM_OK;
}
return retval;
}
typedef struct {
Jim_Obj *objPtr;
int idx;
} Jim_ListIter;
static void JimListIterInit(Jim_ListIter *iter, Jim_Obj *objPtr)
{
iter->objPtr = objPtr;
iter->idx = 0;
}
static Jim_Obj *JimListIterNext(Jim_Interp *interp, Jim_ListIter *iter)
{
if (iter->idx >= Jim_ListLength(interp, iter->objPtr)) {
return NULL;
}
return iter->objPtr->internalRep.listValue.ele[iter->idx++];
}
static int JimListIterDone(Jim_Interp *interp, Jim_ListIter *iter)
{
return iter->idx >= Jim_ListLength(interp, iter->objPtr);
}
static int JimForeachMapHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int doMap)
{
int result = JIM_OK;
int i, numargs;
Jim_ListIter twoiters[2];
Jim_ListIter *iters;
Jim_Obj *script;
Jim_Obj *resultObj;
if (argc < 4 || argc % 2 != 0) {
Jim_WrongNumArgs(interp, 1, argv, "varList list ?varList list ...? script");
return JIM_ERR;
}
script = argv[argc - 1];
numargs = (argc - 1 - 1);
if (numargs == 2) {
iters = twoiters;
}
else {
iters = Jim_Alloc(numargs * sizeof(*iters));
}
for (i = 0; i < numargs; i++) {
JimListIterInit(&iters[i], argv[i + 1]);
if (i % 2 == 0 && JimListIterDone(interp, &iters[i])) {
result = JIM_ERR;
}
}
if (result != JIM_OK) {
Jim_SetResultString(interp, "foreach varlist is empty", -1);
goto empty_varlist;
}
if (doMap) {
resultObj = Jim_NewListObj(interp, NULL, 0);
}
else {
resultObj = interp->emptyObj;
}
Jim_IncrRefCount(resultObj);
while (1) {
for (i = 0; i < numargs; i += 2) {
if (!JimListIterDone(interp, &iters[i + 1])) {
break;
}
}
if (i == numargs) {
break;
}
for (i = 0; i < numargs; i += 2) {
Jim_Obj *varName;
JimListIterInit(&iters[i], argv[i + 1]);
while ((varName = JimListIterNext(interp, &iters[i])) != NULL) {
Jim_Obj *valObj = JimListIterNext(interp, &iters[i + 1]);
if (!valObj) {
valObj = interp->emptyObj;
}
Jim_IncrRefCount(valObj);
result = Jim_SetVariable(interp, varName, valObj);
Jim_DecrRefCount(interp, valObj);
if (result != JIM_OK) {
goto err;
}
}
}
result = Jim_EvalObj(interp, script);
if (JimCheckLoopRetcode(interp, result)) {
goto err;
}
switch (result) {
case JIM_OK:
if (doMap) {
Jim_ListAppendElement(interp, resultObj, interp->result);
}
break;
case JIM_CONTINUE:
break;
case JIM_BREAK:
goto out;
default:
goto err;
}
}
out:
result = JIM_OK;
Jim_SetResult(interp, resultObj);
err:
Jim_DecrRefCount(interp, resultObj);
empty_varlist:
if (numargs > 2) {
Jim_Free(iters);
}
return result;
}
static int Jim_ForeachCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JimForeachMapHelper(interp, argc, argv, 0);
}
static int Jim_LmapCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JimForeachMapHelper(interp, argc, argv, 1);
}
static int Jim_LassignCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int result = JIM_ERR;
int i;
Jim_ListIter iter;
Jim_Obj *resultObj;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "varList list ?varName ...?");
return JIM_ERR;
}
JimListIterInit(&iter, argv[1]);
for (i = 2; i < argc; i++) {
Jim_Obj *valObj = JimListIterNext(interp, &iter);
result = Jim_SetVariable(interp, argv[i], valObj ? valObj : interp->emptyObj);
if (result != JIM_OK) {
return result;
}
}
resultObj = Jim_NewListObj(interp, NULL, 0);
while (!JimListIterDone(interp, &iter)) {
Jim_ListAppendElement(interp, resultObj, JimListIterNext(interp, &iter));
}
Jim_SetResult(interp, resultObj);
return JIM_OK;
}
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) {
if (current >= argc)
goto err;
if ((retval = Jim_GetBoolFromExpr(interp, argv[current++], &boolean))
!= JIM_OK)
return retval;
if (current >= argc)
goto err;
if (Jim_CompareStringImmediate(interp, argv[current], "then"))
current++;
if (current >= argc)
goto err;
if (boolean)
return Jim_EvalObj(interp, argv[current]);
if (++current >= argc) {
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
return JIM_OK;
}
falsebody = current++;
if (Jim_CompareStringImmediate(interp, argv[falsebody], "else")) {
if (current != argc - 1)
goto err;
return Jim_EvalObj(interp, argv[current]);
}
else if (Jim_CompareStringImmediate(interp, argv[falsebody], "elseif"))
continue;
else if (falsebody != argc - 1)
goto err;
return Jim_EvalObj(interp, argv[falsebody]);
}
return JIM_OK;
}
err:
Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
return JIM_ERR;
}
int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
Jim_Obj *stringObj, int flags)
{
Jim_Obj *parms[5];
int argc = 0;
long eq;
int rc;
parms[argc++] = commandObj;
if (flags & JIM_NOCASE) {
parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
}
if (flags & JIM_OPT_END) {
parms[argc++] = Jim_NewStringObj(interp, "--", -1);
}
parms[argc++] = patternObj;
parms[argc++] = stringObj;
rc = Jim_EvalObjVector(interp, argc, parms);
if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
eq = -rc;
}
return eq;
}
static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
int match_flags = 0;
Jim_Obj *command = NULL, *scriptObj = NULL, *strObj;
Jim_Obj **caseList;
if (argc < 3) {
wrongnumargs:
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_String(argv[opt]);
if (*option != '-')
break;
else if (strncmp(option, "--", 2) == 0) {
++opt;
break;
}
else if (strncmp(option, "-exact", 2) == 0)
matchOpt = SWITCH_EXACT;
else if (strncmp(option, "-glob", 2) == 0)
matchOpt = SWITCH_GLOB;
else if (strncmp(option, "-regexp", 2) == 0) {
matchOpt = SWITCH_RE;
match_flags |= JIM_OPT_END;
}
else if (strncmp(option, "-command", 2) == 0) {
matchOpt = SWITCH_CMD;
if ((argc - opt) < 2)
goto wrongnumargs;
command = argv[++opt];
}
else {
Jim_SetResultFormatted(interp,
"bad option \"%#s\": must be -exact, -glob, -regexp, -command procname or --",
argv[opt]);
return JIM_ERR;
}
if ((argc - opt) < 2)
goto wrongnumargs;
}
strObj = argv[opt++];
patCount = argc - opt;
if (patCount == 1) {
JimListGetElements(interp, argv[opt], &patCount, &caseList);
}
else
caseList = (Jim_Obj **)&argv[opt];
if (patCount == 0 || patCount % 2 != 0)
goto wrongnumargs;
for (i = 0; scriptObj == NULL && i < patCount; i += 2) {
Jim_Obj *patObj = caseList[i];
if (!Jim_CompareStringImmediate(interp, patObj, "default")
|| i < (patCount - 2)) {
switch (matchOpt) {
case SWITCH_EXACT:
if (Jim_StringEqObj(strObj, patObj))
scriptObj = caseList[i + 1];
break;
case SWITCH_GLOB:
if (Jim_StringMatchObj(interp, patObj, strObj, 0))
scriptObj = caseList[i + 1];
break;
case SWITCH_RE:
command = Jim_NewStringObj(interp, "regexp", -1);
case SWITCH_CMD:{
int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, match_flags);
if (argc - opt == 1) {
JimListGetElements(interp, argv[opt], &patCount, &caseList);
}
if (rc < 0) {
return -rc;
}
if (rc)
scriptObj = caseList[i + 1];
break;
}
}
}
else {
scriptObj = caseList[i + 1];
}
}
for (; i < patCount && Jim_CompareStringImmediate(interp, scriptObj, "-"); i += 2)
scriptObj = caseList[i + 1];
if (scriptObj && Jim_CompareStringImmediate(interp, scriptObj, "-")) {
Jim_SetResultFormatted(interp, "no body specified for pattern \"%#s\"", caseList[i - 2]);
return JIM_ERR;
}
Jim_SetEmptyResult(interp);
if (scriptObj) {
return Jim_EvalObj(interp, scriptObj);
}
return JIM_OK;
}
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;
}
static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
int ret;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
return JIM_ERR;
}
ret = Jim_ListIndices(interp, argv[1], argv + 2, argc - 2, &objPtr, JIM_NONE);
if (ret < 0) {
ret = JIM_OK;
Jim_SetEmptyResult(interp);
}
else if (ret == JIM_OK) {
Jim_SetResult(interp, objPtr);
}
return ret;
}
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;
}
Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
return JIM_OK;
}
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",
"-stride", "-index", NULL
};
enum
{ OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
OPT_COMMAND, OPT_STRIDE, OPT_INDEX };
int i;
int opt_bool = 0;
int opt_not = 0;
int opt_all = 0;
int opt_inline = 0;
int opt_match = OPT_EXACT;
int listlen;
int rc = JIM_OK;
Jim_Obj *listObjPtr = NULL;
Jim_Obj *commandObj = NULL;
Jim_Obj *indexObj = NULL;
int match_flags = 0;
long stride = 1;
if (argc < 3) {
wrongargs:
Jim_WrongNumArgs(interp, 1, argv,
"?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? ?-stride len? ?-index val? list value");
return JIM_ERR;
}
for (i = 1; i < argc - 2; i++) {
int option;
if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
switch (option) {
case OPT_BOOL:
opt_bool = 1;
opt_inline = 0;
break;
case OPT_NOT:
opt_not = 1;
break;
case OPT_NOCASE:
match_flags |= JIM_NOCASE;
break;
case OPT_INLINE:
opt_inline = 1;
opt_bool = 0;
break;
case OPT_ALL:
opt_all = 1;
break;
case OPT_REGEXP:
opt_match = option;
match_flags |= JIM_OPT_END;
break;
case OPT_COMMAND:
if (i >= argc - 2) {
goto wrongargs;
}
commandObj = argv[++i];
case OPT_EXACT:
case OPT_GLOB:
opt_match = option;
break;
case OPT_INDEX:
if (i >= argc - 2) {
goto wrongargs;
}
indexObj = argv[++i];
break;
case OPT_STRIDE:
if (i >= argc - 2) {
goto wrongargs;
}
if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
return JIM_ERR;
}
if (stride < 1) {
Jim_SetResultString(interp, "stride length must be at least 1", -1);
return JIM_ERR;
}
break;
}
}
argc -= i;
if (argc < 2) {
goto wrongargs;
}
argv += i;
listlen = Jim_ListLength(interp, argv[0]);
if (listlen % stride) {
Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
return JIM_ERR;
}
if (opt_all) {
listObjPtr = Jim_NewListObj(interp, NULL, 0);
}
if (opt_match == OPT_REGEXP) {
commandObj = Jim_NewStringObj(interp, "regexp", -1);
}
if (commandObj) {
Jim_IncrRefCount(commandObj);
}
for (i = 0; i < listlen; i += stride) {
int eq = 0;
Jim_Obj *searchListObj;
Jim_Obj *objPtr;
int offset;
if (indexObj) {
int indexlen = Jim_ListLength(interp, indexObj);
if (stride == 1) {
searchListObj = Jim_ListGetIndex(interp, argv[0], i);
}
else {
searchListObj = Jim_NewListObj(interp, argv[0]->internalRep.listValue.ele + i, stride);
}
Jim_IncrRefCount(searchListObj);
rc = Jim_ListIndices(interp, searchListObj, indexObj->internalRep.listValue.ele, indexlen, &objPtr, JIM_ERRMSG);
if (rc != JIM_OK) {
Jim_DecrRefCount(interp, searchListObj);
rc = JIM_ERR;
goto done;
}
offset = 0;
}
else {
searchListObj = argv[0];
offset = i;
objPtr = Jim_ListGetIndex(interp, searchListObj, i);
Jim_IncrRefCount(searchListObj);
}
switch (opt_match) {
case OPT_EXACT:
eq = Jim_StringCompareObj(interp, argv[1], objPtr, match_flags) == 0;
break;
case OPT_GLOB:
eq = Jim_StringMatchObj(interp, argv[1], objPtr, match_flags);
break;
case OPT_REGEXP:
case OPT_COMMAND:
eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, match_flags);
if (eq < 0) {
Jim_DecrRefCount(interp, searchListObj);
rc = JIM_ERR;
goto done;
}
break;
}
if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
Jim_Obj *resultObj;
if (opt_bool) {
resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
}
else if (!opt_inline) {
resultObj = Jim_NewIntObj(interp, i);
}
else if (stride == 1) {
resultObj = objPtr;
}
else if (opt_all) {
ListInsertElements(listObjPtr, -1, stride,
searchListObj->internalRep.listValue.ele + offset);
resultObj = NULL;
}
else {
resultObj = Jim_NewListObj(interp, searchListObj->internalRep.listValue.ele + offset, stride);
}
if (opt_all) {
if (stride == 1) {
Jim_ListAppendElement(interp, listObjPtr, resultObj);
}
}
else {
Jim_SetResult(interp, resultObj);
Jim_DecrRefCount(interp, searchListObj);
goto done;
}
}
Jim_DecrRefCount(interp, searchListObj);
}
if (opt_all) {
Jim_SetResult(interp, listObjPtr);
listObjPtr = NULL;
}
else {
if (opt_bool) {
Jim_SetResultBool(interp, opt_not);
}
else if (!opt_inline) {
Jim_SetResultInt(interp, -1);
}
}
done:
if (listObjPtr) {
Jim_FreeNewObj(interp, listObjPtr);
}
if (commandObj) {
Jim_DecrRefCount(interp, commandObj);
}
return rc;
}
static int Jim_LappendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *listObjPtr;
int new_obj = 0;
int i;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "varName ?value value ...?");
return JIM_ERR;
}
listObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
if (!listObjPtr) {
listObjPtr = Jim_NewListObj(interp, NULL, 0);
new_obj = 1;
}
else if (Jim_IsShared(listObjPtr)) {
listObjPtr = Jim_DuplicateObj(interp, listObjPtr);
new_obj = 1;
}
for (i = 2; i < argc; i++)
Jim_ListAppendElement(interp, listObjPtr, argv[i]);
if (Jim_SetVariable(interp, argv[1], listObjPtr) != JIM_OK) {
if (new_obj)
Jim_FreeNewObj(interp, listObjPtr);
return JIM_ERR;
}
Jim_SetResult(interp, listObjPtr);
return JIM_OK;
}
static int Jim_LinsertCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int idx, len;
Jim_Obj *listPtr;
if (argc < 3) {
Jim_WrongNumArgs(interp, 1, argv, "list index ?element ...?");
return JIM_ERR;
}
listPtr = argv[1];
if (Jim_IsShared(listPtr))
listPtr = Jim_DuplicateObj(interp, listPtr);
if (Jim_GetIndex(interp, argv[2], &idx) != JIM_OK)
goto err;
len = Jim_ListLength(interp, listPtr);
if (idx >= len)
idx = len;
else if (idx < 0)
idx = len + idx + 1;
Jim_ListInsertElements(interp, listPtr, idx, argc - 3, &argv[3]);
Jim_SetResult(interp, listPtr);
return JIM_OK;
err:
if (listPtr != argv[1]) {
Jim_FreeNewObj(interp, listPtr);
}
return JIM_ERR;
}
static int Jim_LreplaceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int first, last, len, rangeLen;
Jim_Obj *listObj;
Jim_Obj *newListObj;
if (argc < 4) {
Jim_WrongNumArgs(interp, 1, argv, "list first last ?element ...?");
return JIM_ERR;
}
if (Jim_GetIndex(interp, argv[2], &first) != JIM_OK ||
Jim_GetIndex(interp, argv[3], &last) != JIM_OK) {
return JIM_ERR;
}
listObj = argv[1];
len = Jim_ListLength(interp, listObj);
first = JimRelToAbsIndex(len, first);
last = JimRelToAbsIndex(len, last);
JimRelToAbsRange(len, &first, &last, &rangeLen);
if (first > len) {
first = len;
}
newListObj = Jim_NewListObj(interp, listObj->internalRep.listValue.ele, first);
ListInsertElements(newListObj, -1, argc - 4, argv + 4);
ListInsertElements(newListObj, -1, len - first - rangeLen, listObj->internalRep.listValue.ele + first + rangeLen);
Jim_SetResult(interp, newListObj);
return JIM_OK;
}
static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc < 3) {
Jim_WrongNumArgs(interp, 1, argv, "listVar ?index ...? value");
return JIM_ERR;
}
else if (argc == 3) {
if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
return JIM_ERR;
Jim_SetResult(interp, argv[2]);
return JIM_OK;
}
return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
}
static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
{
static const char * const options[] = {
"-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique",
"-stride", "-dictionary", NULL
};
enum {
OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE,
OPT_STRIDE, OPT_DICT
};
Jim_Obj *resObj;
int i;
int retCode;
int shared;
long stride = 1;
Jim_Obj **elements;
int listlen;
struct lsort_info info;
if (argc < 2) {
wrongargs:
Jim_WrongNumArgs(interp, 1, argv, "?options? list");
return JIM_ERR;
}
info.type = JIM_LSORT_ASCII;
info.order = 1;
info.indexc = 0;
info.unique = 0;
info.command = NULL;
info.interp = interp;
for (i = 1; i < (argc - 1); i++) {
int option;
if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
!= JIM_OK)
return JIM_ERR;
switch (option) {
case OPT_ASCII:
info.type = JIM_LSORT_ASCII;
break;
case OPT_DICT:
info.type = JIM_LSORT_DICT;
break;
case OPT_NOCASE:
info.type = JIM_LSORT_NOCASE;
break;
case OPT_INTEGER:
info.type = JIM_LSORT_INTEGER;
break;
case OPT_REAL:
info.type = JIM_LSORT_REAL;
break;
case OPT_INCREASING:
info.order = 1;
break;
case OPT_DECREASING:
info.order = -1;
break;
case OPT_UNIQUE:
info.unique = 1;
break;
case OPT_COMMAND:
if (i >= (argc - 2)) {
Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
return JIM_ERR;
}
info.type = JIM_LSORT_COMMAND;
info.command = argv[i + 1];
i++;
break;
case OPT_STRIDE:
if (i >= argc - 2) {
goto wrongargs;
}
if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
return JIM_ERR;
}
if (stride < 2) {
Jim_SetResultString(interp, "stride length must be at least 2", -1);
return JIM_ERR;
}
break;
case OPT_INDEX:
if (i >= (argc - 2)) {
badindex:
Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
return JIM_ERR;
}
JimListGetElements(interp, argv[i + 1], &info.indexc, &info.indexv);
if (info.indexc == 0) {
goto badindex;
}
i++;
break;
}
}
resObj = argv[argc - 1];
JimListGetElements(interp, resObj, &listlen, &elements);
if (listlen <= 1) {
Jim_SetResult(interp, resObj);
return JIM_OK;
}
if (stride > 1) {
Jim_Obj *tmpListObj;
int i;
if (listlen % stride) {
Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
return JIM_ERR;
}
tmpListObj = Jim_NewListObj(interp, NULL, 0);
Jim_IncrRefCount(tmpListObj);
for (i = 0; i < listlen; i += stride) {
Jim_ListAppendElement(interp, tmpListObj, Jim_NewListObj(interp, elements + i, stride));
}
retCode = ListSortElements(interp, tmpListObj, &info);
if (retCode == JIM_OK) {
resObj = Jim_NewListObj(interp, NULL, 0);
for (i = 0; i < listlen; i += stride) {
Jim_ListAppendList(interp, resObj, Jim_ListGetIndex(interp, tmpListObj, i / stride));
}
Jim_SetResult(interp, resObj);
}
Jim_DecrRefCount(interp, tmpListObj);
}
else {
if ((shared = Jim_IsShared(resObj))) {
resObj = Jim_DuplicateObj(interp, resObj);
}
retCode = ListSortElements(interp, resObj, &info);
if (retCode == JIM_OK) {
Jim_SetResult(interp, resObj);
}
else if (shared) {
Jim_FreeNewObj(interp, resObj);
}
}
return retCode;
}
static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *stringObjPtr;
int i;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "varName ?value ...?");
return JIM_ERR;
}
if (argc == 2) {
stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
if (!stringObjPtr)
return JIM_ERR;
}
else {
int new_obj = 0;
stringObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
if (!stringObjPtr) {
stringObjPtr = Jim_NewEmptyStringObj(interp);
new_obj = 1;
}
else if (Jim_IsShared(stringObjPtr)) {
new_obj = 1;
stringObjPtr = Jim_DuplicateObj(interp, stringObjPtr);
}
for (i = 2; i < argc; i++) {
Jim_AppendObj(interp, stringObjPtr, argv[i]);
}
if (Jim_SetVariable(interp, argv[1], stringObjPtr) != JIM_OK) {
if (new_obj) {
Jim_FreeNewObj(interp, stringObjPtr);
}
return JIM_ERR;
}
}
Jim_SetResult(interp, stringObjPtr);
return JIM_OK;
}
static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int rc;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
return JIM_ERR;
}
if (argc == 2) {
rc = Jim_EvalObj(interp, argv[1]);
}
else {
rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
}
return rc;
}
static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc >= 2) {
int retcode;
Jim_CallFrame *savedCallFrame, *targetCallFrame;
const char *str;
savedCallFrame = interp->framePtr;
str = Jim_String(argv[1]);
if ((str[0] >= '0' && str[0] <= '9') || str[0] == '#') {
targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
argc--;
argv++;
}
else {
targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
}
if (targetCallFrame == NULL) {
return JIM_ERR;
}
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv - 1, "?level? command ?arg ...?");
return JIM_ERR;
}
interp->framePtr = targetCallFrame;
if (argc == 2) {
retcode = Jim_EvalObj(interp, argv[1]);
}
else {
retcode = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
}
interp->framePtr = savedCallFrame;
return retcode;
}
else {
Jim_WrongNumArgs(interp, 1, argv, "?level? command ?arg ...?");
return JIM_ERR;
}
}
static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retcode;
if (argc == 2) {
retcode = Jim_EvalExpression(interp, argv[1]);
}
#ifndef JIM_COMPAT
else {
Jim_WrongNumArgs(interp, 1, argv, "expression");
retcode = JIM_ERR;
}
#else
else if (argc > 2) {
Jim_Obj *objPtr;
objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
Jim_IncrRefCount(objPtr);
retcode = Jim_EvalExpression(interp, objPtr);
Jim_DecrRefCount(interp, objPtr);
}
else {
Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
return JIM_ERR;
}
#endif
return retcode;
}
static int JimBreakContinueHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int retcode)
{
if (argc != 1 && argc != 2) {
Jim_WrongNumArgs(interp, 1, argv, "?level?");
return JIM_ERR;
}
if (argc == 2) {
long level;
int ret = Jim_GetLong(interp, argv[1], &level);
if (ret != JIM_OK) {
return ret;
}
interp->break_level = level;
}
return retcode;
}
static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JimBreakContinueHelper(interp, argc, argv, JIM_BREAK);
}
static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JimBreakContinueHelper(interp, argc, argv, JIM_CONTINUE);
}
static int Jim_StacktraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *listObj;
int i;
jim_wide skip = 0;
jim_wide last = 0;
if (argc > 1) {
if (Jim_GetWideExpr(interp, argv[1], &skip) != JIM_OK) {
return JIM_ERR;
}
}
if (argc > 2) {
if (Jim_GetWideExpr(interp, argv[2], &last) != JIM_OK) {
return JIM_ERR;
}
}
listObj = Jim_NewListObj(interp, NULL, 0);
for (i = skip; i <= interp->procLevel; i++) {
Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
if (frame->procLevel < last) {
break;
}
JimAddStackFrame(interp, frame, listObj);
}
Jim_SetResult(interp, listObj);
return JIM_OK;
}
static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int i;
Jim_Obj *stackTraceObj = NULL;
Jim_Obj *errorCodeObj = NULL;
int returnCode = JIM_OK;
long level = 1;
for (i = 1; i < argc - 1; i += 2) {
if (Jim_CompareStringImmediate(interp, argv[i], "-code")) {
if (Jim_GetReturnCode(interp, argv[i + 1], &returnCode) == JIM_ERR) {
return JIM_ERR;
}
}
else if (Jim_CompareStringImmediate(interp, argv[i], "-errorinfo")) {
stackTraceObj = argv[i + 1];
}
else if (Jim_CompareStringImmediate(interp, argv[i], "-errorcode")) {
errorCodeObj = argv[i + 1];
}
else if (Jim_CompareStringImmediate(interp, argv[i], "-level")) {
if (Jim_GetLong(interp, argv[i + 1], &level) != JIM_OK || level < 0) {
Jim_SetResultFormatted(interp, "bad level \"%#s\"", argv[i + 1]);
return JIM_ERR;
}
}
else {
break;
}
}
if (i != argc - 1 && i != argc) {
Jim_WrongNumArgs(interp, 1, argv,
"?-code code? ?-errorinfo stacktrace? ?-level level? ?result?");
}
if (stackTraceObj && returnCode == JIM_ERR) {
JimSetStackTrace(interp, stackTraceObj);
}
if (errorCodeObj && returnCode == JIM_ERR) {
Jim_SetGlobalVariableStr(interp, "errorCode", errorCodeObj);
}
interp->returnCode = returnCode;
interp->returnLevel = level;
if (i == argc - 1) {
Jim_SetResult(interp, argv[i]);
}
return level == 0 ? returnCode : JIM_RETURN;
}
static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (interp->framePtr->level == 0) {
Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
return JIM_ERR;
}
else if (argc >= 2) {
Jim_CallFrame *cf = interp->framePtr->parent;
Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
if (cmdPtr == NULL) {
return JIM_ERR;
}
JimPanic((cf->tailcallCmd != NULL, "Already have a tailcallCmd"));
JimIncrCmdRefCount(cmdPtr);
cf->tailcallCmd = cmdPtr;
JimPanic((cf->tailcallObj != NULL, "Already have a tailcallobj"));
cf->tailcallObj = Jim_NewListObj(interp, argv + 1, argc - 1);
Jim_IncrRefCount(cf->tailcallObj);
return JIM_EVAL;
}
return JIM_OK;
}
static int JimAliasCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *cmdList;
Jim_Obj *prefixListObj = Jim_CmdPrivData(interp);
cmdList = Jim_DuplicateObj(interp, prefixListObj);
Jim_ListInsertElements(interp, cmdList, Jim_ListLength(interp, cmdList), argc - 1, argv + 1);
return JimEvalObjList(interp, cmdList);
}
static void JimAliasCmdDelete(Jim_Interp *interp, void *privData)
{
Jim_Obj *prefixListObj = privData;
Jim_DecrRefCount(interp, prefixListObj);
}
static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *prefixListObj;
if (argc < 3) {
Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
return JIM_ERR;
}
prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
Jim_IncrRefCount(prefixListObj);
Jim_SetResult(interp, argv[1]);
return Jim_CreateCommandObj(interp, argv[1], JimAliasCmd, prefixListObj, JimAliasCmdDelete);
}
static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Cmd *cmd;
if (argc != 4 && argc != 5) {
Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
return JIM_ERR;
}
if (argc == 4) {
cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
}
else {
cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
}
if (cmd) {
Jim_Obj *nameObjPtr = JimQualifyName(interp, argv[1]);
JimCreateCommand(interp, nameObjPtr, cmd);
JimUpdateProcNamespace(interp, cmd, nameObjPtr);
Jim_DecrRefCount(interp, nameObjPtr);
Jim_SetResult(interp, argv[1]);
return JIM_OK;
}
return JIM_ERR;
}
static int Jim_XtraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 2) {
Jim_WrongNumArgs(interp, 1, argv, "callback");
return JIM_ERR;
}
if (interp->traceCmdObj) {
Jim_DecrRefCount(interp, interp->traceCmdObj);
interp->traceCmdObj = NULL;
}
if (Jim_Length(argv[1])) {
interp->traceCmdObj = argv[1];
Jim_IncrRefCount(interp->traceCmdObj);
}
return JIM_OK;
}
static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retcode;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
return JIM_ERR;
}
interp->local++;
retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
interp->local--;
if (retcode == 0) {
Jim_Obj *cmdNameObj = Jim_GetResult(interp);
if (Jim_GetCommand(interp, cmdNameObj, JIM_ERRMSG) == NULL) {
return JIM_ERR;
}
if (interp->framePtr->localCommands == NULL) {
interp->framePtr->localCommands = Jim_Alloc(sizeof(*interp->framePtr->localCommands));
Jim_InitStack(interp->framePtr->localCommands);
}
Jim_IncrRefCount(cmdNameObj);
Jim_StackPush(interp->framePtr->localCommands, cmdNameObj);
}
return retcode;
}
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;
}
else {
int retcode;
Jim_Cmd *cmdPtr = Jim_GetCommand(interp, argv[1], JIM_ERRMSG);
if (cmdPtr == NULL || !cmdPtr->isproc || !cmdPtr->prevCmd) {
Jim_SetResultFormatted(interp, "no previous command: \"%#s\"", argv[1]);
return JIM_ERR;
}
cmdPtr->u.proc.upcall++;
JimIncrCmdRefCount(cmdPtr);
retcode = Jim_EvalObjVector(interp, argc - 1, argv + 1);
cmdPtr->u.proc.upcall--;
JimDecrCmdRefCount(interp, cmdPtr);
return retcode;
}
}
static int Jim_ApplyCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "lambdaExpr ?arg ...?");
return JIM_ERR;
}
else {
int ret;
Jim_Cmd *cmd;
Jim_Obj *argListObjPtr;
Jim_Obj *bodyObjPtr;
Jim_Obj *nsObj = NULL;
Jim_Obj **nargv;
int len = Jim_ListLength(interp, argv[1]);
if (len != 2 && len != 3) {
Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
return JIM_ERR;
}
if (len == 3) {
#ifdef jim_ext_namespace
nsObj = Jim_ListGetIndex(interp, argv[1], 2);
#else
Jim_SetResultString(interp, "namespaces not enabled", -1);
return JIM_ERR;
#endif
}
argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
cmd = JimCreateProcedureCmd(interp, argListObjPtr, NULL, bodyObjPtr, nsObj);
if (cmd) {
nargv = Jim_Alloc((argc - 2 + 1) * sizeof(*nargv));
nargv[0] = Jim_NewStringObj(interp, "apply lambdaExpr", -1);
Jim_IncrRefCount(nargv[0]);
memcpy(&nargv[1], argv + 2, (argc - 2) * sizeof(*nargv));
ret = JimCallProcedure(interp, cmd, argc - 2 + 1, nargv);
Jim_DecrRefCount(interp, nargv[0]);
Jim_Free(nargv);
JimDecrCmdRefCount(interp, cmd);
return ret;
}
return JIM_ERR;
}
}
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;
}
static int Jim_UpvarCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int i;
Jim_CallFrame *targetCallFrame;
if (argc > 3 && (argc % 2 == 0)) {
targetCallFrame = Jim_GetCallFrameByLevel(interp, argv[1]);
argc--;
argv++;
}
else {
targetCallFrame = Jim_GetCallFrameByLevel(interp, NULL);
}
if (targetCallFrame == NULL) {
return JIM_ERR;
}
if (argc < 3) {
Jim_WrongNumArgs(interp, 1, argv, "?level? otherVar localVar ?otherVar localVar ...?");
return JIM_ERR;
}
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;
}
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;
}
if (interp->framePtr->level == 0)
return JIM_OK;
for (i = 1; i < argc; i++) {
const char *name = Jim_String(argv[i]);
if (name[0] != ':' || name[1] != ':') {
if (Jim_SetVariableLink(interp, argv[i], argv[i], interp->topFramePtr) != JIM_OK)
return JIM_ERR;
}
}
return JIM_OK;
}
static Jim_Obj *JimStringMap(Jim_Interp *interp, Jim_Obj *mapListObjPtr,
Jim_Obj *objPtr, int nocase)
{
int numMaps;
const char *str, *noMatchStart = NULL;
int strLen, i;
Jim_Obj *resultObjPtr;
numMaps = Jim_ListLength(interp, mapListObjPtr);
if (numMaps % 2) {
Jim_SetResultString(interp, "list must contain an even number of elements", -1);
return NULL;
}
str = Jim_String(objPtr);
strLen = Jim_Utf8Length(interp, objPtr);
resultObjPtr = Jim_NewStringObj(interp, "", 0);
while (strLen) {
for (i = 0; i < numMaps; i += 2) {
Jim_Obj *eachObjPtr;
const char *k;
int kl;
eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
k = Jim_String(eachObjPtr);
kl = Jim_Utf8Length(interp, eachObjPtr);
if (strLen >= kl && kl) {
int rc;
rc = JimStringCompareUtf8(str, kl, k, kl, nocase);
if (rc == 0) {
if (noMatchStart) {
Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
noMatchStart = NULL;
}
Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
str += utf8_index(str, kl);
strLen -= kl;
break;
}
}
}
if (i == numMaps) {
int c;
if (noMatchStart == NULL)
noMatchStart = str;
str += utf8_tounicode(str, &c);
strLen--;
}
}
if (noMatchStart) {
Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
}
return resultObjPtr;
}
static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int len;
int opt_case = 1;
int option;
static const char * const nocase_options[] = {
"-nocase", NULL
};
static const char * const nocase_length_options[] = {
"-nocase", "-length", NULL
};
enum {
OPT_BYTELENGTH,
OPT_BYTERANGE,
OPT_CAT,
OPT_COMPARE,
OPT_EQUAL,
OPT_FIRST,
OPT_INDEX,
OPT_IS,
OPT_LAST,
OPT_LENGTH,
OPT_MAP,
OPT_MATCH,
OPT_RANGE,
OPT_REPEAT,
OPT_REPLACE,
OPT_REVERSE,
OPT_TOLOWER,
OPT_TOTITLE,
OPT_TOUPPER,
OPT_TRIM,
OPT_TRIMLEFT,
OPT_TRIMRIGHT,
OPT_COUNT
};
static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
JIM_DEF_SUBCMD("bytelength", "string", 1, 1),
JIM_DEF_SUBCMD("byterange", "string first last", 3, 3),
JIM_DEF_SUBCMD("cat", "?...?", 0, -1),
JIM_DEF_SUBCMD("compare", "?-nocase? ?-length int? string1 string2", 2, 5),
JIM_DEF_SUBCMD("equal", "?-nocase? ?-length int? string1 string2", 2, 5),
JIM_DEF_SUBCMD("first", "subString string ?index?", 2, 3),
JIM_DEF_SUBCMD("index", "string index", 2, 2),
JIM_DEF_SUBCMD("is", "class ?-strict? str", 2, 3),
JIM_DEF_SUBCMD("last", "subString string ?index?", 2, 3),
JIM_DEF_SUBCMD("length","string", 1, 1),
JIM_DEF_SUBCMD("map", "?-nocase? mapList string", 2, 3),
JIM_DEF_SUBCMD("match", "?-nocase? pattern string", 2, 3),
JIM_DEF_SUBCMD("range", "string first last", 3, 3),
JIM_DEF_SUBCMD("repeat", "string count", 2, 2),
JIM_DEF_SUBCMD("replace", "string first last ?string?", 3, 4),
JIM_DEF_SUBCMD("reverse", "string", 1, 1),
JIM_DEF_SUBCMD("tolower", "string", 1, 1),
JIM_DEF_SUBCMD("totitle", "string", 1, 1),
JIM_DEF_SUBCMD("toupper", "string", 1, 1),
JIM_DEF_SUBCMD("trim", "string ?trimchars?", 1, 2),
JIM_DEF_SUBCMD("trimleft", "string ?trimchars?", 1, 2),
JIM_DEF_SUBCMD("trimright", "string ?trimchars?", 1, 2),
{ NULL }
};
const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
if (!ct) {
return JIM_ERR;
}
if (ct->function) {
return ct->function(interp, argc, argv);
}
option = ct - cmds;
switch (option) {
case OPT_LENGTH:
Jim_SetResultInt(interp, Jim_Utf8Length(interp, argv[2]));
return JIM_OK;
case OPT_BYTELENGTH:
Jim_SetResultInt(interp, Jim_Length(argv[2]));
return JIM_OK;
case OPT_CAT:{
Jim_Obj *objPtr;
if (argc == 3) {
objPtr = argv[2];
}
else {
int i;
objPtr = Jim_NewStringObj(interp, "", 0);
for (i = 2; i < argc; i++) {
Jim_AppendObj(interp, objPtr, argv[i]);
}
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
case OPT_COMPARE:
case OPT_EQUAL:
{
long opt_length = -1;
int n = argc - 4;
int i = 2;
while (n > 0) {
int subopt;
if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
JIM_ENUM_ABBREV) != JIM_OK) {
badcompareargs:
Jim_SubCmdArgError(interp, ct, argv[0]);
return JIM_ERR;
}
if (subopt == 0) {
opt_case = 0;
n--;
}
else {
if (n < 2) {
goto badcompareargs;
}
if (Jim_GetLong(interp, argv[i++], &opt_length) != JIM_OK) {
return JIM_ERR;
}
n -= 2;
}
}
if (n) {
goto badcompareargs;
}
argv += argc - 2;
if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
}
else {
const char *s1 = Jim_String(argv[0]);
int l1 = Jim_Utf8Length(interp, argv[0]);
const char *s2 = Jim_String(argv[1]);
int l2 = Jim_Utf8Length(interp, argv[1]);
if (opt_length >= 0) {
if (l1 > opt_length) {
l1 = opt_length;
}
if (l2 > opt_length) {
l2 = opt_length;
}
}
n = JimStringCompareUtf8(s1, l1, s2, l2, !opt_case);
Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
}
return JIM_OK;
}
case OPT_MATCH:
if (argc != 4 &&
(argc != 5 ||
Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
JIM_ENUM_ABBREV) != JIM_OK)) {
Jim_WrongNumArgs(interp, 2, argv, "?-nocase? pattern string");
return JIM_ERR;
}
if (opt_case == 0) {
argv++;
}
Jim_SetResultBool(interp, Jim_StringMatchObj(interp, argv[2], argv[3], !opt_case));
return JIM_OK;
case OPT_MAP:{
Jim_Obj *objPtr;
if (argc != 4 &&
(argc != 5 ||
Jim_GetEnum(interp, argv[2], nocase_options, &opt_case, NULL,
JIM_ENUM_ABBREV) != JIM_OK)) {
Jim_WrongNumArgs(interp, 2, argv, "?-nocase? mapList string");
return JIM_ERR;
}
if (opt_case == 0) {
argv++;
}
objPtr = JimStringMap(interp, argv[2], argv[3], !opt_case);
if (objPtr == NULL) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
case OPT_RANGE:{
Jim_Obj *objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
if (objPtr == NULL) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
case OPT_BYTERANGE:{
Jim_Obj *objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
if (objPtr == NULL) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
case OPT_REPLACE:{
Jim_Obj *objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
if (objPtr == NULL) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
case OPT_REPEAT:{
Jim_Obj *objPtr;
jim_wide count;
if (Jim_GetWideExpr(interp, argv[3], &count) != JIM_OK) {
return JIM_ERR;
}
objPtr = Jim_NewStringObj(interp, "", 0);
if (count > 0) {
while (count--) {
Jim_AppendObj(interp, objPtr, argv[2]);
}
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
case OPT_REVERSE:{
char *buf, *p;
const char *str;
int i;
str = Jim_GetString(argv[2], &len);
buf = Jim_Alloc(len + 1);
assert(buf);
p = buf + len;
*p = 0;
for (i = 0; i < len; ) {
int c;
int l = utf8_tounicode(str, &c);
memcpy(p - l, str, l);
p -= l;
i += l;
str += l;
}
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
return JIM_OK;
}
case OPT_INDEX:{
int idx;
const char *str;
if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
return JIM_ERR;
}
str = Jim_String(argv[2]);
len = Jim_Utf8Length(interp, argv[2]);
idx = JimRelToAbsIndex(len, idx);
if (idx < 0 || idx >= len || str == NULL) {
Jim_SetResultString(interp, "", 0);
}
else if (len == Jim_Length(argv[2])) {
Jim_SetResultString(interp, str + idx, 1);
}
else {
int c;
int i = utf8_index(str, idx);
Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
}
return JIM_OK;
}
case OPT_FIRST:
case OPT_LAST:{
int idx = 0, l1, l2;
const char *s1, *s2;
s1 = Jim_String(argv[2]);
s2 = Jim_String(argv[3]);
l1 = Jim_Utf8Length(interp, argv[2]);
l2 = Jim_Utf8Length(interp, argv[3]);
if (argc == 5) {
if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
return JIM_ERR;
}
idx = JimRelToAbsIndex(l2, idx);
if (idx < 0) {
idx = 0;
}
}
else if (option == OPT_LAST) {
idx = l2;
}
if (option == OPT_FIRST) {
Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
}
else {
#ifdef JIM_UTF8
Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
#else
Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
#endif
}
return JIM_OK;
}
case OPT_TRIM:
Jim_SetResult(interp, JimStringTrim(interp, argv[2], argc == 4 ? argv[3] : NULL));
return JIM_OK;
case OPT_TRIMLEFT:
Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], argc == 4 ? argv[3] : NULL));
return JIM_OK;
case OPT_TRIMRIGHT:{
Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], argc == 4 ? argv[3] : NULL));
return JIM_OK;
}
case OPT_TOLOWER:
Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
return JIM_OK;
case OPT_TOUPPER:
Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
return JIM_OK;
case OPT_TOTITLE:
Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
return JIM_OK;
case OPT_IS:
if (argc == 5 && !Jim_CompareStringImmediate(interp, argv[3], "-strict")) {
Jim_SubCmdArgError(interp, ct, argv[0]);
return JIM_ERR;
}
return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
}
return JIM_OK;
}
static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
long i, count = 1;
jim_wide start, elapsed;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
return JIM_ERR;
}
if (argc == 3) {
if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
return JIM_ERR;
}
if (count < 0)
return JIM_OK;
i = count;
start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
while (i-- > 0) {
int retval;
retval = Jim_EvalObj(interp, argv[1]);
if (retval != JIM_OK) {
return retval;
}
}
elapsed = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
if (elapsed < count * 10) {
Jim_SetResult(interp, Jim_NewDoubleObj(interp, elapsed * 1.0 / count));
}
else {
Jim_SetResultInt(interp, count == 0 ? 0 : elapsed / count);
}
Jim_AppendString(interp, Jim_GetResult(interp)," microseconds per iteration", -1);
return JIM_OK;
}
static int Jim_TimeRateCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
long us = 0;
jim_wide start, delta, overhead;
Jim_Obj *objPtr;
double us_per_iter;
int count;
int n;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "script ?milliseconds?");
return JIM_ERR;
}
if (argc == 3) {
if (Jim_GetLong(interp, argv[2], &us) != JIM_OK)
return JIM_ERR;
us *= 1000;
}
if (us < 1) {
us = 1000 * 1000;
}
start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
count = 0;
do {
int retval = Jim_EvalObj(interp, argv[1]);
delta = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
if (retval != JIM_OK) {
return retval;
}
count++;
} while (delta < us);
start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
n = 0;
do {
int retval = Jim_EvalObj(interp, interp->nullScriptObj);
overhead = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
if (retval != JIM_OK) {
return retval;
}
n++;
} while (n < count);
delta -= overhead;
us_per_iter = (double)delta / count;
objPtr = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "us_per_iter", -1));
Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, us_per_iter));
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "iters_per_sec", -1));
Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, 1e6 / us_per_iter));
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "count", -1));
Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, count));
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "elapsed_us", -1));
Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, delta));
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
long exitCode = 0;
if (argc > 2) {
Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
return JIM_ERR;
}
if (argc == 2) {
if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
return JIM_ERR;
Jim_SetResult(interp, argv[1]);
}
interp->exitCode = exitCode;
return JIM_EXIT;
}
static int JimMatchReturnCodes(Jim_Interp *interp, Jim_Obj *retcodeListObj, int rc)
{
int len = Jim_ListLength(interp, retcodeListObj);
int i;
for (i = 0; i < len; i++) {
int returncode;
if (Jim_GetReturnCode(interp, Jim_ListGetIndex(interp, retcodeListObj, i), &returncode) != JIM_OK) {
return JIM_ERR;
}
if (rc == returncode) {
return JIM_OK;
}
}
return -1;
}
static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *const *argv)
{
static const char * const wrongargs_catchtry[2] = {
"?-?no?code ... --? script ?resultVarName? ?optionVarName?",
"?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?"
};
int exitCode = 0;
int i;
int sig = 0;
int ok;
Jim_Obj *finallyScriptObj = NULL;
Jim_Obj *msgVarObj = NULL;
Jim_Obj *optsVarObj = NULL;
Jim_Obj *handlerScriptObj = NULL;
Jim_Obj *errorCodeObj;
int idx;
jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
static const int max_ignore_code = sizeof(ignore_mask) * 8;
JimPanic((istry != 0 && istry != 1, "wrong args to JimCatchTryHelper"));
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 ignore;
if (strcmp(arg, "--") == 0) {
i++;
break;
}
if (*arg != '-') {
break;
}
if (strncmp(arg, "-no", 3) == 0) {
arg += 3;
ignore = 1;
}
else {
arg++;
ignore = 0;
}
if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
option = -1;
}
if (option < 0) {
option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
}
if (option < 0) {
goto wrongargs;
}
if (ignore) {
ignore_mask |= ((jim_wide)1 << option);
}
else {
ignore_mask &= (~((jim_wide)1 << option));
}
}
idx = i;
if (argc - idx < 1) {
wrongargs:
Jim_WrongNumArgs(interp, 1, argv, wrongargs_catchtry[istry]);
return JIM_ERR;
}
if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
sig++;
}
interp->signal_level += sig;
if (Jim_CheckSignal(interp)) {
exitCode = JIM_SIGNAL;
}
else {
exitCode = Jim_EvalObj(interp, argv[idx]);
interp->errorFlag = 0;
}
interp->signal_level -= sig;
errorCodeObj = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
idx++;
if (istry) {
while (idx < argc) {
int option;
int ret;
static const char * const try_options[] = { "on", "trap", "finally", NULL };
enum { TRY_ON, TRY_TRAP, TRY_FINALLY, };
if (Jim_GetEnum(interp, argv[idx], try_options, &option, "handler", JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
switch (option) {
case TRY_ON:
case TRY_TRAP:
if (idx + 4 > argc) {
goto wrongargs;
}
if (option == TRY_ON) {
ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode);
if (ret > JIM_OK) {
goto wrongargs;
}
}
else if (errorCodeObj) {
int len = Jim_ListLength(interp, argv[idx + 1]);
int i;
ret = JIM_OK;
for (i = 0; i < len; i++) {
Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i);
Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i);
if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) {
ret = -1;
break;
}
}
}
else {
ret = -1;
}
if (ret == JIM_OK && handlerScriptObj == NULL) {
msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0);
optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1);
handlerScriptObj = argv[idx + 3];
}
idx += 4;
break;
case TRY_FINALLY:
if (idx + 2 != argc) {
goto wrongargs;
}
finallyScriptObj = argv[idx + 1];
idx += 2;
break;
}
}
}
else {
if (argc - idx >= 1) {
msgVarObj = argv[idx];
idx++;
if (argc - idx >= 1) {
optsVarObj = argv[idx];
idx++;
}
}
}
if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
if (finallyScriptObj) {
Jim_EvalObj(interp, finallyScriptObj);
}
return exitCode;
}
if (sig && exitCode == JIM_SIGNAL) {
if (interp->signal_set_result) {
interp->signal_set_result(interp, interp->sigmask);
}
else if (!istry) {
Jim_SetResultInt(interp, interp->sigmask);
}
interp->sigmask = 0;
}
ok = 1;
if (msgVarObj && Jim_Length(msgVarObj)) {
if (Jim_SetVariable(interp, msgVarObj, Jim_GetResult(interp)) != JIM_OK) {
ok = 0;
}
}
if (ok && optsVarObj && Jim_Length(optsVarObj)) {
Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
Jim_ListAppendElement(interp, optListObj,
Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
if (exitCode == JIM_ERR) {
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
-1));
Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
if (errorCodeObj) {
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
Jim_ListAppendElement(interp, optListObj, errorCodeObj);
}
}
if (Jim_SetVariable(interp, optsVarObj, optListObj) != JIM_OK) {
ok = 0;
}
}
if (ok && handlerScriptObj) {
exitCode = Jim_EvalObj(interp, handlerScriptObj);
}
if (finallyScriptObj) {
Jim_Obj *prevResultObj = Jim_GetResult(interp);
Jim_IncrRefCount(prevResultObj);
int ret = Jim_EvalObj(interp, finallyScriptObj);
if (ret == JIM_OK) {
Jim_SetResult(interp, prevResultObj);
}
else {
exitCode = ret;
}
Jim_DecrRefCount(interp, prevResultObj);
}
if (!istry) {
Jim_SetResultInt(interp, exitCode);
exitCode = JIM_OK;
}
return exitCode;
}
static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JimCatchTryHelper(interp, 0, argc, argv);
}
static int Jim_TryCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JimCatchTryHelper(interp, 1, argc, argv);
}
static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 3) {
Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
return JIM_ERR;
}
return Jim_RenameCommand(interp, argv[1], argv[2]);
}
#define JIM_DICTMATCH_KEYS 0x0001
#define JIM_DICTMATCH_VALUES 0x002
int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
{
Jim_Obj *listObjPtr;
Jim_Dict *dict;
int i;
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
return JIM_ERR;
}
dict = objPtr->internalRep.dictValue;
listObjPtr = Jim_NewListObj(interp, NULL, 0);
for (i = 0; i < dict->len; i += 2 ) {
Jim_Obj *keyObj = dict->table[i];
Jim_Obj *valObj = dict->table[i + 1];
if (patternObj) {
Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? keyObj : valObj;
if (!Jim_StringMatchObj(interp, patternObj, matchObj, 0)) {
continue;
}
}
if (return_types & JIM_DICTMATCH_KEYS) {
Jim_ListAppendElement(interp, listObjPtr, keyObj);
}
if (return_types & JIM_DICTMATCH_VALUES) {
Jim_ListAppendElement(interp, listObjPtr, valObj);
}
}
Jim_SetResult(interp, listObjPtr);
return JIM_OK;
}
int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
{
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
return -1;
}
return objPtr->internalRep.dictValue->len / 2;
}
Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
int i;
JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));
for (i = 0; i < objc; i++) {
Jim_Obj **table;
int tablelen;
int j;
table = Jim_DictPairs(interp, objv[i], &tablelen);
if (tablelen && !table) {
Jim_FreeNewObj(interp, objPtr);
return NULL;
}
for (j = 0; j < tablelen; j += 2) {
DictAddElement(interp, objPtr, table[j], table[j + 1]);
}
}
return objPtr;
}
int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
{
char buffer[100];
Jim_Obj *output;
Jim_Dict *dict;
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
return JIM_ERR;
}
dict = objPtr->internalRep.dictValue;
snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets", dict->len, dict->size);
output = Jim_NewStringObj(interp, buffer, -1);
Jim_SetResult(interp, output);
return JIM_OK;
}
static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
{
Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
Jim_AppendString(interp, prefixObj, " ", 1);
Jim_AppendString(interp, prefixObj, subcmd, -1);
return Jim_EvalObjPrefix(interp, prefixObj, argc, argv);
}
static int JimDictWith(Jim_Interp *interp, Jim_Obj *dictVarName, Jim_Obj *const *keyv, int keyc, Jim_Obj *scriptObj)
{
int i;
Jim_Obj *objPtr;
Jim_Obj *dictObj;
Jim_Obj **dictValues;
int len;
int ret = JIM_OK;
dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
dictValues = Jim_DictPairs(interp, objPtr, &len);
if (len && dictValues == NULL) {
return JIM_ERR;
}
for (i = 0; i < len; i += 2) {
if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
return JIM_ERR;
}
}
if (Jim_Length(scriptObj)) {
ret = Jim_EvalObj(interp, scriptObj);
if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {
Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
for (i = 0; i < keyc; i++) {
newkeyv[i] = keyv[i];
}
for (i = 0; i < len; i += 2) {
if (Jim_StringCompareObj(interp, dictVarName, dictValues[i], 0) != 0) {
objPtr = Jim_GetVariable(interp, dictValues[i], 0);
newkeyv[keyc] = dictValues[i];
Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, JIM_NORESULT);
}
}
Jim_Free(newkeyv);
}
}
return ret;
}
static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
int types = JIM_DICTMATCH_KEYS;
enum {
OPT_CREATE,
OPT_GET,
OPT_GETDEF,
OPT_GETWITHDEFAULT,
OPT_SET,
OPT_UNSET,
OPT_EXISTS,
OPT_KEYS,
OPT_SIZE,
OPT_INFO,
OPT_MERGE,
OPT_WITH,
OPT_APPEND,
OPT_LAPPEND,
OPT_INCR,
OPT_REMOVE,
OPT_VALUES,
OPT_FOR,
OPT_REPLACE,
OPT_UPDATE,
OPT_COUNT
};
static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
JIM_DEF_SUBCMD("create", "?key value ...?", 0, -2),
JIM_DEF_SUBCMD("get", "dictionary ?key ...?", 1, -1),
JIM_DEF_SUBCMD_HIDDEN("getdef", "dictionary ?key ...? key default", 3, -1),
JIM_DEF_SUBCMD("getwithdefault", "dictionary ?key ...? key default", 3, -1),
JIM_DEF_SUBCMD("set", "varName key ?key ...? value", 3, -1),
JIM_DEF_SUBCMD("unset", "varName key ?key ...?", 2, -1),
JIM_DEF_SUBCMD("exists", "dictionary key ?key ...?", 2, -1),
JIM_DEF_SUBCMD("keys", "dictionary ?pattern?", 1, 2),
JIM_DEF_SUBCMD("size", "dictionary", 1, 1),
JIM_DEF_SUBCMD("info", "dictionary", 1, 1),
JIM_DEF_SUBCMD("merge", "?...?", 0, -1),
JIM_DEF_SUBCMD("with", "dictVar ?key ...? script", 2, -1),
JIM_DEF_SUBCMD("append", "varName key ?value ...?", 2, -1),
JIM_DEF_SUBCMD("lappend", "varName key ?value ...?", 2, -1),
JIM_DEF_SUBCMD("incr", "varName key ?increment?", 2, 3),
JIM_DEF_SUBCMD("remove", "dictionary ?key ...?", 1, -1),
JIM_DEF_SUBCMD("values", "dictionary ?pattern?", 1, 2),
JIM_DEF_SUBCMD("for", "vars dictionary script", 3, 3),
JIM_DEF_SUBCMD("replace", "dictionary ?key value ...?", 1, -1),
JIM_DEF_SUBCMD("update", "varName ?arg ...? script", 2, -1),
{ NULL }
};
const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
if (!ct) {
return JIM_ERR;
}
if (ct->function) {
return ct->function(interp, argc, argv);
}
switch (ct - cmds) {
case OPT_GET:
if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
case OPT_GETDEF:
case OPT_GETWITHDEFAULT:{
int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 4, &objPtr, JIM_ERRMSG);
if (rc == -1) {
return JIM_ERR;
}
if (rc == JIM_ERR) {
Jim_SetResult(interp, argv[argc - 1]);
}
else {
Jim_SetResult(interp, objPtr);
}
return JIM_OK;
}
case OPT_SET:
return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
case OPT_EXISTS:{
int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_NONE);
if (rc < 0) {
return JIM_ERR;
}
Jim_SetResultBool(interp, rc == JIM_OK);
return JIM_OK;
}
case OPT_UNSET:
if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE) != JIM_OK) {
return JIM_ERR;
}
return JIM_OK;
case OPT_VALUES:
types = JIM_DICTMATCH_VALUES;
case OPT_KEYS:
return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);
case OPT_SIZE:
if (Jim_DictSize(interp, argv[2]) < 0) {
return JIM_ERR;
}
Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
return JIM_OK;
case OPT_MERGE:
if (argc == 2) {
return JIM_OK;
}
objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
if (objPtr == NULL) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
case OPT_CREATE:
objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
Jim_SetResult(interp, objPtr);
return JIM_OK;
case OPT_INFO:
return Jim_DictInfo(interp, argv[2]);
case OPT_WITH:
return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
case OPT_UPDATE:
if (argc < 6 || argc % 2) {
argc = 2;
}
default:
return Jim_EvalEnsemble(interp, "dict", Jim_String(argv[1]), argc - 2, argv + 2);
}
}
static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
static const char * const options[] = {
"-nobackslashes", "-nocommands", "-novariables", NULL
};
enum
{ OPT_NOBACKSLASHES, OPT_NOCOMMANDS, OPT_NOVARIABLES };
int i;
int flags = JIM_SUBST_FLAG;
Jim_Obj *objPtr;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "?options? string");
return JIM_ERR;
}
for (i = 1; i < (argc - 1); i++) {
int option;
if (Jim_GetEnum(interp, argv[i], options, &option, NULL,
JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
return JIM_ERR;
}
switch (option) {
case OPT_NOBACKSLASHES:
flags |= JIM_SUBST_NOESC;
break;
case OPT_NOCOMMANDS:
flags |= JIM_SUBST_NOCMD;
break;
case OPT_NOVARIABLES:
flags |= JIM_SUBST_NOVAR;
break;
}
}
if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
#ifdef jim_ext_namespace
static int JimIsGlobalNamespace(Jim_Obj *objPtr)
{
int len;
const char *str = Jim_GetString(objPtr, &len);
return len >= 2 && str[0] == ':' && str[1] == ':';
}
#endif
static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
int mode = 0;
enum {
INFO_ALIAS,
INFO_ARGS,
INFO_BODY,
INFO_CHANNELS,
INFO_COMMANDS,
INFO_COMPLETE,
INFO_EXISTS,
INFO_FRAME,
INFO_GLOBALS,
INFO_HOSTNAME,
INFO_LEVEL,
INFO_LOCALS,
INFO_NAMEOFEXECUTABLE,
INFO_PATCHLEVEL,
INFO_PROCS,
INFO_REFERENCES,
INFO_RETURNCODES,
INFO_SCRIPT,
INFO_SOURCE,
INFO_STACKTRACE,
INFO_STATICS,
INFO_VARS,
INFO_VERSION,
INFO_COUNT
};
static const jim_subcmd_type cmds[INFO_COUNT + 1] = {
JIM_DEF_SUBCMD("alias", "command", 1, 1),
JIM_DEF_SUBCMD("args", "procname", 1, 1),
JIM_DEF_SUBCMD("body", "procname", 1, 1),
JIM_DEF_SUBCMD("channels", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("commands", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("complete", "script ?missing?", 1, 2),
JIM_DEF_SUBCMD("exists", "varName", 1, 1),
JIM_DEF_SUBCMD("frame", "?levelNum?", 0, 1),
JIM_DEF_SUBCMD("globals", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("hostname", NULL, 0, 0),
JIM_DEF_SUBCMD("level", "?levelNum?", 0, 1),
JIM_DEF_SUBCMD("locals", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("nameofexecutable", NULL, 0, 0),
JIM_DEF_SUBCMD("patchlevel", NULL, 0, 0),
JIM_DEF_SUBCMD("procs", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("references", NULL, 0, 0),
JIM_DEF_SUBCMD("returncodes", "?code?", 0, 1),
JIM_DEF_SUBCMD("script", "?filename?", 0, 1),
JIM_DEF_SUBCMD("source", "source ?filename line?", 1, 3),
JIM_DEF_SUBCMD("stacktrace", NULL, 0, 0),
JIM_DEF_SUBCMD("statics", "procname", 1, 1),
JIM_DEF_SUBCMD("vars", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("version", NULL, 0, 0),
{ NULL }
};
const jim_subcmd_type *ct;
#ifdef jim_ext_namespace
int nons = 0;
if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
argc--;
argv++;
nons = 1;
}
#endif
ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
if (!ct) {
return JIM_ERR;
}
if (ct->function) {
return ct->function(interp, argc, argv);
}
int option = ct - cmds;
switch (option) {
case INFO_EXISTS:
Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
return JIM_OK;
case INFO_ALIAS:{
Jim_Cmd *cmdPtr;
if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
return JIM_ERR;
}
if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
return JIM_ERR;
}
Jim_SetResult(interp, (Jim_Obj *)cmdPtr->u.native.privData);
return JIM_OK;
}
case INFO_CHANNELS:
mode++;
#ifndef jim_ext_aio
Jim_SetResultString(interp, "aio not enabled", -1);
return JIM_ERR;
#endif
case INFO_PROCS:
mode++;
case INFO_COMMANDS:
#ifdef jim_ext_namespace
if (!nons) {
if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
}
}
#endif
Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
return JIM_OK;
case INFO_VARS:
mode++;
case INFO_LOCALS:
mode++;
case INFO_GLOBALS:
#ifdef jim_ext_namespace
if (!nons) {
if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
}
}
#endif
Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
return JIM_OK;
case INFO_SCRIPT:
if (argc == 3) {
Jim_IncrRefCount(argv[2]);
Jim_DecrRefCount(interp, interp->currentFilenameObj);
interp->currentFilenameObj = argv[2];
}
Jim_SetResult(interp, interp->currentFilenameObj);
return JIM_OK;
case INFO_SOURCE:{
Jim_Obj *resObjPtr;
Jim_Obj *fileNameObj;
if (argc == 4) {
Jim_SubCmdArgError(interp, ct, argv[0]);
return JIM_ERR;
}
if (argc == 5) {
jim_wide line;
if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
return JIM_ERR;
}
resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
Jim_SetSourceInfo(interp, resObjPtr, argv[3], line);
}
else {
int line;
fileNameObj = Jim_GetSourceInfo(interp, argv[2], &line);
resObjPtr = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
}
Jim_SetResult(interp, resObjPtr);
return JIM_OK;
}
case INFO_STACKTRACE:
Jim_SetResult(interp, interp->stackTrace);
return JIM_OK;
case INFO_LEVEL:
if (argc == 2) {
Jim_SetResultInt(interp, interp->framePtr->level);
}
else {
if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
}
return JIM_OK;
case INFO_FRAME:
if (argc == 2) {
Jim_SetResultInt(interp, interp->procLevel + 1);
}
else {
if (JimInfoFrame(interp, argv[2], &objPtr) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
}
return JIM_OK;
case INFO_BODY:
case INFO_STATICS:
case INFO_ARGS:{
Jim_Cmd *cmdPtr;
if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
return JIM_ERR;
}
if (!cmdPtr->isproc) {
Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
return JIM_ERR;
}
switch (option) {
#ifdef JIM_NO_INTROSPECTION
default:
Jim_SetResultString(interp, "unsupported", -1);
return JIM_ERR;
#else
case INFO_BODY:
Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
break;
case INFO_ARGS:
Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
break;
#endif
case INFO_STATICS:
if (cmdPtr->u.proc.staticVars) {
Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES));
}
break;
}
return JIM_OK;
}
case INFO_VERSION:
case INFO_PATCHLEVEL:{
char buf[(JIM_INTEGER_SPACE * 2) + 1];
sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
Jim_SetResultString(interp, buf, -1);
return JIM_OK;
}
case INFO_COMPLETE: {
char missing;
Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing));
if (missing != ' ' && argc == 4) {
Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
}
return JIM_OK;
}
case INFO_HOSTNAME:
return Jim_Eval(interp, "os.gethostname");
case INFO_NAMEOFEXECUTABLE:
return Jim_Eval(interp, "{info nameofexecutable}");
case INFO_RETURNCODES:
if (argc == 2) {
int i;
Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
for (i = 0; jimReturnCodes[i]; i++) {
Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp,
jimReturnCodes[i], -1));
}
Jim_SetResult(interp, listObjPtr);
}
else if (argc == 3) {
long code;
const char *name;
if (Jim_GetLong(interp, argv[2], &code) != JIM_OK) {
return JIM_ERR;
}
name = Jim_ReturnCode(code);
if (*name == '?') {
Jim_SetResultInt(interp, code);
}
else {
Jim_SetResultString(interp, name, -1);
}
}
return JIM_OK;
case INFO_REFERENCES:
#ifdef JIM_REFERENCES
return JimInfoReferences(interp, argc, argv);
#else
Jim_SetResultString(interp, "not supported", -1);
return JIM_ERR;
#endif
default:
abort();
}
}
static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
int result = 0;
static const char * const options[] = {
"-command", "-proc", "-alias", "-var", NULL
};
enum
{
OPT_COMMAND, OPT_PROC, OPT_ALIAS, OPT_VAR
};
int option;
if (argc == 2) {
option = OPT_VAR;
objPtr = argv[1];
}
else if (argc == 3) {
if (Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
return JIM_ERR;
}
objPtr = argv[2];
}
else {
Jim_WrongNumArgs(interp, 1, argv, "?option? name");
return JIM_ERR;
}
if (option == OPT_VAR) {
result = Jim_GetVariable(interp, objPtr, 0) != NULL;
}
else {
Jim_Cmd *cmd = Jim_GetCommand(interp, objPtr, JIM_NONE);
if (cmd) {
switch (option) {
case OPT_COMMAND:
result = 1;
break;
case OPT_ALIAS:
result = cmd->isproc == 0 && cmd->u.native.cmdProc == JimAliasCmd;
break;
case OPT_PROC:
result = cmd->isproc;
break;
}
}
}
Jim_SetResultBool(interp, result);
return JIM_OK;
}
static int Jim_SplitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *str, *splitChars, *noMatchStart;
int splitLen, strLen;
Jim_Obj *resObjPtr;
int c;
int len;
if (argc != 2 && argc != 3) {
Jim_WrongNumArgs(interp, 1, argv, "string ?splitChars?");
return JIM_ERR;
}
str = Jim_GetString(argv[1], &len);
if (len == 0) {
return JIM_OK;
}
strLen = Jim_Utf8Length(interp, argv[1]);
if (argc == 2) {
splitChars = " \n\t\r";
splitLen = 4;
}
else {
splitChars = Jim_String(argv[2]);
splitLen = Jim_Utf8Length(interp, argv[2]);
}
noMatchStart = str;
resObjPtr = Jim_NewListObj(interp, NULL, 0);
if (splitLen) {
Jim_Obj *objPtr;
while (strLen--) {
const char *sc = splitChars;
int scLen = splitLen;
int sl = utf8_tounicode(str, &c);
while (scLen--) {
int pc;
sc += utf8_tounicode(sc, &pc);
if (c == pc) {
objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
Jim_ListAppendElement(interp, resObjPtr, objPtr);
noMatchStart = str + sl;
break;
}
}
str += sl;
}
objPtr = Jim_NewStringObj(interp, noMatchStart, (str - noMatchStart));
Jim_ListAppendElement(interp, resObjPtr, objPtr);
}
else {
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) {
c -= 9;
if (!commonObj) {
commonObj = Jim_Alloc(sizeof(*commonObj) * NUM_COMMON);
memset(commonObj, 0, sizeof(*commonObj) * NUM_COMMON);
}
if (!commonObj[c]) {
commonObj[c] = Jim_NewStringObj(interp, str, 1);
}
Jim_ListAppendElement(interp, resObjPtr, commonObj[c]);
str++;
continue;
}
#endif
Jim_ListAppendElement(interp, resObjPtr, Jim_NewStringObjUtf8(interp, str, 1));
str += n;
}
Jim_Free(commonObj);
}
Jim_SetResult(interp, resObjPtr);
return JIM_OK;
}
static int Jim_JoinCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *joinStr;
int joinStrLen;
if (argc != 2 && argc != 3) {
Jim_WrongNumArgs(interp, 1, argv, "list ?joinString?");
return JIM_ERR;
}
if (argc == 2) {
joinStr = " ";
joinStrLen = 1;
}
else {
joinStr = Jim_GetString(argv[2], &joinStrLen);
}
Jim_SetResult(interp, Jim_ListJoin(interp, argv[1], joinStr, joinStrLen));
return JIM_OK;
}
static int Jim_FormatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "formatString ?arg arg ...?");
return JIM_ERR;
}
objPtr = Jim_FormatString(interp, argv[1], argc - 2, argv + 2);
if (objPtr == NULL)
return JIM_ERR;
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
static int Jim_ScanCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *listPtr, **outVec;
int outc, i;
if (argc < 3) {
Jim_WrongNumArgs(interp, 1, argv, "string format ?varName varName ...?");
return JIM_ERR;
}
if (argv[2]->typePtr != &scanFmtStringObjType)
SetScanFmtFromAny(interp, argv[2]);
if (FormatGetError(argv[2]) != 0) {
Jim_SetResultString(interp, FormatGetError(argv[2]), -1);
return JIM_ERR;
}
if (argc > 3) {
int maxPos = FormatGetMaxPos(argv[2]);
int count = FormatGetCnvCount(argv[2]);
if (maxPos > argc - 3) {
Jim_SetResultString(interp, "\"%n$\" argument index out of range", -1);
return JIM_ERR;
}
else if (count > argc - 3) {
Jim_SetResultString(interp, "different numbers of variable names and "
"field specifiers", -1);
return JIM_ERR;
}
else if (count < argc - 3) {
Jim_SetResultString(interp, "variable is not assigned by any "
"conversion specifiers", -1);
return JIM_ERR;
}
}
listPtr = Jim_ScanString(interp, argv[1], argv[2], JIM_ERRMSG);
if (listPtr == 0)
return JIM_ERR;
if (argc > 3) {
int rc = JIM_OK;
int count = 0;
if (listPtr != 0 && listPtr != (Jim_Obj *)EOF) {
int len = Jim_ListLength(interp, listPtr);
if (len != 0) {
JimListGetElements(interp, listPtr, &outc, &outVec);
for (i = 0; i < outc; ++i) {
if (Jim_Length(outVec[i]) > 0) {
++count;
if (Jim_SetVariable(interp, argv[3 + i], outVec[i]) != JIM_OK) {
rc = JIM_ERR;
}
}
}
}
Jim_FreeNewObj(interp, listPtr);
}
else {
count = -1;
}
if (rc == JIM_OK) {
Jim_SetResultInt(interp, count);
}
return rc;
}
else {
if (listPtr == (Jim_Obj *)EOF) {
Jim_SetResult(interp, Jim_NewListObj(interp, 0, 0));
return JIM_OK;
}
Jim_SetResult(interp, listPtr);
}
return JIM_OK;
}
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;
}
Jim_SetResult(interp, argv[1]);
if (argc == 3) {
JimSetStackTrace(interp, argv[2]);
return JIM_ERR;
}
return JIM_ERR;
}
static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
if (argc != 4) {
Jim_WrongNumArgs(interp, 1, argv, "list first last");
return JIM_ERR;
}
if ((objPtr = Jim_ListRange(interp, argv[1], argv[2], argv[3])) == NULL)
return JIM_ERR;
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
jim_wide count;
if (argc < 2 || Jim_GetWideExpr(interp, argv[1], &count) != JIM_OK || count < 0) {
Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
return JIM_ERR;
}
if (count == 0 || argc == 2) {
Jim_SetEmptyResult(interp);
return JIM_OK;
}
argc -= 2;
argv += 2;
objPtr = Jim_NewListObj(interp, NULL, 0);
ListEnsureLength(objPtr, argc * count);
while (count--) {
ListInsertElements(objPtr, -1, argc, argv);
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
char **Jim_GetEnviron(void)
{
#if defined(HAVE__NSGETENVIRON)
return *_NSGetEnviron();
#elif defined(_environ)
return _environ;
#else
#if !defined(NO_ENVIRON_EXTERN)
extern char **environ;
#endif
return environ;
#endif
}
void Jim_SetEnviron(char **env)
{
#if defined(HAVE__NSGETENVIRON)
*_NSGetEnviron() = env;
#elif defined(_environ)
_environ = env;
#else
#if !defined(NO_ENVIRON_EXTERN)
extern char **environ;
#endif
environ = env;
#endif
}
static int Jim_EnvCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *key;
const char *val;
if (argc == 1) {
char **e = Jim_GetEnviron();
int i;
Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
for (i = 0; e[i]; i++) {
const char *equals = strchr(e[i], '=');
if (equals) {
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, e[i],
equals - e[i]));
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, equals + 1, -1));
}
}
Jim_SetResult(interp, listObjPtr);
return JIM_OK;
}
if (argc > 3) {
Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
return JIM_ERR;
}
key = Jim_String(argv[1]);
val = getenv(key);
if (val == NULL) {
if (argc < 3) {
Jim_SetResultFormatted(interp, "environment variable \"%#s\" does not exist", argv[1]);
return JIM_ERR;
}
val = Jim_String(argv[2]);
}
Jim_SetResult(interp, Jim_NewStringObj(interp, val, -1));
return JIM_OK;
}
static int Jim_SourceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retval;
if (argc != 2) {
Jim_WrongNumArgs(interp, 1, argv, "fileName");
return JIM_ERR;
}
retval = Jim_EvalFile(interp, Jim_String(argv[1]));
if (retval == JIM_RETURN)
return JIM_OK;
return retval;
}
static int Jim_LreverseCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *revObjPtr, **ele;
int len;
if (argc != 2) {
Jim_WrongNumArgs(interp, 1, argv, "list");
return JIM_ERR;
}
JimListGetElements(interp, argv[1], &len, &ele);
revObjPtr = Jim_NewListObj(interp, NULL, 0);
ListEnsureLength(revObjPtr, len);
len--;
while (len >= 0)
ListAppendElement(revObjPtr, ele[len--]);
Jim_SetResult(interp, revObjPtr);
return JIM_OK;
}
static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
{
jim_wide len;
if (step == 0)
return -1;
if (start == end)
return 0;
else if (step > 0 && start > end)
return -1;
else if (step < 0 && end > start)
return -1;
len = end - start;
if (len < 0)
len = -len;
if (step < 0)
step = -step;
len = 1 + ((len - 1) / step);
if (len > INT_MAX)
len = INT_MAX;
return (int)((len < 0) ? -1 : len);
}
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;
if (argc < 2 || argc > 4) {
Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
return JIM_ERR;
}
if (argc == 2) {
if (Jim_GetWideExpr(interp, argv[1], &end) != JIM_OK)
return JIM_ERR;
}
else {
if (Jim_GetWideExpr(interp, argv[1], &start) != JIM_OK ||
Jim_GetWideExpr(interp, argv[2], &end) != JIM_OK)
return JIM_ERR;
if (argc == 4 && Jim_GetWideExpr(interp, argv[3], &step) != JIM_OK)
return JIM_ERR;
}
if ((len = JimRangeLen(start, end, step)) == -1) {
Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
return JIM_ERR;
}
objPtr = Jim_NewListObj(interp, NULL, 0);
ListEnsureLength(objPtr, len);
for (i = 0; i < len; i++)
ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
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) {
Jim_WrongNumArgs(interp, 1, argv, "?min? max");
return JIM_ERR;
}
if (argc == 1) {
max = JIM_WIDE_MAX;
} else if (argc == 2) {
if (Jim_GetWideExpr(interp, argv[1], &max) != JIM_OK)
return JIM_ERR;
} else if (argc == 3) {
if (Jim_GetWideExpr(interp, argv[1], &min) != JIM_OK ||
Jim_GetWideExpr(interp, argv[2], &max) != JIM_OK)
return JIM_ERR;
}
len = max-min;
if (len < 0) {
Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
return JIM_ERR;
}
maxMul = JIM_WIDE_MAX - (len ? (JIM_WIDE_MAX%len) : 0);
while (1) {
jim_wide r;
JimRandomBytes(interp, &r, sizeof(jim_wide));
if (r < 0 || r >= maxMul) continue;
r = (len == 0) ? 0 : r%len;
Jim_SetResultInt(interp, min+r);
return JIM_OK;
}
}
static const struct {
const char *name;
Jim_CmdProc *cmdProc;
} Jim_CoreCommandsTable[] = {
{"alias", Jim_AliasCoreCommand},
{"set", Jim_SetCoreCommand},
{"unset", Jim_UnsetCoreCommand},
{"puts", Jim_PutsCoreCommand},
{"+", Jim_AddCoreCommand},
{"*", Jim_MulCoreCommand},
{"-", Jim_SubCoreCommand},
{"/", Jim_DivCoreCommand},
{"incr", Jim_IncrCoreCommand},
{"while", Jim_WhileCoreCommand},
{"loop", Jim_LoopCoreCommand},
{"for", Jim_ForCoreCommand},
{"foreach", Jim_ForeachCoreCommand},
{"lmap", Jim_LmapCoreCommand},
{"lassign", Jim_LassignCoreCommand},
{"if", Jim_IfCoreCommand},
{"switch", Jim_SwitchCoreCommand},
{"list", Jim_ListCoreCommand},
{"lindex", Jim_LindexCoreCommand},
{"lset", Jim_LsetCoreCommand},
{"lsearch", Jim_LsearchCoreCommand},
{"llength", Jim_LlengthCoreCommand},
{"lappend", Jim_LappendCoreCommand},
{"linsert", Jim_LinsertCoreCommand},
{"lreplace", Jim_LreplaceCoreCommand},
{"lsort", Jim_LsortCoreCommand},
{"append", Jim_AppendCoreCommand},
{"eval", Jim_EvalCoreCommand},
{"uplevel", Jim_UplevelCoreCommand},
{"expr", Jim_ExprCoreCommand},
{"break", Jim_BreakCoreCommand},
{"continue", Jim_ContinueCoreCommand},
{"proc", Jim_ProcCoreCommand},
{"xtrace", Jim_XtraceCoreCommand},
{"concat", Jim_ConcatCoreCommand},
{"return", Jim_ReturnCoreCommand},
{"upvar", Jim_UpvarCoreCommand},
{"global", Jim_GlobalCoreCommand},
{"string", Jim_StringCoreCommand},
{"time", Jim_TimeCoreCommand},
{"timerate", Jim_TimeRateCoreCommand},
{"exit", Jim_ExitCoreCommand},
{"catch", Jim_CatchCoreCommand},
{"try", Jim_TryCoreCommand},
#ifdef JIM_REFERENCES
{"ref", Jim_RefCoreCommand},
{"getref", Jim_GetrefCoreCommand},
{"setref", Jim_SetrefCoreCommand},
{"finalize", Jim_FinalizeCoreCommand},
{"collect", Jim_CollectCoreCommand},
#endif
{"rename", Jim_RenameCoreCommand},
{"dict", Jim_DictCoreCommand},
{"subst", Jim_SubstCoreCommand},
{"info", Jim_InfoCoreCommand},
{"exists", Jim_ExistsCoreCommand},
{"split", Jim_SplitCoreCommand},
{"join", Jim_JoinCoreCommand},
{"format", Jim_FormatCoreCommand},
{"scan", Jim_ScanCoreCommand},
{"error", Jim_ErrorCoreCommand},
{"lrange", Jim_LrangeCoreCommand},
{"lrepeat", Jim_LrepeatCoreCommand},
{"env", Jim_EnvCoreCommand},
{"source", Jim_SourceCoreCommand},
{"lreverse", Jim_LreverseCoreCommand},
{"range", Jim_RangeCoreCommand},
{"rand", Jim_RandCoreCommand},
{"tailcall", Jim_TailcallCoreCommand},
{"local", Jim_LocalCoreCommand},
{"upcall", Jim_UpcallCoreCommand},
{"apply", Jim_ApplyCoreCommand},
{"stacktrace", Jim_StacktraceCoreCommand},
{NULL, NULL},
};
void Jim_RegisterCoreCommands(Jim_Interp *interp)
{
int i = 0;
while (Jim_CoreCommandsTable[i].name != NULL) {
Jim_CreateCommand(interp,
Jim_CoreCommandsTable[i].name, Jim_CoreCommandsTable[i].cmdProc, NULL, NULL);
i++;
}
}
void Jim_MakeErrorMessage(Jim_Interp *interp)
{
Jim_Obj *argv[2];
argv[0] = Jim_NewStringObj(interp, "errorInfo", -1);
argv[1] = interp->result;
Jim_EvalObjVector(interp, 2, argv);
}
static char **JimSortStringTable(const char *const *tablePtr)
{
int count;
char **tablePtrSorted;
for (count = 0; tablePtr[count]; count++) {
}
tablePtrSorted = Jim_Alloc(sizeof(char *) * (count + 1));
memcpy(tablePtrSorted, tablePtr, sizeof(char *) * count);
qsort(tablePtrSorted, count, sizeof(char *), qsortCompareStringPointers);
tablePtrSorted[count] = NULL;
return tablePtrSorted;
}
static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
const char *prefix, const char *const *tablePtr, const char *name)
{
char **tablePtrSorted;
int i;
if (name == NULL) {
name = "option";
}
Jim_SetResultFormatted(interp, "%s%s \"%s\": must be ", badtype, name, arg);
tablePtrSorted = JimSortStringTable(tablePtr);
for (i = 0; tablePtrSorted[i]; i++) {
if (tablePtrSorted[i + 1] == NULL && i > 0) {
Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
}
Jim_AppendStrings(interp, Jim_GetResult(interp), prefix, tablePtrSorted[i], NULL);
if (tablePtrSorted[i + 1]) {
Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
}
}
Jim_Free(tablePtrSorted);
}
int Jim_CheckShowCommands(Jim_Interp *interp, Jim_Obj *objPtr, const char *const *tablePtr)
{
if (Jim_CompareStringImmediate(interp, objPtr, "-commands")) {
int i;
char **tablePtrSorted = JimSortStringTable(tablePtr);
Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0));
for (i = 0; tablePtrSorted[i]; i++) {
Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, tablePtrSorted[i], -1));
}
Jim_Free(tablePtrSorted);
return JIM_OK;
}
return JIM_ERR;
}
static const Jim_ObjType getEnumObjType = {
"get-enum",
NULL,
NULL,
NULL,
JIM_TYPE_REFERENCES
};
int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
const char *const *tablePtr, int *indexPtr, const char *name, int flags)
{
const char *bad = "bad ";
const char *const *entryPtr = NULL;
int i;
int match = -1;
int arglen;
const char *arg;
if (objPtr->typePtr == &getEnumObjType) {
if (objPtr->internalRep.ptrIntValue.ptr == tablePtr && objPtr->internalRep.ptrIntValue.int1 == flags) {
*indexPtr = objPtr->internalRep.ptrIntValue.int2;
return JIM_OK;
}
}
arg = Jim_GetString(objPtr, &arglen);
*indexPtr = -1;
for (entryPtr = tablePtr, i = 0; *entryPtr != NULL; entryPtr++, i++) {
if (Jim_CompareStringImmediate(interp, objPtr, *entryPtr)) {
match = i;
goto found;
}
if (flags & JIM_ENUM_ABBREV) {
if (strncmp(arg, *entryPtr, arglen) == 0) {
if (*arg == '-' && arglen == 1) {
break;
}
if (match >= 0) {
bad = "ambiguous ";
goto ambiguous;
}
match = i;
}
}
}
if (match >= 0) {
found:
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &getEnumObjType;
objPtr->internalRep.ptrIntValue.ptr = (void *)tablePtr;
objPtr->internalRep.ptrIntValue.int1 = flags;
objPtr->internalRep.ptrIntValue.int2 = match;
*indexPtr = match;
return JIM_OK;
}
ambiguous:
if (flags & JIM_ERRMSG) {
JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
}
return JIM_ERR;
}
int Jim_FindByName(const char *name, const char * const array[], size_t len)
{
int i;
for (i = 0; i < (int)len; i++) {
if (array[i] && strcmp(array[i], name) == 0) {
return i;
}
}
return -1;
}
int Jim_IsDict(Jim_Obj *objPtr)
{
return objPtr->typePtr == &dictObjType;
}
int Jim_IsList(Jim_Obj *objPtr)
{
return objPtr->typePtr == &listObjType;
}
void Jim_SetResultFormatted(Jim_Interp *interp, const char *format, ...)
{
int len = strlen(format);
int extra = 0;
int n = 0;
const char *params[5];
int nobjparam = 0;
Jim_Obj *objparam[5];
char *buf;
va_list args;
int i;
va_start(args, format);
for (i = 0; i < len && n < 5; i++) {
int l;
if (strncmp(format + i, "%s", 2) == 0) {
params[n] = va_arg(args, char *);
l = strlen(params[n]);
}
else if (strncmp(format + i, "%#s", 3) == 0) {
Jim_Obj *objPtr = va_arg(args, Jim_Obj *);
params[n] = Jim_GetString(objPtr, &l);
objparam[nobjparam++] = objPtr;
Jim_IncrRefCount(objPtr);
}
else {
if (format[i] == '%') {
i++;
}
continue;
}
n++;
extra += l;
}
len += extra;
buf = Jim_Alloc(len + 1);
len = snprintf(buf, len + 1, format, params[0], params[1], params[2], params[3], params[4]);
va_end(args);
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
for (i = 0; i < nobjparam; i++) {
Jim_DecrRefCount(interp, objparam[i]);
}
}
int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version)
{
if (abi_version != JIM_ABI_VERSION) {
Jim_SetResultString(interp, "ABI version mismatch", -1);
return JIM_ERR;
}
return JIM_OK;
}
#ifndef jim_ext_package
int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
{
return JIM_OK;
}
#endif
#ifndef jim_ext_aio
int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
{
return -1;
}
#endif
#include <stdio.h>
#include <string.h>
static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JIM_OK;
}
static const jim_subcmd_type dummy_subcmd = {
"dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
};
static Jim_Obj *subcmd_cmd_list(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
{
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
Jim_Obj *sortCmd[2];
for (; ct->cmd; ct++) {
if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, ct->cmd, -1));
}
}
sortCmd[0] = Jim_NewStringObj(interp, "lsort", -1);
sortCmd[1] = listObj;
if (Jim_EvalObjVector(interp, 2, sortCmd) == JIM_OK) {
return Jim_ListJoin(interp, Jim_GetResult(interp), sep, strlen(sep));
}
return Jim_GetResult(interp);
}
static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
Jim_Obj *cmd, Jim_Obj *subcmd)
{
Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be %#s", cmd, type,
subcmd, subcmd_cmd_list(interp, command_table, ", "));
}
static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
Jim_Obj *const *argv)
{
Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: %#s",
argv[0], subcmd_cmd_list(interp, command_table, ", "));
}
static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
{
if (cmd) {
Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
}
Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
if (ct->args && *ct->args) {
Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
}
}
void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *subcmd)
{
Jim_SetResultString(interp, "wrong # args: should be \"", -1);
add_cmd_usage(interp, ct, subcmd);
Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
}
static const Jim_ObjType subcmdLookupObjType = {
"subcmd-lookup",
NULL,
NULL,
NULL,
JIM_TYPE_REFERENCES
};
const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
int argc, Jim_Obj *const *argv)
{
const jim_subcmd_type *ct;
const jim_subcmd_type *partial = 0;
int cmdlen;
Jim_Obj *cmd;
const char *cmdstr;
int help = 0;
int argsok = 1;
if (argc < 2) {
Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n"
"Use \"%#s -help ?command?\" for help", argv[0], argv[0]);
return 0;
}
cmd = argv[1];
if (cmd->typePtr == &subcmdLookupObjType) {
if (cmd->internalRep.ptrIntValue.ptr == command_table) {
ct = command_table + cmd->internalRep.ptrIntValue.int1;
goto found;
}
}
if (Jim_CompareStringImmediate(interp, cmd, "-help")) {
if (argc == 2) {
show_cmd_usage(interp, command_table, argc, argv);
return &dummy_subcmd;
}
help = 1;
cmd = argv[2];
}
if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
Jim_SetResult(interp, subcmd_cmd_list(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)) {
break;
}
if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) {
if (partial) {
if (help) {
show_cmd_usage(interp, command_table, argc, argv);
return &dummy_subcmd;
}
bad_subcmd(interp, command_table, "ambiguous", argv[0], argv[1 + help]);
return 0;
}
partial = ct;
}
continue;
}
if (partial && !ct->cmd) {
ct = partial;
}
if (!ct->cmd) {
if (help) {
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);
add_cmd_usage(interp, ct, argv[0]);
return &dummy_subcmd;
}
Jim_FreeIntRep(interp, cmd);
cmd->typePtr = &subcmdLookupObjType;
cmd->internalRep.ptrIntValue.ptr = (void *)command_table;
cmd->internalRep.ptrIntValue.int1 = ct - command_table;
found:
if (argc - 2 < ct->minargs) {
argsok = 0;
}
else if (ct->maxargs >= 0 && argc - 2 > ct->maxargs) {
argsok = 0;
}
else if (ct->maxargs < -1 && (argc - 2) % -ct->maxargs != 0) {
argsok = 0;
}
if (!argsok) {
Jim_SetResultString(interp, "wrong # args: should be \"", -1);
add_cmd_usage(interp, ct, argv[0]);
Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
return 0;
}
return ct;
}
int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type * ct, int argc, Jim_Obj *const *argv)
{
int ret = JIM_ERR;
if (ct) {
if (ct->flags & JIM_MODFLAG_FULLARGV) {
ret = ct->function(interp, argc, argv);
}
else {
ret = ct->function(interp, argc - 2, argv + 2);
}
if (ret < 0) {
Jim_SubCmdArgError(interp, ct, argv[0]);
ret = JIM_ERR;
}
}
return ret;
}
int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const jim_subcmd_type *ct =
Jim_ParseSubCmd(interp, (const jim_subcmd_type *)Jim_CmdPrivData(interp), argc, argv);
return Jim_CallSubCmd(interp, ct, argc, argv);
}
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
int utf8_fromunicode(char *p, unsigned uc)
{
if (uc <= 0x7f) {
*p = uc;
return 1;
}
else if (uc <= 0x7ff) {
*p++ = 0xc0 | ((uc & 0x7c0) >> 6);
*p = 0x80 | (uc & 0x3f);
return 2;
}
else if (uc <= 0xffff) {
*p++ = 0xe0 | ((uc & 0xf000) >> 12);
*p++ = 0x80 | ((uc & 0xfc0) >> 6);
*p = 0x80 | (uc & 0x3f);
return 3;
}
else {
*p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
*p++ = 0x80 | ((uc & 0x3f000) >> 12);
*p++ = 0x80 | ((uc & 0xfc0) >> 6);
*p = 0x80 | (uc & 0x3f);
return 4;
}
}
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#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_NewEmptyStringObj(interp);
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 && (p - spec <= 5));
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_getchars(spec, code);
formatted_buf = spec;
formatted_chars = 1;
break;
}
case 'b': {
unsigned jim_wide w;
int length;
int i;
int j;
if (Jim_GetWide(interp, objv[objIndex], (jim_wide *)&w) != JIM_OK) {
goto error;
}
length = sizeof(w) * 8;
if (num_buffer_size < length + 1) {
num_buffer_size = length + 1;
num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
}
j = 0;
for (i = length; i > 0; ) {
i--;
if (w & ((unsigned jim_wide)1 << i)) {
num_buffer[j++] = '1';
}
else if (j || i == 0) {
num_buffer[j++] = '0';
}
}
num_buffer[j] = 0;
formatted_chars = formatted_bytes = j;
formatted_buf = num_buffer;
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) {
if (ch == 'd') {
w = (short)w;
}
else {
w = (unsigned short)w;
}
}
*p++ = 'l';
#ifdef HAVE_LONG_LONG
if (sizeof(long long) == sizeof(jim_wide)) {
*p++ = 'l';
}
#endif
}
*p++ = (char) ch;
*p = '\0';
if (width > 10000 || length > 10000 || precision > 10000) {
Jim_SetResultString(interp, "format too long", -1);
goto error;
}
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;
}
#if defined(JIM_REGEXP)
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#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 BOLX 14
#define EOLX 15
#define WORDA 16
#define WORDZ 17
#define OPENNC 1000
#define OPEN 1001
#define CLOSENC 2000
#define CLOSE 2001
#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 1
#define SIMPLE 2
#define SPSTART 4
#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);
static void regoptail(regex_t *preg, int p, int val );
static int regopsize(regex_t *preg, int p );
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
static 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 jim_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->proglen = (strlen(exp) + 1) * 5;
preg->program = malloc(preg->proglen * sizeof(int));
if (preg->program == NULL)
FAIL(preg, REG_ERR_NOMEM);
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) {
if (preg->regparse[0] == '?' && preg->regparse[1] == ':') {
preg->regparse += 2;
parno = -1;
}
else {
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 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 if (*end == '\0') {
preg->err = REG_ERR_UNMATCHED_BRACES;
return 0;
}
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 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 (*s == '{') {
n = parse_hex(s + 1, 6, ch);
if (n > 0 && s[n + 1] == '}' && *ch >= 0 && *ch <= 0x1fffff) {
s += n + 2;
}
else {
*ch = 'u';
}
}
else if ((n = parse_hex(s, 4, ch)) > 0) {
s += n;
}
break;
case 'U':
if ((n = parse_hex(s, 8, 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 != ']') {
int start;
int end;
enum {
CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
CC_NUM
};
int cc;
if (!*pattern) {
preg->err = REG_ERR_UNMATCHED_BRACKET;
return 0;
}
pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
if (start == '\\') {
switch (*pattern) {
case 's':
pattern++;
cc = CC_SPACE;
goto cc_switch;
case 'd':
pattern++;
cc = CC_DIGIT;
goto cc_switch;
case 'w':
pattern++;
reg_addrange(preg, '_', '_');
cc = CC_ALNUM;
goto cc_switch;
}
pattern += reg_decode_escape(pattern, &start);
if (start == 0) {
preg->err = REG_ERR_NULL_CHAR;
return 0;
}
if (start == '\\' && *pattern == 0) {
preg->err = REG_ERR_INVALID_ESCAPE;
return 0;
}
}
if (pattern[0] == '-' && pattern[1] && 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;
}
if (end == '\\' && *pattern == 0) {
preg->err = REG_ERR_INVALID_ESCAPE;
return 0;
}
}
reg_addrange(preg, start, end);
continue;
}
if (start == '[' && pattern[0] == ':') {
static const char *character_class[] = {
":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
};
for (cc = 0; cc < CC_NUM; cc++) {
n = strlen(character_class[cc]);
if (strncmp(pattern, character_class[cc], n) == 0) {
if (pattern[n] != ']') {
preg->err = REG_ERR_UNMATCHED_BRACKET;
return 0;
}
pattern += n + 1;
break;
}
}
if (cc != CC_NUM) {
cc_switch:
switch (cc) {
case CC_ALNUM:
reg_addrange(preg, '0', '9');
case CC_ALPHA:
if ((preg->cflags & REG_ICASE) == 0) {
reg_addrange(preg, 'a', 'z');
}
reg_addrange(preg, 'A', 'Z');
break;
case CC_SPACE:
reg_addrange_str(preg, " \t\r\n\f\v");
break;
case CC_BLANK:
reg_addrange_str(preg, " \t");
break;
case CC_UPPER:
reg_addrange(preg, 'A', 'Z');
break;
case CC_LOWER:
reg_addrange(preg, 'a', 'z');
break;
case CC_XDIGIT:
reg_addrange(preg, 'a', 'f');
reg_addrange(preg, 'A', 'F');
case CC_DIGIT:
reg_addrange(preg, '0', '9');
break;
case CC_CNTRL:
reg_addrange(preg, 0, 31);
reg_addrange(preg, 127, 127);
break;
case CC_PRINT:
reg_addrange(preg, ' ', '~');
break;
case CC_GRAPH:
reg_addrange(preg, '!', '~');
break;
case CC_PUNCT:
reg_addrange(preg, '!', '/');
reg_addrange(preg, ':', '@');
reg_addrange(preg, '[', '`');
reg_addrange(preg, '{', '~');
break;
}
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 '\\':
ch = *preg->regparse++;
switch (ch) {
case '\0':
preg->err = REG_ERR_INVALID_ESCAPE;
return 0;
case 'A':
ret = regnode(preg, BOLX);
break;
case 'Z':
ret = regnode(preg, EOLX);
break;
case '<':
case 'm':
ret = regnode(preg, WORDA);
break;
case '>':
case 'M':
ret = regnode(preg, WORDZ);
break;
case 'd':
case 'D':
ret = regnode(preg, ch == 'd' ? ANYOF : ANYBUT);
reg_addrange(preg, '0', '9');
regc(preg, '\0');
*flagp |= HASWIDTH|SIMPLE;
break;
case 'w':
case 'W':
ret = regnode(preg, ch == 'w' ? ANYOF : ANYBUT);
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':
case 'S':
ret = regnode(preg, ch == 's' ? ANYOF : ANYBUT);
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("<>mMwWdDsSAZ", 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 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 jim_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 += regopsize(preg, scan)) {
int op = OP(preg, scan);
if (op == END)
break;
if (op == REPX || op == REPXMIN)
preg->program[scan + 4] = 0;
}
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) {
if (regtry(preg, string)) {
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;
}
else {
int c;
s += utf8_tounicode(s, &c);
}
}
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;
const char *save;
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 BOLX:
if ((preg->eflags & REG_NOTBOL)) {
return(0);
}
case BOL:
if (preg->reginput != preg->regbol) {
return(0);
}
break;
case EOLX:
if (c != 0) {
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:
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;
case OPENNC:
case CLOSENC:
return regmatch(preg, next);
default:
if (OP(preg, scan) >= OPEN+1 && OP(preg, scan) < CLOSE_END) {
save = preg->reginput;
if (regmatch(preg, next)) {
if (OP(preg, scan) < CLOSE) {
int no = OP(preg, scan) - OPEN;
if (no < preg->nmatch && preg->pmatch[no].rm_so == -1) {
preg->pmatch[no].rm_so = save - preg->start;
}
}
else {
int 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);
}
preg->reginput = save;
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 += utf8_charlen(*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);
}
static int regopsize(regex_t *preg, int p )
{
switch (OP(preg, p)) {
case REP:
case REPMIN:
case REPX:
case REPXMIN:
return 5;
case ANYOF:
case ANYBUT:
case EXACTLY: {
int s = p + 2;
while (preg->program[s++]) {
}
return s - p;
}
}
return 2;
}
size_t jim_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",
"invalid escape \\ sequence",
"corrupted program",
"contains null char",
"brackets [] not balanced",
};
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 jim_regfree(regex_t *preg)
{
free(preg->program);
}
#endif
#include <string.h>
void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
{
Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno()));
}
#if defined(_WIN32) || defined(WIN32)
#include <sys/stat.h>
int Jim_Errno(void)
{
switch (GetLastError()) {
case ERROR_FILE_NOT_FOUND: return ENOENT;
case ERROR_PATH_NOT_FOUND: return ENOENT;
case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
case ERROR_ACCESS_DENIED: return EACCES;
case ERROR_INVALID_HANDLE: return EBADF;
case ERROR_BAD_ENVIRONMENT: return E2BIG;
case ERROR_BAD_FORMAT: return ENOEXEC;
case ERROR_INVALID_ACCESS: return EACCES;
case ERROR_INVALID_DRIVE: return ENOENT;
case ERROR_CURRENT_DIRECTORY: return EACCES;
case ERROR_NOT_SAME_DEVICE: return EXDEV;
case ERROR_NO_MORE_FILES: return ENOENT;
case ERROR_WRITE_PROTECT: return EROFS;
case ERROR_BAD_UNIT: return ENXIO;
case ERROR_NOT_READY: return EBUSY;
case ERROR_BAD_COMMAND: return EIO;
case ERROR_CRC: return EIO;
case ERROR_BAD_LENGTH: return EIO;
case ERROR_SEEK: return EIO;
case ERROR_WRITE_FAULT: return EIO;
case ERROR_READ_FAULT: return EIO;
case ERROR_GEN_FAILURE: return EIO;
case ERROR_SHARING_VIOLATION: return EACCES;
case ERROR_LOCK_VIOLATION: return EACCES;
case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
case ERROR_HANDLE_DISK_FULL: return ENOSPC;
case ERROR_NOT_SUPPORTED: return ENODEV;
case ERROR_REM_NOT_LIST: return EBUSY;
case ERROR_DUP_NAME: return EEXIST;
case ERROR_BAD_NETPATH: return ENOENT;
case ERROR_NETWORK_BUSY: return EBUSY;
case ERROR_DEV_NOT_EXIST: return ENODEV;
case ERROR_TOO_MANY_CMDS: return EAGAIN;
case ERROR_ADAP_HDW_ERR: return EIO;
case ERROR_BAD_NET_RESP: return EIO;
case ERROR_UNEXP_NET_ERR: return EIO;
case ERROR_NETNAME_DELETED: return ENOENT;
case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
case ERROR_BAD_DEV_TYPE: return ENODEV;
case ERROR_BAD_NET_NAME: return ENOENT;
case ERROR_TOO_MANY_NAMES: return ENFILE;
case ERROR_TOO_MANY_SESS: return EIO;
case ERROR_SHARING_PAUSED: return EAGAIN;
case ERROR_REDIR_PAUSED: return EAGAIN;
case ERROR_FILE_EXISTS: return EEXIST;
case ERROR_CANNOT_MAKE: return ENOSPC;
case ERROR_OUT_OF_STRUCTURES: return ENFILE;
case ERROR_ALREADY_ASSIGNED: return EEXIST;
case ERROR_INVALID_PASSWORD: return EPERM;
case ERROR_NET_WRITE_FAULT: return EIO;
case ERROR_NO_PROC_SLOTS: return EAGAIN;
case ERROR_DISK_CHANGE: return EXDEV;
case ERROR_BROKEN_PIPE: return EPIPE;
case ERROR_OPEN_FAILED: return ENOENT;
case ERROR_DISK_FULL: return ENOSPC;
case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
case ERROR_INVALID_TARGET_HANDLE: return EBADF;
case ERROR_INVALID_NAME: return ENOENT;
case ERROR_PROC_NOT_FOUND: return ESRCH;
case ERROR_WAIT_NO_CHILDREN: return ECHILD;
case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
case ERROR_SEEK_ON_DEVICE: return ESPIPE;
case ERROR_BUSY_DRIVE: return EAGAIN;
case ERROR_DIR_NOT_EMPTY: return EEXIST;
case ERROR_NOT_LOCKED: return EACCES;
case ERROR_BAD_PATHNAME: return ENOENT;
case ERROR_LOCK_FAILED: return EACCES;
case ERROR_ALREADY_EXISTS: return EEXIST;
case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
case ERROR_BAD_PIPE: return EPIPE;
case ERROR_PIPE_BUSY: return EAGAIN;
case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
case ERROR_DIRECTORY: return ENOTDIR;
}
return EINVAL;
}
long JimProcessPid(phandle_t pid)
{
if (pid == INVALID_HANDLE_VALUE) {
return -1;
}
return GetProcessId(pid);
}
phandle_t JimWaitPid(long pid, int *status, int nohang)
{
if (pid > 0) {
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid);
if (h) {
long pid = waitpid(h, status, nohang);
CloseHandle(h);
if (pid > 0) {
return h;
}
}
}
return JIM_BAD_PHANDLE;
}
long waitpid(phandle_t phandle, int *status, int nohang)
{
long pid;
DWORD ret = WaitForSingleObject(phandle, nohang ? 0 : INFINITE);
if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
return -1;
}
GetExitCodeProcess(phandle, &ret);
*status = ret;
pid = GetProcessId(phandle);
CloseHandle(phandle);
return pid;
}
int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
{
char name[MAX_PATH];
HANDLE handle;
if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) {
return -1;
}
handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0),
NULL);
if (handle == INVALID_HANDLE_VALUE) {
goto error;
}
Jim_SetResultString(interp, name, -1);
return _open_osfhandle((intptr_t)handle, _O_RDWR | _O_TEXT);
error:
Jim_SetResultErrno(interp, name);
DeleteFile(name);
return -1;
}
int Jim_OpenForWrite(const char *filename, int append)
{
if (strcmp(filename, "/dev/null") == 0) {
filename = "nul:";
}
int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE);
if (fd >= 0 && append) {
_lseek(fd, 0L, SEEK_END);
}
return fd;
}
int Jim_OpenForRead(const char *filename)
{
if (strcmp(filename, "/dev/null") == 0) {
filename = "nul:";
}
return _open(filename, _O_RDONLY | _O_TEXT, 0);
}
#elif defined(HAVE_UNISTD_H)
int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
{
int fd;
mode_t mask;
Jim_Obj *filenameObj;
if (filename_template == NULL) {
const char *tmpdir = getenv("TMPDIR");
if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
tmpdir = "/tmp/";
}
filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
Jim_AppendString(interp, filenameObj, "/", 1);
}
Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
}
else {
filenameObj = Jim_NewStringObj(interp, filename_template, -1);
}
#ifdef HAVE_UMASK
mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
#endif
#ifdef HAVE_MKSTEMP
fd = mkstemp(filenameObj->bytes);
#else
if (mktemp(filenameObj->bytes) == NULL) {
fd = -1;
}
else {
fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC);
}
#endif
#ifdef HAVE_UMASK
umask(mask);
#endif
if (fd < 0) {
Jim_SetResultErrno(interp, Jim_String(filenameObj));
Jim_FreeNewObj(interp, filenameObj);
return -1;
}
if (unlink_file) {
remove(Jim_String(filenameObj));
}
Jim_SetResult(interp, filenameObj);
return fd;
}
int Jim_OpenForWrite(const char *filename, int append)
{
return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
}
int Jim_OpenForRead(const char *filename)
{
return open(filename, O_RDONLY, 0);
}
#endif
#if defined(_WIN32) || defined(WIN32)
#ifndef STRICT
#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)
{
static char msg[121];
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
LANG_NEUTRAL, msg, sizeof(msg) - 1, NULL);
return msg;
}
#endif
#ifdef _MSC_VER
#include <sys/timeb.h>
int gettimeofday(struct timeval *tv, void *unused)
{
struct _timeb tb;
_ftime(&tb);
tv->tv_sec = tb.time;
tv->tv_usec = tb.millitm * 1000;
return 0;
}
DIR *opendir(const char *name)
{
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;
}
}
else {
errno = EINVAL;
}
return dir;
}
int closedir(DIR * dir)
{
int result = -1;
if (dir) {
if (dir->handle != -1)
result = _findclose(dir->handle);
Jim_Free(dir->name);
Jim_Free(dir);
}
if (result == -1)
errno = EBADF;
return result;
}
struct dirent *readdir(DIR * dir)
{
struct dirent *result = 0;
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 {
errno = EBADF;
}
return result;
}
#endif
#endif
#include <stdio.h>
#include <signal.h>
#ifndef SIGPIPE
#define SIGPIPE 13
#endif
#ifndef SIGINT
#define SIGINT 2
#endif
const char *Jim_SignalId(int sig)
{
static char buf[10];
switch (sig) {
case SIGINT: return "SIGINT";
case SIGPIPE: return "SIGPIPE";
}
snprintf(buf, sizeof(buf), "%d", sig);
return buf;
}
#ifndef JIM_BOOTSTRAP_LIB_ONLY
#include <errno.h>
#include <string.h>
#include <stdio.h>
#ifdef USE_LINENOISE
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include "linenoise.h"
#else
#define MAX_LINE_LEN 512
#endif
#ifdef USE_LINENOISE
struct JimCompletionInfo {
Jim_Interp *interp;
Jim_Obj *completion_command;
Jim_Obj *hints_command;
};
static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp);
static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata);
static const char completion_callback_assoc_key[] = "interactive-completion";
static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata);
static void JimFreeHintsCallback(void *hint, void *userdata);
#endif
char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
{
#ifdef USE_LINENOISE
struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
char *result;
Jim_Obj *objPtr;
long mlmode = 0;
if (compinfo->completion_command) {
linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
}
if (compinfo->hints_command) {
linenoiseSetHintsCallback(JimHintsCallback, compinfo);
linenoiseSetFreeHintsCallback(JimFreeHintsCallback);
}
objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE);
if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) {
linenoiseSetMultiLine(mlmode);
}
result = linenoise(prompt);
linenoiseSetCompletionCallback(NULL, NULL);
linenoiseSetHintsCallback(NULL, NULL);
linenoiseSetFreeHintsCallback(NULL);
return result;
#else
int len;
char *line = Jim_Alloc(MAX_LINE_LEN);
fputs(prompt, stdout);
fflush(stdout);
if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
Jim_Free(line);
return NULL;
}
len = strlen(line);
if (len && line[len - 1] == '\n') {
line[len - 1] = '\0';
}
return line;
#endif
}
void Jim_HistoryLoad(const char *filename)
{
#ifdef USE_LINENOISE
linenoiseHistoryLoad(filename);
#endif
}
void Jim_HistoryAdd(const char *line)
{
#ifdef USE_LINENOISE
linenoiseHistoryAdd(line);
#endif
}
void Jim_HistorySave(const char *filename)
{
#ifdef USE_LINENOISE
#ifdef HAVE_UMASK
mode_t mask;
mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
#endif
linenoiseHistorySave(filename);
#ifdef HAVE_UMASK
umask(mask);
#endif
#endif
}
void Jim_HistoryShow(void)
{
#ifdef USE_LINENOISE
int i;
int len;
char **history = linenoiseHistory(&len);
for (i = 0; i < len; i++) {
printf("%4d %s\n", i + 1, history[i]);
}
#endif
}
void Jim_HistorySetMaxLen(int length)
{
#ifdef USE_LINENOISE
linenoiseHistorySetMaxLen(length);
#endif
}
int Jim_HistoryGetMaxLen(void)
{
#ifdef USE_LINENOISE
return linenoiseHistoryGetMaxLen();
#endif
return 0;
}
#ifdef USE_LINENOISE
static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
{
struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
Jim_Obj *objv[2];
int ret;
objv[0] = info->completion_command;
objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
ret = Jim_EvalObjVector(info->interp, 2, objv);
if (ret == JIM_OK) {
int i;
Jim_Obj *listObj = Jim_GetResult(info->interp);
int len = Jim_ListLength(info->interp, listObj);
for (i = 0; i < len; i++) {
linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
}
}
}
static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata)
{
struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
Jim_Obj *objv[2];
int ret;
char *result = NULL;
objv[0] = info->hints_command;
objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
ret = Jim_EvalObjVector(info->interp, 2, objv);
if (ret == JIM_OK) {
Jim_Obj *listObj = Jim_GetResult(info->interp);
Jim_IncrRefCount(listObj);
int len = Jim_ListLength(info->interp, listObj);
if (len >= 1) {
long x;
result = Jim_StrDup(Jim_String(Jim_ListGetIndex(info->interp, listObj, 0)));
if (len >= 2 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 1), &x) == JIM_OK) {
*color = x;
}
if (len >= 3 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 2), &x) == JIM_OK) {
*bold = x;
}
}
Jim_DecrRefCount(info->interp, listObj);
}
return result;
}
static void JimFreeHintsCallback(void *hint, void *userdata)
{
Jim_Free(hint);
}
static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
{
struct JimCompletionInfo *compinfo = data;
if (compinfo->completion_command) {
Jim_DecrRefCount(interp, compinfo->completion_command);
}
if (compinfo->hints_command) {
Jim_DecrRefCount(interp, compinfo->hints_command);
}
Jim_Free(compinfo);
}
static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp)
{
struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key);
if (compinfo == NULL) {
compinfo = Jim_Alloc(sizeof(*compinfo));
compinfo->interp = interp;
compinfo->completion_command = NULL;
compinfo->hints_command = NULL;
Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
}
return compinfo;
}
#endif
void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj)
{
#ifdef USE_LINENOISE
struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
if (completionCommandObj) {
Jim_IncrRefCount(completionCommandObj);
}
if (compinfo->completion_command) {
Jim_DecrRefCount(interp, compinfo->completion_command);
}
compinfo->completion_command = completionCommandObj;
#endif
}
void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj)
{
#ifdef USE_LINENOISE
struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
if (hintsCommandObj) {
Jim_IncrRefCount(hintsCommandObj);
}
if (compinfo->hints_command) {
Jim_DecrRefCount(interp, compinfo->hints_command);
}
compinfo->hints_command = hintsCommandObj;
#endif
}
int Jim_InteractivePrompt(Jim_Interp *interp)
{
int retcode = JIM_OK;
char *history_file = NULL;
#ifdef USE_LINENOISE
const char *home;
home = getenv("HOME");
if (home && isatty(STDIN_FILENO)) {
int history_len = strlen(home) + sizeof("/.jim_history");
history_file = Jim_Alloc(history_len);
snprintf(history_file, history_len, "%s/.jim_history", home);
Jim_HistoryLoad(history_file);
}
Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
Jim_HistorySetHints(interp, Jim_NewStringObj(interp, "tcl::stdhint", -1));
#endif
printf("Welcome to Jim version %d.%d\n",
JIM_VERSION / 100, JIM_VERSION % 100);
Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
while (1) {
Jim_Obj *scriptObjPtr;
const char *result;
int reslen;
char prompt[20];
if (retcode != JIM_OK) {
const char *retcodestr = Jim_ReturnCode(retcode);
if (*retcodestr == '?') {
snprintf(prompt, sizeof(prompt) - 3, "[%d] . ", retcode);
}
else {
snprintf(prompt, sizeof(prompt) - 3, "[%s] . ", retcodestr);
}
}
else {
strcpy(prompt, ". ");
}
scriptObjPtr = Jim_NewStringObj(interp, "", 0);
Jim_IncrRefCount(scriptObjPtr);
while (1) {
char state;
char *line;
line = Jim_HistoryGetline(interp, prompt);
if (line == NULL) {
if (errno == EINTR) {
continue;
}
Jim_DecrRefCount(interp, scriptObjPtr);
retcode = JIM_OK;
goto out;
}
if (Jim_Length(scriptObjPtr) != 0) {
Jim_AppendString(interp, scriptObjPtr, "\n", 1);
}
Jim_AppendString(interp, scriptObjPtr, line, -1);
Jim_Free(line);
if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
break;
snprintf(prompt, sizeof(prompt), "%c> ", state);
}
#ifdef USE_LINENOISE
if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
Jim_HistoryShow();
Jim_DecrRefCount(interp, scriptObjPtr);
continue;
}
Jim_HistoryAdd(Jim_String(scriptObjPtr));
if (history_file) {
Jim_HistorySave(history_file);
}
#endif
retcode = Jim_EvalObj(interp, scriptObjPtr);
Jim_DecrRefCount(interp, scriptObjPtr);
if (retcode == JIM_EXIT) {
break;
}
if (retcode == JIM_ERR) {
Jim_MakeErrorMessage(interp);
}
result = Jim_GetString(Jim_GetResult(interp), &reslen);
if (reslen) {
if (fwrite(result, reslen, 1, stdout) == 0) {
}
putchar('\n');
}
}
out:
Jim_Free(history_file);
return retcode;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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);
for (n = 0; n < argc; n++) {
Jim_Obj *obj = Jim_NewStringObj(interp, argv[n], -1);
Jim_ListAppendElement(interp, listObj, obj);
}
Jim_SetVariableStr(interp, "argv", listObj);
Jim_SetVariableStr(interp, "argc", Jim_NewIntObj(interp, argc));
}
static void JimPrintErrorMessage(Jim_Interp *interp)
{
Jim_MakeErrorMessage(interp);
fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp)));
}
void usage(const char* executable_name)
{
printf("jimsh version %d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
printf("Usage: %s\n", executable_name);
printf("or : %s [options] [filename]\n", executable_name);
printf("\n");
printf("Without options: Interactive mode\n");
printf("\n");
printf("Options:\n");
printf(" --version : prints the version string\n");
printf(" --help : prints this text\n");
printf(" -e CMD : executes command CMD\n");
printf(" NOTE: all subsequent options will be passed as arguments to the command\n");
printf(" [filename|-] : executes the script contained in the named file, or from stdin if \"-\"\n");
printf(" NOTE: all subsequent options will be passed to the script\n\n");
}
int main(int argc, char *const argv[])
{
int retcode;
Jim_Interp *interp;
char *const orig_argv0 = argv[0];
if (argc > 1 && strcmp(argv[1], "--version") == 0) {
printf("%d.%d\n", JIM_VERSION / 100, JIM_VERSION % 100);
return 0;
}
else if (argc > 1 && strcmp(argv[1], "--help") == 0) {
usage(argv[0]);
return 0;
}
interp = Jim_CreateInterp();
Jim_RegisterCoreCommands(interp);
if (Jim_InitStaticExtensions(interp) != JIM_OK) {
JimPrintErrorMessage(interp);
}
Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
#ifdef USE_LINENOISE
Jim_SetVariableStrWithStr(interp, "jim::lineedit", "1");
#else
Jim_SetVariableStrWithStr(interp, "jim::lineedit", "0");
#endif
retcode = Jim_initjimshInit(interp);
if (argc == 1) {
if (retcode == JIM_ERR) {
JimPrintErrorMessage(interp);
}
if (retcode != JIM_EXIT) {
JimSetArgv(interp, 0, NULL);
if (!isatty(STDIN_FILENO)) {
goto eval_stdin;
}
retcode = Jim_InteractivePrompt(interp);
}
}
else {
if (argc > 2 && strcmp(argv[1], "-e") == 0) {
JimSetArgv(interp, argc - 3, argv + 3);
retcode = Jim_Eval(interp, argv[2]);
if (retcode != JIM_ERR) {
int len;
const char *msg = Jim_GetString(Jim_GetResult(interp), &len);
if (fwrite(msg, len, 1, stdout) == 0) {
}
putchar('\n');
}
}
else {
Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
JimSetArgv(interp, argc - 2, argv + 2);
if (strcmp(argv[1], "-") == 0) {
eval_stdin:
retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]");
} else {
retcode = Jim_EvalFile(interp, argv[1]);
}
}
if (retcode == JIM_ERR) {
JimPrintErrorMessage(interp);
}
}
if (retcode == JIM_EXIT) {
retcode = Jim_GetExitCode(interp);
}
else if (retcode == JIM_ERR) {
retcode = 1;
}
else {
retcode = 0;
}
Jim_FreeInterp(interp);
return retcode;
}
#endif