home *** CD-ROM | disk | FTP | other *** search
/ Serving the Web / ServingTheWeb1995.disc1of1.iso / linux / slacksrce / tcl / tcl+tk+t / tclx7.3bl / tclx7 / tclX7.3b / src / tclXsignal.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-16  |  48.0 KB  |  1,712 lines

  1. /*
  2.  * tclXsignal.c --
  3.  *
  4.  * Tcl Unix signal support routines and the signal and commands.
  5.  *-----------------------------------------------------------------------------
  6.  * Copyright 1991-1994 Karl Lehenbauer and Mark Diekhans.
  7.  *
  8.  * Permission to use, copy, modify, and distribute this software and its
  9.  * documentation for any purpose and without fee is hereby granted, provided
  10.  * that the above copyright notice appear in all copies.  Karl Lehenbauer and
  11.  * Mark Diekhans make no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without express or
  13.  * implied warranty.
  14.  *-----------------------------------------------------------------------------
  15.  * $Id: tclXsignal.c,v 4.0 1994/07/16 05:27:50 markd Rel $
  16.  *-----------------------------------------------------------------------------
  17.  */
  18.  
  19. #include "tclExtdInt.h"
  20.  
  21.  
  22. #ifndef SIGCLD
  23. #   define SIGCLD SIGCHLD
  24. #endif
  25. #ifndef SIGCHLD
  26. #   define SIGCHLD SIGCLD
  27. #endif
  28.  
  29. #ifndef MAXSIG
  30. #  ifdef NSIG
  31. #    define MAXSIG NSIG
  32. #  else
  33. #    define MAXSIG 32
  34. #  endif
  35. #endif
  36.  
  37. /*
  38.  * Value returned by Tcl_SignalId when an invalid signal is passed in.
  39.  * Pointer is used as a quick check of a valid signal number.
  40.  */
  41. static char *unknownSignalIdMsg;
  42.  
  43. /*
  44.  * Signal name table maps name to number.  Note, it is possible to have
  45.  * more than MAXSIG entries in this table if the system defines multiple
  46.  * symbols that have the same value.
  47.  */
  48.  
  49. #define SIG_NAME_MAX 9  /* Maximum length of any signal name */
  50.  
  51. static struct {
  52.     char *name;
  53.     short num;
  54. } sigNameTable [] = {
  55. #ifdef SIGABRT
  56.     "ABRT",    SIGABRT,
  57. #endif
  58. #ifdef SIGALRM
  59.     "ALRM",    SIGALRM,
  60. #endif
  61. #ifdef SIGBUS
  62.     "BUS",     SIGBUS,
  63. #endif
  64. #ifdef SIGCHLD
  65.     "CHLD",    SIGCHLD,
  66. #endif
  67. #ifdef SIGCLD
  68.     "CLD",     SIGCLD,
  69. #endif
  70. #ifdef SIGCONT
  71.     "CONT",    SIGCONT,
  72. #endif
  73. #ifdef SIGEMT
  74.     "EMT",     SIGEMT,
  75. #endif
  76. #ifdef SIGFPE
  77.     "FPE",     SIGFPE,
  78. #endif
  79. #ifdef SIGHUP
  80.     "HUP",     SIGHUP,
  81. #endif
  82. #ifdef SIGILL
  83.     "ILL",     SIGILL,
  84. #endif
  85. #ifdef SIGINT
  86.     "INT",     SIGINT,
  87. #endif
  88. #ifdef SIGIO
  89.     "IO",      SIGIO,
  90. #endif
  91. #ifdef SIGIOT
  92.     "IOT",     SIGIOT,
  93. #endif
  94. #ifdef SIGKILL
  95.     "KILL",    SIGKILL,
  96. #endif
  97. #ifdef SIGLOST
  98.     "LOST",    SIGLOST,
  99. #endif
  100. #ifdef SIGPIPE
  101.     "PIPE",    SIGPIPE,
  102. #endif
  103. #ifdef SIGPOLL
  104.     "POLL",    SIGPOLL,
  105. #endif
  106. #ifdef SIGPROF
  107.     "PROF",    SIGPROF,
  108. #endif
  109. #ifdef SIGPWR
  110.     "PWR",     SIGPWR,
  111. #endif
  112. #ifdef SIGQUIT
  113.     "QUIT",    SIGQUIT,
  114. #endif
  115. #ifdef SIGSEGV
  116.     "SEGV",    SIGSEGV,
  117. #endif
  118. #ifdef SIGSTOP
  119.     "STOP",    SIGSTOP,
  120. #endif
  121. #ifdef SIGSYS
  122.     "SYS",     SIGSYS,
  123. #endif
  124. #ifdef SIGTERM
  125.     "TERM",    SIGTERM,
  126. #endif
  127. #ifdef SIGTRAP
  128.     "TRAP",    SIGTRAP,
  129. #endif
  130. #ifdef SIGTSTP
  131.     "TSTP",    SIGTSTP,
  132. #endif
  133. #ifdef SIGTTIN
  134.     "TTIN",    SIGTTIN,
  135. #endif
  136. #ifdef SIGTTOU
  137.     "TTOU",    SIGTTOU,
  138. #endif
  139. #ifdef SIGURG
  140.     "URG",     SIGURG,
  141. #endif
  142. #ifdef SIGUSR1
  143.     "USR1",    SIGUSR1,
  144. #endif
  145. #ifdef SIGUSR2
  146.     "USR2",    SIGUSR2,
  147. #endif
  148. #ifdef SIGVTALRM
  149.     "VTALRM",  SIGVTALRM,
  150. #endif
  151. #ifdef SIGWINCH
  152.     "WINCH",   SIGWINCH,
  153. #endif
  154. #ifdef SIGXCPU
  155.     "XCPU",    SIGXCPU,
  156. #endif
  157. #ifdef SIGXFSZ
  158.     "XFSZ",    SIGXFSZ,
  159. #endif
  160.     NULL,         -1};
  161.  
  162. #ifndef RETSIGTYPE
  163. #   define RETSIGTYPE void
  164. #endif
  165.  
  166. typedef RETSIGTYPE (*signalProcPtr_t) _ANSI_ARGS_((int));
  167.  
  168.  
  169. /*
  170.  * Defines if this is not Posix.
  171.  */
  172. #ifndef SIG_BLOCK
  173. #   define SIG_BLOCK       1
  174. #   define SIG_UNBLOCK     2
  175. #endif
  176.  
  177. /*
  178.  * Symbolic signal actions that can be associated with a signal.
  179.  */
  180. static char *SIGACT_DEFAULT = "default";
  181. static char *SIGACT_IGNORE  = "ignore";
  182. static char *SIGACT_ERROR   = "error";
  183. static char *SIGACT_TRAP    = "trap";
  184. static char *SIGACT_UNKNOWN = "unknown";
  185.  
  186. /*
  187.  * Structure used to save error state of the interpreter.
  188.  */
  189. typedef struct {
  190.     char  *result;
  191.     char  *errorInfo;
  192.     char  *errorCode;
  193. } errState_t;
  194.  
  195. /*
  196.  * Table containing a interpreters and there Async handler cookie.
  197.  */
  198. typedef struct {
  199.     Tcl_Interp       *interp;
  200.     Tcl_AsyncHandler  handler;
  201. } interpHandler_t;
  202.  
  203. static interpHandler_t *interpTable = NULL;
  204. static int              interpTableSize  = 0;
  205. static int              numInterps  = 0;
  206.  
  207. /*
  208.  * A flag indicating that an "error" signal has occured.  This is used to
  209.  * flush interactive input in commands and is only cleared there.  Also an
  210.  * application-supplied function to call if a error signal occurs.  This
  211.  * normally flushes command input.
  212.  */
  213. int tclGotErrorSignal = FALSE;
  214. void (*tclErrorSignalProc) _ANSI_ARGS_((int signalNum)) = NULL;
  215.  
  216. /*
  217.  * Pointer to background error handler (normally NULL or Tk_BackgroundError).
  218.  */
  219. void (*tclSignalBackgroundError) _ANSI_ARGS_((Tcl_Interp *interp)) = NULL;
  220.  
  221. /*
  222.  * Counters of signals that have occured but have not been processed.
  223.  */
  224. static unsigned signalsReceived [MAXSIG];
  225.  
  226. /*
  227.  * Table of commands to evaluate when a signal occurs.  If the command is
  228.  * NULL and the signal is received, an error is returned.
  229.  */
  230. static char *signalTrapCmds [MAXSIG];
  231.  
  232. /*
  233.  * Prototypes of internal functions.
  234.  */
  235. static int
  236. GetSignalState _ANSI_ARGS_((int              signalNum,
  237.                             signalProcPtr_t *sigProcPtr));
  238.  
  239. static int
  240. SetSignalState _ANSI_ARGS_((int             signalNum,
  241.                             signalProcPtr_t sigFunc));
  242.  
  243. static int
  244. BlockSignals _ANSI_ARGS_((Tcl_Interp    *interp,
  245.                           int            action,
  246.                           unsigned char  signals [MAXSIG]));
  247.  
  248. static char *
  249. SignalBlocked _ANSI_ARGS_((Tcl_Interp  *interp,
  250.                            int          signalNum));
  251.  
  252. static int
  253. SigNameToNum _ANSI_ARGS_((Tcl_Interp *interp,
  254.                           char       *sigName,
  255.                           int        *sigNumPtr));
  256.  
  257. static int
  258. ParseSignalSpec _ANSI_ARGS_((Tcl_Interp *interp,
  259.                              char       *signalStr,
  260.                              int         allowZero));
  261.  
  262. static RETSIGTYPE
  263. TclSignalTrap _ANSI_ARGS_((int signalNum));
  264.  
  265. static int
  266. FormatTrapCode  _ANSI_ARGS_((Tcl_Interp  *interp,
  267.                              int          signalNum,
  268.                              Tcl_DString *command));
  269.  
  270. static errState_t *
  271. SaveErrorState _ANSI_ARGS_((Tcl_Interp *interp));
  272.  
  273. static void
  274. RestoreErrorState _ANSI_ARGS_((Tcl_Interp *interp,
  275.                                errState_t *errStatePtr));
  276.  
  277. static int
  278. EvalTrapCode _ANSI_ARGS_((Tcl_Interp *interp,
  279.                           int         signalNum));
  280.  
  281. static int
  282. ProcessASignal _ANSI_ARGS_((Tcl_Interp *interp,
  283.                             int         signalNum));
  284.  
  285. static int
  286. ParseSignalList _ANSI_ARGS_((Tcl_Interp    *interp,
  287.                              char          *signalListStr,
  288.                              unsigned char  signals [MAXSIG]));
  289.  
  290. static int
  291. SetSignalActions _ANSI_ARGS_((Tcl_Interp      *interp,
  292.                               unsigned char    signals [MAXSIG],
  293.                               signalProcPtr_t  actionFunc,
  294.                               char            *command));
  295.  
  296. static char *
  297. FormatSignalListEntry _ANSI_ARGS_((Tcl_Interp *interp,
  298.                                    int         signalNum));
  299.  
  300. static int
  301. ProcessSignalListEntry _ANSI_ARGS_((Tcl_Interp *interp,
  302.                                     char       *signalEntry));
  303.  
  304. static int
  305. GetSignalStates _ANSI_ARGS_((Tcl_Interp    *interp,
  306.                              unsigned char  signals [MAXSIG]));
  307.  
  308. static int
  309. SetSignalStates _ANSI_ARGS_((Tcl_Interp *interp,
  310.                              char       *signalKeyedList));
  311.  
  312. static void
  313. SignalCmdCleanUp _ANSI_ARGS_((ClientData  clientData,
  314.                               Tcl_Interp *interp));
  315.  
  316.  
  317. /*
  318.  *-----------------------------------------------------------------------------
  319.  *
  320.  * GetSignalState --
  321.  *     Get the current state of the specified signal.
  322.  * Parameters:
  323.  *   o signalNum (I) - Signal number to query.
  324.  *   o sigProcPtr (O) - The signal function is returned here.
  325.  * Results
  326.  *   TCL_OK or TCL_ERROR (check errno).
  327.  *-----------------------------------------------------------------------------
  328.  */
  329. static int
  330. GetSignalState (signalNum, sigProcPtr)
  331.     int              signalNum;
  332.     signalProcPtr_t *sigProcPtr;
  333. {
  334. #ifdef HAVE_SIGACTION
  335.     struct sigaction currentState;
  336.  
  337.     if (sigaction (signalNum, NULL, ¤tState) < 0)
  338.         return TCL_ERROR;
  339.     *sigProcPtr = currentState.sa_handler;
  340.     return TCL_OK;
  341. #else
  342.     signalProcPtr_t  actionFunc;
  343.  
  344.     if (signalNum == SIGKILL)
  345.          actionFunc = SIG_DFL;
  346.     else
  347.         actionFunc = signal (signalNum, SIG_DFL);
  348.     if (actionFunc == SIG_ERR)
  349.         return TCL_ERROR;
  350.     if (actionFunc != SIG_DFL)
  351.         signal (signalNum, actionFunc);  /* reset */
  352.     *sigProcPtr = actionFunc;
  353.     return TCL_OK;
  354. #endif
  355. }
  356.  
  357. /*
  358.  *-----------------------------------------------------------------------------
  359.  *
  360.  * SetSignalState --
  361.  *     Set the state of a signal.
  362.  * Parameters:
  363.  *   o signalNum (I) - Signal number to query.
  364.  *   o sigFunc (O) - The signal function or SIG_DFL or SIG_IGN.
  365.  * Results
  366.  *   TCL_OK or TCL_ERROR (check errno).
  367.  *-----------------------------------------------------------------------------
  368.  */
  369. static int
  370. SetSignalState (signalNum, sigFunc)
  371.     int             signalNum;
  372.     signalProcPtr_t sigFunc;
  373. {
  374. #ifdef HAVE_SIGACTION
  375.     struct sigaction newState;
  376.     
  377.     newState.sa_handler = sigFunc;
  378.     sigfillset (&newState.sa_mask);
  379.     newState.sa_flags = 0;
  380.  
  381.     if (sigaction (signalNum, &newState, NULL) < 0)
  382.         return TCL_ERROR;
  383.  
  384.     return TCL_OK;
  385. #else
  386.     if (signal (signalNum, sigFunc) == SIG_ERR)
  387.         return TCL_ERROR;
  388.     else
  389.         return TCL_OK;
  390. #endif
  391. }
  392.  
  393. /*
  394.  *-----------------------------------------------------------------------------
  395.  *
  396.  * BlockSignals --
  397.  *     
  398.  *    Block or unblock the specified signals.  Returns an error if not a Posix
  399.  * system.
  400.  *
  401.  * Parameters::
  402.  *   o interp (O) - Error messages are returned in result.
  403.  *   o action (I) - SIG_BLOCK or SIG_UNBLOCK.
  404.  *   o signals (I) - Boolean array indexed by signal number that indicates
  405.  *     the requested signals.
  406.  * Returns:
  407.  *   TCL_OK or TCL_ERROR, with error message in interp.
  408.  *-----------------------------------------------------------------------------
  409.  */
  410. static int
  411. BlockSignals (interp, action, signals)
  412.     Tcl_Interp    *interp;
  413.     int            action;
  414.     unsigned char  signals [];
  415. {
  416. #ifdef HAVE_SIGACTION
  417.     int      signalNum;
  418.     sigset_t sigBlockSet;
  419.  
  420.     sigemptyset (&sigBlockSet);
  421.  
  422.     for (signalNum = 0; signalNum < MAXSIG; signalNum++) {
  423.         if (signals [signalNum])
  424.             sigaddset (&sigBlockSet, signalNum);
  425.     }
  426.  
  427.     if (sigprocmask (action, &sigBlockSet, NULL)) {
  428.         interp->result = Tcl_PosixError (interp);
  429.         return TCL_ERROR;
  430.     }
  431.  
  432.     return TCL_OK;
  433. #else
  434.     interp->result =
  435.        "Posix signals are not available on this system, can not block signals";
  436.     return TCL_ERROR;
  437. #endif
  438. }
  439.  
  440. /*
  441.  *-----------------------------------------------------------------------------
  442.  *
  443.  * SignalBlocked --
  444.  *     
  445.  *    Determine if a signal is blocked.  On non-Posix systems, always returns
  446.  * "0".
  447.  *
  448.  * Parameters::
  449.  *   o interp (O) - Error messages are returned in result.
  450.  *   o signalNum (I) - The signal to determine the state for.
  451.  * Returns:
  452.  *   NULL if an error occured, or a pointer to a static string of "1" if the
  453.  * signal is block, and a static string of "0" if it is not blocked.
  454.  *-----------------------------------------------------------------------------
  455.  */
  456. static char *
  457. SignalBlocked (interp, signalNum)
  458.     Tcl_Interp  *interp;
  459.     int          signalNum;
  460. {
  461. #ifdef HAVE_SIGACTION
  462.     int      idx;
  463.     sigset_t sigBlockSet;
  464.  
  465.     if (sigprocmask (SIG_BLOCK, NULL, &sigBlockSet)) {
  466.         interp->result = Tcl_PosixError (interp);
  467.         return NULL;
  468.     }
  469.     return sigismember (&sigBlockSet, signalNum) ? "1" : "0";
  470. #else
  471.     return "0";
  472. #endif
  473. }
  474.  
  475. /*
  476.  *-----------------------------------------------------------------------------
  477.  *
  478.  * SigNameToNum --
  479.  *    Converts a UNIX signal name to its number, returns -1 if not found.
  480.  * the name may be upper or lower case and may optionally have the leading
  481.  * "SIG" omitted.
  482.  *
  483.  * Parameters:
  484.  *   o interp (I) - Errors are returned in result.
  485.  *   o sigName (I) - Name of signal to convert.
  486.  *   o sigNumPtr (O) - Signal number is returned here.
  487.  * Returns:
  488.  *   TCL_OK or TCL_ERROR.
  489.  *-----------------------------------------------------------------------------
  490.  */
  491. static int
  492. SigNameToNum (interp, sigName, sigNumPtr)
  493.     Tcl_Interp *interp;
  494.     char       *sigName;
  495.     int        *sigNumPtr;
  496. {
  497.     char  sigNameUp [SIG_NAME_MAX+1];  /* Upshifted signal name */
  498.     char *sigNamePtr; 
  499.     int   idx;
  500.  
  501.     /*
  502.      * Copy and upshift requested name.
  503.      */
  504.     if (strlen (sigName) > SIG_NAME_MAX)
  505.         goto invalidSignal;   /* Name too long */
  506.  
  507.     Tcl_UpShift (sigNameUp, sigName);
  508.  
  509.     if (STRNEQU (sigNameUp, "SIG", 3))
  510.         sigNamePtr = &sigNameUp [3];
  511.     else
  512.         sigNamePtr = sigNameUp;
  513.  
  514.     for (idx = 0; sigNameTable [idx].num != -1; idx++) {
  515.         if (STREQU (sigNamePtr, sigNameTable [idx].name)) {
  516.             *sigNumPtr = sigNameTable [idx].num;
  517.             return TCL_OK;
  518.         }
  519.     }
  520.  
  521.   invalidSignal:
  522.     Tcl_AppendResult (interp, "invalid signal \"", sigName, "\"",
  523.                       (char *) NULL);
  524.     return TCL_ERROR;
  525. }
  526.  
  527. /*
  528.  *-----------------------------------------------------------------------------
  529.  *
  530.  * ParseSignalSpec --
  531.  *  
  532.  *   Parse a signal specified as either a name or a number.
  533.  * 
  534.  * Parameters:
  535.  *   o interp (O) - Interpreter for returning errors.
  536.  *   o signalStr (I) - The signal name or number string.
  537.  *   o allowZero (I) - Allow zero as a valid signal number (for kill).
  538.  * Returns:
  539.  *   The signal number converted, or -1 if an error occures.
  540.  *-----------------------------------------------------------------------------
  541.  */
  542. static int
  543. ParseSignalSpec (interp, signalStr, allowZero)
  544.     Tcl_Interp *interp;
  545.     char       *signalStr;
  546.     int         allowZero;
  547. {
  548.     int  signalNum;
  549.  
  550.     /*
  551.      * If its a number, validate that number is actual a valid signal number
  552.      * for this system.  If either fail, try it as a name.  Just let
  553.      * SigNameToNum generate the error message if its a number, but not a
  554.      * valid signal.
  555.      */
  556.     if (Tcl_StrToInt (signalStr, 0, &signalNum)) {
  557.         if (allowZero && (signalNum == 0))
  558.             return 0;
  559.         if (Tcl_SignalId (signalNum) != unknownSignalIdMsg)
  560.             return signalNum;
  561.     }
  562.     if (SigNameToNum (interp, signalStr, &signalNum) != TCL_OK)
  563.         return -1;
  564.     return signalNum;
  565. }
  566.  
  567. /*
  568.  *-----------------------------------------------------------------------------
  569.  *
  570.  * TclSignalTrap --
  571.  *
  572.  *   Trap handler for UNIX signals.  Sets tells all registered interpreters
  573.  * that a trap has occured and saves the trap info.  The first interpreter to
  574.  * call it's async signal handler will process all pending signals.
  575.  *-----------------------------------------------------------------------------
  576.  */
  577. static RETSIGTYPE
  578. TclSignalTrap (signalNum)
  579.     int signalNum;
  580. {
  581.     int idx;
  582.  
  583.     /*
  584.      * Record the count of the number of this type of signal that has occured
  585.      * and tell all the interpreters to call the async handler when safe.
  586.      */
  587.     signalsReceived [signalNum]++;
  588.  
  589.     for (idx = 0; idx < numInterps; idx++)
  590.         Tcl_AsyncMark (interpTable [idx].handler);
  591.  
  592.     /*
  593.      * Flag used by command input functions to flush input.
  594.      */
  595.     if (signalTrapCmds [signalNum] == NULL) {
  596.         tclGotErrorSignal = TRUE;
  597.         if (tclErrorSignalProc != NULL)
  598.             (*tclErrorSignalProc) (signalNum);
  599.     }
  600. #ifndef HAVE_SIGACTION
  601.     /*
  602.      * For old-style Unix signals, the signal must be explictly re-enabled.
  603.      * Not done for SIGCHLD, as we would continue to the signal until the
  604.      * wait is done.  This is fixed by Posix signals and is not necessary under
  605.      * BSD, but it done this way for consistency.
  606.      */
  607.     if (signalNum != SIGCHLD) {
  608.         if (SetSignalState (signalNum, TclSignalTrap) == TCL_ERROR)
  609.             panic ("TclSignalTrap bug");
  610.     }
  611. #endif
  612. }
  613.  
  614. /*
  615.  *-----------------------------------------------------------------------------
  616.  *
  617.  * SaveErrorState --
  618.  *  
  619.  *   Save the error state of the interpreter (result, errorInfo and errorCode).
  620.  *
  621.  * Parameters:
  622.  *   o interp (I) - The interpreter to save. Result will be reset.
  623.  * Returns:
  624.  *   A dynamically allocated structure containing all three strings,  Its
  625.  * allocated as a single malloc block.
  626.  *-----------------------------------------------------------------------------
  627.  */
  628. static errState_t *
  629. SaveErrorState (interp)
  630.     Tcl_Interp *interp;
  631. {
  632.     errState_t *errStatePtr;
  633.     char       *errorInfo, *errorCode, *nextPtr;
  634.     int         len;
  635.  
  636.     errorInfo = Tcl_GetVar (interp, "errorInfo", TCL_GLOBAL_ONLY);
  637.     errorCode = Tcl_GetVar (interp, "errorCode", TCL_GLOBAL_ONLY);
  638.  
  639.     len = sizeof (errState_t) + strlen (interp->result) + 1;
  640.     if (errorInfo != NULL)
  641.         len += strlen (errorInfo) + 1;
  642.     if (errorCode != NULL)
  643.         len += strlen (errorCode) + 1;
  644.  
  645.  
  646.     errStatePtr = (errState_t *) ckalloc (len);
  647.     nextPtr = ((char *) errStatePtr) + sizeof (errState_t);
  648.  
  649.     errStatePtr->result = nextPtr;
  650.     strcpy (errStatePtr->result, interp->result);
  651.     nextPtr += strlen (interp->result) + 1;
  652.  
  653.     errStatePtr->errorInfo = NULL;
  654.     if (errorInfo != NULL) {
  655.         errStatePtr->errorInfo = nextPtr;
  656.         strcpy (errStatePtr->errorInfo, errorInfo);
  657.         nextPtr += strlen (errorInfo) + 1;
  658.     }
  659.  
  660.     errStatePtr->errorCode = NULL;
  661.     if (errorCode != NULL) {
  662.         errStatePtr->errorCode = nextPtr;
  663.         strcpy (errStatePtr->errorCode, errorCode);
  664.         nextPtr += strlen (errorCode) + 1;
  665.     }
  666.  
  667.     Tcl_ResetResult (interp);
  668.     return errStatePtr;
  669. }
  670.  
  671. /*
  672.  *-----------------------------------------------------------------------------
  673.  *
  674.  * RestoreErrorState --
  675.  *  
  676.  *   Restore the error state of the interpreter that was saved by
  677.  * SaveErrorState.
  678.  *
  679.  * Parameters:
  680.  *   o interp (I) - The interpreter to save.
  681.  *   o errStatePtr (I) - Error state from SaveErrorState.  This structure will
  682.  *     be freed. 
  683.  * Returns:
  684.  *   A dynamically allocated structure containing all three strings,  Its
  685.  * allocated as a single malloc block.
  686.  *-----------------------------------------------------------------------------
  687.  */
  688. static void
  689. RestoreErrorState (interp, errStatePtr)
  690.     Tcl_Interp *interp;
  691.     errState_t *errStatePtr;
  692. {
  693.     Tcl_SetResult (interp, errStatePtr->result, TCL_VOLATILE);
  694.     if (errStatePtr->errorInfo != NULL)
  695.         Tcl_SetVar (interp, "errorInfo", errStatePtr->errorInfo,
  696.                     TCL_GLOBAL_ONLY);
  697.     if (errStatePtr->errorCode != NULL)
  698.         Tcl_SetVar (interp, "errorCode", errStatePtr->errorCode,
  699.                     TCL_GLOBAL_ONLY);
  700.  
  701.     ckfree ((char *) errStatePtr);
  702. }
  703.  
  704. /*
  705.  *-----------------------------------------------------------------------------
  706.  *
  707.  * FormatTrapCode --
  708.  *     Format the signal name into the signal trap command.  Replacing %S with
  709.  * the signal name.
  710.  *
  711.  * Parameters:
  712.  *   o interp (I/O) - The interpreter to return errors in.
  713.  *   o signalNum (I) - The signal number of the signal that occured.
  714.  *   o command (O) - The resulting command adter the formatting.
  715.  *-----------------------------------------------------------------------------
  716.  */
  717. static int
  718. FormatTrapCode (interp, signalNum, command)
  719.     Tcl_Interp  *interp;
  720.     int          signalNum;
  721.     Tcl_DString *command;
  722. {
  723.     char  *signalName, *copyPtr, *scanPtr, prevChar;
  724.  
  725.     /*
  726.      * Force name to always be SIGCHLD, even if system defines only SIGCLD.
  727.      */
  728.     if (signalNum == SIGCHLD)
  729.         signalName = "SIGCHLD";
  730.     else
  731.         signalName = Tcl_SignalId (signalNum);
  732.  
  733.     Tcl_DStringInit (command);
  734.  
  735.     copyPtr = scanPtr = signalTrapCmds [signalNum];
  736.  
  737.     while (*scanPtr != '\0') {
  738.         if (*scanPtr != '%') {
  739.             scanPtr++;
  740.             continue;
  741.         }
  742.         if (scanPtr [1] == '%') {
  743.             scanPtr += 2;
  744.             continue;
  745.         }
  746.         Tcl_DStringAppend (command, copyPtr, (scanPtr - copyPtr));
  747.  
  748.         switch (scanPtr [1]) {
  749.           case 'S': {
  750.               Tcl_DStringAppend (command, signalName, -1);
  751.               break;
  752.           }
  753.           default:
  754.             goto badSpec;
  755.         }
  756.         scanPtr += 2;
  757.         copyPtr = scanPtr;
  758.     }
  759.     Tcl_DStringAppend (command, copyPtr, copyPtr - scanPtr);
  760.  
  761.     return TCL_OK;
  762.  
  763.     /*
  764.      * Handle bad % specification currently pointed to by scanPtr.
  765.      */
  766.   badSpec:
  767.     {
  768.         char badSpec [2];
  769.         
  770.         badSpec [0] = scanPtr [1];
  771.         badSpec [1] = '\0';
  772.         Tcl_AppendResult (interp, "bad signal trap command formatting ",
  773.                           "specification \"%", badSpec,
  774.                           "\", expected one of \"%%\" or \"%S\"",
  775.                           (char *) NULL);
  776.         return TCL_ERROR;
  777.     }
  778. }
  779.  
  780. /*
  781.  *-----------------------------------------------------------------------------
  782.  *
  783.  * EvalTrapCode --
  784.  *     Run code as the result of a signal.  The symbolic signal name is
  785.  * formatted into the command replacing %S with the symbolic signal name.
  786.  *
  787.  * Parameters:
  788.  *   o interp (I) - The interpreter to run the signal in. If an error
  789.  *     occures, then the result will be left in the interp.
  790.  *   o signalNum (I) - The signal number of the signal that occured.
  791.  * Return:
  792.  *   TCL_OK or TCL_ERROR.
  793.  *-----------------------------------------------------------------------------
  794.  */
  795. static int
  796. EvalTrapCode (interp, signalNum)
  797.     Tcl_Interp *interp;
  798.     int         signalNum;
  799. {
  800.     int          result;
  801.     Tcl_DString  command;
  802.  
  803.     Tcl_ResetResult (interp);
  804.  
  805.     /*
  806.      * Format the signal name into the command.  This also allows the signal
  807.      * to be reset in the command.
  808.      */
  809.  
  810.     result = FormatTrapCode (interp,
  811.                              signalNum,
  812.                              &command);
  813.     if (result == TCL_OK)
  814.         result = Tcl_GlobalEval (interp, 
  815.                                  command.string);
  816.  
  817.     Tcl_DStringFree (&command);
  818.  
  819.     if (result == TCL_ERROR) {
  820.         char errorInfo [64];
  821.  
  822.         sprintf (errorInfo, "\n    while executing signal trap code for %s%s",
  823.                  Tcl_SignalId (signalNum), " signal");
  824.         Tcl_AddErrorInfo (interp, errorInfo);
  825.  
  826.         return TCL_ERROR;
  827.     }
  828.     
  829.     Tcl_ResetResult (interp);
  830.     return TCL_OK;
  831. }
  832.  
  833. /*
  834.  *-----------------------------------------------------------------------------
  835.  *
  836.  * ProcessASignal --
  837.  *  
  838.  *   Do processing on the specified signal.
  839.  *
  840.  * Parameters:
  841.  *   o interp (O) - Result will contain the result of the signal handling
  842.  *     code that was evaled.
  843.  *   o signalNum - The signal to process.
  844.  * Returns:
  845.  *   TCL_OK or TCL_ERROR.
  846.  *-----------------------------------------------------------------------------
  847.  */
  848. static int
  849. ProcessASignal (interp, signalNum)
  850.     Tcl_Interp *interp;
  851.     int         signalNum;
  852. {
  853.     char *signalName;
  854.     int   result = TCL_OK;
  855.  
  856.     /*
  857.      * Either return an error or evaluate code associated with this signal.
  858.      * If evaluating code, call it for each time the signal occured.
  859.      */
  860.     if (signalTrapCmds [signalNum] == NULL) {
  861.         signalsReceived [signalNum] = 0;
  862.  
  863.         /*
  864.          * Force name to always be SIGCHLD, even if system defines only SIGCLD.
  865.          */
  866.         if (signalNum == SIGCHLD)
  867.             signalName = "SIGCHLD";
  868.         else
  869.             signalName = Tcl_SignalId (signalNum);
  870.  
  871.         Tcl_SetErrorCode (interp, "POSIX", "SIG", signalName, (char*) NULL);
  872.         Tcl_AppendResult (interp, signalName, " signal received", 
  873.                           (char *)NULL);
  874.         Tcl_SetVar (interp, "errorInfo", "", TCL_GLOBAL_ONLY);
  875.         result = TCL_ERROR;
  876.     } else {
  877.         while (signalsReceived [signalNum] > 0) {
  878.             (signalsReceived [signalNum])--;
  879.             result = EvalTrapCode (interp, signalNum);
  880.             if (result == TCL_ERROR)
  881.                 break;
  882.         }
  883.     }
  884.     return result;
  885. }
  886.  
  887. /*
  888.  *-----------------------------------------------------------------------------
  889.  *
  890.  * Tcl_ProcessSignals --
  891.  *  
  892.  *   Called by Tcl_Eval, etc to process pending signals in a safe state
  893.  * interpreter state.  This is often called just after a command completes.
  894.  * The results of the command are passed to this procedure and may be altered
  895.  * by it.  If trap code is specified for the signal that was received, then
  896.  * the trap will be executed, otherwise an error result will be returned
  897.  * indicating that the signal occured.  If an error is returned, clear the
  898.  * errorInfo variable.  This makes sure it exists and that it is empty,
  899.  * otherwise bogus or non-existant information will be returned if this
  900.  * routine was called somewhere besides Tcl_Eval.  If a signal was received
  901.  * multiple times and a trap is set on it, then that trap will be executed for
  902.  * each time the signal was received.
  903.  * 
  904.  * Parameters:
  905.  *   o clientData (I) - Not used.
  906.  *   o interp (I/O) - interp->result should contain the result for
  907.  *     the command that just executed.  This will either be restored or
  908.  *     replaced with a new result.  If this is NULL, the no interpreter
  909.  *     is directly available (i.e. Tk event loop).  In this case, the first
  910.  *     interpreter in internal interpreter table is used.  If an error occurs,
  911.  *     it is handled via the error handler registerd in the global variable
  912.  *     "tclSignalBackgroundError"
  913.  *   o cmdResultCode (I) - The integer result returned by the command that
  914.  *     Tcl_Eval just completed.  Should be TCL_OK if not called from
  915.  *     Tcl_Eval.
  916.  * Returns:
  917.  *   Either the original result code, an error result if one of the
  918.  *   trap commands returned an error, or an error indicating the
  919.  *   a signal occured.
  920.  *-----------------------------------------------------------------------------
  921.  */
  922. int
  923. Tcl_ProcessSignals (clientData, interp, cmdResultCode)
  924.     ClientData  clientData;
  925.     Tcl_Interp *interp;
  926.     int         cmdResultCode;
  927. {
  928.     Tcl_Interp *sigInterp;
  929.     errState_t *errStatePtr;
  930.     int         signalNum, result, idx;
  931.  
  932.     /*
  933.      * Get the interpreter is it wasn't supplied, if none is available,
  934.      * bail out.
  935.      */
  936.     if (interp == NULL) {
  937.         if (numInterps == 0)
  938.             return cmdResultCode;
  939.         sigInterp = interpTable [0].interp;
  940.     } else {
  941.         sigInterp = interp;
  942.     }
  943.  
  944.     errStatePtr = SaveErrorState (sigInterp);
  945.  
  946.     /*
  947.      * Process all signals.  Don't process any more if one returns an error.
  948.      */
  949.     result = TCL_OK;
  950.  
  951.     for (signalNum = 1; signalNum < MAXSIG; signalNum++) {
  952.         if (signalsReceived [signalNum] == 0)
  953.             continue;
  954.         result = ProcessASignal (sigInterp, signalNum);
  955.         if (result == TCL_ERROR)
  956.             break;
  957.     }
  958.  
  959.     /*
  960.      * Restore result and error state if we didn't get an error in signal
  961.      * handling.
  962.      */
  963.     if (result != TCL_ERROR) {
  964.         RestoreErrorState (sigInterp, errStatePtr);
  965.     } else {
  966.         ckfree (errStatePtr);
  967.         cmdResultCode = TCL_ERROR;
  968.     }
  969.  
  970.     /*
  971.      * Reset the signal received flag in case more signals are pending.
  972.      */
  973.     for (signalNum = 1; signalNum < MAXSIG; signalNum++) {
  974.         if (signalsReceived [signalNum] != 0)
  975.             break;
  976.     }
  977.     if (signalNum < MAXSIG) {
  978.         for (idx = 0; idx < numInterps; idx++)
  979.             Tcl_AsyncMark (interpTable [idx].handler);
  980.     }
  981.  
  982.     /*
  983.      * If we got an error and an interpreter was not supplied, call the
  984.      * background error handler (if available).  Otherwise, lose the error.
  985.      */
  986.     if ((result == TCL_ERROR) && (interp == NULL) &&
  987.         (tclSignalBackgroundError != NULL))
  988.         (*tclSignalBackgroundError) (sigInterp);
  989.  
  990.     return cmdResultCode;
  991. }
  992.  
  993. /*
  994.  *-----------------------------------------------------------------------------
  995.  *
  996.  * ParseSignalList --
  997.  *  
  998.  *   Parse a list of signal names or numbers.  Also handles the special case
  999.  * of the signal being a single entry of "*".
  1000.  * 
  1001.  * Parameters:
  1002.  *   o interp (O) - Interpreter for returning errors.
  1003.  *   o signalListStr (I) - The Tcl list of signals to convert.
  1004.  *   o signals (O) - Boolean array indexed by signal number that indicates
  1005.  *     which signals are set.
  1006.  * Returns:
  1007.  *   TCL_OK or TCL_ERROR.
  1008.  *-----------------------------------------------------------------------------
  1009.  */
  1010. static int
  1011. ParseSignalList (interp, signalListStr, signals)
  1012.     Tcl_Interp    *interp;
  1013.     char          *signalListStr;
  1014.     unsigned char  signals [MAXSIG];
  1015. {
  1016.     char  **signalListArgv;
  1017.     int     signalListSize, signalNum, idx, cnt;
  1018.  
  1019.     if (Tcl_SplitList (interp, signalListStr, &signalListSize, 
  1020.                        &signalListArgv) != TCL_OK)
  1021.         return -1;
  1022.  
  1023.     if (signalListSize == 0) {
  1024.         Tcl_AppendResult (interp, "signal list may not be empty",
  1025.                           (char *) NULL);
  1026.         goto errorExit;
  1027.     }
  1028.  
  1029.     memset (signals, FALSE, sizeof (unsigned char) * MAXSIG);
  1030.  
  1031.     /*
  1032.      * Handle the wild card signal.
  1033.      */
  1034.     if (STREQU (signalListArgv [0], "*")) {
  1035.         if (signalListSize != 1)
  1036.             goto wildMustBeAlone;
  1037.         cnt = 0;
  1038.         for (idx = 0; sigNameTable [idx].name != NULL; idx++) {
  1039.             signalNum = sigNameTable [idx].num;
  1040.             if ((signalNum != SIGKILL) && (signalNum != SIGSTOP))
  1041.                 signals [signalNum] = TRUE;
  1042.         }
  1043.         ckfree ((char *) signalListArgv);
  1044.         return TCL_OK;
  1045.     }
  1046.  
  1047.     /*
  1048.      * Handle individually specified signals.
  1049.      */
  1050.     for (idx = 0; idx < signalListSize; idx++) {
  1051.         if (STREQU (signalListArgv [idx], "*"))
  1052.             goto wildMustBeAlone;
  1053.  
  1054.         signalNum = ParseSignalSpec (interp,
  1055.                                      signalListArgv [idx],
  1056.                                      FALSE);  /* Zero not valid */
  1057.         if (signalNum < 0)
  1058.             return -1;
  1059.         signals [signalNum] = TRUE;
  1060.     }
  1061.  
  1062.     ckfree ((char *) signalListArgv);
  1063.     return TCL_OK;
  1064.  
  1065.   wildMustBeAlone:
  1066.     Tcl_AppendResult (interp, "when \"*\" is specified in the signal list, ",
  1067.                       "no other signals may be specified", (char *) NULL);
  1068.  
  1069.   errorExit:
  1070.     ckfree ((char *) signalListArgv);
  1071.     return TCL_ERROR;
  1072. }
  1073.  
  1074. /*
  1075.  *-----------------------------------------------------------------------------
  1076.  *
  1077.  * SetSignalActions --
  1078.  *     
  1079.  *    Set the signal state for the specified signals.  
  1080.  *
  1081.  * Parameters::
  1082.  *   o interp (O) - The list is returned in the result.
  1083.  *   o signals (I) - Boolean array indexed by signal number that indicates
  1084.  *     the requested signals.
  1085.  *   o actionFunc (I) - The function to run when the signal is received.
  1086.  *   o command (I) - If the function is the "trap" function, this is the
  1087.  *     Tcl command to run when the trap occurs.  Otherwise, NULL.
  1088.  * Returns:
  1089.  *   TCL_OK or TCL_ERROR, with error message in interp.
  1090.  *-----------------------------------------------------------------------------
  1091.  */
  1092. static int
  1093. SetSignalActions (interp, signals, actionFunc, command)
  1094.     Tcl_Interp      *interp;
  1095.     unsigned char    signals [MAXSIG];
  1096.     signalProcPtr_t  actionFunc;
  1097.     char            *command;
  1098. {
  1099.     int signalNum;
  1100.  
  1101.     for (signalNum = 0; signalNum < MAXSIG; signalNum++) {
  1102.         if (!signals [signalNum])
  1103.             continue;
  1104.  
  1105.         if (signalTrapCmds [signalNum] != NULL) {
  1106.             ckfree (signalTrapCmds [signalNum]);
  1107.             signalTrapCmds [signalNum] = NULL;
  1108.         }
  1109.         if (command != NULL)
  1110.             signalTrapCmds [signalNum] = ckstrdup (command);
  1111.  
  1112.         if (SetSignalState (signalNum, actionFunc) == TCL_ERROR) {
  1113.             Tcl_AppendResult (interp, Tcl_PosixError (interp),
  1114.                               " while setting ", Tcl_SignalId (signalNum),
  1115.                               (char *) NULL);
  1116.             return TCL_ERROR;
  1117.         }
  1118.     }
  1119.     return TCL_OK;
  1120. }
  1121.  
  1122. /*
  1123.  *-----------------------------------------------------------------------------
  1124.  *
  1125.  * FormatSignalListEntry --
  1126.  *     
  1127.  *    Retrieve a signal's state and format a keyed list entry used to describe
  1128.  * a that state.
  1129.  *
  1130.  * Parameters::
  1131.  *   o interp (O) - Error messages are returned here.
  1132.  *   o signalNum (I) - The signal to get the state for.
  1133.  * Returns:
  1134.  *   A pointer to the dynamically allocate list entry, or NULL if an error
  1135.  * occurs.
  1136.  *-----------------------------------------------------------------------------
  1137.  */
  1138. static char *
  1139. FormatSignalListEntry (interp, signalNum)
  1140.     Tcl_Interp *interp;
  1141.     int         signalNum;
  1142. {
  1143.     char            *sigState [3], *sigEntry [2], *entry;
  1144.     signalProcPtr_t  actionFunc;
  1145.  
  1146.     if (GetSignalState (signalNum, &actionFunc) == TCL_ERROR)
  1147.         goto unixSigError;
  1148.  
  1149.     sigState [2] = NULL;
  1150.     if (actionFunc == SIG_DFL) {
  1151.         sigState [0]  = SIGACT_DEFAULT;
  1152.     } else if (actionFunc == SIG_IGN) {
  1153.         sigState [0] = SIGACT_IGNORE;
  1154.     } else if (actionFunc == TclSignalTrap) {
  1155.         if (signalTrapCmds [signalNum] == NULL)
  1156.             sigState [0] = SIGACT_ERROR;
  1157.         else {
  1158.             sigState [0] = SIGACT_TRAP;
  1159.             sigState [2] = signalTrapCmds [signalNum];
  1160.         }
  1161.     } else {
  1162.         sigState [0] = SIGACT_UNKNOWN;
  1163.     }
  1164.     
  1165.     sigState [1] = SignalBlocked (interp, signalNum);
  1166.     if (sigState [1] == NULL)
  1167.         goto unixSigError;
  1168.     
  1169.     sigEntry [0] = Tcl_SignalId (signalNum);
  1170.     sigEntry [1] = Tcl_Merge ((sigState [2] == NULL) ? 2 : 3,
  1171.                               sigState);
  1172.  
  1173.     entry = Tcl_Merge (2, sigEntry);
  1174.     ckfree (sigEntry [1]);
  1175.     return entry;
  1176.  
  1177.   unixSigError:
  1178.     Tcl_AppendResult (interp, Tcl_PosixError (interp),
  1179.                       " while getting ", Tcl_SignalId (signalNum),
  1180.                       (char *) NULL);
  1181.     return NULL;
  1182. }
  1183.  
  1184. /*
  1185.  *-----------------------------------------------------------------------------
  1186.  *
  1187.  * ProcessSignalListEntry --
  1188.  *     
  1189.  *    Parse a keyed list entry used to describe a signal state and set the
  1190.  * signal to that state.  If the signal action is specified as "unknown",
  1191.  * it is ignored.
  1192.  *
  1193.  * Parameters::
  1194.  *   o interp (O) - Error messages are returned here.
  1195.  *   o signalEntry (I) - Entry from the keyed list.
  1196.  * Returns:
  1197.  *   TCL_OK or TCL_ERROR;
  1198.  *-----------------------------------------------------------------------------
  1199.  */
  1200. static int
  1201. ProcessSignalListEntry (interp, signalEntry)
  1202.     Tcl_Interp *interp;
  1203.     char       *signalEntry;
  1204. {
  1205.     char           **sigEntry = NULL, **sigState = NULL;
  1206.     int              sigEntrySize, sigStateSize;
  1207.     int              signalNum, blocked;
  1208.     signalProcPtr_t  actionFunc;
  1209.     unsigned char    signals [MAXSIG];
  1210.  
  1211.     /*
  1212.      * Split the list.
  1213.      */
  1214.     if (Tcl_SplitList (interp, signalEntry,
  1215.                        &sigEntrySize, &sigEntry) != TCL_OK)
  1216.         return TCL_ERROR;
  1217.     if (sigEntrySize != 2)
  1218.         goto invalidEntry;
  1219.     if (Tcl_SplitList (interp, sigEntry [1],
  1220.                        &sigStateSize, &sigState) != TCL_OK)
  1221.         goto errorExit;
  1222.     if (sigStateSize < 2 || sigStateSize > 3)
  1223.         goto invalidEntry;
  1224.     
  1225.     /*
  1226.      * Parse the signal name and state.
  1227.      */
  1228.     if (SigNameToNum (interp, sigEntry [0], &signalNum) != TCL_OK)
  1229.         return TCL_ERROR;
  1230.  
  1231.     if (STREQU (sigState [0], SIGACT_DEFAULT)) {
  1232.         actionFunc = SIG_DFL;
  1233.         if (sigStateSize != 2)
  1234.             goto invalidEntry;
  1235.     } else if (STREQU (sigState [0], SIGACT_IGNORE)) {
  1236.         actionFunc = SIG_IGN;
  1237.         if (sigStateSize != 2)
  1238.             goto invalidEntry;
  1239.     } else if (STREQU (sigState [0], SIGACT_ERROR)) {
  1240.         actionFunc = TclSignalTrap;
  1241.         if (sigStateSize != 2)
  1242.             goto invalidEntry;
  1243.     } else if (STREQU (sigState [0], SIGACT_TRAP)) {
  1244.         actionFunc = TclSignalTrap;
  1245.         if (sigStateSize != 3)    /* Must have command */
  1246.             goto invalidEntry;
  1247.     } else if (STREQU (sigState [0], SIGACT_UNKNOWN)) {
  1248.         if (sigStateSize != 2)
  1249.             goto invalidEntry;
  1250.         goto exitPoint;  /* Ignore non-Tcl signals */
  1251.     }
  1252.  
  1253.     if (Tcl_GetBoolean (interp, sigState [1], &blocked) != TCL_OK)
  1254.         goto errorExit;
  1255.     
  1256.     memset (signals, FALSE, sizeof (unsigned char) * MAXSIG);
  1257.     signals [signalNum] = TRUE;
  1258.  
  1259.     /*
  1260.      * Set signal actions and handle blocking if its supported on this
  1261.      * system.  If the signal is to be blocked, we do it before setting up
  1262.      * the handler.  If its to be unblocked, we do it after.
  1263.      */
  1264. #ifdef HAVE_SIGACTION
  1265.     if (blocked) {
  1266.         if (BlockSignals (interp, SIG_BLOCK, signals) != TCL_OK)
  1267.             goto errorExit;
  1268.     }
  1269. #endif
  1270.     if (SetSignalActions (interp, signals, actionFunc,
  1271.                         sigState [2]) != TCL_OK)
  1272.         goto errorExit;
  1273. #ifdef HAVE_SIGACTION
  1274.     if (!blocked) {
  1275.         if (BlockSignals (interp, SIG_UNBLOCK, signals) != TCL_OK)
  1276.             goto errorExit;
  1277.     }
  1278. #endif
  1279.     
  1280.   exitPoint:
  1281.     if (sigEntry != NULL)
  1282.         ckfree ((char *) sigEntry);
  1283.     if (sigState != NULL)
  1284.         ckfree ((char *) sigState);
  1285.     return TCL_OK;
  1286.  
  1287.   invalidEntry:
  1288.     Tcl_AppendResult (interp, "invalid signal keyed list entry \"",
  1289.                       signalEntry, "\"", (char *) NULL);
  1290.  
  1291.   errorExit:
  1292.     if (sigEntry != NULL)
  1293.         ckfree ((char *) sigEntry);
  1294.     if (sigState != NULL)
  1295.         ckfree ((char *) sigState);
  1296.     return TCL_ERROR;
  1297. }
  1298.  
  1299. /*
  1300.  *-----------------------------------------------------------------------------
  1301.  *
  1302.  * GetSignalStates --
  1303.  *     
  1304.  *    Return a keyed list containing the signal states for the specified
  1305.  * signals.
  1306.  *
  1307.  * Parameters::
  1308.  *   o interp (O) - The list is returned in the result.
  1309.  *   o signals (I) - Boolean array indexed by signal number that indicates
  1310.  *     the requested signals.
  1311.  * Returns:
  1312.  *   TCL_OK or TCL_ERROR, with error message in interp.
  1313.  *-----------------------------------------------------------------------------
  1314.  */
  1315. static int
  1316. GetSignalStates (interp, signals)
  1317.     Tcl_Interp    *interp;
  1318.     unsigned char  signals [MAXSIG];
  1319. {
  1320.     int    signalNum, idx, cnt;
  1321.     char  *stateKeyedList [MAXSIG];
  1322.  
  1323.     cnt = 0;
  1324.     for (signalNum = 0; signalNum < MAXSIG; signalNum++) {
  1325.         if (!signals [signalNum])
  1326.             continue;
  1327.         stateKeyedList [cnt] =
  1328.             FormatSignalListEntry (interp, 
  1329.                                    signalNum);
  1330.         if (stateKeyedList [cnt] == NULL)
  1331.             goto errorExit;
  1332.         cnt++;
  1333.     }
  1334.  
  1335.     Tcl_SetResult (interp,
  1336.                    Tcl_Merge (cnt,
  1337.                               stateKeyedList),
  1338.                    TCL_DYNAMIC);
  1339.     for (idx = 0; idx < cnt; idx++)
  1340.         ckfree (stateKeyedList [idx]);
  1341.     return TCL_OK;
  1342.  
  1343.   errorExit:
  1344.     for (idx = 0; idx < cnt; idx++)
  1345.         ckfree (stateKeyedList [idx]);
  1346.     return TCL_ERROR;
  1347. }
  1348.  
  1349. /*
  1350.  *-----------------------------------------------------------------------------
  1351.  *
  1352.  * SetSignalStates --
  1353.  *     
  1354.  *    Set signal states from keyed list in the format returned by
  1355.  * GetSignalStates.
  1356.  *
  1357.  * Parameters::
  1358.  *   o interp (O) - Errors are returned in the result.
  1359.  *   o signalKeyedList (I) - Keyed list describing signal states.
  1360.  * Returns:
  1361.  *   TCL_OK or TCL_ERROR, with error message in interp.
  1362.  *-----------------------------------------------------------------------------
  1363.  */
  1364. static int
  1365. SetSignalStates (interp, signalKeyedList)
  1366.     Tcl_Interp *interp;
  1367.     char       *signalKeyedList;
  1368. {
  1369.     int     idx, signalListSize;
  1370.     char  **signalList;
  1371.  
  1372.     if (Tcl_SplitList (interp, signalKeyedList,
  1373.                        &signalListSize, &signalList) != TCL_OK)
  1374.         return TCL_ERROR;
  1375.  
  1376.     for (idx = 0; idx < signalListSize; idx++) {
  1377.         if (ProcessSignalListEntry (interp, signalList [idx]) != TCL_OK)
  1378.             goto errorExit;
  1379.     }
  1380.     ckfree ((char *) signalList);
  1381.     return TCL_OK;
  1382.  
  1383.   errorExit:
  1384.     ckfree ((char *) signalList);
  1385.     return TCL_ERROR;
  1386. }
  1387.  
  1388. /*
  1389.  *-----------------------------------------------------------------------------
  1390.  *
  1391.  * Tcl_SignalCmd --
  1392.  *     Implements the TCL signal command:
  1393.  *         signal action siglist ?command?
  1394.  *
  1395.  * Results:
  1396.  *      Standard TCL results, may return the UNIX system error message.
  1397.  *
  1398.  * Side effects:
  1399.  *    Signal handling states may be changed.
  1400.  *-----------------------------------------------------------------------------
  1401.  */
  1402. static int
  1403. Tcl_SignalCmd (clientData, interp, argc, argv)
  1404.     char       *clientData;
  1405.     Tcl_Interp *interp;
  1406.     int         argc;
  1407.     char      **argv;
  1408. {
  1409.     int            signalNum, idx;
  1410.     unsigned char  signals [MAXSIG];
  1411.     char          *signalName;
  1412.  
  1413.     if ((argc < 3) || (argc > 4)) {
  1414.         Tcl_AppendResult (interp, tclXWrongArgs, argv [0], 
  1415.                           " action signalList ?command?", (char *) NULL);
  1416.         return TCL_ERROR;
  1417.     }
  1418.  
  1419.     /*
  1420.      * Do the specified action on the signals.  "set" has a special format
  1421.      * for the signal list, so do it first.
  1422.      */
  1423.     if (STREQU (argv [1], "set")) {
  1424.         if (argc != 3)
  1425.             goto cmdNotValid;
  1426.         return SetSignalStates (interp, argv [2]);
  1427.     }
  1428.  
  1429.     if (ParseSignalList (interp,
  1430.                          argv [2],
  1431.                          signals) != TCL_OK)
  1432.         return TCL_ERROR;
  1433.  
  1434.     if (STREQU (argv [1], SIGACT_TRAP)) {
  1435.         if (argc != 4) {
  1436.             Tcl_AppendResult (interp, "command required for ",
  1437.                              "trapping signals", (char *) NULL);
  1438.             return TCL_ERROR;
  1439.         }
  1440.         return SetSignalActions (interp,
  1441.                                  signals,
  1442.                                  TclSignalTrap,
  1443.                                  argv [3]);
  1444.     }
  1445.  
  1446.     if (argc != 3)
  1447.         goto cmdNotValid;
  1448.     
  1449.     if (STREQU (argv [1], SIGACT_DEFAULT)) {
  1450.         return SetSignalActions (interp,
  1451.                                  signals,
  1452.                                  SIG_DFL,
  1453.                                  NULL);
  1454.     }
  1455.  
  1456.     if (STREQU (argv [1], SIGACT_IGNORE)) {
  1457.         return SetSignalActions (interp,
  1458.                                  signals,
  1459.                                  SIG_IGN,
  1460.                                  NULL);
  1461.     }
  1462.  
  1463.     if (STREQU (argv [1], SIGACT_ERROR)) {
  1464.         return SetSignalActions (interp,
  1465.                                  signals,
  1466.                                  TclSignalTrap,
  1467.                                  NULL);
  1468.     }
  1469.  
  1470.     if (STREQU (argv [1], "get")) {
  1471.         return GetSignalStates (interp,
  1472.                                 signals);
  1473.     }
  1474.  
  1475.     if (STREQU (argv [1], "block")) {
  1476.         return BlockSignals (interp,
  1477.                              SIG_BLOCK,
  1478.                              signals);
  1479.     }
  1480.  
  1481.     if (STREQU (argv [1], "unblock")) {
  1482.         return BlockSignals (interp,
  1483.                              SIG_UNBLOCK,
  1484.                              signals);
  1485.     }
  1486.  
  1487.     /*
  1488.      * Not a valid action.
  1489.      */
  1490.     Tcl_AppendResult (interp, "invalid signal action specified: ", 
  1491.                       argv [1], ": expected one of \"default\", ",
  1492.                       "\"ignore\", \"error\", \"trap\", \"get\", ",
  1493.                       "\"set\", \"block\", or \"unblock\"", (char *) NULL);
  1494.     return TCL_ERROR;
  1495.  
  1496.  
  1497.   cmdNotValid:
  1498.     Tcl_AppendResult (interp, "command may not be ",
  1499.                       "specified for \"", argv [1], "\" action",
  1500.                       (char *) NULL);
  1501.     return TCL_ERROR;
  1502. }
  1503.  
  1504. /*
  1505.  *-----------------------------------------------------------------------------
  1506.  *
  1507.  * Tcl_KillCmd --
  1508.  *     Implements the TCL kill command:
  1509.  *        kill ?-pgroup? ?signal? idlist
  1510.  *
  1511.  * Results:
  1512.  *  Standard TCL results, may return the UNIX system error message.
  1513.  *-----------------------------------------------------------------------------
  1514.  */
  1515. int
  1516. Tcl_KillCmd (clientData, interp, argc, argv)
  1517.     ClientData  clientData;
  1518.     Tcl_Interp *interp;
  1519.     int     argc;
  1520.     char      **argv;
  1521. {
  1522.     int    signalNum, nextArg, idx, procId, procArgc;
  1523.     int    pgroup = FALSE;
  1524.     char **procArgv;
  1525.  
  1526.     if (argc < 2)
  1527.         goto usage;
  1528.  
  1529.     nextArg = 1;
  1530.     if (STREQU (argv [nextArg], "-pgroup")) {
  1531.         pgroup = TRUE;
  1532.         nextArg++;
  1533.     }
  1534.         
  1535.     if (((argc - nextArg) < 1) || ((argc - nextArg) > 2))
  1536.         goto usage;
  1537.  
  1538.     /*
  1539.      * Get the signal.
  1540.      */
  1541.     if ((argc - nextArg) == 1) {
  1542.         signalNum = SIGTERM;
  1543.     } else {
  1544.         signalNum = ParseSignalSpec (interp,
  1545.                                      argv [nextArg],
  1546.                                      TRUE);  /* Allow zero */
  1547.         if (signalNum < 0)
  1548.             return TCL_ERROR;
  1549.         nextArg++;
  1550.     }
  1551.  
  1552.     if (Tcl_SplitList (interp, argv [nextArg], &procArgc, 
  1553.                        &procArgv) != TCL_OK)
  1554.         return TCL_ERROR;
  1555.  
  1556.     for (idx = 0; idx < procArgc; idx++) {
  1557.         if (Tcl_GetInt (interp, procArgv [idx], &procId) != TCL_OK)
  1558.             goto errorExit;
  1559.         
  1560.         if (pgroup)
  1561.             procId = -procId;
  1562.  
  1563.         if (kill ((pid_t) procId, signalNum) < 0) {
  1564.             Tcl_AppendResult (interp, Tcl_PosixError (interp),
  1565.                               " sending ", 
  1566.                               (signalNum == 0) ? 0 : Tcl_SignalId (signalNum),
  1567.                               " to process ", procArgv [idx], (char *) NULL);
  1568.             goto errorExit;
  1569.         }
  1570.      }
  1571.  
  1572.     ckfree ((char *) procArgv);
  1573.     return TCL_OK;
  1574.         
  1575.   errorExit:
  1576.     ckfree ((char *) procArgv);
  1577.     return TCL_ERROR;;
  1578.  
  1579.   usage:
  1580.     Tcl_AppendResult (interp, tclXWrongArgs, argv [0], 
  1581.                       " ?-pgroup? ?signal? idlist", (char *) NULL);
  1582.     return TCL_ERROR;
  1583. }
  1584.  
  1585. /*
  1586.  *-----------------------------------------------------------------------------
  1587.  *
  1588.  * SignalCmdCleanUp --
  1589.  *
  1590.  *   Clean up the signal data structure when an interpreter is deleted. If
  1591.  * this is the last interpreter, clean up all tables.
  1592.  *
  1593.  * Parameters:
  1594.  *   o clientData (I) - Not used.
  1595.  *   o interp (I) - Interp that is being deleted.
  1596.  *-----------------------------------------------------------------------------
  1597.  */
  1598. static void
  1599. SignalCmdCleanUp (clientData, interp)
  1600.     ClientData  clientData;
  1601.     Tcl_Interp *interp;
  1602. {
  1603.     int  idx;
  1604.  
  1605.     for (idx = 0; idx < numInterps; idx++) {
  1606.         if (interpTable [idx].interp == interp)
  1607.             break;
  1608.     }
  1609.     if (idx == numInterps)
  1610.         panic ("signal interp lost");
  1611.  
  1612.     interpTable [idx] = interpTable [--numInterps];
  1613.  
  1614.     /*
  1615.      * If there are no more interpreters, clean everything up.
  1616.      */
  1617.     if (numInterps == 0) {
  1618.         ckfree ((char *) interpTable);
  1619.         interpTable = NULL;
  1620.         interpTableSize = 0;
  1621.  
  1622.         for (idx = 0; idx < MAXSIG; idx++) {
  1623.             if (signalTrapCmds [idx] != NULL) {
  1624.                 ckfree (signalTrapCmds [idx]);
  1625.                 signalTrapCmds [idx] = NULL;
  1626.             }
  1627.         }
  1628.     }
  1629. }
  1630.  
  1631. /*
  1632.  *-----------------------------------------------------------------------------
  1633.  *
  1634.  * Tcl_SetupSigInt --
  1635.  *    Set up SIGINT to the "error" state if the current state is default.
  1636.  * This is done because shells set SIGINT to ignore for background processes
  1637.  * so that they don't die on signals generated by the user at the keyboard.
  1638.  * Tcl only enables SIGINT catching if it is an interactive session.
  1639.  *-----------------------------------------------------------------------------
  1640.  */
  1641. void
  1642. Tcl_SetupSigInt ()
  1643. {
  1644.     signalProcPtr_t  actionFunc;
  1645.  
  1646.     if ((GetSignalState (SIGINT, &actionFunc) == TCL_OK) &&
  1647.         (actionFunc == SIG_DFL))
  1648.         SetSignalState (SIGINT, TclSignalTrap);
  1649. }
  1650.  
  1651. /*
  1652.  *-----------------------------------------------------------------------------
  1653.  *
  1654.  * Tcl_InitSignalHandling --
  1655.  *      Initializes singal handling for a interpreter.
  1656.  *-----------------------------------------------------------------------------
  1657.  */
  1658. void
  1659. Tcl_InitSignalHandling (interp)
  1660.     Tcl_Interp *interp;
  1661. {
  1662.     int              idx;
  1663.     interpHandler_t *newTable;
  1664.  
  1665.     /*
  1666.      * If this is the first interpreter, set everything up.
  1667.      */
  1668.     if (numInterps == 0) {
  1669.         interpTableSize = 4;
  1670.         interpTable = (interpHandler_t *)
  1671.             ckalloc (sizeof (interpHandler_t) * interpTableSize);
  1672.  
  1673.         for (idx = 0; idx < MAXSIG; idx++) {
  1674.             signalsReceived [idx] = 0;
  1675.             signalTrapCmds [idx] = NULL;
  1676.         }
  1677.         /*
  1678.          * Get address of "unknown signal" message.
  1679.          */
  1680.         unknownSignalIdMsg = Tcl_SignalId (20000);
  1681.     }
  1682.  
  1683.     /*
  1684.      * If there is not room in this table for another interp, expand it.
  1685.      */
  1686.     if (numInterps == interpTableSize) {
  1687.         newTable = (interpHandler_t *)
  1688.             ckalloc (sizeof (interpHandler_t) * interpTableSize * 2);
  1689.         memcpy (newTable, interpTable,
  1690.                 sizeof (interpHandler_t) * interpTableSize);
  1691.         ckfree ((char *) interpTable);
  1692.         interpTable = newTable;
  1693.         interpTableSize *= 2;
  1694.     }
  1695.  
  1696.     /*
  1697.      * Add this interpreter to the list and set up a async handler.
  1698.      * Arange for clean up on the interpreter being deleted.
  1699.      */
  1700.     interpTable [numInterps].interp = interp;
  1701.     interpTable [numInterps].handler =
  1702.         Tcl_AsyncCreate (Tcl_ProcessSignals, (ClientData) NULL);
  1703.     numInterps++;
  1704.  
  1705.     Tcl_CallWhenDeleted (interp, SignalCmdCleanUp, (ClientData) NULL);
  1706.  
  1707.     Tcl_CreateCommand (interp, "signal", Tcl_SignalCmd,
  1708.                        (ClientData) NULL, (void (*)()) NULL);
  1709.     Tcl_CreateCommand (interp, "kill", Tcl_KillCmd,
  1710.                        (ClientData) NULL, (void (*)()) NULL);
  1711. }
  1712.