diff options
-rw-r--r-- | jim-aio.c | 2 | ||||
-rw-r--r-- | jim-eventloop.h | 10 | ||||
-rw-r--r-- | jim-exec.c | 928 | ||||
-rw-r--r-- | jim-file.c | 677 | ||||
-rw-r--r-- | jim-subcmd.c | 194 | ||||
-rw-r--r-- | jim-subcmd.h | 68 | ||||
-rw-r--r-- | jim.h | 300 | ||||
-rw-r--r-- | jimsh.c | 4 |
8 files changed, 2003 insertions, 180 deletions
@@ -712,7 +712,7 @@ static int JimAioAcceptHelper(Jim_Interp *interp, AioFile *serv_af ) return JIM_OK; } -DLLEXPORT int +int Jim_AioInit(Jim_Interp *interp) { if (Jim_PackageProvide(interp, "aio", "1.0", JIM_ERRMSG) != JIM_OK) diff --git a/jim-eventloop.h b/jim-eventloop.h index 05e8c59..39be1c2 100644 --- a/jim-eventloop.h +++ b/jim-eventloop.h @@ -63,17 +63,17 @@ typedef void Jim_EventFinalizerProc(Jim_Interp *interp, void *clientData); #define JIM_EVENT_EXCEPTION 4 #define JIM_EVENT_FEOF 8 -JIM_EXPORT void JIM_API(Jim_CreateFileHandler) (Jim_Interp *interp, +JIM_EXPORT void Jim_CreateFileHandler (Jim_Interp *interp, void *handle, int mask, Jim_FileProc *proc, void *clientData, Jim_EventFinalizerProc *finalizerProc); -JIM_EXPORT void JIM_API(Jim_DeleteFileHandler) (Jim_Interp *interp, +JIM_EXPORT void Jim_DeleteFileHandler (Jim_Interp *interp, void *handle); -JIM_EXPORT jim_wide JIM_API(Jim_CreateTimeHandler) (Jim_Interp *interp, +JIM_EXPORT jim_wide Jim_CreateTimeHandler (Jim_Interp *interp, jim_wide milliseconds, Jim_TimeProc *proc, void *clientData, Jim_EventFinalizerProc *finalizerProc); -JIM_EXPORT jim_wide JIM_API(Jim_DeleteTimeHandler) (Jim_Interp *interp, jim_wide id); -JIM_EXPORT int JIM_API(Jim_ProcessEvents) (Jim_Interp *interp, int flags); +JIM_EXPORT jim_wide Jim_DeleteTimeHandler (Jim_Interp *interp, jim_wide id); +JIM_EXPORT int Jim_ProcessEvents (Jim_Interp *interp, int flags); #endif /* __JIM_EVENTLOOP_H__ */ diff --git a/jim-exec.c b/jim-exec.c new file mode 100644 index 0000000..4ed70a9 --- /dev/null +++ b/jim-exec.c @@ -0,0 +1,928 @@ +/* + * (c) 2008 Steve Bennett <steveb@workware.net.au> + * + * Implements the exec command for Jim + * + * Based on code originally from Tcl 6.7: + * + * Copyright 1987-1991 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <signal.h> +#include <fcntl.h> +#include <errno.h> + +#include "jim.h" +#include "jim-subcmd.h" + +static int Jim_CreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, + int **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr); +static void Jim_DetachPids(Jim_Interp *interp, int numPids, int *pidPtr); +static int +Jim_CleanupChildren(Jim_Interp *interp, int numPids, int *pidPtr, int errorId); + +static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg) +{ + Jim_SetResultString(interp, "", 0); + Jim_AppendStrings(interp, Jim_GetResult(interp), msg, ": ", strerror(errno), NULL); +} + +/** + * Read from 'fd' and append the data to strObj + */ +static int append_fd_to_string(Jim_Interp *interp, int fd, Jim_Obj *strObj) +{ + while (1) { + char buffer[256]; + int count; + + count = read(fd, buffer, sizeof(buffer)); + + if (count == 0) { + return JIM_OK; + } + if (count < 0) { + return JIM_ERR; + } + Jim_AppendString(interp, strObj, buffer, count); + } +} + +static int +Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + int outputId; /* File id for output pipe. -1 + * means command overrode. */ + int errorId; /* File id for temporary file + * containing error output. */ + int *pidPtr; + int numPids, result; + + /* + * See if the command is to be run in background; if so, create + * the command, detach it, and return. + */ + if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) { + argc--; + numPids = Jim_CreatePipeline(interp, argc-1, argv+1, &pidPtr, NULL, NULL, NULL); + if (numPids < 0) { + return JIM_ERR; + } + Jim_DetachPids(interp, numPids, pidPtr); + Jim_Free(pidPtr); + return JIM_OK; + } + + /* + * Create the command's pipeline. + */ + numPids = Jim_CreatePipeline(interp, argc-1, argv+1, &pidPtr, (int *) NULL, &outputId, &errorId); + if (numPids < 0) { + return JIM_ERR; + } + + /* + * Read the child's output (if any) and put it into the result. + */ + Jim_SetResultString(interp, "", 0); + + result = JIM_OK; + if (outputId != -1) { + result = append_fd_to_string(interp, outputId, Jim_GetResult(interp)); + if (result < 0) { + Jim_SetResultErrno(interp, "error reading from output pipe"); + } + } + close(outputId); + + if (Jim_CleanupChildren(interp, numPids, pidPtr, errorId) != JIM_OK) { + result = JIM_ERR; + } + return result; +} + +/* + * Data structures of the following type are used by Jim_Fork and + * Jim_WaitPids to keep track of child processes. + */ + +typedef struct { + int pid; /* Process id of child. */ + int status; /* Status returned when child exited or suspended. */ + int flags; /* Various flag bits; see below for definitions. */ +} WaitInfo; + +/* + * Flag bits in WaitInfo structures: + * + * WI_READY - Non-zero means process has exited or + * suspended since it was forked or last + * returned by Jim_WaitPids. + * WI_DETACHED - Non-zero means no-one cares about the + * process anymore. Ignore it until it + * exits, then forget about it. + */ + +#define WI_READY 1 +#define WI_DETACHED 2 + +static WaitInfo *waitTable = NULL; +static int waitTableSize = 0; /* Total number of entries available in waitTable. */ +static int waitTableUsed = 0; /* Number of entries in waitTable that + * are actually in use right now. Active + * entries are always at the beginning + * of the table. */ +#define WAIT_TABLE_GROW_BY 4 + +/* + *---------------------------------------------------------------------- + * + * Jim_Fork -- + * + * Create a new process using the vfork system call, and keep + * track of it for "safe" waiting with Jim_WaitPids. + * + * Results: + * The return value is the value returned by the vfork system + * call (0 means child, > 0 means parent (value is child id), + * < 0 means error). + * + * Side effects: + * A new process is created, and an entry is added to an internal + * table of child processes if the process is created successfully. + * + *---------------------------------------------------------------------- + */ +int +Jim_Fork(void) +{ + WaitInfo *waitPtr; + pid_t pid; + + /* + * 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 (waitTable == NULL) { + (void) signal(SIGPIPE, SIG_IGN); + } + + /* + * Enlarge the wait table if there isn't enough space for a new + * entry. + */ + if (waitTableUsed == waitTableSize) { + waitTableSize += WAIT_TABLE_GROW_BY; + waitTable = (WaitInfo *)realloc(waitTable, waitTableSize * sizeof(WaitInfo)); + } + + /* + * Make a new process and enter it into the table if the fork + * is successful. + */ + + waitPtr = &waitTable[waitTableUsed]; + pid = fork(); + if (pid > 0) { + waitPtr->pid = pid; + waitPtr->flags = 0; + waitTableUsed++; + } + return pid; +} + +/* + *---------------------------------------------------------------------- + * + * Jim_WaitPids -- + * + * This procedure is used to wait for one or more processes created + * by Jim_Fork to exit or suspend. It records information about + * all processes that exit or suspend, even those not waited for, + * so that later waits for them will be able to get the status + * information. + * + * Results: + * -1 is returned if there is an error in the wait kernel call. + * Otherwise the pid of an exited/suspended process from *pidPtr + * is returned and *statusPtr is set to the status value returned + * by the wait kernel call. + * + * Side effects: + * Doesn't return until one of the pids at *pidPtr exits or suspends. + * + *---------------------------------------------------------------------- + */ +int +Jim_WaitPids(int numPids, int *pidPtr, int *statusPtr) +{ + int i, count, pid; + WaitInfo *waitPtr; + int anyProcesses; + int status; + + while (1) { + /* + * Scan the table of child processes to see if one of the + * specified children has already exited or suspended. If so, + * remove it from the table and return its status. + */ + + anyProcesses = 0; + for (waitPtr = waitTable, count = waitTableUsed; count > 0; waitPtr++, count--) { + for (i = 0; i < numPids; i++) { + if (pidPtr[i] != waitPtr->pid) { + continue; + } + anyProcesses = 1; + if (waitPtr->flags & WI_READY) { + *statusPtr = *((int *) &waitPtr->status); + pid = waitPtr->pid; + if (WIFEXITED(waitPtr->status) || WIFSIGNALED(waitPtr->status)) { + *waitPtr = waitTable[waitTableUsed-1]; + waitTableUsed--; + } + else { + waitPtr->flags &= ~WI_READY; + } + return pid; + } + } + } + + /* + * Make sure that the caller at least specified one valid + * process to wait for. + */ + if (!anyProcesses) { + errno = ECHILD; + return -1; + } + + /* + * Wait for a process to exit or suspend, then update its + * entry in the table and go back to the beginning of the + * loop to see if it's one of the desired processes. + */ + + pid = wait(&status); + if (pid < 0) { + return pid; + } + for (waitPtr = waitTable, count = waitTableUsed; ; waitPtr++, count--) { + if (count == 0) { + break; /* Ignore unknown processes. */ + } + if (pid != waitPtr->pid) { + continue; + } + + /* + * If the process has been detached, then ignore anything + * other than an exit, and drop the entry on exit. + */ + if (waitPtr->flags & WI_DETACHED) { + if (WIFEXITED(status) || WIFSIGNALED(status)) { + *waitPtr = waitTable[waitTableUsed-1]; + waitTableUsed--; + } + } else { + waitPtr->status = status; + waitPtr->flags |= WI_READY; + } + break; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * Jim_DetachPids -- + * + * This procedure is called to indicate that one or more child + * processes have been placed in background and are no longer + * cared about. They should be ignored in future calls to + * Jim_WaitPids. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void Jim_DetachPids(Jim_Interp *interp, int numPids, int *pidPtr) +{ + WaitInfo *waitPtr; + int i, count, pid; + + for (i = 0; i < numPids; i++) { + pid = pidPtr[i]; + for (waitPtr = waitTable, count = waitTableUsed; count > 0; waitPtr++, count--) { + if (pid != waitPtr->pid) { + continue; + } + + /* + * If the process has already exited then destroy its + * table entry now. + */ + + if ((waitPtr->flags & WI_READY) && (WIFEXITED(waitPtr->status) || WIFSIGNALED(waitPtr->status))) { + *waitPtr = waitTable[waitTableUsed-1]; + waitTableUsed--; + } else { + waitPtr->flags |= WI_DETACHED; + } + goto nextPid; + } + Jim_Panic(interp, "Jim_Detach couldn't find process"); + + nextPid: + continue; + } +} + +/* + *---------------------------------------------------------------------- + * + * Jim_CreatePipeline -- + * + * Given an argc/argv array, instantiate a pipeline of processes + * as described by the argv. + * + * Results: + * The return value is a count of the number of new processes + * created, or -1 if an error occurred while creating the pipeline. + * *pidArrayPtr is filled in with the address of a dynamically + * allocated array giving the ids of all of the processes. It + * is up to the caller to free this array when it isn't needed + * anymore. If inPipePtr is non-NULL, *inPipePtr is filled in + * with the file id for the input pipe for the pipeline (if any): + * the caller must eventually close this file. If outPipePtr + * isn't NULL, then *outPipePtr is filled in with the file id + * for the output pipe from the pipeline: the caller must close + * this file. If errFilePtr isn't NULL, then *errFilePtr is filled + * with a file id that may be used to read error output after the + * pipeline completes. + * + * Side effects: + * Processes and pipes are created. + * + *---------------------------------------------------------------------- + */ +static int +Jim_CreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr) +{ + int *pidPtr = NULL; /* Points to malloc-ed array holding all + * the pids of child processes. */ + int numPids = 0; /* Actual number of processes that exist + * at *pidPtr right now. */ + int cmdCount; /* Count of number of distinct commands + * found in argc/argv. */ + const char *input = NULL; /* Describes input for pipeline, depending + * on "inputFile". NULL means take input + * from stdin/pipe. */ + +#define FILE_NAME 0 /* input/output: filename */ +#define FILE_APPEND 1 /* output only: filename, append */ +#define FILE_HANDLE 2 /* input/output: filehandle */ +#define FILE_TEXT 3 /* input only: input is actual text */ + + 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; /* Readable file id input to current command in + * pipeline (could be file or pipe). -1 + * means use stdin. */ + int outputId = -1; /* Writable file id for output from current + * command in pipeline (could be file or pipe). + * -1 means use stdout. */ + int errorId = -1; /* Writable file id for all standard error + * output from all commands in pipeline. -1 + * means use stderr. */ + int lastOutputId = -1; /* Write file id for output from last command + * in pipeline (could be file or pipe). + * -1 means use stdout. */ + int pipeIds[2]; /* File ids for pipe that's being created. */ + int firstArg, lastArg; /* Indexes of first and last arguments in + * current command. */ + int lastBar; + char *execName; + int i, pid; + + /* Holds the args which will be used to exec */ + 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; + + /* + * 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_GetString(argv[i], NULL); + + if (arg[0] == '<') { + input = arg + 1; + if (*input == '<') { + inputFile = FILE_TEXT; + input++; + } + else if (*input == '@') { + inputFile = FILE_HANDLE; + input++; + } + + if (!*input) { + input = Jim_GetString(argv[++i], NULL); + } + } + else if (arg[0] == '>') { + output = arg + 1; + if (*output == '@') { + outputFile = FILE_HANDLE; + output++; + } + else if (*output == '>') { + outputFile = FILE_APPEND; + output++; + } + if (!*output) { + output = Jim_GetString(argv[++i], NULL); + } + } + else if (arg[0] == '2' && arg[1] == '>') { + error = arg + 2; + if (*error == '@') { + errorFile = FILE_HANDLE; + error++; + } + else if (*error == '>') { + errorFile = FILE_APPEND; + error++; + } + if (!*error) { + error = Jim_GetString(argv[++i], NULL); + } + } + else { + if (arg[0] == '|' && arg[1] == 0) { + if (i == lastBar + 1 || i == argc - 1) { + Jim_SetResultString(interp, "illegal use of | in command", -1); + Jim_Free(arg_array); + return -1; + } + 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_SetResultString(interp, "", 0); + Jim_AppendStrings(interp, Jim_GetResult(interp), "can't specify \"", arg, "\" as last word in command", NULL); + Jim_Free(arg_array); + return -1; + } + } + + if (arg_count == 0) { + Jim_SetResultString(interp, "didn't specify command to execute", -1); + Jim_Free(arg_array); + return -1; + } + + /* + * Set up the redirected input source for the pipeline, if + * so requested. + */ + if (input != NULL) { + if (inputFile == FILE_TEXT) { + /* + * Immediate data in command. Create temporary file and + * put data into file. + */ + +#define TMP_STDIN_NAME "/tmp/tcl.in.XXXXXX" + char inName[sizeof(TMP_STDIN_NAME) + 1]; + int length; + + strcpy(inName, TMP_STDIN_NAME); + inputId = mkstemp(inName); + if (inputId < 0) { + Jim_SetResultErrno(interp, "couldn't create input file for command"); + goto error; + } + length = strlen(input); + if (write(inputId, input, length) != length) { + Jim_SetResultErrno(interp, "couldn't write file input for command"); + goto error; + } + if (lseek(inputId, 0L, SEEK_SET) == -1 || unlink(inName) == -1) { + Jim_SetResultErrno(interp, "couldn't reset or remove input file for command"); + goto error; + } + } + else if (inputFile == FILE_HANDLE) { + /* Should be a file descriptor */ + /* REVISIT: Validate fd */ + inputId = dup(atoi(input)); + } + else { + /* + * File redirection. Just open the file. + */ + inputId = open(input, O_RDONLY, 0); + if (inputId < 0) { + Jim_SetResultString(interp, "", 0); + Jim_AppendStrings(interp, Jim_GetResult(interp), "couldn't read file \"", input, "\": ", strerror(errno), NULL); + 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; + } + + /* + * Set up the redirected output sink for the pipeline from one + * of two places, if requested. + */ + if (output != NULL) { + if (outputFile == FILE_HANDLE) { + /* Should be a file descriptor */ + /* REVISIT: Validate fd */ + lastOutputId = dup(atoi(output)); + + /* REVISIT: ideally should flush output first */ + /* Will aio.fd do this? */ + } + else { + /* + * Output is to go to a file. + */ + int mode = O_WRONLY|O_CREAT|O_TRUNC; + + if (outputFile == FILE_APPEND) { + mode = O_WRONLY|O_CREAT|O_APPEND; + } + + lastOutputId = open(output, mode, 0666); + if (lastOutputId < 0) { + Jim_SetResultString(interp, "", 0); + Jim_AppendStrings(interp, Jim_GetResult(interp), "couldn't write file \"", output, "\": ", strerror(errno), NULL); + goto error; + } + } + } + else if (outPipePtr != NULL) { + /* + * Output is to go to a pipe. + */ + 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 we are redirecting stderr with 2>filename or 2>@fileId, then we ignore errFilePtr */ + if (error != NULL) { + if (errorFile == FILE_HANDLE) { + /* Should be a file descriptor */ + /* REVISIT: Validate fd */ + errorId = dup(atoi(error)); + + /* REVISIT: ideally should flush output first */ + /* Will aio.fd do this? */ + } + else { + /* + * Output is to go to a file. + */ + int mode = O_WRONLY|O_CREAT|O_TRUNC; + + if (errorFile == FILE_APPEND) { + mode = O_WRONLY|O_CREAT|O_APPEND; + } + + errorId = open(error, mode, 0666); + if (errorId < 0) { + Jim_SetResultString(interp, "", 0); + Jim_AppendStrings(interp, Jim_GetResult(interp), "couldn't write file \"", error, "\": ", strerror(errno), NULL); + } + } + } + else if (errFilePtr != NULL) { + /* + * Set up the standard error output sink for the pipeline, if + * requested. Use a temporary file which is opened, then deleted. + * Could potentially just use pipe, but if it filled up it could + * cause the pipeline to deadlock: we'd be waiting for processes + * to complete before reading stderr, and processes couldn't complete + * because stderr was backed up. + */ + + #define TMP_STDERR_NAME "/tmp/tcl.err.XXXXXX" + char errName[sizeof(TMP_STDERR_NAME) + 1]; + + strcpy(errName, TMP_STDERR_NAME); + errorId = mkstemp(errName); + if (errorId < 0) { + errFileError: + Jim_SetResultErrno(interp, "couldn't create error file for command"); + goto error; + } + *errFilePtr = open(errName, O_RDONLY, 0); + if (*errFilePtr < 0) { + goto errFileError; + } + if (unlink(errName) == -1) { + Jim_SetResultErrno(interp, "couldn't remove error file for command"); + goto error; + } + } + + /* + * Scan through the argc array, forking off a process for each + * group of arguments between "|" arguments. + */ + + pidPtr = (int *)Jim_Alloc(cmdCount * sizeof(*pidPtr)); + for (i = 0; i < numPids; i++) { + pidPtr[i] = -1; + } + for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg+1) { + for (lastArg = firstArg; lastArg < arg_count; lastArg++) { + if (strcmp(arg_array[lastArg], "|") == 0) { + break; + } + } + /* Replace | with NULL for execv() */ + arg_array[lastArg] = NULL; + if (lastArg == arg_count) { + outputId = lastOutputId; + } + else { + if (pipe(pipeIds) != 0) { + Jim_SetResultErrno(interp, "couldn't create pipe"); + goto error; + } + outputId = pipeIds[1]; + } + execName = arg_array[firstArg]; + pid = Jim_Fork(); + if (pid == -1) { + Jim_SetResultErrno(interp, "couldn't fork child process"); + goto error; + } + if (pid == 0) { + char errSpace[200]; + + if ((inputId != -1 && dup2(inputId, 0) == -1) + || (outputId != -1 && dup2(outputId, 1) == -1) + || (errorId != -1 &&(dup2(errorId, 2) == -1))) { + + static const char err[] = "forked process couldn't set up input/output\n"; + write(errorId < 0 ? 2 : errorId, err, strlen(err)); + _exit(1); + } + for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) { + close(i); + } + execvp(execName, &arg_array[firstArg]); + sprintf(errSpace, "couldn't find \"%.150s\" to execute\n", arg_array[firstArg]); + write(2, errSpace, strlen(errSpace)); + _exit(1); + } + else { + pidPtr[numPids] = pid; + } + + /* + * Close off our copies of file descriptors that were set up for + * this child, then set up the input for the next child. + */ + + if (inputId != -1) { + close(inputId); + } + if (outputId != -1) { + close(outputId); + } + inputId = pipeIds[0]; + pipeIds[0] = pipeIds[1] = -1; + } + *pidArrayPtr = pidPtr; + + /* + * All done. Cleanup open files lying around and then return. + */ + + cleanup: + if (inputId != -1) { + close(inputId); + } + if (lastOutputId != -1) { + close(lastOutputId); + } + if (errorId != -1) { + close(errorId); + } + Jim_Free(arg_array); + + return numPids; + + /* + * An error occurred. There could have been extra files open, such + * as pipes between children. Clean them all up. Detach any child + * processes that have been created. + */ + + error: + if ((inPipePtr != NULL) && (*inPipePtr != -1)) { + close(*inPipePtr); + *inPipePtr = -1; + } + if ((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] != -1) { + Jim_DetachPids(interp, 1, &pidPtr[i]); + } + } + Jim_Free(pidPtr); + } + numPids = -1; + goto cleanup; +} + +/* + *---------------------------------------------------------------------- + * + * CleanupChildren -- + * + * This is a utility procedure used to wait for child processes + * to exit, record information about abnormal exits, and then + * collect any stderr output generated by them. + * + * Results: + * The return value is a standard Tcl result. If anything at + * weird happened with the child processes, JIM_ERROR is returned + * and a message is left in interp->result. + * + * Side effects: + * If the last character of interp->result is a newline, then it + * is removed. File errorId gets closed, and pidPtr is freed + * back to the storage allocator. + * + *---------------------------------------------------------------------- + */ + +static int +Jim_CleanupChildren(Jim_Interp *interp, int numPids, int *pidPtr, int errorId) +{ + int result = JIM_OK; + int i, pid; + int waitStatus; + int len; + const char *p; + + for (i = 0; i < numPids; i++) { + pid = Jim_WaitPids(1, &pidPtr[i], (int *) &waitStatus); + if (pid == -1) { + /* This can happen if the process was already reaped, so just ignore it */ + continue; + } + + /* + * Create error messages for unusual process exits. An + * extra newline gets appended to each error message, but + * it gets removed below (in the same fashion that an + * extra newline in the command's output is removed). + */ + + if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) { + result = JIM_ERR; + if (WIFEXITED(waitStatus)) { + /* Nothing */ + } else if (WIFSIGNALED(waitStatus)) { + /* REVISIT: Name the signal */ + Jim_SetResultString(interp, "child killed by signal", -1); + } else if (WIFSTOPPED(waitStatus)) { + Jim_SetResultString(interp, "child suspended", -1); + } + } + } + Jim_Free(pidPtr); + + /* + * Read the standard error file. If there's anything there, + * then return an error and add the file's contents to the result + * string. + */ + + if (errorId >= 0) { + if (errorId >= 0) { + result = append_fd_to_string(interp, errorId, Jim_GetResult(interp)); + if (result < 0) { + Jim_SetResultErrno(interp, "error reading from stderr output file"); + } + } + } + + /* + * If the last character of interp->result is a newline, then remove + * the newline character (the newline would just confuse things). + * + * Note: Ideally we could do this by just reducing the length of stringrep + * by 1, but there is not API for this :-( + */ + + p = Jim_GetString(Jim_GetResult(interp), &len); + if (len > 0 && p[len - 1] == '\n') { + Jim_SetResultString(interp, p, len - 1); + } + + return result; +} + +int Jim_ExecInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL); + return JIM_OK; +} diff --git a/jim-file.c b/jim-file.c new file mode 100644 index 0000000..beb7147 --- /dev/null +++ b/jim-file.c @@ -0,0 +1,677 @@ +/* + * (c) 2008 Steve Bennett <steveb@workware.net.au> + * + * Implements the file command for jim + * + * The FreeBSD license + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the Jim Tcl Project. + * + * Based on code originally from Tcl 6.7: + * + * Copyright 1987-1991 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/param.h> + +#include "jim.h" +#include "jim-subcmd.h" + +/* + *---------------------------------------------------------------------- + * + * GetFileType -- + * + * Given a mode word, returns a string identifying the type of a + * file. + * + * Results: + * A static text string giving the file type from mode. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static const char *GetFileType(int mode) +{ + if (S_ISREG(mode)) { + return "file"; + } else if (S_ISDIR(mode)) { + return "directory"; + } else if (S_ISCHR(mode)) { + return "characterSpecial"; + } else if (S_ISBLK(mode)) { + return "blockSpecial"; + } else if (S_ISFIFO(mode)) { + return "fifo"; + } else if (S_ISLNK(mode)) { + return "link"; + } else if (S_ISSOCK(mode)) { + return "socket"; + } + return "unknown"; +} + +static void Jim_SetIntResult(Jim_Interp *interp, jim_wide wide) +{ + Jim_SetResult(interp, Jim_NewIntObj(interp, wide)); +} + +/* + *---------------------------------------------------------------------- + * + * StoreStatData -- + * + * This is a utility procedure that breaks out the fields of a + * "stat" structure and stores them in textual form into the + * elements of an associative array. + * + * Results: + * Returns a standard Tcl return value. If an error occurs then + * a message is left in interp->result. + * + * Side effects: + * Elements of the associative array given by "varName" are modified. + * + *---------------------------------------------------------------------- + */ + +static int set_array_int_value(Jim_Interp *interp, Jim_Obj *container, const char *key, jim_wide value) +{ + Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1); + Jim_Obj *valobj = Jim_NewWideObj(interp, value); + + if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj) != JIM_OK) { + Jim_FreeObj(interp, nameobj); + Jim_FreeObj(interp, valobj); + return JIM_ERR; + } + return JIM_OK; +} + +static int set_array_string_value(Jim_Interp *interp, Jim_Obj *container, const char *key, const char *value) +{ + Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1); + Jim_Obj *valobj = Jim_NewStringObj(interp, value, -1); + + if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj) != JIM_OK) { + Jim_FreeObj(interp, nameobj); + Jim_FreeObj(interp, valobj); + return JIM_ERR; + } + return JIM_OK; +} + +static int +StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb) +{ + if (set_array_int_value(interp, varName, "dev", sb->st_dev) != JIM_OK) { + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), "can't set \"", Jim_GetString(varName, NULL), "(dev)\": variable isn't array", NULL); + return JIM_ERR; + } + set_array_int_value(interp, varName, "ino", sb->st_ino); + set_array_int_value(interp, varName, "mode", sb->st_mode); + set_array_int_value(interp, varName, "nlink", sb->st_nlink); + set_array_int_value(interp, varName, "uid", sb->st_uid); + set_array_int_value(interp, varName, "gid", sb->st_gid); + set_array_int_value(interp, varName, "size", sb->st_size); + set_array_int_value(interp, varName, "atime", sb->st_atime); + set_array_int_value(interp, varName, "mtime", sb->st_mtime); + set_array_int_value(interp, varName, "ctime", sb->st_ctime); + set_array_string_value(interp, varName, "type", GetFileType((int) sb->st_mode)); + + /* And also return the value */ + Jim_SetResult(interp, Jim_GetVariable(interp, varName, 0)); + + return JIM_OK; +} + +static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *path = Jim_GetString(argv[0], NULL); + const char *p = strrchr(path, '/'); + if (!p) { + Jim_SetResultString(interp, ".", -1); + } + else if (p == path) { + Jim_SetResultString(interp, "/", -1); + } + else { + Jim_SetResultString(interp, path, p - path); + } + return JIM_OK; +} + +static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *path = Jim_GetString(argv[0], NULL); + 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) +{ + const char *path = Jim_GetString(argv[0], NULL); + 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; +} + +static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *path = Jim_GetString(argv[0], NULL); + 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_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *path = Jim_GetString(argv[0], NULL); + char *newname = Jim_Alloc(MAXPATHLEN + 1); + + if (realpath(path, newname)) { + Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1)); + } + else { + Jim_Free(newname); + Jim_SetResult(interp, argv[0]); + } + return JIM_OK; +} + +static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode) +{ + const char *path = Jim_GetString(filename, NULL); + int rc = access(path, mode); + + Jim_SetResult(interp, Jim_NewIntObj(interp, rc != -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) +{ + return file_access(interp, argv[0], X_OK); +} + +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) +{ + while (argc--) { + const char *path = Jim_GetString(argv[0], NULL); + if (unlink(path) == -1 && errno != ENOENT) { + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), "couldn't delete \"", path, "\"", NULL); + return JIM_ERR; + } + argv++; + } + return JIM_OK; +} + +static int mkdir_all(const char *path) +{ + /* REVISIT: create intermediate dirs if necessary */ + mkdir(path, 0755); + return 0; +} + +static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + while (argc--) { + mkdir_all(Jim_GetString(argv[0], NULL)); + argv++; + } + return JIM_OK; +} + +static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *source = Jim_GetString(argv[0], NULL); + const char *dest; + int force = 0; + + if (argc == 3) { + if (strcmp(source, "-force") != 0) { + return -1; + } + force++; + source = Jim_GetString(argv[1], NULL); + } + dest = Jim_GetString(argv[force + 1], NULL); + + if (!force && access(dest, F_OK) == 0) { + Jim_SetResultString(interp, "", 0); + Jim_AppendStrings(interp, Jim_GetResult(interp), "error renaming \"", source, "\" to \"", dest, "\": ", strerror(errno), NULL); + return JIM_ERR; + } + + if (rename(source, dest) != 0) { + Jim_SetResultString(interp, "", 0); + Jim_AppendStrings(interp, Jim_GetResult(interp), "error renaming \"", source, "\": ", strerror(errno), NULL); + return JIM_ERR; + } + + return JIM_OK; +} + +static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb) +{ + const char *path = Jim_GetString(filename, NULL); + if (stat(path, sb) == -1) { + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), "could not read \"", path, "\": ", strerror(errno), NULL); + return JIM_ERR; + } + return JIM_OK; +} + +static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb) +{ + const char *path = Jim_GetString(filename, NULL); + if (lstat(path, sb) == -1) { + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), "could not read \"", path, "\": ", strerror(errno), NULL); + return JIM_ERR; + } + return JIM_OK; +} + +static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + + if (file_stat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + Jim_SetIntResult(interp, sb.st_atime); + return JIM_OK; +} + +static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + + if (file_stat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + Jim_SetIntResult(interp, sb.st_mtime); + return JIM_OK; +} + +static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + + if (file_stat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + Jim_SetIntResult(interp, sb.st_size); + return JIM_OK; +} + +static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + int ret = 0; + + if (file_stat(interp, argv[0], &sb) == JIM_OK) { + ret = S_ISDIR(sb.st_mode); + } + Jim_SetIntResult(interp, ret); + return JIM_OK; +} + +static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + int ret = 0; + + if (file_stat(interp, argv[0], &sb) == JIM_OK) { + ret = S_ISREG(sb.st_mode); + } + Jim_SetIntResult(interp, ret); + return JIM_OK; +} + +static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + int ret = 0; + + if (file_stat(interp, argv[0], &sb) == JIM_OK) { + ret = (geteuid() == sb.st_uid); + } + Jim_SetIntResult(interp, ret); + return JIM_OK; +} + +#ifdef S_IFLNK +static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const char *path = Jim_GetString(argv[0], NULL); + char *linkValue = Jim_Alloc(MAXPATHLEN + 1); + + int linkLength = readlink(path, linkValue, MAXPATHLEN); + if (linkLength == -1) { + Jim_Free(linkValue); + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), "couldn't readlink \"", path, "\"", NULL); + return JIM_ERR; + } + 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) +{ + struct stat sb; + + if (file_lstat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + Jim_SetIntResult(interp, sb.st_mode); + return JIM_OK; +} + +static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + + if (file_lstat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + return StoreStatData(interp, argv[1], &sb); +} + +static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + struct stat sb; + + if (file_stat(interp, argv[0], &sb) != JIM_OK) { + return JIM_ERR; + } + return StoreStatData(interp, argv[1], &sb); +} + +static const jim_subcmd_type command_table[] = { + { .cmd = "atime", + .args = "name", + .function = file_cmd_atime, + .minargs = 1, + .maxargs = 1, + .description = "Last access time" + }, + { .cmd = "mtime", + .args = "name", + .function = file_cmd_mtime, + .minargs = 1, + .maxargs = 1, + .description = "Last modification time" + }, + { .cmd = "dirname", + .args = "name", + .function = file_cmd_dirname, + .minargs = 1, + .maxargs = 1, + .description = "Directory part of the name" + }, + { .cmd = "rootname", + .args = "name", + .function = file_cmd_rootname, + .minargs = 1, + .maxargs = 1, + .description = "Name without any extension" + }, + { .cmd = "extension", + .args = "name", + .function = file_cmd_extension, + .minargs = 1, + .maxargs = 1, + .description = "Last extension including the dot" + }, + { .cmd = "tail", + .args = "name", + .function = file_cmd_tail, + .minargs = 1, + .maxargs = 1, + .description = "Last component of the name" + }, + { .cmd = "normalize", + .args = "name", + .function = file_cmd_normalize, + .minargs = 1, + .maxargs = 1, + .description = "Normalized path of name" + }, + { .cmd = "readable", + .args = "name", + .function = file_cmd_readable, + .minargs = 1, + .maxargs = 1, + .description = "Is file readable" + }, + { .cmd = "writable", + .args = "name", + .function = file_cmd_writable, + .minargs = 1, + .maxargs = 1, + .description = "Is file writable" + }, + { .cmd = "executable", + .args = "name", + .function = file_cmd_executable, + .minargs = 1, + .maxargs = 1, + .description = "Is file executable" + }, + { .cmd = "exists", + .args = "name", + .function = file_cmd_exists, + .minargs = 1, + .maxargs = 1, + .description = "Does file exist" + }, + { .cmd = "delete", + .args = "name ...", + .function = file_cmd_delete, + .minargs = 1, + .maxargs = -1, + .description = "Deletes the file(s)" + }, + { .cmd = "mkdir", + .args = "dir ...", + .function = file_cmd_mkdir, + .minargs = 1, + .maxargs = -1, + .description = "Creates the directories" + }, + { .cmd = "rename", + .args = "?-force? source dest", + .function = file_cmd_rename, + .minargs = 2, + .maxargs = 3, + .description = "Renames a file" + }, +#ifdef S_IFLNK + { .cmd = "readlink", + .args = "name", + .function = file_cmd_readlink, + .minargs = 1, + .maxargs = 1, + .description = "Value of the symbolic link" + }, +#endif + { .cmd = "size", + .args = "name", + .function = file_cmd_size, + .minargs = 1, + .maxargs = 1, + .description = "Size of file" + }, + { .cmd = "stat", + .args = "name var", + .function = file_cmd_stat, + .minargs = 2, + .maxargs = 2, + .description = "Stores results of stat in var array" + }, + { .cmd = "lstat", + .args = "name var", + .function = file_cmd_lstat, + .minargs = 2, + .maxargs = 2, + .description = "Stores results of lstat in var array" + }, + { .cmd = "type", + .args = "name", + .function = file_cmd_type, + .minargs = 1, + .maxargs = 1, + .description = "Returns type of the file" + }, + { .cmd = "owned", + .args = "name", + .function = file_cmd_owned, + .minargs = 1, + .maxargs = 1, + .description = "Returns 1 if owned by the current owner" + }, + { .cmd = "isdirectory", + .args = "name", + .function = file_cmd_isdirectory, + .minargs = 1, + .maxargs = 1, + .description = "Returns 1 if name is a directory" + }, + { .cmd = "isfile", + .args = "name", + .function = file_cmd_isfile, + .minargs = 1, + .maxargs = 1, + .description = "Returns 1 if name is a file" + }, + { + .cmd = 0 + } +}; + +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_GetString(argv[1], NULL); + + if (chdir(path) != 0) { + Jim_SetResultString(interp, "", 0); + Jim_AppendStrings(interp, Jim_GetResult(interp), + "couldn't change working directory to \"", path, "\": ", strerror(errno), NULL); + return JIM_ERR; + } + return JIM_OK; +} + +static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + const int cwd_len=2048; + char *cwd=malloc(cwd_len); + getcwd(cwd, cwd_len); + + Jim_SetResultString(interp, cwd, -1); + + free(cwd); + return JIM_OK; +} + +int Jim_FileInit(Jim_Interp *interp) +{ + if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG) != JIM_OK) { + return JIM_ERR; + } + Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)command_table, NULL); + Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL); + Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL); + return JIM_OK; +} diff --git a/jim-subcmd.c b/jim-subcmd.c new file mode 100644 index 0000000..7c2ce03 --- /dev/null +++ b/jim-subcmd.c @@ -0,0 +1,194 @@ +#include <stdio.h> +#include <string.h> + +#include "jim-subcmd.h" + +/** + * Implements the common 'commands' subcommand + */ +static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + /* Nothing to do, since the result has already been created */ + return JIM_OK; +} + +/** + * Do-nothing command to support -commands and -usage + */ +static const jim_subcmd_type dummy_subcmd = { + .cmd = "dummy", + .function = subcmd_null, + .flags = JIM_MODFLAG_HIDDEN, +}; + +static void add_commands(Jim_Interp *interp, const jim_subcmd_type *ct, const char *sep) +{ + const char *s = ""; + + for (; ct->cmd; ct++) { + if (!(ct->flags & JIM_MODFLAG_HIDDEN)) { + Jim_AppendStrings(interp, Jim_GetResult(interp), s, ct->cmd, 0); + s = sep; + } + } +} + +static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type *command_table, const char *type, int argc, Jim_Obj *const *argv) +{ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_GetString(argv[0], NULL), ", ", type, " command \"", Jim_GetString(argv[1], NULL), "\": should be ", NULL); + add_commands(interp, command_table, ", "); +} + +static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv) +{ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), "Usage: \"", Jim_GetString(argv[0], NULL), " command ... \", where command is one of: ", NULL); + add_commands(interp, command_table, ", "); +} + +static void add_subcmd_usage(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv) +{ + Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_GetString(argv[0], NULL), " ", ct->cmd, NULL); + + if (ct->args && *ct->args) { + Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL); + } +} + +static void show_full_usage(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv) +{ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + for (; ct->cmd; ct++) { + if (!(ct->flags & JIM_MODFLAG_HIDDEN)) { + add_subcmd_usage(interp, ct, argc, argv); + if (ct->description) { + Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n ", ct->description, 0); + } + Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n", 0); + } + } +} + +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; + const char *cmdname; + int help = 0; + + cmdname = Jim_GetString(argv[0], NULL); + + if (argc < 2) { + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + Jim_AppendStrings(interp, Jim_GetResult(interp), "wrong # args: should be \"", cmdname, " command ...\"\n", 0); + Jim_AppendStrings(interp, Jim_GetResult(interp), "Use \"", cmdname, " -help\" or \"", cmdname, " -help command\" for help", 0); + return 0; + } + + cmd = argv[1]; + + if (argc == 2 && Jim_CompareStringImmediate(interp, cmd, "-usage")) { + /* Show full usage */ + show_full_usage(interp, command_table, argc, argv); + return &dummy_subcmd; + } + + /* Check for the help command */ + if (Jim_CompareStringImmediate(interp, cmd, "-help")) { + if (argc == 2) { + /* Usage for the command, not the subcommand */ + show_cmd_usage(interp, command_table, argc, argv); + return 0; + } + help = 1; + + /* Skip the 'help' command */ + cmd = argv[2]; + } + + /* Check for special builtin '-commands' command first */ + if (Jim_CompareStringImmediate(interp, cmd, "-commands")) { + /* Build the result here */ + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + add_commands(interp, command_table, " "); + return &dummy_subcmd; + } + + cmdstr = Jim_GetString(cmd, &cmdlen); + + for (ct = command_table; ct->cmd; ct++) { + if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) { + /* Found an exact match */ + break; + } + if (strncmp(cmdstr, ct->cmd, cmdlen) == 0) { + if (partial) { + /* Ambiguous */ + bad_subcmd(interp, command_table, "ambiguous", argc, argv); + return 0; + } + partial = ct; + } + continue; + } + + /* If we had an unambiguous partial match */ + if (partial && !ct->cmd) { + ct = partial; + } + + if (!ct->cmd) { + /* No matching command */ + bad_subcmd(interp, command_table, "unknown", argc, argv); + return 0; + } + + if (help) { + Jim_SetResult(interp, Jim_NewEmptyStringObj(interp)); + add_subcmd_usage(interp, ct, argc, argv); + if (ct->description) { + Jim_AppendStrings(interp, Jim_GetResult(interp), "\n\n", ct->description, 0); + } + return 0; + } + + /* Check the number of args */ + if (argc - 2 < ct->minargs || (ct->maxargs >= 0 && argc - 2> ct->maxargs)) { + Jim_SetResultString(interp, "wrong # args: should be \"", -1); + add_subcmd_usage(interp, ct, argc, argv); + Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); + + return 0; + } + + /* Good command */ + return ct; +} + +int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv) +{ + int ret = JIM_ERR; + + if (ct) { + ret = ct->function(interp, argc - 2, argv + 2); + if (ret < 0) { + Jim_SetResultString(interp, "wrong # args: should be \"", -1); + add_subcmd_usage(interp, ct, argc, argv); + Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL); + 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); +} diff --git a/jim-subcmd.h b/jim-subcmd.h new file mode 100644 index 0000000..973d80c --- /dev/null +++ b/jim-subcmd.h @@ -0,0 +1,68 @@ +/* Provides a common approach to implementing Tcl commands + * which implement subcommands + */ +#ifndef JIM_SUBCMD_H +#define JIM_SUBCMD_H + +#include <jim.h> + +#define JIM_MODFLAG_HIDDEN 0x0001 /* Don't show the subcommand in usage or commands */ + +/* Custom flags start at 0x0100 */ + +/** + * Returns JIM_OK if OK, JIM_ERR (etc.) on error, break, continue, etc. + * Returns -1 if invalid args. + */ +typedef int tclmod_cmd_function(Jim_Interp *interp, int argc, Jim_Obj *const *argv); + +typedef struct { + const char *cmd; /* Name of the (sub)command */ + const char *args; /* Textual description of allowed args */ + tclmod_cmd_function *function; /* Function implementing the subcommand */ + short minargs; /* Minimum required arguments */ + short maxargs; /* Maximum allowed arguments or -1 if no limit */ + unsigned flags; /* JIM_MODFLAG_... plus custom flags */ + const char *description; /* Description of the subcommand */ +} jim_subcmd_type; + +/** + * Looks up the appropriate subcommand in the given command table and return + * the command function which implements the subcommand. + * NULL will be returned and an appropriate error will be set if the subcommand or + * arguments are invalid. + * + * Typical usage is: + * { + * const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, command_table, argc, argv); + * + * return Jim_CallSubCmd(interp, ct, argc, argv); + * } + * + */ +const jim_subcmd_type * +Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv); + +/** + * Parses the args against the given command table and executes the subcommand if found + * or sets an appropriate error if the subcommand or arguments is invalid. + * + * Can be used directly with Jim_CreateCommand() where the ClientData is the command table. + * + * e.g. Jim_CreateCommand(interp, "mycmd", Jim_SubCmdProc, command_table, NULL); + */ +int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv); + +/** + * Invokes the given subcmd with the given args as returned + * by Jim_ParseSubCmd() + * + * If ct is NULL, returns JIM_ERR, leaving any message. + * Otherwise invokes ct->function + * + * If ct->function returns -1, sets an error message and returns JIM_ERR. + * Otherwise returns the result of ct->function. + */ +int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv); + +#endif @@ -100,34 +100,17 @@ extern "C" { * Compiler specific fixes. * ---------------------------------------------------------------------------*/ -/* MSC has _stricmp instead of strcasecmp */ -#ifdef _MSC_VER -# define strcasecmp _stricmp -#endif /* _MSC_VER */ - /* Long Long type and related issues */ #ifdef HAVE_LONG_LONG -# ifdef _MSC_VER /* MSC compiler */ -# define jim_wide _int64 -# ifndef LLONG_MAX -# define LLONG_MAX 9223372036854775807I64 -# endif -# ifndef LLONG_MIN -# define LLONG_MIN (-LLONG_MAX - 1I64) -# endif -# define JIM_WIDE_MIN LLONG_MIN -# define JIM_WIDE_MAX LLONG_MAX -# else /* Other compilers (mainly GCC) */ -# 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 +# 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 @@ -139,13 +122,9 @@ extern "C" { * ---------------------------------------------------------------------------*/ #ifdef HAVE_LONG_LONG -# if defined(_MSC_VER) || defined(__MSVCRT__) -# define JIM_WIDE_MODIFIER "I64d" -# else -# define JIM_WIDE_MODIFIER "lld" -# endif +# define JIM_WIDE_MODIFIER "lld" #else -# define JIM_WIDE_MODIFIER "ld" +# define JIM_WIDE_MODIFIER "ld" #endif /* ----------------------------------------------------------------------------- @@ -194,14 +173,6 @@ extern "C" { #define JIM_NL "\n" #endif -#if defined(__WIN32__) || defined(_WIN32) -#define DLLEXPORT __declspec(dllexport) -#define DLLIMPORT __declspec(dllimport) -#else -#define DLLEXPORT -#define DLLIMPORT -#endif - /* ----------------------------------------------------------------------------- * Stack * ---------------------------------------------------------------------------*/ @@ -546,16 +517,6 @@ typedef struct Jim_Interp { struct Jim_HashTable assocData; /* per-interp storage for use by packages */ Jim_PrngState *prngState; /* per interpreter Random Number Gen. state. */ struct Jim_HashTable packages; /* Provided packages hash table */ -#if 0 - void *cookie_stdin; /* input file pointer, 'stdin' by default */ - void *cookie_stdout; /* output file pointer, 'stdout' by default */ - void *cookie_stderr; /* errors file pointer, 'stderr' by default */ - int (*cb_vfprintf)( void *cookie, const char *fmt, va_list ap); - size_t (*cb_fwrite )( const void *ptr, size_t size, size_t n, void *cookie ); - size_t (*cb_fread )( void *ptr, size_t size, size_t n, void *cookie ); - int (*cb_fflush )( void *cookie ); - char *(*cb_fgets )( char *s, int size, void *cookie ); -#endif } Jim_Interp; /* Currently provided as macro that performs the increment. @@ -608,268 +569,259 @@ typedef struct Jim_Reference { #define Jim_FreeHashTableIterator(iter) Jim_Free(iter) #define JIM_EXPORT -#define JIM_API(X) X /* Memory allocation */ -JIM_EXPORT void * JIM_API(Jim_Alloc) (int size); -JIM_EXPORT void JIM_API(Jim_Free) (void *ptr); -JIM_EXPORT char * JIM_API(Jim_StrDup) (const char *s); +JIM_EXPORT void * Jim_Alloc (int size); +JIM_EXPORT void Jim_Free (void *ptr); +JIM_EXPORT char * Jim_StrDup (const char *s); /* evaluation */ -JIM_EXPORT int JIM_API(Jim_Eval)(Jim_Interp *interp, const char *script); +JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script); /* in C code, you can do this and get better error messages */ /* Jim_Eval_Named( interp, "some tcl commands", __FILE__, __LINE__ ); */ -JIM_EXPORT int JIM_API(Jim_Eval_Named)(Jim_Interp *interp, const char *script,const char *filename, int lineno); -JIM_EXPORT int JIM_API(Jim_EvalGlobal)(Jim_Interp *interp, const char *script); -JIM_EXPORT int JIM_API(Jim_EvalFile)(Jim_Interp *interp, const char *filename); -JIM_EXPORT int JIM_API(Jim_EvalObj) (Jim_Interp *interp, Jim_Obj *scriptObjPtr); -JIM_EXPORT int JIM_API(Jim_EvalObjBackground) (Jim_Interp *interp, +JIM_EXPORT int Jim_Eval_Named(Jim_Interp *interp, const char *script,const char *filename, int lineno); +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_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr); +JIM_EXPORT int Jim_EvalObjBackground (Jim_Interp *interp, Jim_Obj *scriptObjPtr); -JIM_EXPORT int JIM_API(Jim_EvalObjVector) (Jim_Interp *interp, int objc, +JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc, Jim_Obj *const *objv); -JIM_EXPORT int JIM_API(Jim_SubstObj) (Jim_Interp *interp, Jim_Obj *substObjPtr, +JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags); /* stack */ -JIM_EXPORT void JIM_API(Jim_InitStack)(Jim_Stack *stack); -JIM_EXPORT void JIM_API(Jim_FreeStack)(Jim_Stack *stack); -JIM_EXPORT int JIM_API(Jim_StackLen)(Jim_Stack *stack); -JIM_EXPORT void JIM_API(Jim_StackPush)(Jim_Stack *stack, void *element); -JIM_EXPORT void * JIM_API(Jim_StackPop)(Jim_Stack *stack); -JIM_EXPORT void * JIM_API(Jim_StackPeek)(Jim_Stack *stack); -JIM_EXPORT void JIM_API(Jim_FreeStackElements)(Jim_Stack *stack, void (*freeFunc)(void *ptr)); +JIM_EXPORT void Jim_InitStack(Jim_Stack *stack); +JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack); +JIM_EXPORT int Jim_StackLen(Jim_Stack *stack); +JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element); +JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack); +JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack); +JIM_EXPORT void Jim_FreeStackElements(Jim_Stack *stack, void (*freeFunc)(void *ptr)); /* hash table */ -JIM_EXPORT int JIM_API(Jim_InitHashTable) (Jim_HashTable *ht, +JIM_EXPORT int Jim_InitHashTable (Jim_HashTable *ht, Jim_HashTableType *type, void *privdata); -JIM_EXPORT int JIM_API(Jim_ExpandHashTable) (Jim_HashTable *ht, +JIM_EXPORT int Jim_ExpandHashTable (Jim_HashTable *ht, unsigned int size); -JIM_EXPORT int JIM_API(Jim_AddHashEntry) (Jim_HashTable *ht, const void *key, +JIM_EXPORT int Jim_AddHashEntry (Jim_HashTable *ht, const void *key, void *val); -JIM_EXPORT int JIM_API(Jim_ReplaceHashEntry) (Jim_HashTable *ht, +JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht, const void *key, void *val); -JIM_EXPORT int JIM_API(Jim_DeleteHashEntry) (Jim_HashTable *ht, +JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht, const void *key); -JIM_EXPORT int JIM_API(Jim_FreeHashTable) (Jim_HashTable *ht); -JIM_EXPORT Jim_HashEntry * JIM_API(Jim_FindHashEntry) (Jim_HashTable *ht, +JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht); +JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht, const void *key); -JIM_EXPORT int JIM_API(Jim_ResizeHashTable) (Jim_HashTable *ht); -JIM_EXPORT Jim_HashTableIterator *JIM_API(Jim_GetHashTableIterator) +JIM_EXPORT int Jim_ResizeHashTable (Jim_HashTable *ht); +JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator (Jim_HashTable *ht); -JIM_EXPORT Jim_HashEntry * JIM_API(Jim_NextHashEntry) +JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry (Jim_HashTableIterator *iter); /* objects */ -JIM_EXPORT Jim_Obj * JIM_API(Jim_NewObj) (Jim_Interp *interp); -JIM_EXPORT void JIM_API(Jim_FreeObj) (Jim_Interp *interp, Jim_Obj *objPtr); -JIM_EXPORT void JIM_API(Jim_InvalidateStringRep) (Jim_Obj *objPtr); -JIM_EXPORT void JIM_API(Jim_InitStringRep) (Jim_Obj *objPtr, const char *bytes, +JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp); +JIM_EXPORT void Jim_FreeObj (Jim_Interp *interp, Jim_Obj *objPtr); +JIM_EXPORT void Jim_InvalidateStringRep (Jim_Obj *objPtr); +JIM_EXPORT void Jim_InitStringRep (Jim_Obj *objPtr, const char *bytes, int length); -JIM_EXPORT Jim_Obj * JIM_API(Jim_DuplicateObj) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_DuplicateObj (Jim_Interp *interp, Jim_Obj *objPtr); -JIM_EXPORT const char * JIM_API(Jim_GetString)(Jim_Obj *objPtr, +JIM_EXPORT const char * Jim_GetString(Jim_Obj *objPtr, int *lenPtr); -JIM_EXPORT int JIM_API(Jim_Length)(Jim_Obj *objPtr); +JIM_EXPORT int Jim_Length(Jim_Obj *objPtr); /* string object */ -JIM_EXPORT Jim_Obj * JIM_API(Jim_NewStringObj) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_NewStringObj (Jim_Interp *interp, const char *s, int len); -JIM_EXPORT Jim_Obj * JIM_API(Jim_NewStringObjNoAlloc) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_NewStringObjNoAlloc (Jim_Interp *interp, char *s, int len); -JIM_EXPORT void JIM_API(Jim_AppendString) (Jim_Interp *interp, Jim_Obj *objPtr, +JIM_EXPORT void Jim_AppendString (Jim_Interp *interp, Jim_Obj *objPtr, const char *str, int len); -JIM_EXPORT void JIM_API(Jim_AppendString_sprintf) (Jim_Interp *interp, Jim_Obj *objPtr, - const char *fmt, ... ); -JIM_EXPORT void JIM_API(Jim_AppendObj) (Jim_Interp *interp, Jim_Obj *objPtr, +JIM_EXPORT void Jim_AppendString_sprintf (Jim_Interp *interp, Jim_Obj *objPtr, + const char *fmt, ... ); +JIM_EXPORT void Jim_AppendObj (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *appendObjPtr); -JIM_EXPORT void JIM_API(Jim_AppendStrings) (Jim_Interp *interp, +JIM_EXPORT void Jim_AppendStrings (Jim_Interp *interp, Jim_Obj *objPtr, ...); -JIM_EXPORT int JIM_API(Jim_StringEqObj) (Jim_Obj *aObjPtr, +JIM_EXPORT int Jim_StringEqObj (Jim_Obj *aObjPtr, Jim_Obj *bObjPtr, int nocase); -JIM_EXPORT int JIM_API(Jim_StringMatchObj) (Jim_Obj *patternObjPtr, +JIM_EXPORT int Jim_StringMatchObj (Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase); -JIM_EXPORT Jim_Obj * JIM_API(Jim_StringRangeObj) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_StringRangeObj (Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *firstObjPtr, Jim_Obj *lastObjPtr); -JIM_EXPORT Jim_Obj * JIM_API(Jim_FormatString) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_FormatString (Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv); -JIM_EXPORT Jim_Obj * JIM_API(Jim_ScanString) (Jim_Interp *interp, Jim_Obj *strObjPtr, +JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags); -JIM_EXPORT int JIM_API(Jim_CompareStringImmediate) (Jim_Interp *interp, +JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp, Jim_Obj *objPtr, const char *str); /* reference object */ -JIM_EXPORT Jim_Obj * JIM_API(Jim_NewReference) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr); -JIM_EXPORT Jim_Reference * JIM_API(Jim_GetReference) (Jim_Interp *interp, +JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp, Jim_Obj *objPtr); -JIM_EXPORT int JIM_API(Jim_SetFinalizer) (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr); -JIM_EXPORT int JIM_API(Jim_GetFinalizer) (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr); +JIM_EXPORT int Jim_SetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *cmdNamePtr); +JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **cmdNamePtrPtr); /* interpreter */ -JIM_EXPORT Jim_Interp * JIM_API(Jim_CreateInterp) (void); -JIM_EXPORT void JIM_API(Jim_FreeInterp) (Jim_Interp *i); -JIM_EXPORT int JIM_API(Jim_GetExitCode) (Jim_Interp *interp); -JIM_EXPORT void * JIM_API(Jim_SetStdin) (Jim_Interp *interp, void *fp); -JIM_EXPORT void * JIM_API(Jim_SetStdout) (Jim_Interp *interp, void *fp); -JIM_EXPORT void * JIM_API(Jim_SetStderr) (Jim_Interp *interp, void *fp); +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 void * Jim_SetStdin (Jim_Interp *interp, void *fp); +JIM_EXPORT void * Jim_SetStdout (Jim_Interp *interp, void *fp); +JIM_EXPORT void * Jim_SetStderr (Jim_Interp *interp, void *fp); /* commands */ -JIM_EXPORT void JIM_API(Jim_RegisterCoreCommands) (Jim_Interp *interp); -JIM_EXPORT int JIM_API(Jim_CreateCommand) (Jim_Interp *interp, +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_API(Jim_CreateProcedure) (Jim_Interp *interp, +JIM_EXPORT int Jim_CreateProcedure (Jim_Interp *interp, const char *cmdName, Jim_Obj *argListObjPtr, Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, int arityMin, int arityMax); -JIM_EXPORT int JIM_API(Jim_DeleteCommand) (Jim_Interp *interp, +JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp, const char *cmdName); -JIM_EXPORT int JIM_API(Jim_RenameCommand) (Jim_Interp *interp, +JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp, const char *oldName, const char *newName); -JIM_EXPORT Jim_Cmd * JIM_API(Jim_GetCommand) (Jim_Interp *interp, +JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp, Jim_Obj *objPtr, int flags); -JIM_EXPORT int JIM_API(Jim_SetVariable) (Jim_Interp *interp, +JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr); -JIM_EXPORT int JIM_API(Jim_SetVariableStr) (Jim_Interp *interp, +JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp, const char *name, Jim_Obj *objPtr); -JIM_EXPORT int JIM_API(Jim_SetGlobalVariableStr) (Jim_Interp *interp, +JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp, const char *name, Jim_Obj *objPtr); -JIM_EXPORT int JIM_API(Jim_SetVariableStrWithStr) (Jim_Interp *interp, +JIM_EXPORT int Jim_SetVariableStrWithStr (Jim_Interp *interp, const char *name, const char *val); -JIM_EXPORT int JIM_API(Jim_SetVariableLink) (Jim_Interp *interp, +JIM_EXPORT int Jim_SetVariableLink (Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame); -JIM_EXPORT Jim_Obj * JIM_API(Jim_GetVariable) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_GetVariable (Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags); -JIM_EXPORT Jim_Obj * JIM_API(Jim_GetGlobalVariable) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_GetGlobalVariable (Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags); -JIM_EXPORT Jim_Obj * JIM_API(Jim_GetVariableStr) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_GetVariableStr (Jim_Interp *interp, const char *name, int flags); -JIM_EXPORT Jim_Obj * JIM_API(Jim_GetGlobalVariableStr) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_GetGlobalVariableStr (Jim_Interp *interp, const char *name, int flags); -JIM_EXPORT int JIM_API(Jim_UnsetVariable) (Jim_Interp *interp, +JIM_EXPORT int Jim_UnsetVariable (Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags); /* call frame */ -JIM_EXPORT int JIM_API(Jim_GetCallFrameByLevel) (Jim_Interp *interp, +JIM_EXPORT int Jim_GetCallFrameByLevel (Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_CallFrame **framePtrPtr, int *newLevelPtr); /* garbage collection */ -JIM_EXPORT int JIM_API(Jim_Collect) (Jim_Interp *interp); -JIM_EXPORT void JIM_API(Jim_CollectIfNeeded) (Jim_Interp *interp); +JIM_EXPORT int Jim_Collect (Jim_Interp *interp); +JIM_EXPORT void Jim_CollectIfNeeded (Jim_Interp *interp); /* index object */ -JIM_EXPORT int JIM_API(Jim_GetIndex) (Jim_Interp *interp, Jim_Obj *objPtr, +JIM_EXPORT int Jim_GetIndex (Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr); /* list object */ -JIM_EXPORT Jim_Obj * JIM_API(Jim_NewListObj) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_NewListObj (Jim_Interp *interp, Jim_Obj *const *elements, int len); -JIM_EXPORT void JIM_API(Jim_ListInsertElements) (Jim_Interp *interp, +JIM_EXPORT void Jim_ListInsertElements (Jim_Interp *interp, Jim_Obj *listPtr, int index, int objc, Jim_Obj *const *objVec); -JIM_EXPORT void JIM_API(Jim_ListAppendElement) (Jim_Interp *interp, +JIM_EXPORT void Jim_ListAppendElement (Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *objPtr); -JIM_EXPORT void JIM_API(Jim_ListAppendList) (Jim_Interp *interp, +JIM_EXPORT void Jim_ListAppendList (Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *appendListPtr); -JIM_EXPORT void JIM_API(Jim_ListLength) (Jim_Interp *interp, Jim_Obj *listPtr, +JIM_EXPORT void Jim_ListLength (Jim_Interp *interp, Jim_Obj *listPtr, int *intPtr); -JIM_EXPORT int JIM_API(Jim_ListIndex) (Jim_Interp *interp, Jim_Obj *listPrt, +JIM_EXPORT int Jim_ListIndex (Jim_Interp *interp, Jim_Obj *listPrt, int index, Jim_Obj **objPtrPtr, int seterr); -JIM_EXPORT int JIM_API(Jim_SetListIndex) (Jim_Interp *interp, +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_API(Jim_ConcatObj) (Jim_Interp *interp, int objc, +JIM_EXPORT Jim_Obj * Jim_ConcatObj (Jim_Interp *interp, int objc, Jim_Obj *const *objv); /* dict object */ -JIM_EXPORT Jim_Obj * JIM_API(Jim_NewDictObj) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_NewDictObj (Jim_Interp *interp, Jim_Obj *const *elements, int len); -JIM_EXPORT int JIM_API(Jim_DictKey) (Jim_Interp *interp, Jim_Obj *dictPtr, +JIM_EXPORT int Jim_DictKey (Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags); -JIM_EXPORT int JIM_API(Jim_DictKeysVector) (Jim_Interp *interp, +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_API(Jim_SetDictKeysVector) (Jim_Interp *interp, +JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp, Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc, Jim_Obj *newObjPtr); /* return code object */ -JIM_EXPORT int JIM_API(Jim_GetReturnCode) (Jim_Interp *interp, Jim_Obj *objPtr, +JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr); /* expression object */ -JIM_EXPORT int JIM_API(Jim_EvalExpression) (Jim_Interp *interp, +JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr); -JIM_EXPORT int JIM_API(Jim_GetBoolFromExpr) (Jim_Interp *interp, +JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr); /* integer object */ -JIM_EXPORT int JIM_API(Jim_GetWide) (Jim_Interp *interp, Jim_Obj *objPtr, +JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr, jim_wide *widePtr); -JIM_EXPORT int JIM_API(Jim_GetLong) (Jim_Interp *interp, Jim_Obj *objPtr, +JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr, long *longPtr); -JIM_EXPORT void JIM_API(Jim_SetWide) (Jim_Interp *interp, Jim_Obj *objPtr, +JIM_EXPORT void Jim_SetWide (Jim_Interp *interp, Jim_Obj *objPtr, jim_wide wideValue); #define Jim_NewWideObj Jim_NewIntObj -JIM_EXPORT Jim_Obj * JIM_API(Jim_NewIntObj) (Jim_Interp *interp, +JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp, jim_wide wideValue); /* double object */ -JIM_EXPORT int JIM_API(Jim_GetDouble)(Jim_Interp *interp, Jim_Obj *objPtr, +JIM_EXPORT int Jim_GetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double *doublePtr); -JIM_EXPORT void JIM_API(Jim_SetDouble)(Jim_Interp *interp, Jim_Obj *objPtr, +JIM_EXPORT void Jim_SetDouble(Jim_Interp *interp, Jim_Obj *objPtr, double doubleValue); -JIM_EXPORT Jim_Obj * JIM_API(Jim_NewDoubleObj)(Jim_Interp *interp, double doubleValue); +JIM_EXPORT Jim_Obj * Jim_NewDoubleObj(Jim_Interp *interp, double doubleValue); /* shared strings */ -JIM_EXPORT const char * JIM_API(Jim_GetSharedString) (Jim_Interp *interp, +JIM_EXPORT const char * Jim_GetSharedString (Jim_Interp *interp, const char *str); -JIM_EXPORT void JIM_API(Jim_ReleaseSharedString) (Jim_Interp *interp, +JIM_EXPORT void Jim_ReleaseSharedString (Jim_Interp *interp, const char *str); /* commands utilities */ -JIM_EXPORT void JIM_API(Jim_WrongNumArgs) (Jim_Interp *interp, int argc, +JIM_EXPORT void Jim_WrongNumArgs (Jim_Interp *interp, int argc, Jim_Obj *const *argv, const char *msg); -JIM_EXPORT int JIM_API(Jim_GetEnum) (Jim_Interp *interp, Jim_Obj *objPtr, +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_API(Jim_ScriptIsComplete) (const char *s, int len, +JIM_EXPORT int Jim_ScriptIsComplete (const char *s, int len, char *stateCharPtr); /* package utilities */ typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data); -JIM_EXPORT void * JIM_API(Jim_GetAssocData)(Jim_Interp *interp, const char *key); -JIM_EXPORT int JIM_API(Jim_SetAssocData)(Jim_Interp *interp, const char *key, +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_API(Jim_DeleteAssocData)(Jim_Interp *interp, const char *key); +JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key); /* Packages C API */ -JIM_EXPORT int JIM_API(Jim_PackageProvide) (Jim_Interp *interp, +/* jim-package.c */ +JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp, const char *name, const char *ver, int flags); -JIM_EXPORT const char * JIM_API(Jim_PackageRequire) (Jim_Interp *interp, +JIM_EXPORT const char * Jim_PackageRequire (Jim_Interp *interp, const char *name, const char *ver, int flags); /* error messages */ -JIM_EXPORT void JIM_API(Jim_PrintErrorMessage) (Jim_Interp *interp); +JIM_EXPORT void Jim_PrintErrorMessage (Jim_Interp *interp); /* interactive mode */ -JIM_EXPORT int JIM_API(Jim_InteractivePrompt) (Jim_Interp *interp); +JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp); /* Misc */ -JIM_EXPORT void JIM_API(Jim_Panic) (Jim_Interp *interp, const char *fmt, ...); +JIM_EXPORT void Jim_Panic (Jim_Interp *interp, const char *fmt, ...); int Jim_StringToWide(const char *str, jim_wide *widePtr, int base); -int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName); - -/* Jim's STDIO */ -JIM_EXPORT int JIM_API( Jim_fprintf )( Jim_Interp *interp, void *cookie, const char *fmt, ... ); -JIM_EXPORT int JIM_API( Jim_vfprintf )( Jim_Interp *interp, void *cookie, const char *fmt, va_list ap ); -#if 0 -JIM_EXPORT size_t JIM_API( Jim_fwrite )( Jim_Interp *interp, const void *ptr, size_t size, size_t nmeb, void *cookie ); -JIM_EXPORT size_t JIM_API( Jim_fread )( Jim_Interp *interp, void *ptr, size_t size, size_t nmeb, void *cookie ); -JIM_EXPORT int JIM_API( Jim_fflush )( Jim_Interp *interp, void *cookie ); -JIM_EXPORT char * JIM_API( Jim_fgets )( Jim_Interp *interp, char *s, int size, void *cookie ); -#endif +/* jim-load.c */ +int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName); #ifdef __cplusplus } @@ -124,6 +124,8 @@ extern void Jim_AioInit(Jim_Interp *interp); extern void Jim_EventloopInit(Jim_Interp *interp); extern void Jim_RegexpInit(Jim_Interp *interp); extern void Jim_ReaddirInit(Jim_Interp *interp); +extern void Jim_FileInit(Jim_Interp *interp); +extern void Jim_ExecInit(Jim_Interp *interp); int main(int argc, char *const argv[]) { @@ -140,6 +142,8 @@ int main(int argc, char *const argv[]) Jim_EventloopInit(interp); Jim_RegexpInit(interp); Jim_ReaddirInit(interp); + Jim_FileInit(interp); + Jim_ExecInit(interp); /* Append the path where the executed Jim binary is contained * in the jim_libpath list. */ |