/** * Returns the parent of the given namespace. * * ::bob::tom => ::bob * bob::tom => bob * ::bob => :: * bob => "" * :: => "" * "" => "" */ Jim_Obj *Jim_NamespaceQualifiers(Jim_Interp *interp, Jim_Obj *ns) { const char *name = Jim_String(ns); const char *pt = strrchr(name, ':'); if (pt && pt != name && pt[-1] == ':') { return Jim_NewStringObj(interp, name, pt - name - 1); } else { return interp->emptyObj; } }
/* win32.MessageBox message title ?type? */ static int Win32_MessageBox(Jim_Interp *interp, int objc, Jim_Obj * const *objv) { int r; const char *message, *title; long int type = 0; if (objc < 3 || objc > 4) { Jim_WrongNumArgs(interp, 1, objv, "message title ?type?"); return JIM_ERR; } message = Jim_String(objv[1]); title = Jim_String(objv[2]); if (objc == 4) { if (Jim_GetLong(interp, objv[3], &type) != JIM_OK) return JIM_ERR; } r = (int) MessageBoxA(NULL, message, title, (int)type); Jim_SetResultInt(interp, r); return JIM_OK; }
static int file_cmd_extension(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)) { p = ""; } Jim_SetResultString(interp, p, -1); return JIM_OK; }
/* win32.ShellExecute verb file args */ static int Win32_ShellExecute(Jim_Interp *interp, int objc, Jim_Obj * const *objv) { ptrdiff_t r; const char *verb, *file, *parm = NULL; char cwd[MAX_PATH + 1]; if (objc < 3 || objc > 4) { Jim_WrongNumArgs(interp, 1, objv, "verb path ?parameters?"); return JIM_ERR; } verb = Jim_String(objv[1]); file = Jim_String(objv[2]); GetCurrentDirectoryA(MAX_PATH + 1, cwd); if (objc == 4) parm = Jim_String(objv[3]); r = (ptrdiff_t)ShellExecuteA(NULL, verb, file, parm, cwd, SW_SHOWNORMAL); if (r < 33) Jim_SetResult(interp, Win32ErrorObj(interp, "ShellExecute", GetLastError())); return (r < 33) ? JIM_ERR : JIM_OK; }
static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *path = Jim_String(argv[0]); const char *lastSlash = strrchr(path, '/'); if (lastSlash) { Jim_SetResultString(interp, lastSlash + 1, -1); } else { Jim_SetResult(interp, argv[0]); } 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 JimRlReadlineCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { char *line; if (argc != 2) { Jim_WrongNumArgs(interp, 1, argv, "prompt"); return JIM_ERR; } line = readline(Jim_String(argv[1])); if (!line) { return JIM_EXIT; } Jim_SetResult(interp, Jim_NewStringObj(interp, line, -1)); return JIM_OK; }
static int interp_cmd_alias(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Interp *child = Jim_CmdPrivData(interp); Jim_Obj *aliasPrefixList; /* The prefix list will be held inside the child, but it still belongs * to the parent! */ aliasPrefixList = Jim_NewListObj(interp, argv + 1, argc - 1); Jim_IncrRefCount(aliasPrefixList); Jim_CreateCommand(child, Jim_String(argv[0]), JimInterpAliasProc, aliasPrefixList, JimInterpDelAlias); return JIM_OK; }
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 Win32_SetComputerName(Jim_Interp *interp, int objc, Jim_Obj * const *objv) { int r = JIM_OK; const char *name; if (objc != 2) { Jim_WrongNumArgs(interp, 1, objv, "computername"); return JIM_ERR; } name = Jim_String(objv[1]); if (!SetComputerNameA(name)) { Jim_SetResult(interp, Win32ErrorObj(interp, "SetComputerName", GetLastError())); r = JIM_ERR; } return r; }
static int Win32_LoadLibrary(Jim_Interp *interp, int objc, Jim_Obj * const *objv) { HMODULE hLib = NULL; if (objc != 2) { Jim_WrongNumArgs(interp, 1, objv, "path"); return JIM_ERR; } hLib = LoadLibraryA(Jim_String(objv[1])); if (hLib == NULL) { Jim_SetResult(interp, Win32ErrorObj(interp, "LoadLibrary", GetLastError())); return JIM_ERR; } Jim_SetResult(interp, Jim_NewIntObj(interp, (unsigned long)hLib)); return JIM_OK; }
/* *----------------------------------------------------------------------------- * * Jim_ReaddirCmd -- * Implements the rename TCL command: * readdir ?-nocomplain? dirPath * * Results: * Standard TCL result. *----------------------------------------------------------------------------- */ int Jim_ReaddirCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *dirPath; DIR *dirPtr; struct dirent *entryPtr; 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; } }
/* Poor man's implementation of exec with system() * The system() call *may* do command line redirection, etc. * The standard output is not available. * Can't redirect filehandles. */ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp); int i, j; int rc; /* Create a quoted command line */ for (i = 1; i < argc; i++) { int len; const char *arg = Jim_GetString(argv[i], &len); if (i > 1) { Jim_AppendString(interp, cmdlineObj, " ", 1); } if (strpbrk(arg, "\\\" ") == NULL) { /* No quoting required */ Jim_AppendString(interp, cmdlineObj, arg, len); continue; } Jim_AppendString(interp, cmdlineObj, "\"", 1); for (j = 0; j < len; j++) { if (arg[j] == '\\' || arg[j] == '"') { Jim_AppendString(interp, cmdlineObj, "\\", 1); } Jim_AppendString(interp, cmdlineObj, &arg[j], 1); } Jim_AppendString(interp, cmdlineObj, "\"", 1); } rc = system(Jim_String(cmdlineObj)); Jim_FreeNewObj(interp, cmdlineObj); if (rc) { Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0); Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1)); Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, 0)); Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, rc)); Jim_SetGlobalVariableStr(interp, "errorCode", errorCode); return JIM_ERR; } return JIM_OK; }
static int Jim_KillCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int sig; long pid; Jim_Obj *pidObj; const char *signame; if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 1, argv, "?SIG|-0? pid"); return JIM_ERR; } if (argc == 2) { sig = SIGTERM; pidObj = argv[1]; } else { signame = Jim_String(argv[1]); pidObj = argv[2]; /* Special 'kill -0 pid' to determine if a pid exists */ if (strcmp(signame, "-0") == 0 || strcmp(signame, "0") == 0) { sig = 0; } else { sig = find_signal_by_name(interp, signame); if (sig < 0) { return JIM_ERR; } } } if (Jim_GetLong(interp, pidObj, &pid) != JIM_OK) { return JIM_ERR; } if (kill(pid, sig) == 0) { return JIM_OK; } Jim_SetResultString(interp, "kill: Failed to deliver signal", -1); return JIM_ERR; }
static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { #ifdef HAVE_REALPATH const char *path = Jim_String(argv[0]); char *newname = Jim_Alloc(MAXPATHLEN + 1); if (realpath(path, newname)) { Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1)); return JIM_OK; } else { Jim_Free(newname); Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno)); return JIM_ERR; } #else Jim_SetResultString(interp, "Not implemented", -1); return JIM_ERR; #endif }
int BuiltInProc::call(struct Jim_Interp * interp, int argc, Jim_Obj * const * argv) { // Check for the help command if (argc == 2 && Jim_CompareStringImmediate(interp, argv[1], "-help")) { Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_String(argv[0]), NULL); if (strlen(args_) > 0) Jim_AppendStrings(interp, Jim_GetResult(interp), " ", args_, NULL); Jim_AppendStrings(interp, Jim_GetResult(interp), "\""); if (strlen(desc_) > 0) Jim_AppendStrings(interp, Jim_GetResult(interp), " ", desc_, NULL); return JIM_OK; } return proc_(interp, argc, argv); }
static int Win32_GetModuleHandle(Jim_Interp *interp, int objc, Jim_Obj * const *objv) { HMODULE hModule = NULL; const char *name = NULL; if (objc < 1 || objc > 2) { Jim_WrongNumArgs(interp, 1, objv, "?name?"); return JIM_ERR; } if (objc == 2) name = Jim_String(objv[1]); hModule = GetModuleHandleA(name); if (hModule == NULL) { Jim_SetResult(interp, Win32ErrorObj(interp, "GetModuleHandle", GetLastError())); return JIM_ERR; } Jim_SetResult(interp, Jim_NewIntObj(interp, (unsigned long)hModule)); return JIM_OK; }
static int signal_cmd_check(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int clear = 0; jim_wide mask = 0; jim_wide blocked; if (argc > 0 && Jim_CompareStringImmediate(interp, argv[0], "-clear")) { clear++; } if (argc > clear) { int i; /* Signals specified */ for (i = clear; i < argc; i++) { int sig = find_signal_by_name(interp, Jim_String(argv[i])); if (sig < 0 || sig >= MAX_SIGNALS) { return -1; } mask |= sig_to_bit(sig); } } else { /* No signals specified, so check/clear all */ mask = ~mask; } if ((sigsblocked & mask) == 0) { /* No matching signals, so empty result and nothing to do */ return JIM_OK; } /* Be careful we don't have a race condition where signals are cleared but not returned */ blocked = sigsblocked & mask; if (clear) { sigsblocked &= ~blocked; } /* Set the result */ signal_set_sigmask_result(interp, blocked); return JIM_OK; }
/** * nsObj is a canonical namespace name (.e.g. "" for root, "abc" for ::abc) * * The given name is appended to the namespace name to produce a complete canonical name. * * e.g. "" "abc" => abc * "" "::abc" => abc * "" "abc::def" => abc::def * "abc" "def" => abc::def * "abc" "::def" => def * */ Jim_Obj *JimCanonicalNamespace(Jim_Interp *interp, Jim_Obj *nsObj, Jim_Obj *nameObj) { Jim_Obj *objPtr; const char *name = Jim_String(nameObj); assert(nameObj->refCount != 0); assert(nsObj->refCount != 0); if (name[0] == ':' && name[1] == ':') { /* Absolute namespace */ while (*++name == ':') { } return Jim_NewStringObj(interp, name, -1); } if (Jim_Length(nsObj) == 0) { /* Relative to the global namespace */ return nameObj; } /* Relative to non-global namespace */ objPtr = Jim_DuplicateObj(interp, nsObj); Jim_AppendString(interp, objPtr, "::", 2); Jim_AppendObj(interp, objPtr, nameObj); return objPtr; }
static regex_t *SetRegexpFromAny(Jim_Interp *interp, Jim_Obj *objPtr, unsigned flags) { regex_t *compre; const char *pattern; int ret; /* Check if the object is already an uptodate variable */ if (objPtr->typePtr == ®expObjType && objPtr->internalRep.regexpValue.compre && objPtr->internalRep.regexpValue.flags == flags) { /* nothing to do */ return objPtr->internalRep.regexpValue.compre; } /* Not a regexp or the flags do not match */ /* Get the string representation */ pattern = Jim_String(objPtr); compre = Jim_Alloc(sizeof(regex_t)); if ((ret = regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) { char buf[100]; regerror(ret, compre, buf, sizeof(buf)); Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf); regfree(compre); Jim_Free(compre); return NULL; } Jim_FreeIntRep(interp, objPtr); objPtr->typePtr = ®expObjType; objPtr->internalRep.regexpValue.flags = flags; objPtr->internalRep.regexpValue.compre = compre; return compre; }
int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr) { Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); Jim_CallFrame *savedFramePtr; int retval; savedFramePtr = interp->framePtr; interp->framePtr = interp->topFramePtr; retval = Jim_EvalObj(interp, scriptObjPtr); interp->framePtr = savedFramePtr; /* Try to report the error (if any) via the bgerror proc */ if (retval != JIM_OK && retval != JIM_RETURN && !eventLoop->suppress_bgerror) { Jim_Obj *objv[2]; int rc = JIM_ERR; objv[0] = Jim_NewStringObj(interp, "bgerror", -1); objv[1] = Jim_GetResult(interp); Jim_IncrRefCount(objv[0]); Jim_IncrRefCount(objv[1]); if (Jim_GetCommand(interp, objv[0], JIM_NONE) == NULL || (rc = Jim_EvalObjVector(interp, 2, objv)) != JIM_OK) { if (rc == JIM_BREAK) { /* No more bgerror calls */ eventLoop->suppress_bgerror++; } else { /* Report the error to stderr. */ Jim_MakeErrorMessage(interp); fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp))); /* And reset the result */ Jim_SetResultString(interp, "", -1); } } Jim_DecrRefCount(interp, objv[0]); Jim_DecrRefCount(interp, objv[1]); } return retval; }
static int JimSqliteOpenCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { sqlite3 *db; char buf[60]; int r; if (argc != 2) { Jim_WrongNumArgs(interp, 1, argv, "dbname"); return JIM_ERR; } r = sqlite3_open(Jim_String(argv[1]), &db); if (r != SQLITE_OK) { Jim_SetResultString(interp, sqlite3_errmsg(db), -1); sqlite3_close(db); return JIM_ERR; } /* Create the file command */ snprintf(buf, sizeof(buf), "sqlite.handle%ld", Jim_GetId(interp)); Jim_CreateCommand(interp, buf, JimSqliteHandlerCommand, db, JimSqliteDelProc); Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, Jim_NewStringObj(interp, buf, -1))); return JIM_OK; }
static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { const char *path = Jim_String(argv[0]); const char *p = strrchr(path, '/'); if (!p && path[0] == '.' && path[1] == '.' && path[2] == '\0') { Jim_SetResultString(interp, "..", -1); } else if (!p) { Jim_SetResultString(interp, ".", -1); } else if (p == path) { Jim_SetResultString(interp, "/", -1); } #if defined(__MINGW32__) || defined(_MSC_VER) else if (p[-1] == ':') { /* z:/dir => z:/ */ Jim_SetResultString(interp, path, p - path + 1); } #endif else { Jim_SetResultString(interp, path, p - path); } return JIM_OK; }
static int do_signal_cmd(Jim_Interp *interp, int action, int argc, Jim_Obj *const *argv) { struct sigaction sa; int i; if (argc == 0) { Jim_SetResult(interp, Jim_NewListObj(interp, NULL, 0)); for (i = 1; i < MAX_SIGNALS; i++) { if (siginfo[i].status == action) { /* Add signal name to the list */ Jim_ListAppendElement(interp, Jim_GetResult(interp), Jim_NewStringObj(interp, Jim_SignalId(i), -1)); } } return JIM_OK; } /* Catch all the signals we care about */ if (action != SIGNAL_ACTION_DEFAULT) { sa.sa_flags = 0; sigemptyset(&sa.sa_mask); if (action == SIGNAL_ACTION_HANDLE) { sa.sa_handler = signal_handler; } else { sa.sa_handler = signal_ignorer; } } /* Iterate through the provided signals */ for (i = 0; i < argc; i++) { int sig = find_signal_by_name(interp, Jim_String(argv[i])); if (sig < 0) { return JIM_ERR; } if (action != siginfo[sig].status) { /* Need to change the action for this signal */ switch (action) { case SIGNAL_ACTION_HANDLE: case SIGNAL_ACTION_IGNORE: if (siginfo[sig].status == SIGNAL_ACTION_DEFAULT) { if (!sa_old) { /* Allocate the structure the first time through */ sa_old = Jim_Alloc(sizeof(*sa_old) * MAX_SIGNALS); } sigaction(sig, &sa, &sa_old[sig]); } else { sigaction(sig, &sa, 0); } break; case SIGNAL_ACTION_DEFAULT: /* Restore old handler */ if (sa_old) { sigaction(sig, &sa_old[sig], 0); } } siginfo[sig].status = action; } } return JIM_OK; }
static int JimNamespaceCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *nsObj; Jim_Obj *objPtr; int option; static const char * const options[] = { "eval", "current", "canonical", "qualifiers", "parent", "tail", "delete", "origin", "code", "inscope", "import", "export", "which", "upvar", NULL }; enum { OPT_EVAL, OPT_CURRENT, OPT_CANONICAL, OPT_QUALIFIERS, OPT_PARENT, OPT_TAIL, OPT_DELETE, OPT_ORIGIN, OPT_CODE, OPT_INSCOPE, OPT_IMPORT, OPT_EXPORT, OPT_WHICH, OPT_UPVAR, }; if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "subcommand ?arg ...?"); return JIM_ERR; } if (Jim_GetEnum(interp, argv[1], options, &option, "subcommand", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { return JIM_ERR; } switch (option) { case OPT_EVAL: if (argc < 4) { Jim_WrongNumArgs(interp, 2, argv, "name arg ?arg...?"); return JIM_ERR; } if (argc == 4) { objPtr = argv[3]; } else { objPtr = Jim_ConcatObj(interp, argc - 3, argv + 3); } nsObj = JimCanonicalNamespace(interp, interp->framePtr->nsObj, argv[2]); return Jim_EvalNamespace(interp, objPtr, nsObj); case OPT_CURRENT: if (argc != 2) { Jim_WrongNumArgs(interp, 2, argv, ""); return JIM_ERR; } Jim_SetResult(interp, JimNamespaceCurrent(interp)); return JIM_OK; case OPT_CANONICAL: if (argc > 4) { Jim_WrongNumArgs(interp, 2, argv, "?current? ?name?"); return JIM_ERR; } if (argc == 2) { Jim_SetResult(interp, interp->framePtr->nsObj); } else if (argc == 3) { Jim_SetResult(interp, JimCanonicalNamespace(interp, interp->framePtr->nsObj, argv[2])); } else { Jim_SetResult(interp, JimCanonicalNamespace(interp, argv[2], argv[3])); } return JIM_OK; case OPT_QUALIFIERS: if (argc != 3) { Jim_WrongNumArgs(interp, 2, argv, "string"); return JIM_ERR; } Jim_SetResult(interp, Jim_NamespaceQualifiers(interp, argv[2])); return JIM_OK; case OPT_EXPORT: return JIM_OK; case OPT_TAIL: if (argc != 3) { Jim_WrongNumArgs(interp, 2, argv, "string"); return JIM_ERR; } Jim_SetResult(interp, Jim_NamespaceTail(interp, argv[2])); return JIM_OK; case OPT_PARENT: if (argc != 2 && argc != 3) { Jim_WrongNumArgs(interp, 2, argv, "?name?"); return JIM_ERR; } else { const char *name; if (argc == 3) { objPtr = argv[2]; } else { objPtr = interp->framePtr->nsObj; } if (Jim_Length(objPtr) == 0 || Jim_CompareStringImmediate(interp, objPtr, "::")) { return JIM_OK; } objPtr = Jim_NamespaceQualifiers(interp, objPtr); name = Jim_String(objPtr); if (name[0] != ':' || name[1] != ':') { /* Make it fully scoped */ Jim_SetResultString(interp, "::", 2); Jim_AppendObj(interp, Jim_GetResult(interp), objPtr); Jim_IncrRefCount(objPtr); Jim_DecrRefCount(interp, objPtr); } else { Jim_SetResult(interp, objPtr); } } return JIM_OK; } /* Implemented as a Tcl helper proc. * Note that calling a proc will change the current namespace, * so helper procs must call [uplevel namespace canon] to get the callers * namespace. */ return Jim_EvalEnsemble(interp, "namespace", options[option], argc - 2, argv + 2); }
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); } #endif printf("Welcome to Jim version %d.%d" JIM_NL, 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]; const char *str; if (retcode != 0) { const char *retcodestr = Jim_ReturnCode(retcode); if (*retcodestr == '?') { snprintf(prompt, sizeof(prompt) - 3, "[%d] ", retcode); } else { snprintf(prompt, sizeof(prompt) - 3, "[%s] ", retcodestr); } } else { prompt[0] = '\0'; } strcat(prompt, ". "); scriptObjPtr = Jim_NewStringObj(interp, "", 0); Jim_IncrRefCount(scriptObjPtr); while (1) { char state; int len; char *line; line = Jim_HistoryGetline(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); free(line); str = Jim_GetString(scriptObjPtr, &len); if (len == 0) { continue; } if (Jim_ScriptIsComplete(str, len, &state)) break; snprintf(prompt, sizeof(prompt), "%c> ", state); } #ifdef USE_LINENOISE if (strcmp(str, "h") == 0) { /* built-in history command */ 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) { retcode = JIM_EXIT; break; } if (retcode == JIM_ERR) { Jim_MakeErrorMessage(interp); } result = Jim_GetString(Jim_GetResult(interp), &reslen); if (reslen) { printf("%s\n", result); } } out: Jim_Free(history_file); return retcode; }
int Jim_Nvp_name2value_obj(Jim_Interp *interp, const Jim_Nvp *p, Jim_Obj *o, Jim_Nvp **result) { return Jim_Nvp_name2value(interp, p, Jim_String(o), result); }
/* *---------------------------------------------------------------------- * * JimCreatePipeline -- * * Given an argc/argv array, instantiate a pipeline of processes * as described by the argv. * * Results: * The return value is a count of the number of new processes * created, or -1 if an error occurred while creating the pipeline. * *pidArrayPtr is filled in with the address of a dynamically * allocated array giving the ids of all of the processes. It * is up to the caller to free this array when it isn't needed * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in * with the file id for the input pipe for the pipeline (if any): * the caller must eventually close this file. If outPipePtr * isn't NULL, then *outPipePtr is filled in with the file id * for the output pipe from the pipeline: the caller must close * this file. If errFilePtr isn't NULL, then *errFilePtr is filled * with a file id that may be used to read error output after the * pipeline completes. * * Side effects: * Processes and pipes are created. * *---------------------------------------------------------------------- */ static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr) { pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all * the pids of child processes. */ int numPids = 0; /* Actual number of processes that exist * at *pidPtr right now. */ int cmdCount; /* Count of number of distinct commands * found in argc/argv. */ const char *input = NULL; /* Describes input for pipeline, depending * on "inputFile". NULL means take input * from stdin/pipe. */ int input_len = 0; /* Length of input, if relevant */ #define FILE_NAME 0 /* input/output: filename */ #define FILE_APPEND 1 /* output only: filename, append */ #define FILE_HANDLE 2 /* input/output: filehandle */ #define FILE_TEXT 3 /* input only: input is actual text */ 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. */ fdtype inputId = JIM_BAD_FD; /* Readable file id input to current command in * pipeline (could be file or pipe). JIM_BAD_FD * means use stdin. */ fdtype outputId = JIM_BAD_FD; /* Writable file id for output from current * command in pipeline (could be file or pipe). * JIM_BAD_FD means use stdout. */ fdtype errorId = JIM_BAD_FD; /* Writable file id for all standard error * output from all commands in pipeline. JIM_BAD_FD * means use stderr. */ fdtype lastOutputId = JIM_BAD_FD; /* Write file id for output from last command * in pipeline (could be file or pipe). * -1 means use stdout. */ fdtype pipeIds[2]; /* File ids for pipe that's being created. */ int firstArg, lastArg; /* Indexes of first and last arguments in * current command. */ int lastBar; int i; pidtype pid; char **save_environ; struct WaitInfoTable *table = Jim_CmdPrivData(interp); /* Holds the args which will be used to exec */ char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1)); int arg_count = 0; JimReapDetachedPids(table); if (inPipePtr != NULL) { *inPipePtr = JIM_BAD_FD; } if (outPipePtr != NULL) { *outPipePtr = JIM_BAD_FD; } if (errFilePtr != NULL) { *errFilePtr = JIM_BAD_FD; } pipeIds[0] = pipeIds[1] = JIM_BAD_FD; /* * First, scan through all the arguments to figure out the structure * of the pipeline. Count the number of distinct processes (it's the * number of "|" arguments). If there are "<", "<<", or ">" arguments * then make note of input and output redirection and remove these * arguments and the arguments that follow them. */ cmdCount = 1; lastBar = -1; for (i = 0; i < argc; i++) { const char *arg = Jim_String(argv[i]); 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 == '&') { /* Redirect stderr too */ 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++; } /* Either |, |& or a "normal" arg, so store it in the arg array */ 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; } /* Must do this before vfork(), so do it now */ save_environ = JimSaveEnv(JimBuildEnv(interp)); /* * Set up the redirected input source for the pipeline, if * so requested. */ if (input != NULL) { if (inputFile == FILE_TEXT) { /* * Immediate data in command. Create temporary file and * put data into file. */ inputId = JimCreateTemp(interp, input, input_len); if (inputId == JIM_BAD_FD) { goto error; } } else if (inputFile == FILE_HANDLE) { /* Should be a file descriptor */ FILE *fh = JimGetAioFilehandle(interp, input); if (fh == NULL) { goto error; } inputId = JimDupFd(JimFileno(fh)); } else { /* * File redirection. Just open the file. */ inputId = JimOpenForRead(input); if (inputId == JIM_BAD_FD) { Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError()); goto error; } } } else if (inPipePtr != NULL) { if (JimPipe(pipeIds) != 0) { Jim_SetResultErrno(interp, "couldn't create input pipe for command"); goto error; } inputId = pipeIds[0]; *inPipePtr = pipeIds[1]; pipeIds[0] = pipeIds[1] = JIM_BAD_FD; } /* * Set up the redirected output sink for the pipeline from one * of two places, if requested. */ if (output != NULL) { if (outputFile == FILE_HANDLE) { FILE *fh = JimGetAioFilehandle(interp, output); if (fh == NULL) { goto error; } fflush(fh); lastOutputId = JimDupFd(JimFileno(fh)); } else { /* * Output is to go to a file. */ lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND); if (lastOutputId == JIM_BAD_FD) { Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError()); goto error; } } } else if (outPipePtr != NULL) { /* * Output is to go to a pipe. */ if (JimPipe(pipeIds) != 0) { Jim_SetResultErrno(interp, "couldn't create output pipe"); goto error; } lastOutputId = pipeIds[1]; *outPipePtr = pipeIds[0]; pipeIds[0] = pipeIds[1] = JIM_BAD_FD; } /* If we are redirecting stderr with 2>filename or 2>@fileId, then we ignore errFilePtr */ if (error != NULL) { if (errorFile == FILE_HANDLE) { if (strcmp(error, "1") == 0) { /* Special 2>@1 */ if (lastOutputId != JIM_BAD_FD) { errorId = JimDupFd(lastOutputId); } else { /* No redirection of stdout, so just use 2>@stdout */ error = "stdout"; } } if (errorId == JIM_BAD_FD) { FILE *fh = JimGetAioFilehandle(interp, error); if (fh == NULL) { goto error; } fflush(fh); errorId = JimDupFd(JimFileno(fh)); } } else { /* * Output is to go to a file. */ errorId = JimOpenForWrite(error, errorFile == FILE_APPEND); if (errorId == JIM_BAD_FD) { Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError()); goto error; } } } else if (errFilePtr != NULL) { /* * Set up the standard error output sink for the pipeline, if * requested. Use a temporary file which is opened, then deleted. * Could potentially just use pipe, but if it filled up it could * cause the pipeline to deadlock: we'd be waiting for processes * to complete before reading stderr, and processes couldn't complete * because stderr was backed up. */ errorId = JimCreateTemp(interp, NULL, 0); if (errorId == JIM_BAD_FD) { goto error; } *errFilePtr = JimDupFd(errorId); } /* * Scan through the argc array, forking off a process for each * group of arguments between "|" arguments. */ pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr)); for (i = 0; i < numPids; i++) { pidPtr[i] = JIM_BAD_PID; } for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) { int pipe_dup_err = 0; fdtype origErrorId = errorId; for (lastArg = firstArg; lastArg < arg_count; lastArg++) { if (arg_array[lastArg][0] == '|') { if (arg_array[lastArg][1] == '&') { pipe_dup_err = 1; } break; } } /* Replace | with NULL for execv() */ arg_array[lastArg] = NULL; if (lastArg == arg_count) { outputId = lastOutputId; } else { if (JimPipe(pipeIds) != 0) { Jim_SetResultErrno(interp, "couldn't create pipe"); goto error; } outputId = pipeIds[1]; } /* Now fork the child */ #ifdef __MINGW32__ pid = JimStartWinProcess(interp, &arg_array[firstArg], save_environ ? save_environ[0] : NULL, inputId, outputId, errorId); if (pid == JIM_BAD_PID) { Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]); goto error; } #else /* * Disable SIGPIPE signals: if they were allowed, this process * might go away unexpectedly if children misbehave. This code * can potentially interfere with other application code that * expects to handle SIGPIPEs; what's really needed is an * arbiter for signals to allow them to be "shared". */ if (table->info == NULL) { (void)signal(SIGPIPE, SIG_IGN); } /* Need to do this befor vfork() */ if (pipe_dup_err) { errorId = outputId; } /* * Make a new process and enter it into the table if the fork * is successful. */ pid = vfork(); if (pid < 0) { Jim_SetResultErrno(interp, "couldn't fork child process"); goto error; } if (pid == 0) { /* Child */ if (inputId != -1) dup2(inputId, 0); if (outputId != -1) dup2(outputId, 1); if (errorId != -1) dup2(errorId, 2); for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) { close(i); } execvpe(arg_array[firstArg], &arg_array[firstArg], Jim_GetEnviron()); /* Need to prep an error message before vfork(), just in case */ fprintf(stderr, "couldn't exec \"%s\"", arg_array[firstArg]); _exit(127); } #endif /* parent */ /* * Enlarge the wait table if there isn't enough space for a new * entry. */ if (table->used == table->size) { table->size += WAIT_TABLE_GROW_BY; table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info)); } table->info[table->used].pid = pid; table->info[table->used].flags = 0; table->used++; pidPtr[numPids] = pid; /* Restore in case of pipe_dup_err */ errorId = origErrorId; /* * Close off our copies of file descriptors that were set up for * this child, then set up the input for the next child. */ if (inputId != JIM_BAD_FD) { JimCloseFd(inputId); } if (outputId != JIM_BAD_FD) { JimCloseFd(outputId); } inputId = pipeIds[0]; pipeIds[0] = pipeIds[1] = JIM_BAD_FD; } *pidArrayPtr = pidPtr; /* * All done. Cleanup open files lying around and then return. */ cleanup: if (inputId != JIM_BAD_FD) { JimCloseFd(inputId); } if (lastOutputId != JIM_BAD_FD) { JimCloseFd(lastOutputId); } if (errorId != JIM_BAD_FD) { JimCloseFd(errorId); } Jim_Free(arg_array); JimRestoreEnv(save_environ); return numPids; /* * An error occurred. There could have been extra files open, such * as pipes between children. Clean them all up. Detach any child * processes that have been created. */ error: if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) { JimCloseFd(*inPipePtr); *inPipePtr = JIM_BAD_FD; } if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) { JimCloseFd(*outPipePtr); *outPipePtr = JIM_BAD_FD; } if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) { JimCloseFd(*errFilePtr); *errFilePtr = JIM_BAD_FD; } if (pipeIds[0] != JIM_BAD_FD) { JimCloseFd(pipeIds[0]); } if (pipeIds[1] != JIM_BAD_FD) { JimCloseFd(pipeIds[1]); } if (pidPtr != NULL) { for (i = 0; i < numPids; i++) { if (pidPtr[i] != JIM_BAD_PID) { JimDetachPids(interp, 1, &pidPtr[i]); } } Jim_Free(pidPtr); } numPids = -1; goto cleanup; }
static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char *env, fdtype inputId, fdtype outputId, fdtype errorId) { STARTUPINFO startInfo; PROCESS_INFORMATION procInfo; HANDLE hProcess, h; char execPath[MAX_PATH]; char *originalName; pidtype pid = JIM_BAD_PID; Jim_Obj *cmdLineObj; if (JimWinFindExecutable(argv[0], execPath) < 0) { return JIM_BAD_PID; } originalName = argv[0]; argv[0] = execPath; hProcess = GetCurrentProcess(); cmdLineObj = JimWinBuildCommandLine(interp, argv); /* * STARTF_USESTDHANDLES must be used to pass handles to child process. * Using SetStdHandle() and/or dup2() only works when a console mode * parent process is spawning an attached console mode child process. */ 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; /* * Duplicate all the handles which will be passed off as stdin, stdout * and stderr of the child process. The duplicate handles are set to * be inheritable, so the child process can use them. */ if (inputId == JIM_BAD_FD) { if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) { CloseHandle(h); } } else { DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS); } if (startInfo.hStdInput == JIM_BAD_FD) { goto end; } if (outputId == JIM_BAD_FD) { startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0, JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); } else { DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); } if (startInfo.hStdOutput == JIM_BAD_FD) { goto end; } if (errorId == JIM_BAD_FD) { /* * If handle was not set, errors should be sent to an infinitely * deep sink. */ startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0, JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); } else { DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS); } if (startInfo.hStdError == JIM_BAD_FD) { goto end; } if (!CreateProcess(NULL, (char *)Jim_String(cmdLineObj), NULL, NULL, TRUE, 0, env, NULL, &startInfo, &procInfo)) { goto end; } /* * "When an application spawns a process repeatedly, a new thread * instance will be created for each process but the previous * instances may not be cleaned up. This results in a significant * virtual memory loss each time the process is spawned. If there * is a WaitForInputIdle() call between CreateProcess() and * CloseHandle(), the problem does not occur." PSS ID Number: Q124121 */ WaitForInputIdle(procInfo.hProcess, 5000); CloseHandle(procInfo.hThread); pid = procInfo.hProcess; end: Jim_FreeNewObj(interp, cmdLineObj); if (startInfo.hStdInput != JIM_BAD_FD) { CloseHandle(startInfo.hStdInput); } if (startInfo.hStdOutput != JIM_BAD_FD) { CloseHandle(startInfo.hStdOutput); } if (startInfo.hStdError != JIM_BAD_FD) { CloseHandle(startInfo.hStdError); } return pid; }
/* Syslog_Log - * implements syslog tcl command. General format: syslog ?options? level text * options -facility -ident -options * * syslog ?-facility cron|daemon|...? ?-ident string? ?-options int? ?debug|info|...? text */ int Jim_SyslogCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { int priority = LOG_INFO; int i = 1; SyslogInfo *info = Jim_CmdPrivData(interp); if (argc <= 1) { wrongargs: Jim_WrongNumArgs(interp, 1, argv, "?-facility cron|daemon|...? ?-ident string? ?-options int? ?debug|info|...? message"); return JIM_ERR; } while (i < argc - 1) { if (Jim_CompareStringImmediate(interp, argv[i], "-facility")) { int entry = Jim_FindByName(Jim_String(argv[i + 1]), facilities, sizeof(facilities) / sizeof(*facilities)); if (entry < 0) { Jim_SetResultString(interp, "Unknown facility", -1); return JIM_ERR; } if (info->facility != entry) { info->facility = entry; if (info->logOpened) { closelog(); info->logOpened = 0; } } } else if (Jim_CompareStringImmediate(interp, argv[i], "-options")) { long tmp; if (Jim_GetLong(interp, argv[i + 1], &tmp) == JIM_ERR) { return JIM_ERR; } info->options = tmp; if (info->logOpened) { closelog(); info->logOpened = 0; } } else if (Jim_CompareStringImmediate(interp, argv[i], "-ident")) { strncpy(info->ident, Jim_String(argv[i + 1]), sizeof(info->ident)); info->ident[sizeof(info->ident) - 1] = 0; if (info->logOpened) { closelog(); info->logOpened = 0; } } else { break; } i += 2; } /* There should be either 0, 1 or 2 args left */ if (i == argc) { /* No args, but they have set some options, so OK */ return JIM_OK; } if (i < argc - 1) { priority = Jim_FindByName(Jim_String(argv[i]), priorities, sizeof(priorities) / sizeof(*priorities)); if (priority < 0) { Jim_SetResultString(interp, "Unknown priority", -1); return JIM_ERR; } i++; } if (i != argc - 1) { goto wrongargs; } if (!info->logOpened) { if (!info->ident[0]) { Jim_Obj *argv0 = Jim_GetGlobalVariableStr(interp, "argv0", JIM_NONE); if (argv0) { strncpy(info->ident, Jim_String(argv0), sizeof(info->ident)); } else { strcpy(info->ident, "Tcl script"); } info->ident[sizeof(info->ident) - 1] = 0; } openlog(info->ident, info->options, info->facility); info->logOpened = 1; } syslog(priority, "%s", Jim_String(argv[i])); return JIM_OK; }