+++ /dev/null
-/*
- * bltBgexec.c --
- *
- * This module implements a background "exec" command for the
- * BLT toolkit.
- *
- * Copyright 1993-1998 Lucent Technologies, Inc.
- *
- * 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 and that both that the copyright notice and warranty
- * disclaimer appear in supporting documentation, and that the names
- * of Lucent Technologies any of their entities not be used in
- * advertising or publicity pertaining to distribution of the software
- * without specific, written prior permission.
- *
- * Lucent Technologies disclaims all warranties with regard to this
- * software, including all implied warranties of merchantability and
- * fitness. In no event shall Lucent Technologies be liable for any
- * special, indirect or consequential damages or any damages
- * whatsoever resulting from loss of use, data or profits, whether in
- * an action of contract, negligence or other tortuous action, arising
- * out of or in connection with the use or performance of this
- * software.
- *
- * The "bgexec" command was created by George Howlett.
- */
-
-#include "bltInt.h"
-
-#ifndef NO_BGEXEC
-
-#include <fcntl.h>
-#include <signal.h>
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#include <sys/types.h>
-
-#include "bltWait.h"
-#include "bltSwitch.h"
-
-#if (TCL_MAJOR_VERSION == 7)
-#define FILEHANDLER_USES_TCLFILES 1
-#else
-typedef int Tcl_File;
-#endif
-
-static Tcl_CmdProc BgexecCmd;
-
-#ifdef WIN32
-typedef struct {
- DWORD pid;
- HANDLE hProcess;
-} Process;
-#else
-typedef int Process;
-#endif
-
-#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
-typedef void *Tcl_Encoding; /* Make up dummy type for encoding. */
-#endif
-
-#define ENCODING_ASCII ((Tcl_Encoding)NULL)
-#define ENCODING_BINARY ((Tcl_Encoding)1)
-
-/*
- * As of Tcl 7.6, we're using our own version of the old
- * Tcl_CreatePipeline routine. I would have tried to use
- * Tcl_OpenCommandChannel but you can't get at the array of
- * process ids, unless of course you pry open the undocumented
- * structure PipeStatus as clientData. Nor could I figure out
- * how to set one side of the pipe to be non-blocking. The whole
- * channel API seems overly complex for what its supposed to
- * do. [And maybe that's why it keeps changing every release.]
- */
-extern int Blt_CreatePipeline _ANSI_ARGS_((Tcl_Interp *interp, int argc,
- char **argv, Process **pidPtrPtr, int *inPipePtr, int *outPipePtr,
- int *errFilePtr));
-
-#ifdef WIN32
-#define read(fd, buf, size) Blt_AsyncRead((fd),(buf),(size))
-#define close(fd) CloseHandle((HANDLE)fd)
-#define Tcl_CreateFileHandler Blt_CreateFileHandler
-#define Tcl_DeleteFileHandler Blt_DeleteFileHandler
-#define kill KillProcess
-#define waitpid WaitProcess
-#endif
-
-#define EXPECTED_NUM_TIMERS 4
-
-#define READ_AGAIN (0)
-#define READ_EOF (-1)
-#define READ_ERROR (-2)
-
-/* The wait-related definitions are taken from tclUnix.h */
-
-#define TRACE_FLAGS (TCL_TRACE_WRITES | TCL_TRACE_UNSETS | TCL_GLOBAL_ONLY)
-
-#define BLOCK_SIZE 1024 /* Size of allocation blocks for buffer */
-#define DEF_BUFFER_SIZE (BLOCK_SIZE * 8)
-#define MAX_READS 100 /* Maximum number of successful reads
- * before stopping to let Tcl catch up
- * on events */
-
-#ifndef NSIG
-#define NSIG 32 /* Number of signals available */
-#endif /*NSIG*/
-
-#ifndef SIGINT
-#define SIGINT 2
-#endif /* SIGINT */
-
-#ifndef SIGQUIT
-#define SIGQUIT 3
-#endif /* SIGQUIT */
-
-#ifndef SIGKILL
-#define SIGKILL 9
-#endif /* SIGKILL */
-
-#ifndef SIGTERM
-#define SIGTERM 14
-#endif /* SIGTERM */
-
-typedef struct {
- int number;
- char *name;
-} SignalId;
-
-static SignalId signalIds[] =
-{
-#ifdef SIGABRT
- {SIGABRT, "SIGABRT"},
-#endif
-#ifdef SIGALRM
- {SIGALRM, "SIGALRM"},
-#endif
-#ifdef SIGBUS
- {SIGBUS, "SIGBUS"},
-#endif
-#ifdef SIGCHLD
- {SIGCHLD, "SIGCHLD"},
-#endif
-#if defined(SIGCLD) && (!defined(SIGCHLD) || (SIGCLD != SIGCHLD))
- {SIGCLD, "SIGCLD"},
-#endif
-#ifdef SIGCONT
- {SIGCONT, "SIGCONT"},
-#endif
-#if defined(SIGEMT) && (!defined(SIGXCPU) || (SIGEMT != SIGXCPU))
- {SIGEMT, "SIGEMT"},
-#endif
-#ifdef SIGFPE
- {SIGFPE, "SIGFPE"},
-#endif
-#ifdef SIGHUP
- {SIGHUP, "SIGHUP"},
-#endif
-#ifdef SIGILL
- {SIGILL, "SIGILL"},
-#endif
-#ifdef SIGINT
- {SIGINT, "SIGINT"},
-#endif
-#ifdef SIGIO
- {SIGIO, "SIGIO"},
-#endif
-#if defined(SIGIOT) && (!defined(SIGABRT) || (SIGIOT != SIGABRT))
- {SIGIOT, "SIGIOT"},
-#endif
-#ifdef SIGKILL
- {SIGKILL, "SIGKILL"},
-#endif
-#if defined(SIGLOST) && (!defined(SIGIOT) || (SIGLOST != SIGIOT)) && (!defined(SIGURG) || (SIGLOST != SIGURG))
- {SIGLOST, "SIGLOST"},
-#endif
-#ifdef SIGPIPE
- {SIGPIPE, "SIGPIPE"},
-#endif
-#if defined(SIGPOLL) && (!defined(SIGIO) || (SIGPOLL != SIGIO))
- {SIGPOLL, "SIGPOLL"},
-#endif
-#ifdef SIGPROF
- {SIGPROF, "SIGPROF"},
-#endif
-#if defined(SIGPWR) && (!defined(SIGXFSZ) || (SIGPWR != SIGXFSZ))
- {SIGPWR, "SIGPWR"},
-#endif
-#ifdef SIGQUIT
- {SIGQUIT, "SIGQUIT"},
-#endif
-#ifdef SIGSEGV
- {SIGSEGV, "SIGSEGV"},
-#endif
-#ifdef SIGSTOP
- {SIGSTOP, "SIGSTOP"},
-#endif
-#ifdef SIGSYS
- {SIGSYS, "SIGSYS"},
-#endif
-#ifdef SIGTERM
- {SIGTERM, "SIGTERM"},
-#endif
-#ifdef SIGTRAP
- {SIGTRAP, "SIGTRAP"},
-#endif
-#ifdef SIGTSTP
- {SIGTSTP, "SIGTSTP"},
-#endif
-#ifdef SIGTTIN
- {SIGTTIN, "SIGTTIN"},
-#endif
-#ifdef SIGTTOU
- {SIGTTOU, "SIGTTOU"},
-#endif
-#if defined(SIGURG) && (!defined(SIGIO) || (SIGURG != SIGIO))
- {SIGURG, "SIGURG"},
-#endif
-#if defined(SIGUSR1) && (!defined(SIGIO) || (SIGUSR1 != SIGIO))
- {SIGUSR1, "SIGUSR1"},
-#endif
-#if defined(SIGUSR2) && (!defined(SIGURG) || (SIGUSR2 != SIGURG))
- {SIGUSR2, "SIGUSR2"},
-#endif
-#ifdef SIGVTALRM
- {SIGVTALRM, "SIGVTALRM"},
-#endif
-#ifdef SIGWINCH
- {SIGWINCH, "SIGWINCH"},
-#endif
-#ifdef SIGXCPU
- {SIGXCPU, "SIGXCPU"},
-#endif
-#ifdef SIGXFSZ
- {SIGXFSZ, "SIGXFSZ"},
-#endif
- {-1, "unknown signal"},
-};
-
-/*
- * Sink buffer:
- * ____________________
- * | | "size" current allocated length of buffer.
- * | |
- * |--------------------| "fill" fill point (# characters in buffer).
- * | Raw |
- * |--------------------| "mark" Marks end of cooked characters.
- * | |
- * | Cooked |
- * | |
- * | |
- * |--------------------| "lastMark" Mark end of processed characters.
- * | |
- * | |
- * | Processed |
- * | |
- * |____________________| 0
- */
-typedef struct {
- char *name; /* Name of the sink */
-
- char *doneVar; /* Name of a Tcl variable (malloc'ed)
- * set to the collected data of the
- * last UNIX subprocess. */
-
- char *updateVar; /* Name of a Tcl variable (malloc'ed)
- * updated as data is read from the
- * pipe. */
-
- char **updateCmd; /* Start of a Tcl command executed
- * whenever data is read from the
- * pipe. */
-
-#if (TCL_MAJOR_VERSION >= 8)
- Tcl_Obj **objv; /* */
- int objc; /* */
-#endif
-
- int flags;
-
- Tcl_File file; /* Used for backward compatability
- * with Tcl 7.5 */
- Tcl_Encoding encoding;
- int fd; /* File descriptor of the pipe. */
- int status;
-
- int echo; /* Indicates if the pipeline's stderr stream
- * should be echoed */
- unsigned char *byteArr; /* Stores pipeline output (malloc-ed):
- * Initially points to static storage
- */
- size_t size; /* Size of dynamically allocated buffer. */
-
- size_t fill; /* # of bytes read into the buffer. Marks
- * the current fill point of the buffer. */
-
- size_t mark; /* # of bytes translated (cooked). */
- size_t lastMark; /* # of bytes as of the last read.
- * This indicates the start of the new
- * data in the buffer since the last
- * time the "update" variable was
- * set. */
-
- char *command; /* Command callback. */
- unsigned char staticSpace[DEF_BUFFER_SIZE]; /* Static space */
-
-} Sink;
-
-#define SINK_BUFFERED (1<<0)
-#define SINK_KEEP_NL (1<<1)
-#define SINK_NOTIFY (1<<2)
-
-typedef struct {
- char *statVar; /* Name of a Tcl variable set to the
- * exit status of the last
- * process. Setting this variable
- * triggers the termination of all
- * subprocesses (regardless whether
- * they have already completed) */
-
- int signalNum; /* If non-zero, indicates the signal
- * to send subprocesses when cleaning
- * up.*/
- int localVars; /* Use local vars. */
- int keepNewline; /* If non-zero, indicates to set Tcl
- * output variables with trailing
- * newlines intact */
-
- int lineBuffered; /* If non-zero, indicates provide data
- * to update variable and update proc on
- * a line-by-line basis. */
-
- int interval; /* Interval to poll for the exiting
- * processes */
- char *outputEncodingName; /* Name of decoding scheme to use when
- * translating output data. */
- char *errorEncodingName; /* Name of decoding scheme to use when
- * translating output data. */
-
- /* Private */
- Tcl_Interp *interp; /* Interpreter containing variables */
-
- int nProcs; /* Number of processes in pipeline */
- Process *procArr; /* Array of process tokens from pipeline.
- * The token for Unix are pid_t, while
- * for Win32 they're handles. */
-
- int traced; /* Indicates that the status variable
- * is currently being traced. */
- int detached; /* Indicates that the pipeline is
- * detached from standard I/O, running
- * in the background. */
- int nTimers; /* Number of timer handlers */
- Tcl_TimerToken *timerTokens;/* Array of tokens for timer handlers which
- * polls for the exit status of each
- * sub-process. If zero, there's no
- * timer handler queued. */
-
- int *exitCodePtr; /* Pointer to a memory location to
- * contain the last process' exit
- * code. */
- int *donePtr;
-
- Sink sink1, sink2;
- int maxBytes; /* Max string length. */
- int maxExceed;
- int varFlags;
- int doRaise;
- int closeKill;
- int cKilled;
-} BackgroundInfo;
-
-
-static Blt_SwitchParseProc StringToSignal;
-static Blt_SwitchCustom killSignalSwitch =
-{
- StringToSignal, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
-};
-
-static Blt_SwitchSpec switchSpecs[] =
-{
- {BLT_SWITCH_INT, "-check",
- Blt_Offset(BackgroundInfo, interval), 0},
- {BLT_SWITCH_INT, "-closeonkill",
- Blt_Offset(BackgroundInfo, closeKill), 0},
- {BLT_SWITCH_STRING, "-command",
- Blt_Offset(BackgroundInfo, sink1.command), 0},
- {BLT_SWITCH_STRING, "-decodeerror",
- Blt_Offset(BackgroundInfo, errorEncodingName), 0},
- {BLT_SWITCH_STRING, "-decodeoutput",
- Blt_Offset(BackgroundInfo, outputEncodingName), 0},
- {BLT_SWITCH_BOOLEAN, "-echo",
- Blt_Offset(BackgroundInfo, sink2.echo), 0},
- {BLT_SWITCH_STRING, "-error",
- Blt_Offset(BackgroundInfo, sink2.doneVar), 0},
- {BLT_SWITCH_BOOLEAN, "-keepnewline",
- Blt_Offset(BackgroundInfo, keepNewline), 0},
- {BLT_SWITCH_CUSTOM, "-killsignal",
- Blt_Offset(BackgroundInfo, signalNum), 0, &killSignalSwitch},
- {BLT_SWITCH_STRING, "-lasterror",
- Blt_Offset(BackgroundInfo, sink2.updateVar), 0},
- {BLT_SWITCH_STRING, "-lastoutput",
- Blt_Offset(BackgroundInfo, sink1.updateVar), 0},
- {BLT_SWITCH_INT, "-limit",
- Blt_Offset(BackgroundInfo, maxBytes), 0},
- {BLT_SWITCH_BOOLEAN, "-linebuffered",
- Blt_Offset(BackgroundInfo, lineBuffered), 0},
- {BLT_SWITCH_BOOLEAN, "-local",
- Blt_Offset(BackgroundInfo, localVars), 0},
- {BLT_SWITCH_LIST, "-onerror",
- Blt_Offset(BackgroundInfo, sink2.updateCmd), 0},
- {BLT_SWITCH_LIST, "-onoutput",
- Blt_Offset(BackgroundInfo, sink1.updateCmd), 0},
- {BLT_SWITCH_STRING, "-output",
- Blt_Offset(BackgroundInfo, sink1.doneVar), 0},
- {BLT_SWITCH_BOOLEAN, "-raise",
- Blt_Offset(BackgroundInfo, doRaise), 0},
- /* {BLT_SWITCH_STRING, "-update",
- Blt_Offset(BackgroundInfo, sink1.updateVar), 0},*/
- {BLT_SWITCH_END, NULL, 0, 0}
-};
-
-static char *VariableProc _ANSI_ARGS_((ClientData clientData,
- Tcl_Interp *interp, char *part1, char *part2, int flags));
-static void TimerProc _ANSI_ARGS_((ClientData clientData));
-static void StdoutProc _ANSI_ARGS_((ClientData clientData, int mask));
-static void StderrProc _ANSI_ARGS_((ClientData clientData, int mask));
-static void AddTimerHandler _ANSI_ARGS_((BackgroundInfo *bgPtr,int interval));
-static void InterpDeleted _ANSI_ARGS_(( ClientData clientData, Tcl_Interp *interp));
-
-/*
- *----------------------------------------------------------------------
- *
- * GetSignal --
- *
- * Convert a string represent a signal number into its integer
- * value.
- *
- * Results:
- * The return value is a standard Tcl result.
- *
- *----------------------------------------------------------------------
- */
-/*ARGSUSED*/
-static int
-StringToSignal(clientData, interp, switchName, string, record, offset)
- ClientData clientData; /* Contains a pointer to the tabset containing
- * this image. */
- Tcl_Interp *interp; /* Interpreter to send results back to */
- char *switchName; /* Not used. */
- char *string; /* String representation */
- char *record; /* Structure record */
- int offset; /* Offset to field in structure */
-{
- int *signalPtr = (int *)(record + offset);
- int signalNum;
-
- if ((string == NULL) || (*string == '\0')) {
- *signalPtr = 0;
- return TCL_OK;
- }
- if (isdigit(UCHAR(string[0]))) {
- if (Tcl_GetInt(interp, string, &signalNum) != TCL_OK) {
- return TCL_ERROR;
- }
- } else {
- char *name;
- register SignalId *sigPtr;
-
- name = string;
-
- /* Clip off any "SIG" prefix from the signal name */
- if ((name[0] == 'S') && (name[1] == 'I') && (name[2] == 'G')) {
- name += 3;
- }
- signalNum = -1;
- for (sigPtr = signalIds; sigPtr->number > 0; sigPtr++) {
- if (strcmp(sigPtr->name + 3, name) == 0) {
- signalNum = sigPtr->number;
- break;
- }
- }
- if (signalNum < 0) {
- Tcl_AppendResult(interp, "unknown signal \"", string, "\"",
- (char *)NULL);
- return TCL_ERROR;
- }
- }
- if ((signalNum < 0) || (signalNum > NSIG)) {
- /* Outside range of signals */
- Tcl_AppendResult(interp, "signal number \"", string,
- "\" is out of range", (char *)NULL);
- return TCL_ERROR;
- }
- *signalPtr = signalNum;
- return TCL_OK;
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * GetSinkData --
- *
- * Returns the data currently saved in the buffer
- *
- *----------------------------------------------------------------------
- */
-static void
-GetSinkData(sinkPtr, dataPtr, lengthPtr)
- Sink *sinkPtr;
- unsigned char **dataPtr;
- size_t *lengthPtr;
-{
- size_t length;
-
- sinkPtr->byteArr[sinkPtr->mark] = '\0';
- length = sinkPtr->mark;
- if ((sinkPtr->mark > 0) && (sinkPtr->encoding != ENCODING_BINARY)) {
- unsigned char *last;
-
- last = sinkPtr->byteArr + (sinkPtr->mark - 1);
- if ((!(sinkPtr->flags & SINK_KEEP_NL)) && (*last == '\n')) {
- length--;
- }
- }
- *dataPtr = sinkPtr->byteArr;
- *lengthPtr = length;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * NextBlock --
- *
- * Returns the next block of data since the last time this
- * routine was called.
- *
- *----------------------------------------------------------------------
- */
-static unsigned char *
-NextBlock(sinkPtr, lengthPtr)
- Sink *sinkPtr;
- int *lengthPtr;
-{
- unsigned char *string;
- int length;
-
- string = sinkPtr->byteArr + sinkPtr->lastMark;
- length = sinkPtr->mark - sinkPtr->lastMark;
- sinkPtr->lastMark = sinkPtr->mark;
- if (length > 0) {
- if ((!(sinkPtr->flags & SINK_KEEP_NL)) &&
- (string[length - 1] == '\n')) {
- length--;
- }
- *lengthPtr = length;
- return string;
- }
- return NULL;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * NextLine --
- *
- * Returns the next line of data.
- *
- *----------------------------------------------------------------------
- */
-static unsigned char *
-NextLine(sinkPtr, lengthPtr)
- Sink *sinkPtr;
- int *lengthPtr;
-{
- if (sinkPtr->mark > sinkPtr->lastMark) {
- unsigned char *string;
- int newBytes;
- register int i;
-
- string = sinkPtr->byteArr + sinkPtr->lastMark;
- newBytes = sinkPtr->mark - sinkPtr->lastMark;
- for (i = 0; i < newBytes; i++) {
- if (string[i] == '\n') {
- int length;
-
- length = i + 1;
- sinkPtr->lastMark += length;
- if (!(sinkPtr->flags & SINK_KEEP_NL)) {
- length--; /* Backup over the newline. */
- }
- *lengthPtr = length;
- return string;
- }
- }
- /* Newline not found. On errors or EOF, also return a partial line. */
- if (sinkPtr->status < 0) {
- *lengthPtr = newBytes;
- sinkPtr->lastMark = sinkPtr->mark;
- return string;
- }
- }
- return NULL;
-}
-/*
- *----------------------------------------------------------------------
- *
- * ResetSink --
- *
- * Removes the bytes already processed from the buffer, possibly
- * resetting it to empty. This used when we don't care about
- * keeping all the data collected from the channel (no -output
- * flag and the process is detached).
- *
- *----------------------------------------------------------------------
- */
-static void
-ResetSink(sinkPtr)
- Sink *sinkPtr;
-{
- if ((sinkPtr->flags & SINK_BUFFERED) &&
- (sinkPtr->fill > sinkPtr->lastMark)) {
- register size_t i, j;
-
- /* There may be bytes remaining in the buffer, awaiting
- * another read before we see the next newline. So move the
- * bytes to the front of the array. */
-
- for (i = 0, j = sinkPtr->lastMark; j < sinkPtr->fill; i++, j++) {
- sinkPtr->byteArr[i] = sinkPtr->byteArr[j];
- }
- /* Move back the fill point and processed point. */
- sinkPtr->fill -= sinkPtr->lastMark;
- sinkPtr->mark -= sinkPtr->lastMark;
- } else {
- sinkPtr->mark = sinkPtr->fill = 0;
- }
- sinkPtr->lastMark = 0;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * InitSink --
- *
- * Initializes the buffer's storage.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Storage is cleared.
- *
- *----------------------------------------------------------------------
- */
-static void
-InitSink(bgPtr, sinkPtr, name, encoding)
- BackgroundInfo *bgPtr;
- Sink *sinkPtr;
- char *name;
- Tcl_Encoding encoding;
-{
- sinkPtr->name = name;
- sinkPtr->echo = FALSE;
- sinkPtr->fd = -1;
- sinkPtr->file = (Tcl_File)NULL;
- sinkPtr->byteArr = sinkPtr->staticSpace;
- sinkPtr->size = DEF_BUFFER_SIZE;
- sinkPtr->encoding = encoding;
- if (bgPtr->keepNewline) {
- sinkPtr->flags |= SINK_KEEP_NL;
- }
- if (bgPtr->lineBuffered) {
- sinkPtr->flags |= SINK_BUFFERED;
- }
- if ((sinkPtr->updateCmd != NULL) ||
- (sinkPtr->updateVar != NULL) ||
- (sinkPtr->echo)) {
- sinkPtr->flags |= SINK_NOTIFY;
- }
-#if (TCL_MAJOR_VERSION >= 8)
- if (sinkPtr->updateCmd != NULL) {
- Tcl_Obj **objArr;
- char **p;
- int count;
- register int i;
-
- count = 0;
- for (p = sinkPtr->updateCmd; *p != NULL; p++) {
- count++;
- }
- objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *));
- for (i = 0; i < count; i++) {
- objArr[i] = Tcl_NewStringObj(sinkPtr->updateCmd[i], -1);
- Tcl_IncrRefCount(objArr[i]);
- }
- sinkPtr->objv = objArr;
- sinkPtr->objc = count + 1;
- }
-#endif
- ResetSink(sinkPtr);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * FreeSinkBuffer --
- *
- * Frees the buffer's storage, freeing any malloc'ed space.
- *
- * Results:
- * None.
- *
- *----------------------------------------------------------------------
- */
-static void
-FreeSinkBuffer(sinkPtr)
- Sink *sinkPtr;
-{
- if (sinkPtr->byteArr != sinkPtr->staticSpace) {
- Blt_Free(sinkPtr->byteArr);
- }
- sinkPtr->fd = -1;
- sinkPtr->file = (Tcl_File)NULL;
-#if (TCL_MAJOR_VERSION >= 8)
- if (sinkPtr->objv != NULL) {
- register int i;
-
- for (i = 0; i < sinkPtr->objc - 1; i++) {
- Tcl_DecrRefCount(sinkPtr->objv[i]);
- }
- Blt_Free(sinkPtr->objv);
- }
-#endif
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * ExtendSinkBuffer --
- *
- * Doubles the size of the current buffer.
- *
- * Results:
- * None.
- *
- *----------------------------------------------------------------------
- */
-static int
-ExtendSinkBuffer(sinkPtr)
- Sink *sinkPtr;
-{
- unsigned char *arrayPtr;
- register unsigned char *srcPtr, *destPtr, *endPtr;
- /*
- * Allocate a new array, double the old size
- */
- sinkPtr->size += sinkPtr->size;
- arrayPtr = Blt_Malloc(sizeof(unsigned char) * sinkPtr->size);
- if (arrayPtr == NULL) {
- return -1;
- }
- srcPtr = sinkPtr->byteArr;
- endPtr = sinkPtr->byteArr + sinkPtr->fill;
- destPtr = arrayPtr;
- while (srcPtr < endPtr) {
- *destPtr++ = *srcPtr++;
- }
- if (sinkPtr->byteArr != sinkPtr->staticSpace) {
- Blt_Free(sinkPtr->byteArr);
- }
- sinkPtr->byteArr = arrayPtr;
-
- return (sinkPtr->size - sinkPtr->fill); /* Return bytes left. */
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * ReadBytes --
- *
- * Reads and appends any available data from a given file descriptor
- * to the buffer.
- *
- * Results:
- * Returns TCL_OK when EOF is found, TCL_RETURN if reading
- * data would block, and TCL_ERROR if an error occurred.
- *
- *----------------------------------------------------------------------
- */
-static void
-ReadBytes(bgPtr, sinkPtr)
- BackgroundInfo *bgPtr;
- Sink *sinkPtr;
-{
- int nBytes, bytesLeft;
- register int i;
- unsigned char *array;
-
- /*
- * ------------------------------------------------------------------
- *
- * Worry about indefinite postponement.
- *
- * Typically we want to stay in the read loop as long as it takes
- * to collect all the data that's currently available. But if
- * it's coming in at a constant high rate, we need to arbitrarily
- * break out at some point. This allows for both setting the
- * update variable and the Tk program to handle idle events.
- *
- * ------------------------------------------------------------------
- */
-
- for (i = 0; i < MAX_READS; i++) {
-
- /* Allocate a larger buffer when the number of remaining bytes
- * is below the threshold BLOCK_SIZE. */
-
- bytesLeft = sinkPtr->size - sinkPtr->fill;
-
- if (bytesLeft < BLOCK_SIZE) {
- bytesLeft = ExtendSinkBuffer(sinkPtr);
- if (bytesLeft < 0) {
- sinkPtr->status = READ_ERROR;
- return;
- }
- }
- array = sinkPtr->byteArr + sinkPtr->fill;
-
- /*
- * Read into a buffer but make sure we leave room for a
- * trailing NUL byte.
- */
- nBytes = read(sinkPtr->fd, array, bytesLeft - 1);
- if (nBytes == 0) { /* EOF: break out of loop. */
- sinkPtr->status = READ_EOF;
- return;
- }
- if (nBytes < 0) {
-#ifdef O_NONBLOCK
-#define BLOCKED EAGAIN
-#else
-#define BLOCKED EWOULDBLOCK
-#endif /*O_NONBLOCK*/
- /* Either an error has occurred or no more data is
- * currently available to read. */
- if (errno == BLOCKED) {
- sinkPtr->status = READ_AGAIN;
- return;
- }
- sinkPtr->byteArr[0] = '\0';
- sinkPtr->status = READ_ERROR;
- return;
- }
- sinkPtr->fill += nBytes;
- sinkPtr->byteArr[sinkPtr->fill] = '\0';
- }
- sinkPtr->status = nBytes;
-}
-
-#define IsOpenSink(sinkPtr) ((sinkPtr)->fd != -1)
-
-static void
-CloseSink(bgPtr, interp, sinkPtr)
- BackgroundInfo *bgPtr;
- Tcl_Interp *interp;
- Sink *sinkPtr;
-{
- unsigned char *data = NULL;
- size_t length;
-
- if (IsOpenSink(sinkPtr)) {
- close(sinkPtr->fd);
-#ifdef FILEHANDLER_USES_TCLFILES
- Tcl_DeleteFileHandler(sinkPtr->file);
- Tcl_FreeFile(sinkPtr->file);
-#else
- Tcl_DeleteFileHandler(sinkPtr->fd);
-#endif
- sinkPtr->file = (Tcl_File)NULL;
- sinkPtr->fd = -1;
-
-#if WINDEBUG
- PurifyPrintf("CloseSink: set done var %s\n", sinkPtr->name);
-#endif
- if (sinkPtr->doneVar != NULL && !Tcl_InterpDeleted(interp)) {
- /*
- * If data is to be collected, set the "done" variable
- * with the contents of the buffer.
- */
- GetSinkData(sinkPtr, &data, &length);
-#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
- data[length] = '\0';
- if (Tcl_SetVar(interp, sinkPtr->doneVar, data,
- bgPtr->varFlags) == NULL) {
- Tcl_BackgroundError(interp);
- }
-#else
- if (Tcl_SetVar2Ex(interp, sinkPtr->doneVar, NULL,
- Tcl_NewByteArrayObj(data, length),
- (bgPtr->varFlags | TCL_LEAVE_ERR_MSG)) == NULL) {
- Tcl_BackgroundError(interp);
- }
-#endif
- }
-#if WINDEBUG
- PurifyPrintf("CloseSink %s: done\n", sinkPtr->name);
-#endif
- }
-}
-
-static void
-SinkCallback(interp, sinkPtr, status)
- Tcl_Interp *interp;
- Sink *sinkPtr;
- char *status;
-{
- unsigned char *data = NULL;
- size_t length;
- if (sinkPtr->command != NULL && !Tcl_InterpDeleted(interp)) {
- Tcl_DString dStr;
- Tcl_DStringInit(&dStr);
- GetSinkData(sinkPtr, &data, &length);
- Tcl_DStringAppend(&dStr, sinkPtr->command, -1);
- Tcl_DStringAppendElement(&dStr, data);
- Tcl_DStringAppendElement(&dStr, status);
- if (Tcl_GlobalEval(interp, Tcl_DStringValue(&dStr)) != TCL_OK) {
- Tcl_BackgroundError(interp);
- }
- Tcl_DStringFree(&dStr);
- }
-}
-/*
- *----------------------------------------------------------------------
- *
- * CookSink --
- *
- * For Windows, translate CR/NL combinations to NL alone.
- *
- * Results:
- * None.
- *
- * Side Effects:
- * The size of the byte array may shrink and array contents
- * shifted as carriage returns are found and removed.
- *
- *----------------------------------------------------------------------
- */
-static void
-CookSink(interp, sinkPtr)
- Tcl_Interp *interp;
- Sink *sinkPtr;
-{
- unsigned char *srcPtr, *endPtr;
-#ifdef WIN32
- size_t oldMark;
-
- oldMark = sinkPtr->mark;
-#endif
- if (sinkPtr->encoding == ENCODING_BINARY) { /* binary */
- /* No translation needed. */
- sinkPtr->mark = sinkPtr->fill;
- } else if (sinkPtr->encoding == ENCODING_ASCII) { /* ascii */
-#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
- /* Convert NUL bytes to question marks. */
- srcPtr = sinkPtr->byteArr + sinkPtr->mark;
- endPtr = sinkPtr->byteArr + sinkPtr->fill;
- while (srcPtr < endPtr) {
- if (*srcPtr == '\0') {
- *srcPtr = '?';
- }
- srcPtr++;
- }
-#endif /* < 8.1.0 */
- /* One-to-one translation. mark == fill. */
- sinkPtr->mark = sinkPtr->fill;
-#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
- } else { /* unicode. */
- int nSrcCooked, nCooked;
- int result;
- size_t cookedSize, spaceLeft, needed;
- size_t nRaw, nLeftOver;
- unsigned char *destPtr;
- unsigned char *raw, *cooked;
- unsigned char leftover[100];
-
- raw = sinkPtr->byteArr + sinkPtr->mark;
- nRaw = sinkPtr->fill - sinkPtr->mark;
- /* Ideally, the cooked buffer size should be smaller */
- cookedSize = nRaw * TCL_UTF_MAX + 1;
- cooked = Blt_Malloc(cookedSize);
- result = Tcl_ExternalToUtf(interp, sinkPtr->encoding,
- (char *)raw, nRaw, 0, NULL, (char *)cooked,
- cookedSize, &nSrcCooked, &nCooked, NULL);
- nLeftOver = 0;
- if (result == TCL_CONVERT_MULTIBYTE) {
- /*
- * Last multibyte sequence wasn't completed. Save the
- * extra characters in a temporary buffer.
- */
- nLeftOver = (nRaw - nSrcCooked);
- srcPtr = sinkPtr->byteArr + (sinkPtr->mark + nSrcCooked);
- endPtr = srcPtr + nLeftOver;
- destPtr = leftover;
- while (srcPtr < endPtr) {
- *destPtr++ = *srcPtr++;
- }
- }
- /*
- * Create a bigger
- */
-
- needed = nLeftOver + nCooked;
- spaceLeft = sinkPtr->size - sinkPtr->mark;
- if (spaceLeft >= needed) {
- spaceLeft = ExtendSinkBuffer(sinkPtr);
- }
- assert(spaceLeft > needed);
- /*
- * Replace the characters from the mark with the translated
- * characters.
- */
- srcPtr = cooked;
- endPtr = cooked + nCooked;
- destPtr = sinkPtr->byteArr + sinkPtr->mark;
- while (srcPtr < endPtr) {
- *destPtr++ = *srcPtr++;
- }
- /* Add the number of newly translated characters to the mark */
- sinkPtr->mark += nCooked;
-
- srcPtr = leftover;
- endPtr = leftover + nLeftOver;
- while (srcPtr < endPtr) {
- *destPtr++ = *srcPtr++;
- }
- sinkPtr->fill = sinkPtr->mark + nLeftOver;
-#endif /* >= 8.1.0 */
- }
-#ifdef WIN32
- /*
- * Translate CRLF character sequences to LF characters. We have to
- * do this after converting the string to UTF from UNICODE.
- */
- if (sinkPtr->encoding != ENCODING_BINARY) {
- int count;
- unsigned char *destPtr;
-
- destPtr = srcPtr = sinkPtr->byteArr + oldMark;
- endPtr = sinkPtr->byteArr + sinkPtr->fill;
- *endPtr = '\0';
- count = 0;
- for (endPtr--; srcPtr < endPtr; srcPtr++) {
- if ((*srcPtr == '\r') && (*(srcPtr + 1) == '\n')) {
- count++;
- continue; /* Skip the CR in CR/LF sequences. */
- }
- if (srcPtr != destPtr) {
- *destPtr = *srcPtr; /* Collapse the string, overwriting
- * the \r's encountered. */
- }
- destPtr++;
- }
- sinkPtr->mark -= count;
- sinkPtr->fill -= count;
- *destPtr = *srcPtr; /* Copy the last byte */
- if (*destPtr == '\r') {
- sinkPtr->mark--;
- }
- }
-#endif /* WIN32 */
-}
-
-#ifdef WIN32
-/*
- *----------------------------------------------------------------------
- *
- * WaitProcess --
- *
- * Emulates the waitpid system call under the Win32 API.
- *
- * Results:
- * Returns 0 if the process is still alive, -1 on an error, or
- * the pid on a clean close.
- *
- * Side effects:
- * Unless WNOHANG is set and the wait times out, the process
- * information record will be deleted and the process handle
- * will be closed.
- *
- *----------------------------------------------------------------------
- */
-#define WINDEBUG 0
-static int
-WaitProcess(
- Process child,
- int *statusPtr,
- int flags)
-{
- int result;
- DWORD status, exitCode;
- int timeout;
-
-#if WINDEBUG
- PurifyPrintf("WAITPID(%x)\n", child.hProcess);
-#endif
- *statusPtr = 0;
- if (child.hProcess == INVALID_HANDLE_VALUE) {
- errno = EINVAL;
- return -1;
- }
-#if WINDEBUG
- PurifyPrintf("WAITPID: waiting for 0x%x\n", child.hProcess);
-#endif
- timeout = (flags & WNOHANG) ? 0 : INFINITE;
- status = WaitForSingleObject(child.hProcess, timeout);
-
-#if WINDEBUG
- PurifyPrintf("WAITPID: wait status is %d\n", status);
-#endif
- switch (status) {
- case WAIT_FAILED:
- errno = ECHILD;
- *statusPtr = ECHILD;
- result = -1;
- break;
-
- case WAIT_TIMEOUT:
- if (timeout == 0) {
- return 0; /* Try again */
- }
- result = 0;
- break;
-
- default:
- case WAIT_ABANDONED:
- case WAIT_OBJECT_0:
- GetExitCodeProcess(child.hProcess, &exitCode);
- *statusPtr = ((exitCode << 8) & 0xff00);
-#if WINDEBUG
- PurifyPrintf("WAITPID: exit code of %d is %d (%x)\n", child.hProcess,
- *statusPtr, exitCode);
-#endif
- result = child.pid;
- assert(result != -1);
- break;
- }
- CloseHandle(child.hProcess);
- return result;
-}
-
-static BOOL CALLBACK
-EnumWindowsProc(HWND hWnd, LPARAM lParam)
-{
- DWORD pid = 0;
- Process *procPtr = (Process *)lParam;
-
- GetWindowThreadProcessId(hWnd, &pid);
- if (pid == procPtr->pid) {
- PostMessage(hWnd, WM_CLOSE, 0, 0);
- }
- return TRUE;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * KillProcess --
- *
- * Emulates the UNIX kill system call under Win32 API.
- *
- * Results:
- * Returns 0 if the process is killed, -1 on an error.
- *
- * Side effects:
- * Process is terminated.
- *
- *----------------------------------------------------------------------
- */
-static int
-KillProcess(Process proc, int signal)
-{
- DWORD status;
-
- if ((proc.hProcess == NULL) || (proc.hProcess == INVALID_HANDLE_VALUE)) {
- errno = EINVAL;
- return -1;
- }
-
- EnumWindows(EnumWindowsProc, (LPARAM)&proc);
-
- /*
- * Wait on the handle. If it signals, great. If it times out,
- * then call TerminateProcess on it.
- *
- * On Windows 95/98 this also has the added benefit of stopping
- * KERNEL32.dll from dumping. The 2 second number is arbitrary.
- * (1 second seems to fail intermittently).
- */
- status = WaitForSingleObject(proc.hProcess, 2000);
- if (status == WAIT_OBJECT_0) {
- return 0;
- }
- if (!TerminateProcess(proc.hProcess, 1)) {
-#if WINDEBUG
- PurifyPrintf("can't terminate process (handle=%d): %s\n",
- proc.hProcess, Blt_LastError());
-#endif /* WINDEBUG */
- return -1;
- }
- return 0;
-}
-
-#endif /* WIN32 */
-
-#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
-
-static void
-NotifyOnUpdate(bgPtr, interp, sinkPtr, data, nBytes)
- BackgroundInfo *bgPtr;
- Tcl_Interp *interp;
- Sink *sinkPtr;
- unsigned char *data;
- int nBytes;
-{
- char save;
-
-#if WINDEBUG_0
- PurifyPrintf("read %s\n", data);
-#endif
- if (data[0] == '\0') {
- return;
- }
- save = data[nBytes];
- data[nBytes] = '\0';
- if (sinkPtr->echo) {
- Tcl_Channel channel;
-
- channel = Tcl_GetStdChannel(TCL_STDERR);
- if (channel == NULL) {
- Tcl_AppendResult(interp, "can't get stderr channel", (char *)NULL);
- Tcl_BackgroundError(interp);
- sinkPtr->echo = FALSE;
- } else {
- Tcl_Write(channel, data, nBytes);
- if (save == '\n') {
- Tcl_Write(channel, "\n", 1);
- }
- Tcl_Flush(channel);
- }
- }
- if (sinkPtr->updateCmd != NULL) {
- Tcl_DString dString;
- int result;
- register char **p;
-
- Tcl_DStringInit(&dString);
- for (p = sinkPtr->updateCmd; *p != NULL; p++) {
- Tcl_DStringAppendElement(&dString, *p);
- }
- Tcl_DStringAppendElement(&dString, data);
- result = Tcl_GlobalEval(interp, Tcl_DStringValue(&dString));
- Tcl_DStringFree(&dString);
- if (result != TCL_OK) {
- Tcl_BackgroundError(interp);
- }
- }
- if (sinkPtr->updateVar != NULL) {
- int flags;
- char *result;
-
- flags = (bgPtr->varFlags | TCL_LEAVE_ERR_MSG);
- result = Tcl_SetVar(interp, sinkPtr->updateVar, data, flags);
- if (result == NULL) {
- Tcl_BackgroundError(interp);
- }
- }
- data[nBytes] = save;
-}
-
-#else
-
-static void
-NotifyOnUpdate(bgPtr, interp, sinkPtr, data, nBytes)
- BackgroundInfo *bgPtr;
- Tcl_Interp *interp;
- Sink *sinkPtr;
- unsigned char *data;
- int nBytes;
-{
- Tcl_Obj *objPtr;
-
-#if WINDEBUG_0
- PurifyPrintf("read %s\n", data);
-#endif
- if ((nBytes == 0) || (data[0] == '\0')) {
- return;
- }
- if (sinkPtr->echo) {
- Tcl_Channel channel;
-
- channel = Tcl_GetStdChannel(TCL_STDERR);
- if (channel == NULL) {
- Tcl_AppendResult(interp, "can't get stderr channel", (char *)NULL);
- Tcl_BackgroundError(interp);
- sinkPtr->echo = FALSE;
- } else {
- if (data[nBytes] == '\n') {
- objPtr = Tcl_NewByteArrayObj(data, nBytes + 1);
- } else {
- objPtr = Tcl_NewByteArrayObj(data, nBytes);
- }
- Tcl_WriteObj(channel, objPtr);
- Tcl_Flush(channel);
- }
- }
-
- objPtr = Tcl_NewByteArrayObj(data, nBytes);
- Tcl_IncrRefCount(objPtr);
- if (sinkPtr->objv != NULL) {
- int result;
-
- sinkPtr->objv[sinkPtr->objc - 1] = objPtr;
- result = Tcl_EvalObjv(interp, sinkPtr->objc, sinkPtr->objv, TCL_GLOBAL_ONLY);
- if (result != TCL_OK) {
- Tcl_BackgroundError(interp);
- }
- }
- if (sinkPtr->updateVar != NULL) {
- Tcl_Obj *result;
-
- result = Tcl_SetVar2Ex(interp, sinkPtr->updateVar, NULL, objPtr,
- (bgPtr->varFlags | TCL_LEAVE_ERR_MSG));
- if (result == NULL) {
- Tcl_BackgroundError(interp);
- }
- }
- Tcl_DecrRefCount(objPtr);
-}
-
-#endif /* < 8.1.0 */
-
-static int
-CollectData(bgPtr, sinkPtr)
- BackgroundInfo *bgPtr;
- Sink *sinkPtr;
-{
- if ((bgPtr->detached) && (sinkPtr->doneVar == NULL) && (sinkPtr->command == NULL)) {
- ResetSink(sinkPtr);
- }
- ReadBytes(bgPtr, sinkPtr);
- CookSink(bgPtr->interp, sinkPtr);
- if ((sinkPtr->mark > sinkPtr->lastMark) &&
- (sinkPtr->flags & SINK_NOTIFY)) {
- unsigned char *data;
- int length;
-
- if (sinkPtr->flags & SINK_BUFFERED) {
- /* For line-by-line updates, call NotifyOnUpdate for each
- * new complete line. */
- while ((data = NextLine(sinkPtr, &length)) != NULL) {
- NotifyOnUpdate(bgPtr, bgPtr->interp, sinkPtr, data, length);
- }
- } else {
- data = NextBlock(sinkPtr, &length);
- NotifyOnUpdate(bgPtr, bgPtr->interp, sinkPtr, data, length);
- }
- }
- if (bgPtr->maxBytes>0) {
- if (sinkPtr->mark >= bgPtr->maxBytes) {
- bgPtr->maxExceed = 1;
- return TCL_BREAK;
- }
- }
- if (sinkPtr->status >= 0) {
- return TCL_OK;
- }
- if (sinkPtr->status == READ_ERROR) {
- Tcl_AppendResult(bgPtr->interp, "can't read data from ", sinkPtr->name,
- ": ", Tcl_PosixError(bgPtr->interp), (char *)NULL);
- Tcl_BackgroundError(bgPtr->interp);
- return TCL_ERROR;
- }
-#if WINDEBUG
- PurifyPrintf("CollectData %s: done\n", sinkPtr->name);
-#endif
- return TCL_RETURN;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * CreateSinkHandler --
- *
- * Creates a file handler for the given sink. The file
- * descriptor is also set for non-blocking I/O.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The memory allocated to the BackgroundInfo structure released.
- *
- *----------------------------------------------------------------------
- */
-static int
-CreateSinkHandler(bgPtr, sinkPtr, proc)
- BackgroundInfo *bgPtr;
- Sink *sinkPtr;
- Tcl_FileProc *proc;
-{
-#ifndef WIN32
- int flags;
-
- flags = fcntl(sinkPtr->fd, F_GETFL);
-#ifdef O_NONBLOCK
- flags |= O_NONBLOCK;
-#else
- flags |= O_NDELAY;
-#endif
- if (fcntl(sinkPtr->fd, F_SETFL, flags) < 0) {
- Tcl_AppendResult(bgPtr->interp, "can't set file descriptor ",
- Blt_Itoa(sinkPtr->fd), " to non-blocking:",
- Tcl_PosixError(bgPtr->interp), (char *)NULL);
- return TCL_ERROR;
- }
-#endif /* WIN32 */
-#ifdef FILEHANDLER_USES_TCLFILES
- sinkPtr->file = Tcl_GetFile((ClientData)sinkPtr->fd, TCL_UNIX_FD);
- Tcl_CreateFileHandler(sinkPtr->file, TCL_READABLE, proc, bgPtr);
-#else
- Tcl_CreateFileHandler(sinkPtr->fd, TCL_READABLE, proc, bgPtr);
-#endif /* FILEHANDLER_USES_TCLFILES */
- return TCL_OK;
-}
-
-static void
-DisableTriggers(bgPtr)
- BackgroundInfo *bgPtr; /* Background info record. */
-{
- int k;
-
- if (bgPtr->traced) {
- Tcl_UntraceVar(bgPtr->interp, bgPtr->statVar, TRACE_FLAGS,
- VariableProc, bgPtr);
- bgPtr->traced = FALSE;
- }
- if (IsOpenSink(&bgPtr->sink1)) {
- CloseSink(bgPtr, bgPtr->interp, &bgPtr->sink1);
- }
- if (IsOpenSink(&bgPtr->sink2)) {
- CloseSink(bgPtr, bgPtr->interp, &bgPtr->sink2);
- }
- for( k = 0 ; k < bgPtr->nTimers ; k++ ){
- Tcl_DeleteTimerHandler(bgPtr->timerTokens[k]);
- }
- bgPtr->nTimers = 0;
- if (bgPtr->donePtr != NULL) {
- *bgPtr->donePtr = TRUE;
- }
-}
-
-
-/*
- *----------------------------------------------------------------------
- *
- * FreeBackgroundInfo --
- *
- * Releases the memory allocated for the backgrounded process.
- *
- *----------------------------------------------------------------------
- */
-static void
-FreeBackgroundInfo(bgPtr)
- BackgroundInfo *bgPtr;
-{
- Blt_FreeSwitches(bgPtr->interp, switchSpecs, (char *)bgPtr, 0);
- if (bgPtr->statVar != NULL) {
- Blt_Free(bgPtr->statVar);
- }
- if (bgPtr->procArr != NULL) {
- Blt_Free(bgPtr->procArr);
- }
- if (bgPtr->timerTokens != NULL) {
- Blt_Free(bgPtr->timerTokens);
- }
- Blt_Free(bgPtr);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DestroyBackgroundInfo --
- *
- * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
- * to clean up the internal structure (BackgroundInfo) at a safe
- * time (when no one is using it anymore).
- *
- * Results:
- * None.b
- *
- * Side effects:
- * The memory allocated to the BackgroundInfo structure released.
- *
- *----------------------------------------------------------------------
- */
-/* ARGSUSED */
-static void
-DestroyBackgroundInfo(bgPtr)
- BackgroundInfo *bgPtr; /* Background info record. */
-{
- Tcl_DontCallWhenDeleted(bgPtr->interp, InterpDeleted, (ClientData)bgPtr);
- DisableTriggers(bgPtr);
- FreeSinkBuffer(&bgPtr->sink2);
- FreeSinkBuffer(&bgPtr->sink1);
- if (bgPtr->procArr != NULL) {
- register int i;
-
- for (i = 0; i < bgPtr->nProcs; i++) {
- if (bgPtr->signalNum > 0) {
- kill(bgPtr->procArr[i], bgPtr->signalNum);
- }
-#ifdef WIN32
- Tcl_DetachPids(1, (Tcl_Pid *)&bgPtr->procArr[i].pid);
-#else
-#if (TCL_MAJOR_VERSION == 7)
- Tcl_DetachPids(1, &bgPtr->procArr[i]);
-#else
- Tcl_DetachPids(1, (Tcl_Pid *)&bgPtr->procArr[i]);
-#endif /* TCL_MAJOR_VERSION == 7 */
-#endif /* WIN32 */
- }
- }
- FreeBackgroundInfo(bgPtr);
- Tcl_ReapDetachedProcs();
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * VariableProc --
- *
- * Kills all currently running subprocesses (given the specified
- * signal). This procedure is called when the user sets the status
- * variable associated with this group of child subprocesses.
- *
- * Results:
- * Always returns NULL. Only called from a variable trace.
- *
- * Side effects:
- * The subprocesses are signaled for termination using the
- * specified kill signal. Additionally, any resources allocated
- * to track the subprocesses is released.
- *
- * ----------------------------------------------------------------------
- */
-/* ARGSUSED */
-static char *
-VariableProc(
- ClientData clientData, /* File output information. */
- Tcl_Interp *interp,
- char *part1,
- char *part2, /* Not Used. */
- int flags)
-{
- if (flags & TRACE_FLAGS) {
- BackgroundInfo *bgPtr = clientData;
-
- /* Kill all child processes that remain alive. */
- if ((bgPtr->procArr != NULL) && (bgPtr->signalNum > 0)) {
- register int i;
-
- for (i = 0; i < bgPtr->nProcs; i++) {
- kill(bgPtr->procArr[i], bgPtr->signalNum);
- }
- if (bgPtr->closeKill>0 && bgPtr->nProcs>0) {
- bgPtr->cKilled = 1;
- AddTimerHandler( bgPtr, bgPtr->closeKill ) ;
- }
- }
- }
- return NULL;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TimerProc --
- *
- * This is a timer handler procedure which gets called
- * periodically to reap any of the sub-processes if they have
- * terminated. After the last process has terminated, the
- * contents of standard output are stored
- * in the output variable, which triggers the cleanup proc (using
- * a variable trace). The status the last process to exit is
- * written to the status variable.
- *
- * Results:
- * None. Called from the Tcl event loop.
- *
- * Side effects:
- * Many. The contents of procArr is shifted, leaving only those
- * sub-processes which have not yet terminated. If there are
- * still subprocesses left, this procedure is placed in the timer
- * queue again. Otherwise the output and possibly the status
- * variables are updated. The former triggers the cleanup
- * routine which will destroy the information and resources
- * associated with these background processes.
- *
- *----------------------------------------------------------------------
- */
-static void
-TimerProc(clientData)
- ClientData clientData;
-{
- BackgroundInfo *bgPtr = clientData;
- register int i;
- unsigned int lastPid;
- int pid;
- enum PROCESS_STATUS {
- PROCESS_EXITED, PROCESS_STOPPED, PROCESS_KILLED, PROCESS_UNKNOWN,
- PROCESS_MAX
- } pcode;
- WAIT_STATUS_TYPE waitStatus, lastStatus;
- int nLeft; /* Number of processes still not reaped */
- char string[200];
- Tcl_DString dString;
- int code;
- CONST char *result;
-
- lastPid = (unsigned int)-1;
- *((int *)&waitStatus) = 0;
- *((int *)&lastStatus) = 0;
-
- nLeft = 0;
- for (i = 0; i < bgPtr->nProcs; i++) {
-#ifdef WIN32
- pid = WaitProcess(bgPtr->procArr[i], (int *)&waitStatus, WNOHANG);
-#else
- pid = waitpid(bgPtr->procArr[i], (int *)&waitStatus, WNOHANG);
-#endif
- if (pid == 0) { /* Process has not terminated yet */
- if (nLeft < i) {
- bgPtr->procArr[nLeft] = bgPtr->procArr[i];
- }
- nLeft++; /* Count the number of processes left */
- } else if (pid != -1) {
- /*
- * Save the status information associated with the subprocess.
- * We'll use it only if this is the last subprocess to be reaped.
- */
- lastStatus = waitStatus;
- lastPid = (unsigned int)pid;
- }
- }
- bgPtr->nProcs = nLeft;
-
- if ((nLeft > 0) || (IsOpenSink(&bgPtr->sink1)) ||
- (IsOpenSink(&bgPtr->sink2))) {
- /* Keep polling for the status of the children that are left */
- if (bgPtr->closeKill>0 && bgPtr->cKilled) {
- if (IsOpenSink(&bgPtr->sink1)) {
- CloseSink(bgPtr, bgPtr->interp, &bgPtr->sink1);
- }
- if (IsOpenSink(&bgPtr->sink2)) {
- CloseSink(bgPtr, bgPtr->interp, &bgPtr->sink2);
- }
- }
- AddTimerHandler( bgPtr, bgPtr->interval ) ;
-#if WINDEBUG
- PurifyPrintf("schedule TimerProc(nProcs=%d)\n", nLeft);
-#endif
- return;
- }
-
- /*
- * All child processes have completed. Set the status variable
- * with the status of the last process reaped. The status is a
- * list of an error token, the exit status, and a message.
- */
-
- code = WEXITSTATUS(lastStatus);
- Tcl_DStringInit(&dString);
- if (bgPtr->maxExceed) {
- Tcl_DStringAppendElement(&dString, "MAXBYTES");
- pcode = PROCESS_MAX;
- } else if (WIFEXITED(lastStatus)) {
- Tcl_DStringAppendElement(&dString, "EXITED");
- pcode = PROCESS_EXITED;
- } else if (WIFSIGNALED(lastStatus)) {
- Tcl_DStringAppendElement(&dString, "KILLED");
- pcode = PROCESS_KILLED;
- code = -1;
- } else if (WIFSTOPPED(lastStatus)) {
- Tcl_DStringAppendElement(&dString, "STOPPED");
- pcode = PROCESS_STOPPED;
- code = -1;
- } else {
- Tcl_DStringAppendElement(&dString, "UNKNOWN");
- pcode = PROCESS_UNKNOWN;
- }
-#ifdef WIN32
- sprintf(string, "%u", lastPid);
- Tcl_DStringAppendElement(&dString, string);
-#else
- Tcl_DStringAppendElement(&dString, Blt_Itoa(lastPid));
-#endif
- Tcl_DStringAppendElement(&dString, Blt_Itoa(code));
- switch(pcode) {
- case PROCESS_MAX:
- Tcl_DStringAppendElement(&dString, "max bytes exceeded");
- break;
- case PROCESS_EXITED:
- if (bgPtr->cKilled) {
- Tcl_DStringAppendElement(&dString, "kill signal");
- } else {
- Tcl_DStringAppendElement(&dString, "child completed normally");
- }
- break;
- case PROCESS_KILLED:
- Tcl_DStringAppendElement(&dString,
- Tcl_SignalMsg((int)(WTERMSIG(lastStatus))));
- break;
- case PROCESS_STOPPED:
- Tcl_DStringAppendElement(&dString,
- Tcl_SignalMsg((int)(WSTOPSIG(lastStatus))));
- break;
- case PROCESS_UNKNOWN:
- sprintf(string, "child completed with unknown status 0x%x",
- *((int *)&lastStatus));
- Tcl_DStringAppendElement(&dString, string);
- break;
- }
- if (bgPtr->exitCodePtr != NULL) {
- *bgPtr->exitCodePtr = code;
- }
- DisableTriggers(bgPtr);
- result = Tcl_SetVar(bgPtr->interp, bgPtr->statVar,
- Tcl_DStringValue(&dString), bgPtr->varFlags | TCL_LEAVE_ERR_MSG);
- SinkCallback(bgPtr->interp, &bgPtr->sink1, Tcl_DStringValue(&dString));
- Tcl_DStringFree(&dString);
- if (result == NULL) {
- Tcl_BackgroundError(bgPtr->interp);
- }
- if (bgPtr->detached) {
- DestroyBackgroundInfo(bgPtr);
- }
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Stdoutproc --
- *
- * This procedure is called when output from the detached pipeline
- * is available. The output is read and saved in a buffer in the
- * BackgroundInfo structure.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Data is stored in the buffer. This character array may
- * be increased as more space is required to contain the output
- * of the pipeline.
- *
- *----------------------------------------------------------------------
- */
-/* ARGSUSED */
-static void
-StdoutProc(clientData, mask)
- ClientData clientData; /* File output information. */
- int mask; /* Not used. */
-{
- BackgroundInfo *bgPtr = clientData;
-
- if (CollectData(bgPtr, &bgPtr->sink1) == TCL_OK) {
- return;
- }
- /*
- * Either EOF or an error has occurred. In either case, close the
- * sink. Note that closing the sink will also remove the file
- * handler, so this routine will not be called again.
- */
- CloseSink(bgPtr, bgPtr->interp, &bgPtr->sink1);
-
- /*
- * If both sinks (stdout and stderr) are closed, this doesn't
- * necessarily mean that the process has terminated. Set up a
- * timer handler to periodically poll for the exit status of each
- * process. Initially check at the next idle interval.
- */
- if (!IsOpenSink(&bgPtr->sink2)) {
- AddTimerHandler( bgPtr, 0 ) ;
- }
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * StderrProc --
- *
- * This procedure is called when error from the detached pipeline
- * is available. The error is read and saved in a buffer in the
- * BackgroundInfo structure.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Data is stored in the buffer. This character array may
- * be increased as more space is required to contain the stderr
- * of the pipeline.
- *
- *----------------------------------------------------------------------
- */
-/* ARGSUSED */
-static void
-StderrProc(clientData, mask)
- ClientData clientData; /* File output information. */
- int mask; /* Not used. */
-{
- BackgroundInfo *bgPtr = clientData;
-
- if (CollectData(bgPtr, &bgPtr->sink2) == TCL_OK) {
- return;
- }
- /*
- * Either EOF or an error has occurred. In either case, close the
- * sink. Note that closing the sink will also remove the file
- * handler, so this routine will not be called again.
- */
- CloseSink(bgPtr, bgPtr->interp, &bgPtr->sink2);
-
- /*
- * If both sinks (stdout and stderr) are closed, this doesn't
- * necessarily mean that the process has terminated. Set up a
- * timer handler to periodically poll for the exit status of each
- * process. Initially check at the next idle interval.
- */
- if (!IsOpenSink(&bgPtr->sink1)) {
- AddTimerHandler( bgPtr, 0 ) ;
- }
-}
-
-static void
-AddTimerHandler( bgPtr, interval )
- BackgroundInfo *bgPtr;
- int interval;
-{
- if(!(bgPtr->timerTokens)){
- bgPtr->timerTokens = Blt_Calloc(EXPECTED_NUM_TIMERS, sizeof(Tcl_TimerToken *));
- }
- if( ++bgPtr->nTimers >= EXPECTED_NUM_TIMERS ){
- bgPtr->timerTokens = Blt_Realloc( bgPtr->timerTokens,
- bgPtr->nTimers * sizeof(Tcl_TimerToken *) ) ;
- }
- bgPtr->timerTokens[bgPtr->nTimers-1] = Tcl_CreateTimerHandler(interval,
- TimerProc,
- bgPtr);
-}
-
-static void
-InterpDeleted(clientData, interp)
-ClientData clientData; /* Thread-specific data. */
-Tcl_Interp *interp; /* Current interpreter. */
-{
- BackgroundInfo *bgPtr = (BackgroundInfo *)clientData;
- DestroyBackgroundInfo(bgPtr);
-}
-
-void
-MakeLocal( char *ns, char **str) {
- Tcl_DString dStr;
- if (ns == NULL) return;
- if (!strcmp(ns,"::")) return;
- if (!strncmp(*str,"::",2)) return;
- Tcl_DStringInit(&dStr);
- Tcl_DStringAppend(&dStr, ns, -1);
- Tcl_DStringAppend(&dStr, "::", -1);
- Tcl_DStringAppend(&dStr, *str, -1);
- Blt_Free(*str);
- *str = Blt_Strdup( Tcl_DStringValue(&dStr) );
- Tcl_DStringFree(&dStr);
-}
-/*
- *----------------------------------------------------------------------
- *
- * BgexecCmd --
- *
- * This procedure is invoked to process the "bgexec" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-/* ARGSUSED */
-static int
-BgexecCmd(clientData, interp, argc, argv)
- ClientData clientData; /* Thread-specific data. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
-{
- int *outFdPtr, *errFdPtr;
- int nProcs;
- Process *pidPtr;
- char *lastArg;
- BackgroundInfo *bgPtr;
- int i, term = 0, tail = 0;
- int detached;
- Tcl_Encoding encoding;
-
- if (argc < 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " varName ?options? command ?arg...?\"", (char *)NULL);
- return TCL_ERROR;
- }
-
- /* Check if the command line is to be run detached (the last
- * argument is "&") */
- lastArg = argv[argc - 1];
- detached = ((lastArg[0] == '&') && (lastArg[1] == '\0'));
- if (detached) {
- argc--;
- argv[argc] = NULL; /* Remove the '&' argument */
- }
- bgPtr = Blt_Calloc(1, sizeof(BackgroundInfo));
- assert(bgPtr);
-
- /* Initialize the background information record */
- bgPtr->interp = interp;
- bgPtr->signalNum = SIGKILL;
- bgPtr->nProcs = -1;
- bgPtr->interval = 1000;
- bgPtr->detached = detached;
- bgPtr->keepNewline = TRUE;
- bgPtr->statVar = Blt_Strdup(argv[1]);
- bgPtr->timerTokens = NULL;
- bgPtr->nTimers = 0;
- bgPtr->closeKill = 0;
-
- /* Try to clean up any detached processes */
- Tcl_ReapDetachedProcs();
-
- for (i=2; i<argc; i += 2) {
- if (!strcmp(argv[i],"--")) {
- term = i+1;
- tail = (argc-i);
- break;
- }
- }
- i = Blt_ProcessSwitches(interp, switchSpecs, argc - 2 - tail, argv + 2,
- (char *)bgPtr, BLT_SWITCH_ARGV_PARTIAL);
- if (i < 0) {
- FreeBackgroundInfo(bgPtr);
- return TCL_ERROR;
- }
- bgPtr->varFlags = TCL_GLOBAL_ONLY;
- if (bgPtr->localVars) {
- if (detached) {
- bgPtr->varFlags = TCL_NAMESPACE_ONLY;
- } else {
- bgPtr->varFlags = 0;
- }
- }
- if (term) {
- i = term;
- } else {
- i += 2;
- }
- /* Must be at least one argument left as the command to execute. */
- if (argc <= i) {
- Tcl_AppendResult(interp, "missing command to execute: should be \"",
- argv[0], " varName ?options? command ?arg...?\"", (char *)NULL);
- FreeBackgroundInfo(bgPtr);
- return TCL_ERROR;
- }
-
- if (bgPtr->localVars && (detached)) {
- char *ns = NULL;
- Tcl_Namespace *nsPtr;
- nsPtr = Tcl_GetCurrentNamespace(interp);
-
- if (nsPtr != NULL) {
- ns = nsPtr->fullName;
- }
- if (bgPtr->statVar) MakeLocal(ns, &bgPtr->statVar);
- if (bgPtr->sink1.doneVar) MakeLocal(ns, &bgPtr->sink1.doneVar);
- if (bgPtr->sink1.command) MakeLocal(ns, &bgPtr->sink1.command);
- if (bgPtr->sink1.updateVar) MakeLocal(ns, &bgPtr->sink1.updateVar);
- if (bgPtr->sink2.doneVar) MakeLocal(ns, &bgPtr->sink2.doneVar);
- if (bgPtr->sink2.command) MakeLocal(ns, &bgPtr->sink2.command);
- if (bgPtr->sink2.updateVar) MakeLocal(ns, &bgPtr->sink2.updateVar);
- }
-
- /* Put a trace on the exit status variable. The will also allow
- * the user to prematurely terminate the pipeline by simply
- * setting it. */
- if (Tcl_TraceVar(interp, bgPtr->statVar, TRACE_FLAGS, VariableProc, bgPtr) != TCL_OK) {
- FreeBackgroundInfo(bgPtr);
- return TCL_ERROR;
- }
- bgPtr->traced = TRUE;
-
- encoding = ENCODING_ASCII;
- if (bgPtr->outputEncodingName != NULL) {
- if (strcmp(bgPtr->outputEncodingName, "binary") == 0) {
- encoding = ENCODING_BINARY;
- } else {
-#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
- encoding = Tcl_GetEncoding(interp, bgPtr->outputEncodingName);
- if (encoding == NULL) {
- goto error;
- }
-#endif
- }
- }
- InitSink(bgPtr, &bgPtr->sink1, "stdout", encoding);
- if (bgPtr->errorEncodingName != NULL) {
- if (strcmp(bgPtr->errorEncodingName, "binary") == 0) {
- encoding = ENCODING_BINARY;
- } else {
-#if (TCL_VERSION_NUMBER >= _VERSION(8,1,0))
- encoding = Tcl_GetEncoding(interp, bgPtr->errorEncodingName);
- if (encoding == NULL) {
- goto error;
- }
-#endif
- }
- }
- InitSink(bgPtr, &bgPtr->sink2, "stderr", encoding);
-
- outFdPtr = errFdPtr = (int *)NULL;
-#ifdef WIN32
- if ((!bgPtr->detached) ||
- (bgPtr->sink1.doneVar != NULL) ||
- (bgPtr->sink1.command != NULL) ||
- (bgPtr->sink1.updateVar != NULL) ||
- (bgPtr->sink1.updateCmd != NULL)) {
- outFdPtr = &bgPtr->sink1.fd;
- }
-#else
- outFdPtr = &bgPtr->sink1.fd;
-#endif
- if ((bgPtr->sink2.doneVar != NULL) ||
- (bgPtr->sink2.updateVar != NULL) ||
- (bgPtr->sink2.updateCmd != NULL) ||
- (bgPtr->sink2.echo)) {
- errFdPtr = &bgPtr->sink2.fd;
- }
- nProcs = Blt_CreatePipeline(interp, argc - i, argv + i, &pidPtr,
- (int *)NULL, outFdPtr, errFdPtr);
- if (nProcs < 0) {
- goto error;
- }
- bgPtr->procArr = pidPtr;
- bgPtr->nProcs = nProcs;
-
- if (bgPtr->sink1.fd == -1) {
-
- /* If output has been redirected, start polling immediately
- * for the exit status of each process. Normally, this is
- * done only after stdout has been closed by the last process,
- * but here stdout has been redirected. The default polling
- * interval is every 1 second. */
-
- AddTimerHandler( bgPtr, bgPtr->interval ) ;
-
- } else if (CreateSinkHandler(bgPtr, &bgPtr->sink1, StdoutProc) != TCL_OK) {
- goto error;
- }
- if ((bgPtr->sink2.fd != -1) &&
- (CreateSinkHandler(bgPtr, &bgPtr->sink2, StderrProc) != TCL_OK)) {
- goto error;
- }
- Tcl_CallWhenDeleted(interp, InterpDeleted, (ClientData)bgPtr);
- if (bgPtr->detached) {
- char string[200];
-
- /* If detached, return a list of the child process ids instead
- * of the output of the pipeline. */
- for (i = 0; i < nProcs; i++) {
-#ifdef WIN32
- sprintf(string, "%u", (unsigned int)bgPtr->procArr[i].pid);
-#else
- sprintf(string, "%d", bgPtr->procArr[i]);
-#endif
- Tcl_AppendElement(interp, string);
- }
- } else {
- int exitCode;
- int done, doRaise;
-
- bgPtr->exitCodePtr = &exitCode;
- bgPtr->donePtr = &done;
-
- exitCode = done = 0;
- while (!done) {
- Tcl_DoOneEvent(0);
- }
- DisableTriggers(bgPtr);
- if ((exitCode == 0 || bgPtr->doRaise==0) && (bgPtr->sink1.doneVar == NULL) && (bgPtr->sink1.command == NULL)) {
- unsigned char *data;
- size_t length;
-
- /* Return the output of the pipeline. */
- GetSinkData(&bgPtr->sink1, &data, &length);
-#if (TCL_VERSION_NUMBER < _VERSION(8,1,0))
- data[length] = '\0';
- Tcl_SetResult(interp, data, TCL_VOLATILE);
-#else
- Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(data, length));
-#endif
- }
- /* Clean up resources used. */
- doRaise = bgPtr->doRaise;
- DestroyBackgroundInfo(bgPtr);
- if ((doRaise) && exitCode != 0) {
- Tcl_AppendResult(interp, "child process exited abnormally",
- (char *)NULL);
- return TCL_ERROR;
- }
- }
- return TCL_OK;
- error:
- DisableTriggers(bgPtr);
- DestroyBackgroundInfo(bgPtr);
- return TCL_ERROR;
-}
-
-
-static int
-KillCmd(clientData, interp, argc, argv)
- ClientData clientData; /* Thread-specific data. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
-{
- char *string;
- int signalNum = SIGKILL;
- int pid;
-#ifdef WIN32
- HANDLE h;
-#endif
-
- if (argc>3) {
- Tcl_AppendResult(interp, "too many args", 0);
- return TCL_ERROR;
- }
- if (argc<2) {
- Tcl_AppendResult(interp, "pid required", 0);
- return TCL_ERROR;
- }
- if (Tcl_GetInt(interp, argv[1], &pid) != TCL_OK) {
- return TCL_ERROR;
- }
-
-#ifdef WIN32
- if (argc!=2) {
- Tcl_AppendResult(interp, "windows does not support signo", 0);
- return TCL_ERROR;
- }
- if (!(h = OpenProcess (PROCESS_TERMINATE, 0, pid))) {
- Tcl_AppendResult (interp, "invalid pid or access", NULL);
- return TCL_ERROR;
- }
-
- if (TerminateProcess (h, 1) == 0) {
- Tcl_AppendResult (interp, "TerminateProcess failure", NULL);
- return TCL_ERROR;
- }
-
- CloseHandle (h);
-#else
- if (argc==3) {
- string = argv[2];
- if (isdigit(UCHAR(string[0]))) {
- if (Tcl_GetInt(interp, string, &signalNum) != TCL_OK) {
- return TCL_ERROR;
- }
- } else {
- char *name;
- register SignalId *sigPtr;
-
- name = string;
-
- /* Clip off any "SIG" prefix from the signal name */
- if ((name[0] == 'S') && (name[1] == 'I') && (name[2] == 'G')) {
- name += 3;
- }
- signalNum = -1;
- for (sigPtr = signalIds; sigPtr->number > 0; sigPtr++) {
- if (strcmp(sigPtr->name + 3, name) == 0) {
- signalNum = sigPtr->number;
- break;
- }
- }
- if (signalNum < 0) {
- Tcl_AppendResult(interp, "unknown signal \"", string, "\"",
- (char *)NULL);
- return TCL_ERROR;
- }
- }
- if ((signalNum < 0) || (signalNum > NSIG)) {
- /* Outside range of signals */
- Tcl_AppendResult(interp, "signal number \"", string,
- "\" is out of range", (char *)NULL);
- return TCL_ERROR;
- }
- }
- kill(pid, signalNum);
-#endif
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Blt_BgexecInit --
- *
- * This procedure is invoked to initialize the "bgexec" Tcl
- * command. See the user documentation for details on what it
- * does.
- *
- * Results:
- * None.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-int
-Blt_BgexecInit(interp)
- Tcl_Interp *interp;
-{
- static Blt_CmdSpec cmdSpec = {"bgexec", BgexecCmd, };
- static Blt_CmdSpec cmdSpec2 = {"kill", KillCmd, };
-
- if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
- return TCL_ERROR;
- }
- if (Blt_InitCmd(interp, "blt", &cmdSpec2) == NULL) {
- return TCL_ERROR;
- }
- return TCL_OK;
-}
-#endif /* NO_BGEXEC */