═══ 1. Rexx Named Pipes ═══ The following sections describe RxNPipes, a function package for use by IBM employees that provides access to OS/2 Named Pipes from Rexx. ═══ 1.1. Introduction ═══ RxNPipes is a Dynamic Link Library (DLL) which provides the REXX programmer with many functions dealing with the operation of OS/2 Named Pipes. These functions allow Rexx procedures running in different sessions to communicate among themselves. This package is for use by IBM employees only. RxNPipes requires that you are already running under OS/2 2.x or higher with OS/2 Procedures Language 2/REXX installed. Installation of RxNPipes is described in Installation. To be able to use an RxNPipes function in a REXX program, you must first add the functions using the built-in REXX function RxFuncAdd. Example: call RxFuncAdd "RxNPCreate","RxNPipes","RxNPCreate" The above example would add the RxNPCreate function so that it can be used. The RxNPipes function RxNPLoadFuncs will automatically load all other RxNPipes functions. To do this from within a programme, add the following instructions: call RxFuncAdd "RxNPLoadFuncs","RxNPipes","RxNPLoadFuncs" call RxNPLoadFuncs Once the RxNPipes functions are loaded by RxNPLoadFuncs they are usable by all OS/2 sessions. The main RxNPipes functions are listed in the Contents. Additional functions that return values used in return code are displayed when you select the "+" sign beside "Errors and Error Codes". The functions provided are:  RxNPLoadFuncs  RxNPDropFuncs  RxNPCreate  RxNPOpen  RxNPRead  RxNPWrite  RxNPReconnect  RxNPClose  RxNPCreateSem  RxNPWaitFree  RxNPWaitRead  RxNPQuery  RxNPDebug  RxNPVersion  Functions which explain errors and error codes. - RxNPErrNotFound - RxNPErrAccessDenied - RxNPErrInvalidHandle - RxNPErrNoMemory - RxNPErrNoStructures - RxNPErrInvalidParm - RxNPErrInterrupt - RxNPErrBroken - RxNPErrBad - RxNPErrBusy - RxNPErrNoData - RxNPErrNotConnected - RxNPErrMoreData - RxNPErrTimeout - RxNPExplainErr These functions should normally be used in a REXX parse statement, as: parse value RxNPOpen(PipeName) with retcode handle . In some cases the RxNPipes functions return debugging tokens beyond those documented here. These additional tokens are subject to change and should be ignored. They are not part of the programming interface to RxNPipes. ═══ 1.2. Debugging ═══ The underlying Dos... function calls, their return codes, and other pertinent information are available to the user of these functions. This is triggered by the REXX Variable RxNPipes.0, as described below. The information displayed depends on the particular function call and is subject to change from release to release. A single RxNPipes function call may result in more than one Dos... function call, and therefore more than one line of diagnostic output. The format and content of this information is not a programming interface. There are 4 possible destinations for trace information:  The screen (stdout),  A Rexx stem variable,  A queue, such as that provided by PMPrintf, or  A named file. selected according to the value, which may be specified in upper, lower, or mixed case, in RxNPipes.0:  If this variable has the value "TRACE", then diagnostic information about every Dos... function call will be displayed on Standard Output (stdout).  If this has a non-negative numeric value RxNPipes.0 will be incremented and its new value, i, will be used to store the debugging information in RxNPipes.i instead of displaying it.  If the variable RxNPipes.0 begins with "\QUEUES\", diagnostic information about every Dos... function call is sent to the queue named by RxNPipes.0. If RxNPipes.0 contains "\QUEUES\" alone the queue name "\QUEUES\PRINTF32" (the default for PMPrintf) is assumed.  If the variable RxNPipes.0 contains a file name, which is recognised by: - having a period (.) or a backslash (\) in the first position, or - having a colon (:) in the second position, and - not being a queue name. the trace information is appended to the named file which is opened, written to, and closed for each trace event. Trace information sent to queues or files is preceded by a timestamp taking the form "YYYYMMDD HHMMSShh [pid]: ", where pid is the process id. If the queue or file cannot be opened the trace request is silently ignored. In addition, the function RxNPDebug can be used to place arbitrary information in the debug stream. ═══ 1.3. Return Codes ═══ These functions return the last-seen return code from the underlying Dos... functions executed on behalf of the user. Arbitrarily, the return codes ERROR_FILE_NOT_FOUND and ERROR_PATH_NOT_FOUND are consolidated into a single NotFound value (RxNPErrNotFound(), which happens to be the ERROR_PATH_NOT_FOUND value) in places where this type of return is expected. Equally arbitrarily, the return codes ERROR_SEM_TIMEOUT and ERROR_TIMEOUT are consolidated into a single Timeout value (RxNPErrTimeout(), which happens to be the ERROR_TIMEOUT value) in places where this type of return is expected. These consolidations happen after the debugging information that is described in Debugging Information is produced. ═══ 1.4. RxNPLoadFuncs ═══ Function: RxNPLoadFuncs Purpose: Use this functions to load all the remaining functions in the RxNPipes Package. Syntax: parse value RxNPLoadFuncs() with version version Version identification. This always takes the form "RxNPipes Version X.X ..." Example: call RxNPLoadFuncs ═══ 1.5. RxNPDropFuncs ═══ Function: RxNPDropFuncs Purpose: Use this functions to drop all the loaded RxNPipes functions. Once this function is processed by any REXX programme, the RxNPipes functions are no longer accessible in any OS/2 session. Syntax: call RxNPDropFuncs Example: call RxNPDropFuncs ═══ 1.6. RxNPCreate ═══ Function: RxNPCreate Purpose: Use this function to Create a named pipe, specify its characteristics, and make it ready for communication. Syntax: parse value RxNPCreate(name,direction,blocking,instances,buffer1,buffer2) with retcode handle . name The name of the Pipe. This must take the form "\pipe\...". This parameter is required. direction The direction of flow through the pipe. This must be one of: "Duplex" (the default), "Inbound, or "Outbound". Only the first character of this parameter is significant. Case is Ignored. blocking The blocking mode of the pipe. This must be "Wait" (the default) or "Nowait". Only the first character of this parameter is significant. Case is Ignored. instances The number of instances permitted for this pipe. This must be "Unlimited" (the default, only the first character of this parameter being significant with case ignored), or a number between 1 and 254. buffer1 The buffer size for the pipe. For Duplex pipes, this is the size of the inbound buffer. The default is 4096 bytes. buffer2 The outbound buffer size for a duplex pipe. This is valid for Duplex pipes only. The default is 4096 bytes. retcode The return code from the underlying Dos... function. handle The pipe handle Example: parse value RxNPCreate(pipe,"Duplex","Wait",1) with retcode handle . ═══ 1.7. RxNPOpen ═══ Function: RxNPOpen Purpose: Use this function to open an existing Named Pipe. Syntax: parse value RxNPOpen(name) with retcode handle . name The name of the Pipe. This must take the form "\pipe\...". This parameter is required. retcode The return code from the underlying Dos... function. handle The handle of the pipe, to be used in subsequent calls to RxNPipes functions. Example: parse value RxNPOpen("\pipe\exmppipe") with retcode handle . if retcode=RxNPErrNotFound() then do say "Pipe does not exist" return end if retcode=RxNPErrBusy() then retcode=RxNPWaitFree("\pipe\exmppipe") ═══ 1.8. RxNPRead ═══ Function: RxNPRead Purpose: Use this function to read messages from an opened Named Pipe. Syntax: parse value RxNPRead(handle,,) with retcode len message handle The pipe handle (as returned by RxNPCreate or RxNPOpen) from which a message is to be read. This parameter is required. symbol The name of a REXX variable in which the returned message is placed. This parameter is optional; if not specified the received data are returned as part of the function return value. If an invalid Rexx symbol name is given, up to buffer size bytes will be read from the pipe (and discarded) before the syntax error is detected. buffer size The size of buffer to be used to receive the message. This parameter is optional; if not specified, a 4096-byte buffer will be used. retcode The return code from the underlying Dos... function. len The number of bytes in the returned message. message The text of the message read. This is supplied only if there is no symbol parameter. Example: parse value RxNPRead(handle,"retval") with retcode len if retcode <> 0 then do say "Read Error: retcode="retcode"," RxNPExplainErr(retcode) return end say "Received message:" retval ═══ 1.9. RxNPWrite ═══ Function: RxNPWrite Purpose: Use this function to write a message to an opened Named Pipe. Syntax: parse value RxNPWrite(handle,message) with retcode . handle The pipe handle (as returned by RxNPCreate or RxNPOpen) from which a message is to be read. This parameter is required. message The message to be written to the Named Pipe. This parameter is required. retcode The return code from the underlying Dos... function. Example: parse value RxNPWrite(handle,"return message") with retcode . if retcode<>0 then do say "Write Error: retcode="retcode"," RxNPExplainErr(retcode) return end ═══ 1.10. RxNPReconnect ═══ Function: RxNPReconnect Purpose: Use this function to re-enable a Named Pipe for communication with another client. Syntax: parse value RxNPReconnect(handle) with retcode . handle The pipe handle (as returned by RxNPCreate or RxNPOpen) from which a message is to be read. This parameter is required. retcode The return code from the underlying Dos... function. Example: parse value RxNPQuery(handle) with retcode state . select when retcode<>0 then do say "Return code="retcode "querying pipe" pipe RxNPExplainErr(retcode) return end when state="CLOSING" then do parse value RxNPReconnect(handle) with retcode . if retcode<>0 then do say "Reconnect: retcode="retcode RxNPExplainErr(retcode) return end end otherwise nop end ═══ 1.11. RxNPClose ═══ Function: RxNPClose Purpose: Use this function to close an Named Pipe. Syntax: parse value RxNPClose(handle) with retcode . handle The pipe handle (as returned by RxNPCreate or RxNPOpen) which is to be closed. This parameter is required. retcode The return code from the underlying Dos... function. Example: call RxNPClose handle ═══ 1.12. RxNPCreateSem ═══ Function: RxNPCreateSem Purpose: Use this function to create a semaphore that can be used to wait for Pipes that do not block. Syntax: parse value RxNPCreateSem(handle) with retcode semaphore . handle The pipe handle (as returned by RxNPCreate or RxNPOpen) with which this semaphore is to be associated. This parameter is required. retcode The return code from the underlying Dos... function. semaphore A semaphore that can be used by RxNPWaitRead to wait for activity at the other end of the named pipe identified by handle. Example: See RxNPWaitRead. ═══ 1.13. RxNPWaitFree ═══ Function: RxNPWaitFree Purpose: Use this function to wait until a Named Pipe is available for opening and subsequent use. Syntax: parse value RxNPWaitFree(name,) with retcode . name The name of the Pipe. This must take the form "\pipe\...". This parameter is required. timeout This parameter is optional. If specified, it it the time (in milliseconds) to wait for the specified Named Pipe to be available for opening. retcode The return code from the underlying Dos... function. Example: do until retcode<>RxNPErrBusy() /* repeat; someone else may have been waiting too! */ parse value RxNPWaitFree("\pipe\exampipe",10000) with retcode . /* wait 10 sec for the pipe */ if retcode=RxNPErrTimeout() then do say "Timout waiting for pipe" return 0 end parse value RxNPOpen("\pipe\exampipe") with retcode handle . end if retcode <> 0 then do say "Open Error: retcode="retcode"," RxNPExplainErr(retcode) return end ═══ 1.14. RxNPWaitRead ═══ Function: RxNPWaitRead Purpose: Use this function to wait for data to be available. Syntax: parse value RxNPWaitRead(handle,semaphore,timeout) with retcode . handle The pipe handle (as returned by RxNPCreate or RxNPOpen) from which a message will be read by a subsequent call to RxNPRead. This parameter is required. semaphore The semaphore handle (as returned by RxNPCreateSem) timeout This parameter is optional. If specified, it it the time (in milliseconds) to wait for data to become available in the specified pipe. retcode The return code from the underlying Dos... function. Example: parse value RxNPRead(handle) with retcode len message do while retcode=RxNPErrNodata() | len=0 do forever parse value RxNPWaitRead(handle,semaphore,4000) with retcode . if retcode<>RxNPTimeout() then leave say "timeout tick every 4 seconds at" time() /* do here whatever needs doing every 4 seconds */ end parse value RxNPRead(handle) with retcode len message end ═══ 1.15. RxNPQuery ═══ Function: RxNPQuery Purpose: Use this function to retrieve information about the current state of the Named Pipe. Syntax: parse value RxNPQuery(handle) with retcode state instance pipebytes msgbytes cs nw . handle The pipe handle (as returned by RxNPCreate or RxNPOpen) for which information is required. This parameter is required. retcode The return code from the underlying Dos... function. state The current state of the Pipe. This will be one of "LISTENING", "CONNECTED", "CLOSING", or "DISCONNECTED". The pipe is in a listening state after the server issues DosConnectNPipe by calling RxNPCreate or RxNPReconnect. A listening pipe is ready to accept an RxNPOpen() request. If the pipe is not in a listening state, DosOpen returns RxNPErrBusy(). The pipe is in a connected state after a client has successfully issued DosOpen by calling RxNPOpen(). The connected pipe allows the server and the client to read and write to the pipe provided both have valid handles. The pipe is in a closing state after the last DosClose request has been made by calling RxNPClose to the pipe by either the client or the server. When DosClose has been issued for the client handle and all of its duplicates, the client end of the pipe is closed. The serving end must acknowledge the closing of the client end by issuing either DosDisConnectNPipe by calling RxNPReconnect or DosClose by calling RxNPClose. Issuing DosClose deallocates the pipe. The pipe is in a disconnected state immediately after a call to DosCreateNPipe, or DosDisConnectNPipe. A disconnected pipe cannot accept a call to DosOpen. The server must issue DosDisConnectNPipe before the pipe can be opened by a client. This state should not occur when using RxNPipes. instance The current instance of a named pipe that can have more than one instance (as specified in the RxNPCreate call). pipebytes The number of bytes buffered in the pipe (including message header bytes) msgbytes The number of bytes in the current message. cs "C" if called from the client end of the pipe, "S" if called from the server end. nw "N" of the Pipe does not block (opened with "Nowait"), "W" if it does (opened with "Wait"). Example: parse value RxNPQuery(handle) with retcode state . if retcode<>0 then do say "Query Error: retcode="retcode"," RxNPExplainErr(retcode) return end if state="CLOSING" then do parse value RxNPReconnect(handle) with retcode . if retcode<>0 then do say "Reconnect: retcode="retcode return end end ═══ 1.16. RxNPDebug ═══ Function: RxNPDebug Purpose: Use this functions to put arbitrary strings on the debug stream (see Debugging). Syntax: call RxNPDebug string1<,string2>... Example: parse source . . self parse args argstring call RxNPDebug self "starting",argstring ═══ 1.17. RxNPVersion ═══ Function: RxNPVersion Purpose: From version 2.0, this function returns the numeric version of the RxNPipes DLL. Syntax: parse value RxNPVersion() with ver . ═══ 1.18. Errors and Error codes ═══ These functions return the error codes associated with various Named Pipe conditions. ═══ 1.18.1. RxNPErrNotFound (3) ═══ This function returns the error code associated with the File Not Found Condition. ═══ 1.18.2. RxNPErrAccessDenied (5) ═══ This function returns the error code associated with the Access Denied Condition. ═══ 1.18.3. RxNPErrInvalidHandle (6) ═══ This function returns the error code associated with the Invalid Handle Condition. ═══ 1.18.4. RxNPErrNoMemory (8) ═══ This function returns the error code associated with the No Memory Condition. ═══ 1.18.5. RxNPErrNoStructures (84) ═══ This function returns the error code associated with the Out of Structures Condition. ═══ 1.18.6. RxNPErrInvalidParm (87) ═══ This function returns the error code associated with the Invalid Parameter Condition. ═══ 1.18.7. RxNPErrInterrupt (95) ═══ This function returns the error code associated with the Interrupt Condition. ═══ 1.18.8. RxNPErrBroken (109) ═══ This function returns the error code associated with the Broken Pipe Condition. ═══ 1.18.9. RxNPErrBad (230) ═══ This function returns the error code associated with the Bad Pipe Condition. ═══ 1.18.10. RxNPErrBusy (231) ═══ This function returns the error code associated with the Pipe Busy Condition. ═══ 1.18.11. RxNPErrNoData (232) ═══ This function returns the error code associated with the No Data Condition. ═══ 1.18.12. RxNPErrNotConnected (233) ═══ This function returns the error code associated with the Pipe Not Connected Condition. ═══ 1.18.13. RxNPErrMoreData (234) ═══ This function returns the error code associated with the More Data Condition. ═══ 1.18.14. RxNPErrTimeout (640) ═══ This function returns the error code associated with the Timeout Condition. ═══ 1.18.15. RxNPExplainErr ═══ Function: RxNPExplainErr Purpose: Use this function to return a string explaining the meaning of a return code. Syntax: errstring = RxNPExplainErr(retcode) retcode A return code returned by a Named Pipe function. This parameter is required. errstring A text string explaining the error. Example: if retcode <> 0 then do say "Retcode="retcode"," RxNPExplainErr(retcode) return end ═══ 1.19. Examples ═══ The following examples illustrate many of these functions ═══ 1.19.1. CalcSamp - A full duplex Named Pipe with no Asychronous processing ═══ /* sample for RxNPipes, duplex, wait calculator */ trace 'o' signal on syntax pipe="\pipe\calculator" mark = "/SERVER" RxNPipes.0="TRACE" if RxFuncQuery("SysSleep") then call RxFuncAdd 'SysSleep', 'RexxUtil', 'SysSleep' if RxFuncQuery("RxNpLoadFuncs") then do call RxFuncAdd 'RxNPLoadFuncs', 'RxNPipes', 'RxNPLoadFuncs' say RxNPLoadFuncs() end parse source . . self . parse arg expression if left(strip(expression),length(mark)) = mark then signal server RxNPipes.0=0 busy=RxNPErrBusy() timeout=RxNPErrTimeout() notfound=RxNPErrNotFound() parse value RxNPOpen(pipe) with retcode handle . if retcode=notfound then do "start ""Piped Calculator Server""/f" self mark do 60 parse value RxNPOpen(pipe) with retcode handle . if retcode <> notfound then leave say "Waiting for Server to start" call syssleep 1 end end do 10 while retcode=busy x=time() retcode=RxNPWaitFree(pipe,10000) /* wait 10 sec for it to free up */ if retcode=timeout then do /* this happens only if one process ties it up for 10 seconds */ say "Timout waiting for server" return 0 end if retcode<>0 then do say "Wait Error: retcode="retcode"," RxNPExplainErr(retcode) "in line" whatline() return end parse value RxNPOpen(pipe) with retcode handle . end if retcode <> 0 then do say "Open Error: retcode="retcode"," RxNPExplainErr(retcode) "in line" whatline() return end say 'Expression: "'expression'"' retcode=RxNPWrite(handle,expression) if retcode <> 0 then do say "Write Error: retcode="retcode"," RxNPExplainErr(retcode) "in line" whatline() return end parse value RxNPRead(handle,"retval") with retcode len if retcode <> 0 then do say "Read Error: retcode="retcode"," RxNPExplainErr(retcode) "in line" whatline() return end say 'Result: "'retval'"' say call RxNPClose handle do i=1 to RxNPipes.0 parse var RxNPipes.i f1 f2 d say "["i"]" f1 f2 say "["i"]" strip(d) say end return server: "mode co80,45" parse value RxNPCreate(pipe,"Duplex","Wait",1) with retcode handle . if retcode<>0 then do say "Create Error: retcode="retcode"," RxNPExplainErr(retcode) "creating pipe" pipe "in line" whatline() return end signal on halt do forever parse value RxNPRead(handle) with retcode len expression if retcode<>0 then do say "Read Error: retcode="retcode"," RxNPExplainErr(retcode) "in line" whatline() call syssleep 30 return end if right(expression,2)="0d0a"x then expression=left(expression,length(expression)-2) /* in case message comes from ECHO */ parse value RxNPQuery(handle) with retcode state . select when retcode<>0 then do say "Query Error: retcode="retcode"," RxNPExplainErr(retcode) "in line" whatline() call syssleep 30 return end when translate(expression)="STOP" then do call RxNPClose handle "exit" end when translate(expression)="DISP" then do if datatype(RxNPipes.0,"N") then do i=1 to RxNPipes.0 parse var RxNPipes.i f1 f2 d say "["i"]" f1 f2 say "["i"]" strip(d) say end drop RxNPipes. RxNPipes.0=0 parse value RxNPWrite(handle,"Done") with retcode . end when translate(expression)="NOTRACE" then do drop RxNPipes. end when translate(expression)="DROP" then do call RxNPClose handle call RxNPDropFuncs "exit" end when state="CLOSING" then do parse value RxNPReconnect(handle) with retcode . if retcode<>0 then do say "Reconnect Error: retcode="retcode"," RxNPExplainErr(retcode) "in line" whatline() call syssleep 30 return end end otherwise do if len=0 then retval="Empty expression received" else retval=evaluate(expression) say 'Received: "'expression'" Returning: "'retval'"' parse value RxNPWrite(handle,retval) with retcode . if retcode<>0 then do say "Write Error: retcode="retcode"," RxNPExplainErr(retcode) "in line" whatline() return end end end end evaluate: procedure parse arg expression signal on syntax name BADVAL signal on novalue name BADVAL interpret "Push" expression parse pull retval call syssleep 3 /* simulate processing time */ return retval BADVAL: return expression syntax: errcode=rc errline=sigl say "Error" errcode "("errortext(errcode)") in line" errline":" say sourceline(errline) return halt: errline=sigl say "Halt in line" errline":" say sourceline(errline) call SysSleep 10 "exit" whatline: return "line="sigl ═══ 1.19.2. MsgSamp - An Inbound Named Pipe with Asychronous processing ═══ /* sample for RxNPipes, inbound, nowait message receiver */ trace 'o' signal on syntax pipe="\pipe\message" mark = "/SERVER" if RxFuncQuery("SysSleep") then call RxFuncAdd 'SysSleep', 'RexxUtil', 'SysSleep' if RxFuncQuery("RxNpLoadFuncs") then do call RxFuncAdd 'RxNPLoadFuncs', 'RxNPipes', 'RxNPLoadFuncs' say RxNPLoadFuncs() end parse source . . self . parse arg message if left(strip(message),length(mark)) = mark then signal server RxNPipes.0=".\rxnpipes.log" busy=RxNPErrBusy() timeout=RxNPErrTimeout() notfound=RxNPErrNotFound() parse value RxNPOpen(pipe) with retcode handle . if retcode=notfound then do "start ""Piped Message Server"" /f" self mark do 60 parse value RxNPOpen(pipe) with retcode handle . if retcode <> notfound then leave say "Waiting for Server to start" call syssleep 1 end end do 20 while retcode=busy x=time() retcode=RxNPWaitFree(pipe,10000) /* wait 10 sec for it to free up */ if retcode=timeout then do /* this happens only if one process ties it up for 10 seconds */ say "Timout waiting for server" return 0 end if retcode<>0 then do say "Wait Error: retcode="retcode"," RxNPExplainErr(retcode)"in line" whatline() return end parse value RxNPOpen(pipe) with retcode handle . end if retcode <> 0 then do say "Open Error: retcode="retcode"," RxNPExplainErr(retcode) "in line" whatline() if retcode <> 0 then return end say "message:" message retcode=RxNPWrite(handle,message) if retcode <> 0 then do say "Write Error: retcode="retcode"," RxNPExplainErr(retcode) "in line" whatline() return end call RxNPClose handle return server: trace 'o' "mode co80,45" busy=RxNPErrBusy() timeout=RxNPErrTimeout() notfound=RxNPErrNotFound() nodata=RxNPErrNoData() parse value RxNPCreate(pipe,"Inbound","NoWait") with retcode handle . if retcode<>0 then do say "Return code="retcode "creating pipe" pipe return end signal on halt parse value RxNPCreateSem(handle) with retcode semaphore . if retcode<>0 then do say "Return code="retcode "creating semaphore" RxNPExplainErr(retcode) "in line" whatline() return end do forever parse value RxNPRead(handle) with retcode len message do while retcode=nodata | len=0 parse value RxNPQuery(handle) with retcode state . select when retcode<>0 then do say "Return code="retcode "querying pipe" pipe RxNPExplainErr(retcode) "in line" whatline() call syssleep 30 return end when state="CLOSING" then do parse value RxNPReconnect(handle) with retcode . if retcode<>0 then do say "Reconnect: retcode="retcode RxNPExplainErr(retcode) "in line" whatline() call syssleep 30 return end end otherwise nop end SaveRxNpipe="" TimeCount=-1 do forever parse value RxNPWaitRead(handle,semaphore,4000) with retcode . if retcode<>timeout then do leave end TimeCount=TimeCount+1 say "timeout tick every 4 seconds at" time() if datatype(RxNPipes.0,"N") then do SaveRxNpipe=1+RxNPipes.0 drop RxNPipes.0 end end if SaveRxNPipe<>"" then do RxNPipes.SaveRxNPipe="= =" TimeCount "timeouts skipped" RxNPipes.0=SaveRxNPipe end parse value RxNPRead(handle) with retcode len message end if retcode<>0 then do say "Return code="retcode "reading pipe" pipe RxNPExplainErr(retcode) "in line" whatline() call syssleep 30 return end if right(message,2)="0d0a"x then message=left(message,length(message)-2) /* in case message comes from ECHO */ select when translate(message)="STOP" then do call RxNPClose handle "exit" end when translate(message)="DROP" then do call RxNPClose handle call RxNPDropFuncs "exit" end when translate(message)="DISP" then do if datatype(RxNPipes.0,"N") then do i=1 to RxNPipes.0 parse var RxNPipes.i f1 f2 d say "["i"]" f1 f2 say "["i"]" strip(d) say end drop RxNPipes. RxNPipes.0=0 end when translate(message)="NOTRACE" then do drop RxNPipes. end otherwise do say 'Received: "'message'"' end end parse value RxNPQuery(handle) with retcode state . select when retcode<>0 then do say "Return code="retcode "querying pipe" pipe RxNPExplainErr(retcode) "in line" whatline() call syssleep 30 return end when state="CLOSING" then do parse value RxNPReconnect(handle) with retcode . if retcode<>0 then do say "Reconnect: retcode="retcode RxNPExplainErr(retcode) "in line" whatline() call syssleep 30 return end end otherwise nop end end return syntax: errcode=rc errline=sigl say "Error" errcode "("errortext(errcode)") in line" errline":" say sourceline(errline) return halt: errline=sigl say "Halt in line" errline":" say sourceline(errline) call SysSleep 10 "exit" whatline: return sigl ═══ 1.20. Installation ═══ There are 4 files in this package:  RxNPipes.dll  RxNPipes.inf  magsamp.cmd  calcsamp.cmd the latter 2 being examples only, and not essential to the operation of RxNPipes. RxNPipes.dll must be copied to a directory in the LIBPATH (in CONFIG.SYS) to enable the facilities described here. RxNPipes.inf may be copied to a directory in the BOOKSHELF path (in CONFIG.SYS) to enable the commands view RxNpipes and help RxNPipes topic or included as part of the REXX environment variable, as in set rexx=c:\os2\book\rexx.inf+d:\packages\rxpipe\rxnpipes.inf to enable these functions to appear as a natural part of the REXX environment. In the latter case the help text in this file will appear as part of the overall REXX help. ═══ 1.21. Notes ═══ ═══ 1.21.1. Version 2.00: 22nd December 1995 ═══ Fixed a bug in the trace output for the openmode parameter in RxNPOpen. Allowed tracing to a PMPRINTF (or any other Queue-supporting) session. Added RxNPDebug() and RxNPVersion(). ═══ 1.21.2. Version 1.40: 27th July 1994 ═══ Added an optional parameter to RxNPRead to specify the size of receive buffer to use. The default size is still 4096 bytes. ═══ 1.21.3. Version 1.30: 23rd May 1994 ═══ Fixed a bug in returning the received message (from RxNPRead) as part of the function return value when the number of bytes received was greater than about 250. ═══ 1.21.4. Version 1.20: 20th February 1994 ═══ Trace to file contains process id. Formatting improvements made to .inf file Incorrect references to RXNPipe changed to RXNPipes in the .inf file. This version was never put on OS2TOOLS ═══ 1.21.5. Version 1.10: 17th October 1993 ═══ Trace output can be directed to a file. See Debugging. Samples fixed to load the SysSleep() function from REXXUTIL if required. ═══ 1.21.6. Version 1.00: 15th September 1993 ═══ Minor editorial changes Compiled with GA-level of IBM C Set++. ═══ 1.21.7. Version 0.71: 7th September 1993 ═══ Code and INF file marked "IBM Internal Use Only" ═══ 1.21.8. Version 0.07: 6th September 1993 ═══ This is the first external release. This package was compiled with a Beta Version of the IBM C/C++ Tools Product. My GA version has been ordered but has not arrived; this package will remain at version 0.x until I can rebuild it with the GA product. RxNPipes was built as a static-linked subsystem, so there should be no requirement for other DLLs on your system. When I first started working on this, but before I released it to anyone else, I called it RxNPipe in some places and RxNPipes in others. It should be called RxNPipes consistently; any reference to the name RxNPipe, either in this .inf file or in the code, is an error.