home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tcltk805.zip / tcl805s.zip / tcl8.0.5 / os2 / tclOS2Pipe.c < prev    next >
C/C++ Source or Header  |  2001-02-09  |  67KB  |  2,254 lines

  1. /* 
  2.  * tclOS2Pipe.c -- This file implements the OS/2-specific pipeline exec
  3.  *                 functions.
  4.  *      
  5.  *
  6.  * Copyright (c) 1996 Sun Microsystems, Inc.
  7.  * Copyright (c) 1996-2001 Illya Vaes
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  */
  12.  
  13.  
  14. #include "tclOS2Int.h"
  15.  
  16. #define HF_STDIN  0
  17. #define HF_STDOUT 1
  18. #define HF_STDERR 2
  19.  
  20. /*
  21.  * The following variable is used to tell whether this module has been
  22.  * initialized.
  23.  */
  24.  
  25. static int initialized = 0;
  26.  
  27. /*
  28.  * The following defines identify the various types of applications that
  29.  * run under OS/2.  There is special case code for the various types.
  30.  */
  31.  
  32. #define APPL_NONE       0
  33. #define APPL_DOS        1
  34. #define APPL_WIN3X      2
  35. #define APPL_WIN32      3
  36. #define APPL_OS2WIN     4
  37. #define APPL_OS2FS      5
  38. #define APPL_OS2PM      6
  39. #define APPL_OS2CMD     7
  40.  
  41. /*
  42.  * The following constants and structures are used to encapsulate the state
  43.  * of various types of files used in a pipeline.
  44.  */
  45.  
  46. #define OS2_TMPFILE    1           /* OS/2 emulated temporary file. */
  47. #define OS2_FILE    2           /* Basic OS/2 file. */
  48. #define OS2_FIRST_TYPE    OS2_TMPFILE
  49. #define OS2_LAST_TYPE    OS2_FILE
  50.  
  51. /*
  52.  * This structure encapsulates the common state associated with all file
  53.  * types used in a pipeline.
  54.  */
  55.  
  56. typedef struct OS2File {
  57.     int type;                   /* One of the file types defined above. */
  58.     HFILE handle;               /* Open file handle. */
  59. } OS2File;
  60.  
  61. /*
  62.  * The following structure is used to keep track of temporary files
  63.  * and delete the disk file when the open handle is closed.
  64.  * The type field will be OS2_TMPFILE.
  65.  */
  66.  
  67. typedef struct TmpFile {
  68.     OS2File file;               /* Common part. */
  69.     char *name;                 /* Name of temp file. */
  70. } TmpFile;
  71.  
  72. /*
  73.  * State flags used in the PipeInfo structure below.
  74.  */
  75.  
  76. #define PIPE_PENDING    (1<<0)  /* Message is pending in the queue. */
  77. #define PIPE_ASYNC      (1<<1)  /* Channel is non-blocking. */
  78.  
  79. /*
  80.  * This structure describes per-instance data for a pipe based channel.
  81.  */
  82.  
  83. typedef struct PipeInfo {
  84.     Tcl_Channel channel;        /* Pointer to channel structure. */
  85.     int validMask;              /* OR'ed combination of TCL_READABLE,
  86.                                  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
  87.                                  * which operations are valid on the file. */
  88.     int watchMask;              /* OR'ed combination of TCL_READABLE,
  89.                                  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
  90.                                  * which events should be reported. */
  91.     int flags;                  /* State flags, see above for a list. */
  92.     TclFile readFile;           /* Output from pipe. */
  93.     TclFile writeFile;          /* Input from pipe. */
  94.     TclFile errorFile;          /* Error output from pipe. */
  95.     int numPids;                /* Number of processes attached to pipe. */
  96.     Tcl_Pid *pidPtr;            /* Pids of attached processes. */
  97.     struct PipeInfo *nextPtr;   /* Pointer to next registered pipe. */
  98. } PipeInfo;
  99.  
  100. /*
  101.  * The following pointer refers to the head of the list of pipes
  102.  * that are being watched for file events.
  103.  */
  104.  
  105. static PipeInfo *firstPipePtr;
  106.  
  107. /*
  108.  * The following structure is what is added to the Tcl event queue when
  109.  * pipe events are generated.
  110.  */
  111.  
  112. typedef struct PipeEvent {
  113.     Tcl_Event header;           /* Information that is standard for
  114.                                  * all events. */
  115.     PipeInfo *infoPtr;          /* Pointer to pipe info structure.  Note
  116.                                  * that we still have to verify that the
  117.                                  * pipe exists before dereferencing this
  118.                                  * pointer. */
  119. } PipeEvent;
  120.  
  121. /*
  122.  * Declarations for functions used only in this file.
  123.  */
  124. static int      ApplicationType(Tcl_Interp *interp, const char *fileName,
  125.                     char *fullName);
  126. static TclFile  MakeFile(HFILE handle);
  127. static int      PipeBlockModeProc(ClientData instanceData, int mode);
  128. static void     PipeCheckProc _ANSI_ARGS_((ClientData clientData,
  129.                     int flags));
  130. static int      PipeCloseProc(ClientData instanceData, Tcl_Interp *interp);
  131. static int      PipeEventProc(Tcl_Event *evPtr, int flags);
  132. static void     PipeExitHandler(ClientData clientData);
  133. static int      PipeGetHandleProc(ClientData instanceData, int direction,
  134.                     ClientData *handlePtr);
  135. static void     PipeInit(void);
  136. static int      PipeInputProc(ClientData instanceData, char *buf, int toRead,
  137.                     int *errorCode);
  138. static int      PipeOutputProc(ClientData instanceData, char *buf, int toWrite,
  139.                     int *errorCode);
  140. static void     PipeWatchProc(ClientData instanceData, int mask);
  141. static void     PipeSetupProc _ANSI_ARGS_((ClientData clientData,
  142.                     int flags));
  143. static void     TempFileCleanup _ANSI_ARGS_((ClientData clientData));
  144.  
  145. /*
  146.  * This structure describes the channel type structure for command pipe
  147.  * based IO.
  148.  */
  149.  
  150. static Tcl_ChannelType pipeChannelType = {
  151.     "pipe",                     /* Type name. */
  152.     PipeBlockModeProc,          /* Set blocking or non-blocking mode.*/
  153.     PipeCloseProc,              /* Close proc. */
  154.     PipeInputProc,              /* Input proc. */
  155.     PipeOutputProc,             /* Output proc. */
  156.     NULL,                       /* Seek proc. */
  157.     NULL,                       /* Set option proc. */
  158.     NULL,                       /* Get option proc. */
  159.     PipeWatchProc,              /* Set up notifier to watch the channel. */
  160.     PipeGetHandleProc,          /* Get an OS handle from channel. */
  161. };
  162. /*
  163.  *----------------------------------------------------------------------
  164.  *
  165.  * PipeInit --
  166.  *
  167.  *      This function initializes the static variables for this file.
  168.  *
  169.  * Results:
  170.  *      None.
  171.  *
  172.  * Side effects:
  173.  *      Creates a new event source.
  174.  *
  175.  *----------------------------------------------------------------------
  176.  */
  177.  
  178. static void
  179. PipeInit()
  180. {
  181. #ifdef VERBOSE
  182.     printf("PipeInit\n");
  183.     fflush(stdout);
  184. #endif
  185.  
  186.     initialized = 1;
  187.     firstPipePtr = NULL;
  188.     Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL);
  189.     Tcl_CreateExitHandler(PipeExitHandler, NULL);
  190. }
  191.  
  192. /*
  193.  *----------------------------------------------------------------------
  194.  *
  195.  * PipeExitHandler --
  196.  *
  197.  *      This function is called to cleanup the pipe module before
  198.  *      Tcl is unloaded.
  199.  *
  200.  * Results:
  201.  *      None.
  202.  *
  203.  * Side effects:
  204.  *      Removes the pipe event source.
  205.  *
  206.  *----------------------------------------------------------------------
  207.  */
  208.  
  209. static void
  210. PipeExitHandler(clientData)
  211.     ClientData clientData;      /* Old window proc */
  212. {
  213. #ifdef VERBOSE
  214.     printf("PipeExitHandler\n");
  215.     fflush(stdout);
  216. #endif
  217.  
  218.     Tcl_DeleteEventSource(PipeSetupProc, PipeCheckProc, NULL);
  219.     initialized = 0;
  220. }
  221.  
  222. /*
  223.  *----------------------------------------------------------------------
  224.  *
  225.  * PipeSetupProc --
  226.  *
  227.  *      This procedure is invoked before Tcl_DoOneEvent blocks waiting
  228.  *      for an event.
  229.  *
  230.  * Results:
  231.  *      None.
  232.  *
  233.  * Side effects:
  234.  *      Adjusts the block time if needed.
  235.  *
  236.  *----------------------------------------------------------------------
  237.  */
  238.  
  239. void
  240. PipeSetupProc(data, flags)
  241.     ClientData data;            /* Not used. */
  242.     int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
  243. {
  244.     PipeInfo *infoPtr;
  245.     Tcl_Time blockTime = { 0, 0 };
  246.  
  247. #ifdef VERBOSE
  248.     printf("PipeSetupProc\n");
  249.     fflush(stdout);
  250. #endif
  251.  
  252.     if (!(flags & TCL_FILE_EVENTS)) {
  253.         return;
  254.     }
  255.  
  256.     /*
  257.      * Check to see if there is a watched pipe.  If so, poll.
  258.      */
  259.  
  260.     for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
  261.         if (infoPtr->watchMask) {
  262.             Tcl_SetMaxBlockTime(&blockTime);
  263.             break;
  264.         }
  265.     }
  266.     return;
  267. }
  268.  
  269. /*
  270.  *----------------------------------------------------------------------
  271.  *
  272.  * PipeCheckProc --
  273.  *
  274.  *      This procedure is called by Tcl_DoOneEvent to check the pipe
  275.  *      event source for events.
  276.  *
  277.  * Results:
  278.  *      None.
  279.  *
  280.  * Side effects:
  281.  *      May queue an event.
  282.  *
  283.  *----------------------------------------------------------------------
  284.  */
  285.  
  286. static void
  287. PipeCheckProc(data, flags)
  288.     ClientData data;            /* Not used. */
  289.     int flags;                  /* Event flags as passed to Tcl_DoOneEvent. */
  290. {
  291.     PipeInfo *infoPtr;
  292.     PipeEvent *evPtr;
  293.  
  294. #ifdef VERBOSE
  295.     printf("PipeSetupProc\n");
  296.     fflush(stdout);
  297. #endif
  298.  
  299.     if (!(flags & TCL_FILE_EVENTS)) {
  300.         return;
  301.     }
  302.  
  303.     /*
  304.      * Queue events for any watched pipes that don't already have events
  305.      * queued.
  306.      */
  307.  
  308.     for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
  309.         if (infoPtr->watchMask && !(infoPtr->flags & PIPE_PENDING)) {
  310.             infoPtr->flags |= PIPE_PENDING;
  311.             evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent));
  312.             evPtr->header.proc = PipeEventProc;
  313.             evPtr->infoPtr = infoPtr;
  314.             Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
  315.         }
  316.     }
  317. }
  318.  
  319. /*
  320.  *----------------------------------------------------------------------
  321.  *
  322.  * MakeFile --
  323.  *
  324.  *      This function constructs a new TclFile from a given data and
  325.  *      type value.
  326.  *
  327.  * Results:
  328.  *      Returns a newly allocated OS2File as a TclFile.
  329.  *
  330.  * Side effects:
  331.  *      None.
  332.  *
  333.  *----------------------------------------------------------------------
  334.  */
  335.  
  336. static TclFile
  337. MakeFile(handle)
  338.     HFILE handle;    /* Type-specific data. */
  339. {
  340.     OS2File *filePtr;
  341.  
  342. #ifdef VERBOSE
  343.     printf("MakeFile handle [%d]\n", handle);
  344.     fflush(stdout);
  345. #endif
  346.  
  347.     filePtr = (OS2File *) ckalloc(sizeof(OS2File));
  348.     if (filePtr != (OS2File *)NULL) {
  349.         filePtr->type = OS2_FILE;
  350.         filePtr->handle = handle;
  351.     }
  352.  
  353.     return (TclFile)filePtr;
  354. }
  355.  
  356. /*
  357.  *----------------------------------------------------------------------
  358.  *
  359.  * TclpMakeFile --
  360.  *
  361.  *      Make a TclFile from a channel.
  362.  *
  363.  * Results:
  364.  *      Returns a new TclFile or NULL on failure.
  365.  *
  366.  * Side effects:
  367.  *      None.
  368.  *
  369.  *----------------------------------------------------------------------
  370.  */
  371.  
  372. TclFile
  373. TclpMakeFile(channel, direction)
  374.     Tcl_Channel channel;        /* Channel to get file from. */
  375.     int direction;              /* Either TCL_READABLE or TCL_WRITABLE. */
  376. {
  377.     HFILE handle;
  378.  
  379. #ifdef VERBOSE
  380.     printf("TclpMakeFile\n");
  381.     fflush(stdout);
  382. #endif
  383.  
  384.     if (Tcl_GetChannelHandle(channel, direction,
  385.             (ClientData *) &handle) == TCL_OK) {
  386.         return MakeFile(handle);
  387.     } else {
  388.         return (TclFile) NULL;
  389.     }
  390. }
  391.  
  392. /*
  393.  *----------------------------------------------------------------------
  394.  *
  395.  * TclpCreateTempFile --
  396.  *
  397.  *      This function opens a unique file with the property that it
  398.  *      will be deleted when its file handle is closed.  The temporary
  399.  *      file is created in the system temporary directory.
  400.  *
  401.  * Results:
  402.  *      Returns a valid TclFile, or NULL on failure.
  403.  *
  404.  * Side effects:
  405.  *      Creates a new temporary file.
  406.  *
  407.  *----------------------------------------------------------------------
  408.  */
  409.  
  410. TclFile
  411. TclpCreateTempFile(contents, namePtr)
  412.     char *contents;             /* String to write into temp file, or NULL. */
  413.     Tcl_DString *namePtr;       /* If non-NULL, pointer to initialized
  414.                                  * DString that is filled with the name of
  415.                                  * the temp file that was created. */
  416. {
  417.     char *name;
  418.     HFILE handle;
  419.     ULONG action, length, result;
  420.     TmpFile *tmpFilePtr;
  421.  
  422. #ifdef VERBOSE
  423.     printf("TclpCreateTempFile\n");
  424.     fflush(stdout);
  425. #endif
  426.  
  427.  
  428.     /*
  429.      * tempnam allocates space for name with *malloc*
  430.      * use free() when deleting the file
  431.      * First argument is directory that is used when the directory in the
  432.      * TMP environment variable doesn't exist or the variable isn't set.
  433.      * Make sure we can always create a temp file => c:\ .
  434.      */
  435.     name = tempnam("C:\\", "Tcl");
  436.     if (name == (char *)NULL) return NULL;
  437.  
  438.     tmpFilePtr = (TmpFile *) ckalloc(sizeof(TmpFile));
  439.     /*
  440.      * See if we can get memory for later before creating the file to minimize
  441.      * system interaction
  442.      */
  443.     if (tmpFilePtr == (TmpFile *)NULL) {
  444.         /* We couldn't allocate memory, so free the tempname memory and abort */
  445.         free((char *)name);
  446.     return NULL;
  447.     }
  448.  
  449.     rc = DosOpen(name, &handle, &action, 0, FILE_NORMAL,
  450.                  OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS,
  451.                  OPEN_SHARE_DENYNONE | OPEN_ACCESS_READWRITE, NULL);
  452.  
  453. #ifdef VERBOSE
  454.     if (rc == NO_ERROR) openedFiles++;
  455.     printf("TclpCreateTempFile: DosOpen [%s] handle [%d] rc [%d]\n",
  456.            name, handle, rc);
  457.     fflush(stdout);
  458. #endif
  459.     if (rc != NO_ERROR) {
  460.         goto error;
  461.     }
  462.  
  463.     /*
  464.      * Write the file out, doing line translations on the way.
  465.      */
  466.  
  467.     if (contents != NULL) {
  468.         char *p;
  469.  
  470.         for (p = contents; *p != '\0'; p++) {
  471.             if (*p == '\n') {
  472.                 length = p - contents;
  473.                 if (length > 0) {
  474.                     rc = DosWrite(handle, (PVOID)contents, length, &result);
  475. #ifdef VERBOSE
  476.                     printf("DosWrite handle %d [%s] returned [%d]\n",
  477.                            handle, contents, rc);
  478.     fflush(stdout);
  479. #endif
  480.                     if (rc != NO_ERROR) {
  481.                         goto error;
  482.                     }
  483.                 }
  484.                 if (DosWrite(handle, "\r\n", 2, &result) != NO_ERROR) {
  485.                     goto error;
  486.                 }
  487.                 contents = p+1;
  488.             }
  489.         }
  490.         length = p - contents;
  491.         if (length > 0) {
  492.             rc = DosWrite(handle, (PVOID)contents, length, &result);
  493. #ifdef VERBOSE
  494.             printf("DosWrite handle %d [%s] returned [%d]\n",
  495.                    handle, contents, rc);
  496.     fflush(stdout);
  497. #endif
  498.             if (rc != NO_ERROR) {
  499.                 goto error;
  500.             }
  501.         }
  502.     }
  503.  
  504.     rc = DosSetFilePtr(handle, 0, FILE_BEGIN, &result);
  505. #ifdef VERBOSE
  506.     printf("TclpCreateTempFile: DosSetFilePtr BEGIN handle [%d] returns %d\n",
  507.            handle, rc);
  508.     fflush(stdout);
  509. #endif
  510.     if (rc != NO_ERROR) {
  511.         goto error;
  512.     }
  513.  
  514.     if (namePtr != NULL) {
  515.         Tcl_DStringAppend(namePtr, name, -1);
  516.     }
  517.  
  518.     /*
  519.      * A file won't be deleted when it is closed, so we have to do it ourselves.
  520.      */
  521.  
  522.     tmpFilePtr->file.type = OS2_TMPFILE;
  523.     tmpFilePtr->file.handle = handle;
  524.     tmpFilePtr->name = name;
  525.     /* Queue undeleted files for removal on exiting Tcl */
  526.     Tcl_CreateExitHandler(TempFileCleanup, (ClientData)tmpFilePtr);
  527.     return (TclFile)tmpFilePtr;
  528.  
  529.   error:
  530.     TclOS2ConvertError(rc);
  531.     rc = DosClose(handle);
  532. #ifdef VERBOSE
  533.     if (rc == NO_ERROR) openedFiles--;
  534. #endif
  535.     rc = DosDelete(name);
  536. #ifdef VERBOSE
  537.     printf("DosDelete [%s] (was handle [%d] returns %d\n", name, handle, rc);
  538.     fflush(stdout);
  539. #endif
  540.     ckfree((char *)tmpFilePtr);
  541.     /* NB! EMX has allocated name with malloc, use free! */
  542.     free((char *)name);
  543.     return NULL;
  544. }
  545.  
  546. /*
  547.  *----------------------------------------------------------------------
  548.  *
  549.  * TclpOpenFile --
  550.  *
  551.  *      This function opens files for use in a pipeline.
  552.  *
  553.  * Results:
  554.  *      Returns a newly allocated TclFile structure containing the
  555.  *      file handle.
  556.  *
  557.  * Side effects:
  558.  *      None.
  559.  *
  560.  *----------------------------------------------------------------------
  561.  */
  562.  
  563. TclFile
  564. TclpOpenFile(path, mode)
  565.     char *path;
  566.     int mode;
  567. {
  568.     HFILE handle;
  569.     ULONG accessMode, createMode, flags, exist, result;
  570.  
  571. #ifdef VERBOSE
  572.     printf("TclpOpenFile\n");
  573.     fflush(stdout);
  574. #endif
  575.  
  576.     /*
  577.      * Map the access bits to the OS/2 access mode.
  578.      */
  579.  
  580.     switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
  581.         case O_RDONLY:
  582.            accessMode = OPEN_ACCESS_READONLY;
  583.            break;
  584.        case O_WRONLY:
  585.            accessMode = OPEN_ACCESS_WRITEONLY;
  586.            break;
  587.        case O_RDWR:
  588.            accessMode = OPEN_ACCESS_READWRITE;
  589.            break;
  590.        default:
  591.            TclOS2ConvertError(ERROR_INVALID_FUNCTION);
  592.            return NULL;
  593.     }
  594.  
  595.     /*
  596.      * Map the creation flags to the OS/2 open mode.
  597.      */
  598.  
  599.     switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) {
  600.         case (O_CREAT | O_EXCL | O_TRUNC):
  601.             createMode = OPEN_ACTION_CREATE_IF_NEW |
  602.                          OPEN_ACTION_FAIL_IF_EXISTS;
  603.             break;
  604.         case (O_CREAT | O_EXCL):
  605.             createMode = OPEN_ACTION_CREATE_IF_NEW |
  606.                          OPEN_ACTION_FAIL_IF_EXISTS;
  607.             break;
  608.         case (O_CREAT | O_TRUNC):
  609.             createMode = OPEN_ACTION_CREATE_IF_NEW |
  610.                          OPEN_ACTION_REPLACE_IF_EXISTS;
  611.             break;
  612.         case O_CREAT:
  613.             createMode = OPEN_ACTION_CREATE_IF_NEW |
  614.                          OPEN_ACTION_OPEN_IF_EXISTS;
  615.             break;
  616.         case O_TRUNC:
  617.         case (O_TRUNC | O_EXCL):
  618.             createMode = OPEN_ACTION_FAIL_IF_NEW |
  619.                          OPEN_ACTION_REPLACE_IF_EXISTS;
  620.             break;
  621.         default:
  622.             createMode = OPEN_ACTION_FAIL_IF_NEW |
  623.                          OPEN_ACTION_OPEN_IF_EXISTS;
  624.     }
  625.  
  626.     /*
  627.      * If the file is not being created, use the existing file attributes.
  628.      */
  629.  
  630.     flags = 0;
  631.     if (!(mode & O_CREAT)) {
  632.         FILESTATUS3 infoBuf;
  633.  
  634.         rc = DosQueryPathInfo(path, FIL_STANDARD, &infoBuf, sizeof(infoBuf));
  635.         if (rc == NO_ERROR) {
  636.             flags = infoBuf.attrFile;
  637.         } else {
  638.             flags = 0;
  639.         }
  640.     }
  641.  
  642.  
  643.     /*
  644.      * Set up the attributes so this file is not inherited by child processes.
  645.      */
  646.  
  647.     accessMode |= OPEN_FLAGS_NOINHERIT;
  648.  
  649.     /*
  650.      * Set up the file sharing mode.  We want to allow simultaneous access.
  651.      */
  652.  
  653.     accessMode |= OPEN_SHARE_DENYNONE;
  654.  
  655.     /*
  656.      * Now we get to create the file.
  657.      */
  658.  
  659.     rc = DosOpen(path, &handle, &exist, 0, flags, createMode, accessMode,
  660.                  (PEAOP2)NULL);
  661. #ifdef VERBOSE
  662.     if (rc == NO_ERROR) openedFiles++;
  663.     printf("TclOpenFile: DosOpen [%s] returns [%d]\n", path, rc);
  664.     fflush(stdout);
  665. #endif
  666.     if (rc != NO_ERROR) {
  667.         ULONG err = 0;
  668.  
  669.         switch (rc) {
  670.             case ERROR_FILE_NOT_FOUND:
  671.             case ERROR_PATH_NOT_FOUND:
  672.                 err = ERROR_FILE_NOT_FOUND;
  673.                 break;
  674.             case ERROR_ACCESS_DENIED:
  675.             case ERROR_INVALID_ACCESS:
  676.             case ERROR_SHARING_VIOLATION:
  677.             case ERROR_CANNOT_MAKE:
  678.                 err = (mode & O_CREAT) ? ERROR_FILE_EXISTS
  679.                                        : ERROR_FILE_NOT_FOUND;
  680.                 break;
  681.         }
  682.         TclOS2ConvertError(err);
  683.         return NULL;
  684.     }
  685.  
  686.     /*
  687.      * Seek to the end of file if we are writing.
  688.      */
  689.  
  690.     if (mode & O_WRONLY) {
  691.         rc = DosSetFilePtr(handle, 0, FILE_END, &result);
  692.     }
  693.  
  694.     return MakeFile(handle);
  695. }
  696.  
  697. /*
  698.  *----------------------------------------------------------------------
  699.  *
  700.  * TclpCreatePipe --
  701.  *
  702.  *      Creates an anonymous pipe.
  703.  *
  704.  * Results:
  705.  *      Returns 1 on success, 0 on failure. 
  706.  *
  707.  * Side effects:
  708.  *      Creates a pipe.
  709.  *
  710.  *----------------------------------------------------------------------
  711.  */
  712.  
  713. int
  714. TclpCreatePipe(readPipe, writePipe)
  715.     TclFile *readPipe;         /* Location to store file handle for
  716.                                 * read side of pipe. */
  717.     TclFile *writePipe;        /* Location to store file handle for
  718.                                 * write side of pipe. */
  719. {
  720.     HFILE readHandle, writeHandle;
  721.  
  722. #ifdef VERBOSE
  723.     printf("TclpCreatePipe\n");
  724.     fflush(stdout);
  725. #endif
  726.  
  727.     /*
  728.      * Using 1024 makes for processes hanging around until their output was
  729.      * read, which doesn't always happen (eg. the "defs" file in the test
  730.      * suite). The Control Program Reference gives 4096 in an example, which
  731.      * "happens" to be the page size of the Intel x86.
  732.      */
  733.     rc = DosCreatePipe(&readHandle, &writeHandle, 4096);
  734. #ifdef VERBOSE
  735.     if (rc == NO_ERROR) openedFiles += 2;
  736.     printf("DosCreatePipe returned [%d], read [%d], write [%d]\n",
  737.            rc, readHandle, writeHandle);
  738.     fflush(stdout);
  739. #endif
  740.  
  741.     if (rc == NO_ERROR) {
  742.         *readPipe = MakeFile(readHandle);
  743.         *writePipe = MakeFile(writeHandle);
  744.         return 1;
  745.     }
  746.  
  747.     TclOS2ConvertError(rc);
  748.     return 0;
  749. }
  750.  
  751. /*
  752.  *----------------------------------------------------------------------
  753.  *
  754.  * TclpCloseFile --
  755.  *
  756.  *      Closes a pipeline file handle.  These handles are created by
  757.  *      TclpOpenFile, TclpCreatePipe, or TclpMakeFile.
  758.  *
  759.  * Results:
  760.  *      0 on success, -1 on failure.
  761.  *
  762.  * Side effects:
  763.  *      The file is closed and deallocated.
  764.  *
  765.  *----------------------------------------------------------------------
  766.  */
  767.  
  768. int
  769. TclpCloseFile(file)
  770.     TclFile file;       /* The file to close. */
  771. {
  772.     OS2File *filePtr = (OS2File *) file;
  773.  
  774. #ifdef VERBOSE
  775.     printf("TclpCloseFile [%d] type %d\n", filePtr->handle, filePtr->type);
  776.     fflush(stdout);
  777. #endif
  778.     if (filePtr->type < OS2_FIRST_TYPE || filePtr->type > OS2_LAST_TYPE) {
  779.         panic("Tcl_CloseFile: unexpected file type");
  780.     }
  781.  
  782.     rc = DosClose(filePtr->handle);
  783. #ifdef VERBOSE
  784.     if (rc == NO_ERROR) openedFiles--;
  785.     printf("TclpCloseFile: DosClose [%d] returns %d\n", filePtr->handle, rc);
  786.     fflush(stdout);
  787. #endif
  788.     if (rc != NO_ERROR) {
  789.         TclOS2ConvertError(rc);
  790.         ckfree((char *) filePtr);
  791.         return -1;
  792.     }
  793.  
  794.     ckfree((char *) filePtr);
  795.     return 0;
  796. }
  797.  
  798. /*
  799.  *----------------------------------------------------------------------
  800.  *
  801.  * TclpCreateProcess --
  802.  *
  803.  *      Create a child process that has the specified files as its
  804.  *      standard input, output, and error.  The child process runs
  805.  *      asynchronously and runs with the same environment variables
  806.  *      as the creating process.
  807.  *
  808.  *      The complete OS/2 search path is searched to find the specified
  809.  *      executable.  If an executable by the given name is not found,
  810.  *      automatically tries appending ".com", ".exe", and ".bat" to the
  811.  *      executable name.
  812.  *
  813.  * Results:
  814.  *      The return value is TCL_ERROR and an error message is left in
  815.  *      interp->result if there was a problem creating the child
  816.  *      process.  Otherwise, the return value is TCL_OK and *pidPtr is
  817.  *      filled with the process id of the child process.
  818.  *
  819.  * Side effects:
  820.  *      A process is created.
  821.  *
  822.  *----------------------------------------------------------------------
  823.  */
  824.  
  825. int
  826. TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile,
  827.         pidPtr)
  828.     Tcl_Interp *interp;         /* Interpreter in which to leave errors that
  829.                                  * occurred when creating the child process.
  830.                                  * Error messages from the child process
  831.                                  * itself are sent to errorFile. */
  832.     int argc;                   /* Number of arguments in following array. */
  833.     char **argv;                /* Array of argument strings.  argv[0]
  834.                                  * contains the name of the executable
  835.                                  * converted to native format (using the
  836.                                  * Tcl_TranslateFileName call).  Additional
  837.                                  * arguments have not been converted. */
  838.     TclFile inputFile;          /* If non-NULL, gives the file to use as
  839.                                  * input for the child process.  If inputFile
  840.                                  * file is not readable or is NULL, the child
  841.                                  * will receive no standard input. */
  842.     TclFile outputFile;         /* If non-NULL, gives the file that
  843.                                  * receives output from the child process.  If
  844.                                  * outputFile file is not writeable or is
  845.                                  * NULL, output from the child will be
  846.                                  * discarded. */
  847.     TclFile errorFile;          /* If non-NULL, gives the file that
  848.                                  * receives errors from the child process.  If
  849.                                  * errorFile file is not writeable or is NULL,
  850.                                  * errors from the child will be discarded.
  851.                                  * errorFile may be the same as outputFile. */
  852.     Tcl_Pid *pidPtr;            /* If this procedure is successful, pidPtr
  853.                                  * is filled with the process id of the child
  854.                                  * process. */
  855. {
  856.     int result, applType, nextArg, count, mode;
  857.     HFILE inputHandle, outputHandle, errorHandle;
  858.     HFILE stdIn = HF_STDIN, stdOut = HF_STDOUT, stdErr = HF_STDERR;
  859.     HFILE orgIn = NEW_HANDLE, orgOut = NEW_HANDLE, orgErr = NEW_HANDLE;
  860.     BOOL stdinChanged, stdoutChanged, stderrChanged;
  861.     char execPath[MAX_PATH];
  862.     char *originalName = NULL;
  863.     OS2File *filePtr;
  864.     ULONG action;
  865.     char *arguments[256];
  866.  
  867. #ifdef VERBOSE
  868.     int remember[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  869.     printf("TclpCreateProcess\n");
  870.     fflush(stdout);
  871. #endif
  872.  
  873.     if (!initialized) {
  874.         PipeInit();
  875.     }
  876.  
  877.     applType = ApplicationType(interp, argv[0], execPath);
  878.     if (applType != APPL_NONE) {
  879. #ifdef VERBOSE
  880.         printf("argv[0] %s, execPath %s\n", argv[0], execPath);
  881.         fflush(stdout);
  882. #endif
  883.         originalName = argv[0];
  884.         argv[0] = execPath;
  885.     }
  886.  
  887.     result = TCL_ERROR;
  888.  
  889. #ifdef VERBOSE
  890.     printf("1 opened files %d\n", openedFiles);;
  891.     fflush(stdout);
  892. #endif
  893.     /* Backup original stdin, stdout, stderr by Dup-ing to new handle */
  894.     rc = DosDupHandle(stdIn, &orgIn);
  895. #ifdef VERBOSE
  896.     if (rc == NO_ERROR) openedFiles++;
  897.     printf("2 opened files %d\n", openedFiles);;
  898.     printf("DosDupHandle stdIn %d returns %d orgIn %d\n", stdIn, rc, orgIn);
  899.     fflush(stdout);
  900. #endif
  901.     rc = DosDupHandle(stdOut, &orgOut);
  902. #ifdef VERBOSE
  903.     if (rc == NO_ERROR) openedFiles++;
  904.     printf("3 opened files %d\n", openedFiles);;
  905.     printf("DosDupHandle stdOut %d returns %d, orgOut %d\n", stdOut, rc,
  906.            orgOut);
  907.     fflush(stdout);
  908. #endif
  909.     rc = DosDupHandle(stdErr, &orgErr);
  910. #ifdef VERBOSE
  911.     if (rc == NO_ERROR) openedFiles++;
  912.     printf("4 opened files %d\n", openedFiles);;
  913.     printf("DosDupHandle stdErr %d returns %d orgErr %d\n", stdErr, rc,
  914.            orgErr);
  915.     fflush(stdout);
  916. #endif
  917.  
  918.     /*
  919.      * We have to check the type of each file, since we cannot duplicate
  920.      * some file types.
  921.      */
  922.  
  923.     inputHandle = NEW_HANDLE;
  924.     if (inputFile != NULL) {
  925.         filePtr = (OS2File *)inputFile;
  926.         if (filePtr->type >= OS2_FIRST_TYPE || filePtr->type <= OS2_LAST_TYPE) {
  927.             inputHandle = filePtr->handle;
  928.         }
  929.     }
  930.     outputHandle = NEW_HANDLE;
  931.     if (outputFile != NULL) {
  932.         filePtr = (OS2File *)outputFile;
  933.         if (filePtr->type >= OS2_FIRST_TYPE || filePtr->type <= OS2_LAST_TYPE) {
  934.             outputHandle = filePtr->handle;
  935.         }
  936.     }
  937.     errorHandle = NEW_HANDLE;
  938.     if (errorFile != NULL) {
  939.         filePtr = (OS2File *)errorFile;
  940.         if (filePtr->type >= OS2_FIRST_TYPE || filePtr->type <= OS2_LAST_TYPE) {
  941.             errorHandle = filePtr->handle;
  942.         }
  943.     }
  944. #ifdef VERBOSE
  945.     printf("    inputHandle [%d]\n", inputHandle);
  946.     printf("    outputHandle [%d]\n", outputHandle);
  947.     printf("    errorHandle [%d]\n", errorHandle);
  948.     fflush(stdout);
  949. #endif
  950.  
  951.     /*
  952.      * Duplicate all the handles which will be passed off as stdin, stdout
  953.      * and stderr of the child process. The duplicate handles are set to
  954.      * be inheritable, so the child process can use them.
  955.      */
  956.  
  957.     stdinChanged = stdoutChanged = stderrChanged = FALSE;
  958.     if (inputHandle == NEW_HANDLE) {
  959.         /*
  960.          * If handle was not set, open NUL as input.
  961.          */
  962. #ifdef VERBOSE
  963.         printf("Opening NUL as input\n");
  964.         fflush(stdout);
  965. #endif
  966.         rc = DosOpen((PSZ)"NUL", &inputHandle, &action, 0, FILE_NORMAL,
  967.                      OPEN_ACTION_CREATE_IF_NEW,
  968.                      OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY, NULL);
  969. #ifdef VERBOSE
  970.         if (rc == NO_ERROR) openedFiles++;
  971.         printf("5 opened files %d\n", openedFiles);;
  972.     fflush(stdout);
  973. #endif
  974.         if (rc != NO_ERROR) {
  975.             TclOS2ConvertError(rc);
  976.             Tcl_AppendResult(interp, "couldn't open NUL as input handle: ",
  977.                     Tcl_PosixError(interp), (char *) NULL);
  978. #ifdef VERBOSE
  979.             printf("1 goto end\n");;
  980.     fflush(stdout);
  981. #endif
  982.             goto end;
  983.         }
  984.     }
  985.     if (inputHandle != stdIn) {
  986.         /* Duplicate to standard input handle */
  987.         rc = DosDupHandle(inputHandle, &stdIn);
  988. #ifdef VERBOSE
  989.         if (rc == NO_ERROR) openedFiles++;
  990.         printf("6 opened files %d\n", openedFiles);;
  991.         printf("DosDupHandle inputHandle [%d] returned [%d], handle [%d]\n",
  992.                inputHandle, rc, stdIn);
  993.         fflush(stdout);
  994. #endif
  995.         if (rc != NO_ERROR) {
  996.             TclOS2ConvertError(rc);
  997.             Tcl_AppendResult(interp, "couldn't duplicate input handle: ",
  998.                     Tcl_PosixError(interp), (char *) NULL);
  999. #ifdef VERBOSE
  1000.             printf("2 goto end\n");;
  1001.     fflush(stdout);
  1002. #endif
  1003.             goto end;
  1004.         }
  1005.         stdinChanged = TRUE;
  1006.     }
  1007.  
  1008.     if (outputHandle == NEW_HANDLE) {
  1009.         /*
  1010.          * If handle was not set, open NUL as output.
  1011.          */
  1012. #ifdef VERBOSE
  1013.         printf("Opening NUL as output\n");
  1014.         fflush(stdout);
  1015. #endif
  1016.         rc = DosOpen((PSZ)"NUL", &outputHandle, &action, 0, FILE_NORMAL,
  1017.                      OPEN_ACTION_CREATE_IF_NEW,
  1018.                      OPEN_SHARE_DENYNONE | OPEN_ACCESS_WRITEONLY, NULL);
  1019. #ifdef VERBOSE
  1020.         if (rc == NO_ERROR) openedFiles++;
  1021.         printf("7 opened files %d\n", openedFiles);;
  1022.     fflush(stdout);
  1023. #endif
  1024.         if (rc != NO_ERROR) {
  1025.             TclOS2ConvertError(rc);
  1026.             Tcl_AppendResult(interp, "couldn't open NUL as output handle: ",
  1027.                     Tcl_PosixError(interp), (char *) NULL);
  1028. #ifdef VERBOSE
  1029.             printf("3 goto end\n");;
  1030.     fflush(stdout);
  1031. #endif
  1032.             goto end;
  1033.         }
  1034.     }
  1035.     if (outputHandle != stdOut) {
  1036.         /* Duplicate to standard output handle */
  1037.         rc = DosDupHandle(outputHandle, &stdOut);
  1038. #ifdef VERBOSE
  1039.         if (rc == NO_ERROR) openedFiles++;
  1040.         printf("8 opened files %d\n", openedFiles);;
  1041.         printf("DosDupHandle outputHandle [%d] returned [%d], handle [%d]\n",
  1042.                outputHandle, rc, stdOut);
  1043.         fflush(stdout);
  1044. #endif
  1045.         if (rc != NO_ERROR) {
  1046.             TclOS2ConvertError(rc);
  1047.             Tcl_AppendResult(interp, "couldn't duplicate output handle: ",
  1048.                     Tcl_PosixError(interp), (char *) NULL);
  1049. #ifdef VERBOSE
  1050.             printf("4 goto end\n");;
  1051.     fflush(stdout);
  1052. #endif
  1053.             goto end;
  1054.         }
  1055.         stdoutChanged = TRUE;
  1056.     }
  1057.  
  1058.     if (errorHandle == NEW_HANDLE) {
  1059.         /*
  1060.          * If handle was not set, open NUL as output.
  1061.          */
  1062. #ifdef VERBOSE
  1063.         printf("Opening NUL as error\n");
  1064.         fflush(stdout);
  1065. #endif
  1066.         rc = DosOpen((PSZ)"NUL", &errorHandle, &action, 0, FILE_NORMAL,
  1067.                      OPEN_ACTION_CREATE_IF_NEW,
  1068.                      OPEN_SHARE_DENYNONE | OPEN_ACCESS_WRITEONLY, NULL);
  1069. #ifdef VERBOSE
  1070.         if (rc == NO_ERROR) openedFiles++;
  1071.         printf("9 opened files %d\n", openedFiles);;
  1072.     fflush(stdout);
  1073. #endif
  1074.         if (rc != NO_ERROR) {
  1075.             TclOS2ConvertError(rc);
  1076.             Tcl_AppendResult(interp, "couldn't open NUL as error handle: ",
  1077.                     Tcl_PosixError(interp), (char *) NULL);
  1078. #ifdef VERBOSE
  1079.             printf("5 goto end\n");;
  1080.     fflush(stdout);
  1081. #endif
  1082.             goto end;
  1083.         }
  1084.     }
  1085.     if (errorHandle != stdErr) {
  1086.         /* Duplicate to standard error handle */
  1087.         rc = DosDupHandle(errorHandle, &stdErr);
  1088. #ifdef VERBOSE
  1089.         if (rc == NO_ERROR) openedFiles++;
  1090.         printf("10 opened files %d\n", openedFiles);;
  1091.         printf("DosDupHandle errorHandle [%d] returned [%d], handle [%d]\n",
  1092.                errorHandle, rc, stdErr);
  1093.         fflush(stdout);
  1094. #endif
  1095.         if (rc != NO_ERROR) {
  1096.             TclOS2ConvertError(rc);
  1097.             Tcl_AppendResult(interp, "couldn't duplicate error handle: ",
  1098.                     Tcl_PosixError(interp), (char *) NULL);
  1099. #ifdef VERBOSE
  1100.             printf("6 goto end\n");;
  1101.     fflush(stdout);
  1102. #endif
  1103.             goto end;
  1104.         }
  1105.         stderrChanged = TRUE;
  1106.     }
  1107.  
  1108.     /*
  1109.      * EMX's spawnv handles all the nitty-gritty DosStartSession stuff (like
  1110.      * session/no-session, building environment and arguments with/without
  1111.      * quoting etc.) for us, so we'll just use that and keep ourselves to
  1112.      * it's arguments like P_WAIT, P_PM, ....
  1113.      */
  1114.  
  1115.     /*
  1116.      * Run DOS (incl. .bat) and .cmd files via cmd.exe.
  1117.      */
  1118.  
  1119.     mode = P_SESSION;
  1120.     nextArg = 0;
  1121.  
  1122.     switch (applType) {
  1123.     case APPL_NONE:
  1124.     case APPL_DOS:
  1125.     case APPL_OS2CMD:
  1126.     case APPL_WIN3X:
  1127.         arguments[0] = "cmd.exe";
  1128.         arguments[1] = "/c";
  1129.         nextArg = 2;
  1130.         mode |= P_DEFAULT | P_MINIMIZE | P_BACKGROUND;
  1131.         break;
  1132.     case APPL_OS2WIN:
  1133.         mode |= P_DEFAULT | P_MINIMIZE | P_BACKGROUND;
  1134.         break;
  1135.     case APPL_OS2FS:
  1136.         mode |= P_FULLSCREEN | P_BACKGROUND;
  1137.         break;
  1138.     case APPL_OS2PM:
  1139.         if (usePm) {
  1140.             mode = P_PM | P_BACKGROUND;
  1141.         } else {
  1142.             mode |= P_PM | P_BACKGROUND;
  1143.         }
  1144.         break;
  1145.     default:
  1146.         mode |= P_DEFAULT | P_MINIMIZE | P_BACKGROUND;
  1147.     }
  1148.     for (count = 0; count < argc && nextArg < 256; count++) {
  1149.         arguments[nextArg] = argv[count];
  1150.         nextArg++;
  1151.     }
  1152.     arguments[nextArg] = '\0';
  1153.  
  1154.     *pidPtr = (Tcl_Pid) spawnv(mode,
  1155.                                arguments[0], arguments);
  1156.     if (*pidPtr == (Tcl_Pid) -1) {
  1157.         TclOS2ConvertError(rc);
  1158.         Tcl_AppendResult(interp, "couldn't execute \"", originalName,
  1159.                 "\": ", Tcl_PosixError(interp), (char *) NULL);
  1160. #ifdef VERBOSE
  1161.             printf("1 goto end\n");;
  1162.     fflush(stdout);
  1163. #endif
  1164.         goto end;
  1165.     }
  1166. #ifdef VERBOSE
  1167.     printf("spawned pid %d\n", *pidPtr);
  1168.     fflush(stdout);
  1169. #endif
  1170.  
  1171.     result = TCL_OK;
  1172.  
  1173.  
  1174.     end:
  1175.  
  1176.     /* Restore original stdin, stdout, stderr by Dup-ing from new handle */
  1177.     stdIn = HF_STDIN; stdOut = HF_STDOUT; stdErr = HF_STDERR;
  1178. #ifdef VERBOSE
  1179.     remember[0] = stdinChanged;
  1180.     remember[1] = stdoutChanged;
  1181.     remember[2] = stderrChanged;
  1182. #endif
  1183.     if (stdinChanged) {
  1184.         rc = DosClose(stdIn);
  1185. #ifdef VERBOSE
  1186.         if (rc == NO_ERROR) openedFiles--;
  1187.         printf("11 opened files %d\n", openedFiles);;
  1188.         remember[3] = rc;
  1189.     fflush(stdout);
  1190. #endif
  1191.         rc = DosDupHandle(orgIn, &stdIn);
  1192. #ifdef VERBOSE
  1193.         remember[4] = rc;
  1194. #endif
  1195.     }
  1196.     rc = DosClose(orgIn);
  1197. #ifdef VERBOSE
  1198.     if (rc == NO_ERROR) openedFiles--;
  1199.     printf("12 opened files %d\n", openedFiles);;
  1200.     remember[5] = rc;
  1201.     fflush(stdout);
  1202. #endif
  1203.  
  1204.     if (stdoutChanged) {
  1205.         rc = DosClose(stdOut);
  1206. #ifdef VERBOSE
  1207.         if (rc == NO_ERROR) openedFiles--;
  1208.         printf("13 opened files %d\n", openedFiles);;
  1209.         remember[6] = rc;
  1210.     fflush(stdout);
  1211. #endif
  1212.         rc = DosDupHandle(orgOut, &stdOut);
  1213. #ifdef VERBOSE
  1214.         remember[7] = rc;
  1215. #endif
  1216.     }
  1217.     rc = DosClose(orgOut);
  1218. #ifdef VERBOSE
  1219.     if (rc == NO_ERROR) openedFiles--;
  1220.     printf("14 opened files %d\n", openedFiles);;
  1221.     remember[8] = rc;
  1222.     printf("stdinChanged %d, stdoutChanged %d, stderrChanged %d\n",
  1223.            remember[0], remember[1], remember[2]);
  1224.     if (remember[0]) {
  1225.         printf("DosClose \"new\" stdIn [%d] returned [%d]\n", stdIn,
  1226.                remember[3]);
  1227.         printf("DosDupHandle orgIn [%d] returned [%d]\n", orgIn,
  1228.                remember[4]);
  1229.         printf("DosClose orgIn [%d] returned [%d]\n", orgIn,
  1230.                remember[5]);
  1231.     }
  1232.     if (remember[1]) {
  1233.         printf("DosClose \"new\" stdOut [%d] returned [%d]\n", stdOut,
  1234.                remember[6]);
  1235.         printf("DosDupHandle orgOut [%d] returned [%d]\n", orgOut,
  1236.                remember[7]);
  1237.         printf("DosClose orgOut [%d] returned [%d]\n", orgOut,
  1238.                remember[8]);
  1239.     }
  1240.     fflush(stdout);
  1241. #endif
  1242.  
  1243.     if (stderrChanged) {
  1244.         rc = DosClose(stdErr);
  1245. #ifdef VERBOSE
  1246.         if (rc == NO_ERROR) openedFiles--;
  1247.         printf("15 opened files %d\n", openedFiles);;
  1248.         printf("DosClose \"new\" stdErr [%d] returned [%d]\n", stdErr, rc);
  1249.         fflush(stdout);
  1250. #endif
  1251.         rc = DosDupHandle(orgErr, &stdErr);
  1252. #ifdef VERBOSE
  1253.         printf("DosDupHandle orgErr [%d] returned [%d]\n", orgErr, rc);
  1254.         fflush(stdout);
  1255. #endif
  1256.     }
  1257.     rc = DosClose(orgErr);
  1258. #ifdef VERBOSE
  1259.     if (rc == NO_ERROR) openedFiles--;
  1260.     printf("16 opened files %d\n", openedFiles);;
  1261.     fflush(stdout);
  1262. #endif
  1263.  
  1264.     return result;
  1265. }
  1266.  
  1267. /*
  1268.  *--------------------------------------------------------------------
  1269.  *
  1270.  * ApplicationType --
  1271.  *
  1272.  *      Search for the specified program and identify if it refers to a DOS,
  1273.  *      Windows 3.x, OS/2 Windowable, OS/2 Full-Screen, OS/2 PM program.
  1274.  *      Used to determine how to invoke a program (if it can even be invoked).
  1275.  * Results:
  1276.  *      The return value is one of APPL_DOS, APPL_WIN3X, or APPL_WIN32
  1277.  *      if the filename referred to the corresponding application type.
  1278.  *      If the file name could not be found or did not refer to any known
  1279.  *      application type, APPL_NONE is returned and an error message is
  1280.  *      left in interp.  .bat files are identified as APPL_DOS.
  1281.  *
  1282.  * Side effects:
  1283.  *      None.
  1284.  *
  1285.  *----------------------------------------------------------------------
  1286.  */
  1287.  
  1288. static int
  1289. ApplicationType(interp, originalName, fullPath)
  1290.     Tcl_Interp *interp;         /* Interp, for error message. */
  1291.     const char *originalName;   /* Name of the application to find. */
  1292.     char fullPath[MAX_PATH];    /* Filled with complete path to
  1293.                                  * application. */
  1294. {
  1295.     int applType, i;
  1296.     char *ext;
  1297.     static char *extensions[] = {"", ".cmd", ".exe", ".bat", ".com", NULL};
  1298.     char tmpPath[MAX_PATH];
  1299.     FILESTATUS3 filestat;
  1300.     ULONG flags;
  1301.  
  1302. #ifdef VERBOSE
  1303.     printf("ApplicationType\n");
  1304.     fflush(stdout);
  1305. #endif
  1306.  
  1307.     applType = APPL_NONE;
  1308.     for (i = 0; extensions[i] != NULL; i++) {
  1309.         strncpy((char *)tmpPath, originalName, MAX_PATH - 5);
  1310. #ifdef VERBOSE
  1311.         printf("after strncpy, tmpPath %s, originalName %s\n", tmpPath,
  1312.                 originalName);
  1313.         fflush(stdout);
  1314. #endif
  1315.         strcat(tmpPath, extensions[i]);
  1316. #ifdef VERBOSE
  1317.         printf("after strcat, tmpPath %s, extensions[%d] [%s]\n", tmpPath,
  1318.                 i, extensions[i]);
  1319.         fflush(stdout);
  1320. #endif
  1321.  
  1322.         if (tmpPath[1] != ':' && tmpPath[0] != '\\') {
  1323.             rc = DosSearchPath(SEARCH_ENVIRONMENT | SEARCH_CUR_DIRECTORY |
  1324.                                SEARCH_IGNORENETERRS, "PATH", tmpPath,
  1325.                                (PBYTE)fullPath, MAXPATH);
  1326.             if (rc != NO_ERROR) {
  1327. #ifdef VERBOSE
  1328.                 printf("DosSearchPath %s ERROR %d\n", tmpPath, rc);
  1329.                 fflush(stdout);
  1330. #endif
  1331.                 continue;
  1332.             }
  1333. #ifdef VERBOSE
  1334.             printf("DosSearchPath %s OK (%s)\n", tmpPath, fullPath);
  1335.             fflush(stdout);
  1336. #endif
  1337.         } else {
  1338.             strcpy(fullPath, tmpPath);
  1339.         }
  1340.  
  1341.         /*
  1342.          * Ignore matches on directories or data files, return if identified
  1343.          * a known type.
  1344.          */
  1345.  
  1346.         rc = DosQueryPathInfo(fullPath, FIL_STANDARD, &filestat,
  1347.                               sizeof(FILESTATUS3));
  1348.         if (rc != NO_ERROR || filestat.attrFile & FILE_DIRECTORY) {
  1349.             continue;
  1350.         }
  1351.  
  1352.         rc = DosQueryAppType(fullPath, &flags);
  1353.         if (rc != NO_ERROR) {
  1354.             continue;
  1355.         } 
  1356.  
  1357.         if ((flags & FAPPTYP_DLL) || (flags & FAPPTYP_PHYSDRV) ||
  1358.             (flags & FAPPTYP_VIRTDRV) || (flags & FAPPTYP_PROTDLL)) {
  1359.             /* No executable */
  1360.             continue;
  1361.         } 
  1362.  
  1363.         if (flags & FAPPTYP_NOTSPEC) {
  1364.  
  1365.             /* Not a recognized type, try to see if it's a .cmd or .bat */
  1366.             ext = strrchr(fullPath, '.');
  1367.             if ((ext != NULL) && (stricmp(ext, ".cmd") == 0)) {
  1368.                 applType = APPL_OS2CMD;
  1369.                 break;
  1370.             }
  1371.             if ((ext != NULL) && (stricmp(ext, ".bat") == 0)) {
  1372.                 applType = APPL_DOS;
  1373.                 break;
  1374.             }
  1375.             /* Still not recognized, might be a Win32 PE-executable */
  1376.             applType = APPL_NONE;
  1377.             break;
  1378.         }
  1379.  
  1380.         /*
  1381.          * NB! Some bozo defined FAPPTYP_WINDOWAPI as 0x0003 instead of 0x0004,
  1382.          * thereby causing it to have bits in common with both
  1383.          * FAPPTYP_NOTWINDOWCOMPAT (0x0001) and FAPPTYP_WINDOWCOMPAT (0x0002),
  1384.          * which means that for any OS/2 app, you get "PM app" as answer if
  1385.          * you don't take extra measures.
  1386.          * This is found in EMX 0.9c, 0.9b *AND* in IBM's own Visual Age C++
  1387.          * 3.0, so I must assume Eberhard Mattes was forced to follow the
  1388.          * drunk that defined these defines in the LX format....
  1389.          */
  1390.         if (flags & FAPPTYP_NOTWINDOWCOMPAT) {
  1391.             applType = APPL_OS2FS;
  1392.         }
  1393.         if (flags & FAPPTYP_WINDOWCOMPAT) {
  1394.             applType = APPL_OS2WIN;
  1395.         }
  1396.         /*
  1397.          * This won't work:
  1398.         if (flags & FAPPTYP_WINDOWAPI) {
  1399.             applType = APPL_OS2PM;
  1400.         }
  1401.          * Modified version:
  1402.          */
  1403.         if ((flags & FAPPTYP_NOTWINDOWCOMPAT)
  1404.             && (flags & FAPPTYP_WINDOWCOMPAT)) {
  1405.             applType = APPL_OS2PM;
  1406.         }
  1407.         if (flags & FAPPTYP_DOS) {
  1408.             applType = APPL_DOS;
  1409.         }
  1410.         if ((flags & FAPPTYP_WINDOWSREAL) || (flags & FAPPTYP_WINDOWSPROT)
  1411.             || (flags & FAPPTYP_WINDOWSPROT31)) {
  1412.             applType = APPL_WIN3X;
  1413.         }
  1414.  
  1415.         break;
  1416.     }
  1417.  
  1418.     if (applType == APPL_NONE) {
  1419. #ifdef VERBOSE
  1420.         printf("ApplicationType: APPL_NONE\n");
  1421.     fflush(stdout);
  1422. #endif
  1423.         TclOS2ConvertError(rc);
  1424.         Tcl_AppendResult(interp, "couldn't execute \"", originalName,
  1425.                 "\": ", Tcl_PosixError(interp), (char *) NULL);
  1426.         return APPL_NONE;
  1427.     }
  1428.     return applType;
  1429. }
  1430.  
  1431. /*
  1432.  *----------------------------------------------------------------------
  1433.  *
  1434.  * TclpCreateCommandChannel --
  1435.  *
  1436.  *      This function is called by Tcl_OpenCommandChannel to perform
  1437.  *      the platform specific channel initialization for a command
  1438.  *      channel.
  1439.  *
  1440.  * Results:
  1441.  *      Returns a new channel or NULL on failure.
  1442.  *
  1443.  * Side effects:
  1444.  *      Allocates a new channel.
  1445.  *
  1446.  *----------------------------------------------------------------------
  1447.  */
  1448.  
  1449. Tcl_Channel
  1450. TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)
  1451.     TclFile readFile;           /* If non-null, gives the file for reading. */
  1452.     TclFile writeFile;          /* If non-null, gives the file for writing. */
  1453.     TclFile errorFile;          /* If non-null, gives the file where errors
  1454.                                  * can be read. */
  1455.     int numPids;                /* The number of pids in the pid array. */
  1456.     Tcl_Pid *pidPtr;            /* An array of process identifiers. */
  1457. {
  1458.     char channelName[20];
  1459.     int channelId;
  1460.     PipeInfo *infoPtr = (PipeInfo *) ckalloc((unsigned) sizeof(PipeInfo));
  1461.  
  1462. #ifdef VERBOSE
  1463.     printf("TclpCreateCommandChannel\n");
  1464.     fflush(stdout);
  1465. #endif
  1466.  
  1467.     if (!initialized) {
  1468.         PipeInit();
  1469.     }
  1470.  
  1471.     infoPtr->watchMask = 0;
  1472.     infoPtr->flags = 0;
  1473.     infoPtr->readFile = readFile;
  1474.     infoPtr->writeFile = writeFile;
  1475.     infoPtr->errorFile = errorFile;
  1476.     infoPtr->numPids = numPids;
  1477.     infoPtr->pidPtr = pidPtr;
  1478.  
  1479.     /*
  1480.      * Use one of the fds associated with the channel as the
  1481.      * channel id.
  1482.      */
  1483.  
  1484.     if (readFile) {
  1485.         channelId = (int) ((OS2File*)readFile)->handle;
  1486.     } else if (writeFile) {
  1487.         channelId = (int) ((OS2File*)writeFile)->handle;
  1488.     } else if (errorFile) {
  1489.         channelId = (int) ((OS2File*)errorFile)->handle;
  1490.     } else {
  1491.         channelId = 0;
  1492.     }
  1493.  
  1494.     infoPtr->validMask = 0;
  1495.     if (readFile != NULL) {
  1496.         infoPtr->validMask |= TCL_READABLE;
  1497.     }
  1498.     if (writeFile != NULL) {
  1499.         infoPtr->validMask |= TCL_WRITABLE;
  1500.     }
  1501.  
  1502.     /*
  1503.      * For backward compatibility with previous versions of Tcl, we
  1504.      * use "file%d" as the base name for pipes even though it would
  1505.      * be more natural to use "pipe%d".
  1506.      */
  1507.  
  1508.     sprintf(channelName, "file%d", channelId);
  1509.     infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
  1510.             (ClientData) infoPtr, infoPtr->validMask);
  1511.  
  1512.     /*
  1513.      * Pipes have AUTO translation mode on OS/2 and ^Z eof char, which
  1514.      * means that a ^Z will be appended to them at close. This is needed
  1515.      * for programs that expect a ^Z at EOF.
  1516.      */
  1517.  
  1518.     Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
  1519.             "-translation", "auto");
  1520.     Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
  1521.             "-eofchar", "\032 {}");
  1522.     return infoPtr->channel;
  1523. }
  1524.  
  1525. /*
  1526.  *----------------------------------------------------------------------
  1527.  *
  1528.  * TclGetAndDetachPids --
  1529.  *
  1530.  *      Stores a list of the command PIDs for a command channel in
  1531.  *      interp->result.
  1532.  *
  1533.  * Results:
  1534.  *      None.
  1535.  *
  1536.  * Side effects:
  1537.  *      Modifies interp->result.
  1538.  *
  1539.  *----------------------------------------------------------------------
  1540.  */
  1541.  
  1542. void
  1543. TclGetAndDetachPids(interp, chan)
  1544.     Tcl_Interp *interp;
  1545.     Tcl_Channel chan;
  1546. {
  1547.     PipeInfo *pipePtr;
  1548.     Tcl_ChannelType *chanTypePtr;
  1549.     int i;
  1550.     char buf[20];
  1551.  
  1552. #ifdef VERBOSE
  1553.     printf("TclGetAndDetachPids\n");
  1554.     fflush(stdout);
  1555. #endif
  1556.  
  1557.     /*
  1558.      * Punt if the channel is not a command channel.
  1559.      */
  1560.  
  1561.     chanTypePtr = Tcl_GetChannelType(chan);
  1562.     if (chanTypePtr != &pipeChannelType) {
  1563.         return;
  1564.     }
  1565.  
  1566.     pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
  1567.     for (i = 0; i < pipePtr->numPids; i++) {
  1568.         sprintf(buf, "%lu", (unsigned long) pipePtr->pidPtr[i]);
  1569.         Tcl_AppendElement(interp, buf);
  1570.         Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
  1571.     }
  1572.     if (pipePtr->numPids > 0) {
  1573.         ckfree((char *) pipePtr->pidPtr);
  1574.         pipePtr->numPids = 0;
  1575.     }
  1576. }
  1577.  
  1578. /*
  1579.  *----------------------------------------------------------------------
  1580.  *
  1581.  * PipeBlockModeProc --
  1582.  *
  1583.  *      Set blocking or non-blocking mode on channel.
  1584.  *
  1585.  * Results:
  1586.  *      0 if successful, errno when failed.
  1587.  *
  1588.  * Side effects:
  1589.  *      Sets the device into blocking or non-blocking mode.
  1590.  *
  1591.  *----------------------------------------------------------------------
  1592.  */
  1593.  
  1594. static int
  1595. PipeBlockModeProc(instanceData, mode)
  1596.     ClientData instanceData;    /* Instance data for channel. */
  1597.     int mode;                   /* TCL_MODE_BLOCKING or
  1598.                                  * TCL_MODE_NONBLOCKING. */
  1599. {
  1600.     PipeInfo *infoPtr = (PipeInfo *) instanceData;
  1601.  
  1602. #ifdef VERBOSE
  1603.     printf("PipeBlockModeProc\n");
  1604.     fflush(stdout);
  1605. #endif
  1606.  
  1607.     /*
  1608.      * Unnamed pipes on OS/2 can not be switched between blocking and
  1609.      * nonblocking, hence we have to emulate the behavior. This is done in
  1610.      * the input function by checking against a bit in the state. We set or
  1611.      * unset the bit here to cause the input function to emulate the correct
  1612.      * behavior.
  1613.      */
  1614.  
  1615.     if (mode == TCL_MODE_NONBLOCKING) {
  1616.         infoPtr->flags |= PIPE_ASYNC;
  1617.     } else {
  1618.         infoPtr->flags &= ~(PIPE_ASYNC);
  1619.     }
  1620.     return 0;
  1621. }
  1622.  
  1623. /*
  1624.  *----------------------------------------------------------------------
  1625.  *
  1626.  * PipeCloseProc --
  1627.  *
  1628.  *      Closes a pipe based IO channel.
  1629.  *
  1630.  * Results:
  1631.  *      0 on success, errno otherwise.
  1632.  *
  1633.  * Side effects:
  1634.  *      Closes the physical channel.
  1635.  *
  1636.  *----------------------------------------------------------------------
  1637.  */
  1638.  
  1639. static int
  1640. PipeCloseProc(instanceData, interp)
  1641.     ClientData instanceData;    /* Pointer to PipeInfo structure. */
  1642.     Tcl_Interp *interp;         /* For error reporting. */
  1643. {
  1644.     PipeInfo *pipePtr = (PipeInfo *) instanceData;
  1645.     Tcl_Channel errChan;
  1646.     int errorCode, result;
  1647.     PipeInfo *infoPtr, **nextPtrPtr;
  1648.  
  1649. #ifdef VERBOSE
  1650.     printf("PipeCloseProc\n");
  1651.     fflush(stdout);
  1652. #endif
  1653.  
  1654.     /*
  1655.      * Remove the file from the list of watched files.
  1656.      */
  1657.  
  1658.     for (nextPtrPtr = &firstPipePtr, infoPtr = *nextPtrPtr; infoPtr != NULL;
  1659.             nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
  1660.         if (infoPtr == (PipeInfo *)pipePtr) {
  1661.             *nextPtrPtr = infoPtr->nextPtr;
  1662.             break;
  1663.         }
  1664.     }
  1665.  
  1666.     errorCode = 0;
  1667.     if (pipePtr->readFile != NULL) {
  1668. #ifdef VERBOSE
  1669.         printf("PipeCloseProc closing readFile\n");
  1670.         fflush(stdout);
  1671. #endif
  1672.         if (TclpCloseFile(pipePtr->readFile) != 0) {
  1673.             errorCode = errno;
  1674.         }
  1675.     }
  1676.     if (pipePtr->writeFile != NULL) {
  1677. #ifdef VERBOSE
  1678.         printf("PipeCloseProc closing writeFile\n");
  1679.         fflush(stdout);
  1680. #endif
  1681.         if (TclpCloseFile(pipePtr->writeFile) != 0) {
  1682.             if (errorCode == 0) {
  1683.                 errorCode = errno;
  1684.             }
  1685.         }
  1686.     }
  1687.  
  1688.     /*
  1689.      * Wrap the error file into a channel and give it to the cleanup
  1690.      * routine.
  1691.      */
  1692.  
  1693.     if (pipePtr->errorFile) {
  1694.         OS2File *filePtr;
  1695.         filePtr = (OS2File *)pipePtr->errorFile;
  1696.         errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle,
  1697.                 TCL_READABLE);
  1698. /*
  1699.         TclpCloseFile(pipePtr->errorFile);
  1700. */
  1701.     } else {
  1702.         errChan = NULL;
  1703.     }
  1704.     result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
  1705.             errChan);
  1706.     if (pipePtr->numPids > 0) {
  1707.         ckfree((char *) pipePtr->pidPtr);
  1708.     }
  1709.     ckfree((char*) pipePtr);
  1710.  
  1711.     if (errorCode == 0) {
  1712.         return result;
  1713.     }
  1714.     return errorCode;
  1715. }
  1716.  
  1717. /*
  1718.  *----------------------------------------------------------------------
  1719.  *
  1720.  * PipeInputProc --
  1721.  *
  1722.  *      Reads input from the IO channel into the buffer given. Returns
  1723.  *      count of how many bytes were actually read, and an error indication.
  1724.  *
  1725.  * Results:
  1726.  *      A count of how many bytes were read is returned and an error
  1727.  *      indication is returned in an output argument.
  1728.  *
  1729.  * Side effects:
  1730.  *      Reads input from the actual channel.
  1731.  *
  1732.  *----------------------------------------------------------------------
  1733.  */
  1734.  
  1735. static int
  1736. PipeInputProc(instanceData, buf, bufSize, errorCode)
  1737.     ClientData instanceData;            /* Pipe state. */
  1738.     char *buf;                          /* Where to store data read. */
  1739.     int bufSize;                        /* How much space is available
  1740.                                          * in the buffer? */
  1741.     int *errorCode;                     /* Where to store error code. */
  1742. {
  1743.     PipeInfo *infoPtr = (PipeInfo *) instanceData;
  1744.     OS2File *filePtr = (OS2File*) infoPtr->readFile;
  1745.     ULONG bytesRead;
  1746. #if 0
  1747.     BYTE output[200];
  1748.     AVAILDATA avail;
  1749.     ULONG state;
  1750. #endif
  1751.  
  1752. #ifdef VERBOSE
  1753.     printf("PipeInputProc\n");
  1754.     fflush(stdout);
  1755. #endif
  1756.  
  1757.     *errorCode = 0;
  1758.  
  1759.     /*
  1760.      * Pipes will block until the requested number of bytes has been
  1761.      * read.  To avoid blocking unnecessarily, we look ahead and only
  1762.      * read as much as is available.
  1763.      */
  1764.  
  1765. #if 0
  1766.  
  1767.     rc = DosPeekNPipe(filePtr->handle, &output, bufSize, &bytesRead, &avail,
  1768.                       &state);
  1769. #ifdef VERBOSE
  1770.     printf("DosPeekNPipe %d returns %d\n", filePtr->handle, rc);
  1771.     fflush(stdout);
  1772. #endif
  1773.     if (rc == NO_ERROR) {
  1774.         if ((bytesRead != 0) && ((USHORT) bufSize > avail.cbpipe)) {
  1775.             bufSize = (int) bytesRead;
  1776.         } else if ((bytesRead == 0) && (infoPtr->flags & PIPE_ASYNC)) {
  1777.             errno = *errorCode = EAGAIN;
  1778.             return -1;
  1779.         } else if ((bytesRead == 0) && !(infoPtr->flags & PIPE_ASYNC)) {
  1780.             bufSize = 1;
  1781.         }
  1782.     } else {
  1783.         goto error;
  1784.     }
  1785.  
  1786. #endif
  1787.  
  1788.     /*
  1789.      * Note that we will block on reads from a console buffer until a
  1790.      * full line has been entered.  The only way I know of to get
  1791.      * around this is to write a console driver.  We should probably
  1792.      * do this at some point, but for now, we just block.
  1793.      */
  1794.  
  1795.     rc = DosRead(filePtr->handle, (PVOID) buf, (ULONG) bufSize, &bytesRead);
  1796. #ifdef VERBOSE
  1797.     { int i;
  1798.     printf("DosRead handle [%d] returns [%d], bytes read [%d]\n",
  1799.            filePtr->handle, rc, bytesRead);
  1800.     fflush(stdout);
  1801.     }
  1802. #endif
  1803.     if (rc != NO_ERROR) {
  1804.         goto error;
  1805.     }
  1806.  
  1807.     return bytesRead;
  1808.  
  1809. error:
  1810.     TclOS2ConvertError(rc);
  1811.     if (errno == EPIPE) {
  1812.         return 0;
  1813.     }
  1814.     *errorCode = errno;
  1815.     return -1;
  1816. }
  1817.  
  1818. /*
  1819.  *----------------------------------------------------------------------
  1820.  *
  1821.  * PipeOutputProc --
  1822.  *
  1823.  *      Writes the given output on the IO channel. Returns count of how
  1824.  *      many characters were actually written, and an error indication.
  1825.  *
  1826.  * Results:
  1827.  *      A count of how many characters were written is returned and an
  1828.  *      error indication is returned in an output argument.
  1829.  *
  1830.  * Side effects:
  1831.  *      Writes output on the actual channel.
  1832.  *
  1833.  *----------------------------------------------------------------------
  1834.  */
  1835.  
  1836. static int
  1837. PipeOutputProc(instanceData, buf, toWrite, errorCode)
  1838.     ClientData instanceData;            /* Pipe state. */
  1839.     char *buf;                          /* The data buffer. */
  1840.     int toWrite;                        /* How many bytes to write? */
  1841.     int *errorCode;                     /* Where to store error code. */
  1842. {
  1843.     PipeInfo *infoPtr = (PipeInfo *) instanceData;
  1844.     OS2File *filePtr = (OS2File *) infoPtr->writeFile;
  1845.     ULONG bytesWritten;
  1846.  
  1847. #ifdef VERBOSE
  1848.     printf("PipeOutputProc\n");
  1849.     fflush(stdout);
  1850. #endif
  1851.  
  1852.     *errorCode = 0;
  1853.     rc = DosWrite(filePtr->handle, (PVOID) buf, (ULONG) toWrite, &bytesWritten);
  1854.     if (rc != NO_ERROR) {
  1855.         TclOS2ConvertError(rc);
  1856.         if (errno == EPIPE) {
  1857.             return 0;
  1858.         }
  1859.         *errorCode = errno;
  1860.         return -1;
  1861.     }
  1862.     return bytesWritten;
  1863. }
  1864.  
  1865. /*
  1866.  *----------------------------------------------------------------------
  1867.  *
  1868.  * PipeEventProc --
  1869.  *
  1870.  *      This function is invoked by Tcl_ServiceEvent when a file event
  1871.  *      reaches the front of the event queue.  This procedure invokes
  1872.  *      Tcl_NotifyChannel on the pipe.
  1873.  *
  1874.  * Results:
  1875.  *      Returns 1 if the event was handled, meaning it should be removed
  1876.  *      from the queue.  Returns 0 if the event was not handled, meaning
  1877.  *      it should stay on the queue.  The only time the event isn't
  1878.  *      handled is if the TCL_FILE_EVENTS flag bit isn't set.
  1879.  *
  1880.  * Side effects:
  1881.  *      Whatever the notifier callback does.
  1882.  *
  1883.  *----------------------------------------------------------------------
  1884.  */
  1885.  
  1886. static int
  1887. PipeEventProc(evPtr, flags)
  1888.     Tcl_Event *evPtr;           /* Event to service. */
  1889.     int flags;                  /* Flags that indicate what events to
  1890.                                  * handle, such as TCL_FILE_EVENTS. */
  1891. {
  1892.     PipeEvent *pipeEvPtr = (PipeEvent *)evPtr;
  1893.     PipeInfo *infoPtr;
  1894.     OS2File *filePtr;
  1895.     int mask;
  1896. #if 0
  1897.     ULONG count;
  1898.     BYTE output[200];
  1899.     AVAILDATA avail;
  1900.     ULONG state;
  1901. #endif
  1902.  
  1903. #ifdef VERBOSE
  1904.     printf("PipeEventProc\n");
  1905.     fflush(stdout);
  1906. #endif
  1907.  
  1908.     if (!(flags & TCL_FILE_EVENTS)) {
  1909.         return 0;
  1910.     }
  1911.  
  1912.     /*
  1913.      * Search through the list of watched pipes for the one whose handle
  1914.      * matches the event.  We do this rather than simply dereferencing
  1915.      * the handle in the event so that pipes can be deleted while the
  1916.      * event is in the queue.
  1917.      */
  1918.  
  1919.     for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
  1920.         if (pipeEvPtr->infoPtr == infoPtr) {
  1921.             infoPtr->flags &= ~(PIPE_PENDING);
  1922.             break;
  1923.         }
  1924.     }
  1925.  
  1926.     /*
  1927.      * Remove stale events.
  1928.      */
  1929.  
  1930.     if (!infoPtr) {
  1931.         return 1;
  1932.     }
  1933.  
  1934.     /*
  1935.      * Check to see if the pipe is readable.
  1936.      * Note that if we can't tell if a pipe is writable, we always report it
  1937.      * as being writable.
  1938.      */
  1939.  
  1940.     filePtr = (OS2File*) ((PipeInfo*)infoPtr)->readFile;
  1941.     mask = TCL_WRITABLE|TCL_READABLE;
  1942.  
  1943. #if 0
  1944.  
  1945.     rc = DosPeekNPipe(filePtr->handle, &output, sizeof(output), &count,
  1946.                       &avail, &state);
  1947. #ifdef VERBOSE
  1948.     printf("DosPeekNPipe %d returns %d\n", filePtr->handle, rc);
  1949.     fflush(stdout);
  1950. #endif
  1951.     if (rc == NO_ERROR) {
  1952.         if (avail.cbpipe != 0) {
  1953.             mask |= TCL_READABLE;
  1954.         }
  1955.     } else {
  1956.  
  1957.         /*
  1958.          * If the pipe has been closed by the other side, then
  1959.          * mark the pipe as readable, but not writable.
  1960.          */
  1961.  
  1962.         if (state == NP_STATE_CLOSING) {
  1963.             mask = TCL_READABLE;
  1964.         }
  1965.     }
  1966.  
  1967. #endif
  1968.  
  1969.     /*
  1970.      * Inform the channel of the events.
  1971.      */
  1972.  
  1973.     Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
  1974.     return 1;
  1975. }
  1976.  
  1977. /*
  1978.  *----------------------------------------------------------------------
  1979.  *
  1980.  * PipeWatchProc --
  1981.  *
  1982.  *      Called by the notifier to set up to watch for events on this
  1983.  *      channel.
  1984.  *
  1985.  * Results:
  1986.  *      None.
  1987.  *
  1988.  * Side effects:
  1989.  *      None.
  1990.  *
  1991.  *----------------------------------------------------------------------
  1992.  */
  1993.  
  1994. static void
  1995. PipeWatchProc(instanceData, mask)
  1996.     ClientData instanceData;            /* Pipe state. */
  1997.     int mask;                           /* What events to watch for; OR-ed
  1998.                                          * combination of TCL_READABLE,
  1999.                                          * TCL_WRITABLE and TCL_EXCEPTION. */
  2000. {
  2001.     PipeInfo **nextPtrPtr, *ptr;
  2002.     PipeInfo *infoPtr = (PipeInfo *) instanceData;
  2003.     int oldMask = infoPtr->watchMask;
  2004.  
  2005. #ifdef VERBOSE
  2006.     printf("PipeWatchProc\n");
  2007.     fflush(stdout);
  2008. #endif
  2009.  
  2010.     /*
  2011.      * For now, we just send a message to ourselves so we can poll the
  2012.      * channel for readable events.
  2013.      */
  2014.  
  2015.     infoPtr->watchMask = mask & infoPtr->validMask;
  2016.     if (infoPtr->watchMask) {
  2017.         Tcl_Time blockTime = { 0, 0 };
  2018.         if (!oldMask) {
  2019.             infoPtr->nextPtr = firstPipePtr;
  2020.             firstPipePtr = infoPtr;
  2021.         }
  2022.         Tcl_SetMaxBlockTime(&blockTime);
  2023.     } else {
  2024.         if (oldMask) {
  2025.             /*
  2026.              * Remove the pipe from the list of watched pipes.
  2027.              */
  2028.  
  2029.             for (nextPtrPtr = &firstPipePtr, ptr = *nextPtrPtr;
  2030.                  ptr != NULL;
  2031.                  nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
  2032.                 if (infoPtr == ptr) {
  2033.                     *nextPtrPtr = ptr->nextPtr;
  2034.                     break;
  2035.                 }
  2036.             }
  2037.         }
  2038.     }
  2039. }
  2040.  
  2041. /*
  2042.  *----------------------------------------------------------------------
  2043.  *
  2044.  * PipeGetHandleProc --
  2045.  *
  2046.  *      Called from Tcl_GetChannelHandle to retrieve OS handles from
  2047.  *      inside a command pipeline based channel.
  2048.  *
  2049.  * Results:
  2050.  *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
  2051.  *      there is no handle for the specified direction.
  2052.  *
  2053.  * Side effects:
  2054.  *      None.
  2055.  *
  2056.  *----------------------------------------------------------------------
  2057.  */
  2058.  
  2059. static int
  2060. PipeGetHandleProc(instanceData, direction, handlePtr)
  2061.     ClientData instanceData;    /* The pipe state. */
  2062.     int direction;              /* TCL_READABLE or TCL_WRITABLE */
  2063.     ClientData *handlePtr;      /* Where to store the handle.  */
  2064. {
  2065.     PipeInfo *infoPtr = (PipeInfo *) instanceData;
  2066.     OS2File *filePtr;
  2067.  
  2068. #ifdef VERBOSE
  2069.     printf("PipeGetHandleProc\n");
  2070.     fflush(stdout);
  2071. #endif
  2072.  
  2073.     if (direction == TCL_READABLE && infoPtr->readFile) {
  2074.         filePtr = (OS2File*) infoPtr->readFile;
  2075.         *handlePtr = (ClientData) filePtr->handle;
  2076.         return TCL_OK;
  2077.     }
  2078.     if (direction == TCL_WRITABLE && infoPtr->writeFile) {
  2079.         filePtr = (OS2File*) infoPtr->writeFile;
  2080.         *handlePtr = (ClientData) filePtr->handle;
  2081.         return TCL_OK;
  2082.     }
  2083.     return TCL_ERROR;
  2084. }
  2085.  
  2086. /*
  2087.  *----------------------------------------------------------------------
  2088.  *
  2089.  * Tcl_WaitPid --
  2090.  *
  2091.  *      Emulates the waitpid system call.
  2092.  *
  2093.  * Results:
  2094.  *      Returns 0 if the process is still alive, -1 on an error, or
  2095.  *      the pid on a clean close.
  2096.  *
  2097.  * Side effects:
  2098.  *      None.
  2099.  *
  2100.  *----------------------------------------------------------------------
  2101.  */
  2102.  
  2103. Tcl_Pid
  2104. Tcl_WaitPid(pid, statPtr, options)
  2105.     Tcl_Pid pid;
  2106.     int *statPtr;
  2107.     int options;
  2108. {
  2109.     ULONG flags;
  2110.  
  2111. #ifdef VERBOSE
  2112.     printf("Tcl_WaitPid\n");
  2113.     fflush(stdout);
  2114. #endif
  2115.  
  2116.     if (!initialized) {
  2117.         PipeInit();
  2118.     }
  2119.  
  2120.     if (options & WNOHANG) {
  2121.         flags = DCWW_NOWAIT;
  2122.     } else {
  2123.         flags = DCWW_WAIT;
  2124.     }
  2125.  
  2126. #ifdef VERBOSE
  2127.     printf("Waiting for PID %d (%s)", pid,
  2128.            options & WNOHANG ? "WNOHANG" : "WAIT");
  2129.     fflush(stdout);
  2130. #endif
  2131.     rc = waitpid((int)pid, statPtr, options);
  2132. #ifdef VERBOSE
  2133.     printf(", returns %d (*statPtr %x) %s %d\n", rc, *statPtr,
  2134.            WIFEXITED(*statPtr) ? "WIFEXITED" :
  2135.            (WIFSIGNALED(*statPtr) ? "WIFSIGNALED" :
  2136.             (WIFSTOPPED(*statPtr) ? "WIFSTOPPED" : "unknown")),
  2137.            WIFEXITED(*statPtr) ? WEXITSTATUS(*statPtr) :
  2138.            (WIFSIGNALED(*statPtr) ? WTERMSIG(*statPtr) :
  2139.             (WIFSTOPPED(*statPtr) ? WSTOPSIG(*statPtr) : 0)));
  2140.     fflush(stdout);
  2141. #endif
  2142.     return (Tcl_Pid)rc;
  2143. }
  2144.  
  2145. /*
  2146.  *----------------------------------------------------------------------
  2147.  *
  2148.  * Tcl_PidObjCmd --
  2149.  *
  2150.  *      This procedure is invoked to process the "pid" Tcl command.
  2151.  *      See the user documentation for details on what it does.
  2152.  *
  2153.  * Results:
  2154.  *      A standard Tcl result.
  2155.  *
  2156.  * Side effects:
  2157.  *      See the user documentation.
  2158.  *
  2159.  *----------------------------------------------------------------------
  2160.  */
  2161.  
  2162.         /* ARGSUSED */
  2163. int
  2164. Tcl_PidObjCmd(dummy, interp, objc, objv)
  2165.     ClientData dummy;           /* Not used. */
  2166.     Tcl_Interp *interp;         /* Current interpreter. */
  2167.     int objc;                   /* Number of arguments. */
  2168.     Tcl_Obj *CONST *objv;       /* Argument strings. */
  2169. {
  2170.     Tcl_Channel chan;
  2171.     Tcl_ChannelType *chanTypePtr;
  2172.     PipeInfo *pipePtr;
  2173.     int i;
  2174.     Tcl_Obj *resultPtr;
  2175.     char buf[20];
  2176.  
  2177. #ifdef VERBOSE
  2178.     printf("Tcl_PidObjCmd\n");
  2179.     fflush(stdout);
  2180. #endif
  2181.  
  2182.     if (objc > 2) {
  2183.         Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
  2184.         return TCL_ERROR;
  2185.     }
  2186.     if (objc == 1) {
  2187.         resultPtr = Tcl_GetObjResult(interp);
  2188.         sprintf(buf, "%lu", (unsigned long) getpid());
  2189.         Tcl_SetStringObj(resultPtr, buf, -1);
  2190.     } else {
  2191.         chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL),
  2192.                 NULL);
  2193.         if (chan == (Tcl_Channel) NULL) {
  2194.             return TCL_ERROR;
  2195.         }
  2196.         chanTypePtr = Tcl_GetChannelType(chan);
  2197.         if (chanTypePtr != &pipeChannelType) {
  2198.             return TCL_OK;
  2199.         }
  2200.  
  2201.         pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
  2202.         resultPtr = Tcl_GetObjResult(interp);
  2203.         for (i = 0; i < pipePtr->numPids; i++) {
  2204.             sprintf(buf, "%lu", (unsigned long)pipePtr->pidPtr[i]);
  2205.             Tcl_ListObjAppendElement(/*interp*/ NULL, resultPtr,
  2206.                     Tcl_NewStringObj(buf, -1));
  2207.         }
  2208.     }
  2209.     return TCL_OK;
  2210. }
  2211.  
  2212. /*
  2213.  *----------------------------------------------------------------------
  2214.  *
  2215.  * TempFileCleanup
  2216.  *
  2217.  *      This procedure is a Tcl_ExitProc used to clean up the left-over
  2218.  *      temporary files made by TclpCreateTempFile (IF they still exist).
  2219.  *
  2220.  * Results:
  2221.  *      None.
  2222.  *
  2223.  * Side effects:
  2224.  *      Closes/deletes files in, and deallocates storage used by list.
  2225.  *
  2226.  *----------------------------------------------------------------------
  2227.  */
  2228.  
  2229. static void
  2230. TempFileCleanup(clientData)
  2231.     ClientData clientData;      /* Address of TmpFile structure */
  2232. {
  2233.     TmpFile *deleteFile = (TmpFile *)clientData;
  2234. #ifdef VERBOSE
  2235.     printf("TempFileCleanup %x [%s] (was handle [%d])\n", clientData,
  2236.            ((TmpFile*)deleteFile)->name, deleteFile->file.handle);
  2237.     fflush(stdout);
  2238. #endif
  2239.     rc = DosDelete((PSZ)((TmpFile*)deleteFile)->name);
  2240. #ifdef VERBOSE
  2241.     if (rc != NO_ERROR) {
  2242.         printf("    DosDelete ERROR %d\n", rc);
  2243.     } else {
  2244.         printf("    DosDelete OK\n");
  2245.     }
  2246.     fflush(stdout);
  2247. #endif
  2248.     /* Watch it! name was *malloc*ed by tempnam, so don't use ckfree */
  2249.     if (deleteFile->name != NULL) {
  2250.         free((char *)deleteFile->name);
  2251.     }
  2252.     ckfree((char *)deleteFile);
  2253. }
  2254.