SCCSID  equ     "@(#)nampipe1.asm       6.17 91/12/16"
TITLE   NAMPIPE1 -- Local named pipe support
NAME    NAMPIPE1

;**     NAMPIPE1 -- routines supporting creation, opening, closing, status
;               of local named pipes.
;
;       See the header comment in nampipe2.asm for an overview of named
;       pipe functionality.
;
;       Copyright (c) 1987, Microsoft Corp.
;
; @6535 09/05/91  GAG  PTR    CP20 B726535 Enable DosRead/DosWrite timeout
;                                          in blocking mode.
;
; @4109 11/10/91  ROC  PTR    CP20 AA04109 Change way of determining a
;                                          local/remote pipe.
;
; @0237 08/07/92 GAG   DEFECT R202 050237  Fix DosConnectNamePipe RC of 109
;                                          on a pipe that was opened written
;                                          to and closed in a different process.
;
; @73173 9/2/93  WCH  DEFECT R206 73173    Work around to prevent the overwritten
;                                          of NP_STATE due to a racing condition
;                                          between client thread (close) and
;                                          server thread (connect)
;
;  75228 10/28/93 Nizinski                 Fix checking of PathNameType
;                                          in w_WaitNmPipe and w_CallNmPipe
;                                          to avoid ERROR_BAD_PIPE.
;                                          Checking of PathNameType must be
;                                          done via 'test' instruction
;                                          (instead of 'cmp') since multiple
;                                          bits can be on in this byte.
; 80655 03/24/94 wdm Bill Miller  merged fix made by Beth Chen to release
;                                 the FS serialization semaphore.
;
; 88284 07/07/94 lrn Scott Cottrill        Changed flushpipe routine to
;                                          check rc from np_sem_wait before
;                                          calling np_sem_sig.
;
; 89862  8/01/94   Change Team    Check latest pipe state after unblocking in
;                                 w_ConnectNmPipe.  A race condition allows
;                                 a thread to re-block if w_DisconnectNmPipe
;                                 is called before the blocked thread is
;                                 scheduled.  See defect for detailed info.
;
; 111380  02/08/94  Change Team  Fix timing problem where a pipe closed while
;                                another thread was blocked inside disconnect
;                                for the same pipe.  Once the thread came out
;                                of the block in disconnect, the pipe was
;                                gone so we trashed data and traped.
;
; 72417   11/27/95  MLM
;                 moved from PTDA to TCB:
;                 cMeta, WFP_Start, PathNameType
;
; 150371  04/01/96  DRB         System Anchor Block
;
; 151989  04/18/96  DRB         w_CallNmPipe returns ERROR_BAD_PIPE instead of
;                               ERROR_FILENAME_EXCED_RANGE
;
;
;   Public routines:
;       w_MakeNmPipe
;       w_ConnectNmPipe
;       w_DisconnectNmPipe
;       w_WaitNmPipe
;       NmPipeOpen
;       NmPipeClose
;       w_QNmPHandState
;       w_SetNmPHandState
;       w_QNmPipeInfo
;       w_CallNmPipe
;       w_SetNmPipeSem
;       w_QNmPipeSemState
;       CheckPipeDir
;
;   Private routines:
;       npm_memalloc
;       wnp_sub
;       check_server_mode
;       flushpipe
;       npcl_dealloc_all
;       npcl_dealloc_buf
;       npqi_bufsize
;       np_handle_chk
;       qnps_xfer_rec
;       findpipe
;       get_next_instance
;       get_free_npn
;       find_npn
;       get_free_np
;       dealloc_np

.286p
.xlist
include dosseg.inc
.xcref
include basemaca.inc
include osmaca.inc
include fnofs.inc
include oldmaca.inc
include curdir.inc
include dossymc.inc
include argframe.inc
include error.inc
include filesys.inc
include fsc.inc
include vmowner.inc
include mi.inc
include mult.inc
include opncreat.inc
include protmode.inc
include sem.inc
include sft.inc
include filemode.inc
include chardd.inc
include semxport.inc
.cref
.list
include tkexport.inc
.xlist
.xcref
include osconst.inc
include pgexport.inc
include vmexport.inc
include seldesc.inc
include virtsym.inc
include selxport.inc
include codelock.inc
.cref
.list

include nampipe.inc

        ModName , FNO_NAMPIPE1_ASM

        ReferGlobal     SpoolDevRMPRec,DWORD           ; Spool record seg.
        ReferGlobal     SpoolDevSem,DWORD
        ReferGlobal     UNCfscList,BYTE ; offset into DOS segmt for UNCFscList
        ReferGlobal     UNCfscLast,BYTE ; offset of last entry in UNCFscList

        ReferGDT        GDT_FSC,WORD
                                                ;72417 removed PTDA entries now
                                                ;      in TCB

        ReferTask       CurrTCB,WORD
        ReferTask       JFN_Flg_Ptr,DWORD       ; ptr to array of per-fd flags

        EXTRN           Bmp_SegSize:ABS

        EXTRN           np_sem_clear:NEAR
        EXTRN           np_sem_sig:NEAR
        EXTRN           np_sem_wait:NEAR
        EXTRN           np_sft_io:NEAR
        EXTRN           np_trans_sft:NEAR
        EXTRN           rd_chk_avail:NEAR
        EXTRN           wrt_chk_avail:NEAR

        EXTRNFAR        Clear_Kernel_Sem
        EXTRNFAR        Take_Kernel_Sem
        EXTRNFAR        dh_OpenEventSem
        EXTRNFAR        dh_CloseEventSem

        EXTRNFAR        Bmp_Init
        EXTRNFAR        DStrLen
        EXTRNFAR        FreeTempBuffer
        EXTRNFAR        FSD_Close
        EXTRNFAR        GetTempBuffer
        EXTRNFAR        ProcBlock
        EXTRNFAR        ProcRun
        EXTRNFAR        SFLinkFree
        EXTRNFAR        SFFromHandle
        EXTRNFAR        SFNAlloc
        EXTRNFAR        JFNFindFree
        EXTRNFAR        UCase
        EXTRNFAR        SuBuff
        EXTRNFAR        XCHGP
        EXTRNFAR        Bmp_Free
        EXTRNFAR        Bmp_Get
        EXTRNFAR        Rmp_Alloc
        EXTRNFAR        Rmp_Add
        EXTRNFAR        Rmp_Rem
        EXTRNFAR        Rmp_Find
        EXTRNFAR        SegAlloc
        EXTRNFAR        TransPath
        EXTRNFAR        SemHandle
        EXTRNFAR        Rmp_FindN
        EXTRNFAR        Rmp_Rem

        EXTRN32         _SEMFindType
        EXTRN32         _SELAllocKHB
        EXTRN32         _SELFreeKHB
        EXTRN32         CodeLockProc
        EXTRN32         CodeUnlockProc
        EXTRN32         _KSEMRequestMutex
        EXTRN32         _KSEMReleaseMutex

            FARCODE HIGH4
NMPIPESWAPCODE   SEGMENT

        ASSUME  SS:TASKAREA

        PUBLIC _StartNMPIPEGROUP
_StartNMPIPEGROUP LABEL BYTE

MINBUFSIZE      equ     3               ; minimum permissible buffer size
DEFBUFSIZE      equ     1024            ; default buffer size
MAXBUFSIZE      equ (65535-(size npb+1)) ; maximum permissible buffer size

DEFTIMEOUT      equ     50              ; default timeout on MakeNmPipe

BUFFERHEAP      equ     VM_HKH_PUB_RESRW


;*      ShareMode gives the open_sharing_mode bits given the open_access bits
ShareMode       db      sharing_deny_read       ; inbound pipe
                db      sharing_deny_write      ; outbound pipe
                db      sharing_deny_none       ; duplex pipe

        public  PipeDir,PIPEDIRLEN
PipeDir db      "\PIPE\",0              ; pipe pseudo-dir name

PIPEDIRLEN = ($ - PipeDir) - 1


Break   <Named pipe creation>

;***    w_MakeNmPipe - Create a named pipe
;
;       Create a named pipe and return a file handle to it.
;
;       ENTRY   (ds:dx) = pointer to file name buffer
;               (ax)    = pipe open mode mask
;               (bx)    = pipe specific mode
;               [TCBBytCnt1] = outgoing buffer size
;               [TCBBytCnt2] = incoming buffer size
;               (di:si) = default timeout
;
;       EXIT    C flag clear:
;                       (ax) = file handle of named pipe
;               C flag set:
;                       (ax) = error code
;
;       USES    EAX, EBX, ECX, EDX, SI, DI, DS, ES, Flags
;
;       Pseudo-code:
;               check for invalid params, adjust defaults
;               translate path name
;               if (path name does not start with /pipe/)
;                       return ERROR_PATH_NOT_FOUNDA
;               np = findpipe(name)
;               if (np found)
;                       for (icount = np_icount; np = get_next_instance(np);
;                               icount--)
;                               if (icount == 0)
;                                       return ERROR_PIPE_BUSY
;               else
;                       if (err = get_free_npn(name))
;                               return err
;               if (err = get_free_np())
;                       return err
;               fill in NP fields
;               allocate JFN and SFT
;               fill in SFT
;               return JFN


        ASSUME  DS:NOTHING,ES:NOTHING
Procedure w_MakeNmPipe,FARONLY
LocalVar        npm_name,DWORD          ; pipe file name
LocalVar        npm_omd,WORD            ; pipe open mode
LocalVar        npm_pmd,WORD            ; pipe mode word
LocalVar        npm_tmo,DWORD           ; default timeout
LocalVar        npm_size1,WORD          ; size of outgoing buffer
LocalVar        npm_size2,WORD          ; size of incoming buffer
LocalVar        npm_np,DWORD            ; pointer to named_pipe structure
LocalVar        npm_JFN,WORD
LocalVar        npm_pJFN,DWORD
LocalVar        npm_openbuf,PMPATHLEN
LocalVar        npm_dummy,WORD          ; KLUDGE: see gshare.asm
LocalVar        npm_dummyCDS,<SIZE curdir>

        CodeLock        NMPIPE_GROUP

        EnterProc       <ds,dx, ax, bx, di,si>  ; initialize locals
;### PTR B720732 push di
        SaveReg <di,bx>                                 ;### PTR B720732
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        lea     di,npm_openbuf
        mov     TASKAREA:[bx].TCBpOpenBuf,di            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],di ; Initialize ptda_pOpenBuf
        lea     di,npm_dummyCDS
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,di       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],di ; Initialize ptda_pCDS
;### PTR B720732        pop     di
        RestoreReg <bx,di>                              ;### PTR B720732

        or      di,si                   ; default timeout?
        jnz     npm00
        mov     npm_tmol,DEFTIMEOUT

npm00:  test    ax,NOT (open_write_through+open_no_inherit+open_access)
        jnz     npm_eip                 ; illegal open mode bit
        and     al,open_access
        cmp     al,open_for_both
        ja      npm_eip                 ; illegal access mode
        inc     al                      ; (al) = read/write bitmask
        mov     bx,CurrTCB
        mov     cx,ss:[bx.TCBBytCnt2]   ; (cx) = incoming buffer size
        mov     bx,ss:[bx.TCBBytCnt1]   ; (bx) = outgoing buffer size
        call    npm_chk_buf_size        ; check incoming buffer size
        mov     npm_size2,cx
        mov     cx,bx
        shr     al,1                    ; (al) = 0/1, 1 if write permitted
        call    npm_chk_buf_size        ; check outgoing buffer size
        mov     npm_size1,cx

        mov     ax,npm_pmd              ; ax = Pmode (al = Icount)
        call    sphs_check_pmode        ; check pipe modes
        jc      npm_eip                 ; bad mode
        or      al,al                   ; zero Icount?
        jz      npm_eip                 ; yes, another bad mode
        test    ax,NP_SERVER            ; reserved bit to MakeNamedPipe?
        jz      npm07                   ; no, all okay
npm_eip:
        mov     ax,ERROR_INVALID_PARAMETER
        jmp     npm_err

npm07:  push    bp                      ; save frame pointer
        lea     di,npm_openbuf          ; appropriate buffer
        mov     bx,CurrTCB
        push    word ptr TASKAREA:[bx].TCBNewFlags
        or      TASKAREA:[bx].TCBNewFlags,TCBNEWFILES ; enable NEWFILES

        lds     si,npm_name             ; (ds:si) = pipe name
        CALLFAR TransPath               ; convert the path

        mov     bx,CurrTCB
        pop     word ptr TASKAREA:[bx].TCBNewFlags
    ASSUME   DS:TASKAREA
        pop     bp                      ; restore frame pointer
        jc      npm_err
        mov     ax,ERROR_FILE_NOT_FOUND
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        cmp     [bp].TCBcMeta,-1        ; any meta characters found?
        UnSetBP_CurrTCB
        jne     npm_err                 ; yes, return error

        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBWFP_Start    ; (ds:si) = pipe name
                                        ;72417 end
        call    CheckPipeDir            ; proper pipe name?
        mov     ax,ERROR_PATH_NOT_FOUND
        jnz     npm_err                 ; no, give error
;
;       See if there is an existing instance of the pipe
;
        call    findpipe
        jc      npm25                   ; none currently exist
        mov     bx,es:[di.np_namkey]    ; (bx) = npn struct key value
        mov     ax,word ptr es:[di.np_timeo].loword     ; revise timeout
        mov     word ptr npm_tmo.loword,ax
        mov     ax,word ptr es:[di.np_timeo].hiword
        mov     word ptr npm_tmo.hiword,ax
        mov     cl,byte ptr es:[di.np_pipmod]
        mov     byte ptr npm_pmd,cl     ; revise instance count
        cmp     cl,255
        je      npm3                    ; no limit on instances
        xor     ch,ch                   ; cx = maximum instance count
        dec     cx                      ; count instance we already found
        jcxz    npm2                    ; ran out of allowed instances
npm1:   call    get_next_instance       ; find next instance
        jc      npm3
        loop    npm1
npm2:   mov     ax,ERROR_PIPE_BUSY
npm_err:                                ; too many instances of pipe
        stc
        jmp     npmx

;*      New pipe name.  Need to allocate a new name structure.
;
;       (DS:SI) = pipe name

npm25:  call    get_free_npn            ; (bx) = named pipe name struct key
        jc      npm_err                 ; couldn't find one, AX already set

;*      Allowable instance of existing pipe
;
;       (DS:SI) = pipe name
;       (BX) = npn key value

npm3:   call    get_free_np             ; find a free structure
        jc      npm_err                 ; couldn't find one, AX already set

        mov     npm_np.Offst,di         ; save the np offset
        mov     npm_np.Segmt,es         ; save the np segment

        mov     es:[di.np_refcnt],1     ; claim it
        mov     ax,npm_pmd
        mov     es:[di.np_pipmod],ax

        mov     ax,npm_tmo.loword       ; save timeout in np struct
        mov     es:[di.np_timeo].loword,ax
        mov     ax,npm_tmo.hiword
        mov     es:[di.np_timeo].hiword,ax

        xor     ax,ax
        mov     es:[di.np_selector1],ax         ; zero buffer selectors
        mov     es:[di.np_selector2],ax
        mov     es:[di.np_ssem.loword],ax       ; zero user semaphore handles
        mov     es:[di.np_ssem.hiword],ax
        mov     es:[di.np_csem.loword],ax
        mov     es:[di.np_csem.hiword],ax
        mov     es:[di.np_csft.Offst],ax        ; clear client sft pointer
        mov     es:[di.np_csft.Segmt],ax        ;

;*      Allocate data buffers for incoming and outgoing data
;
;       (es:di) = pointer to named pipe structure

        mov     bx,npm_size2            ; (bx) = incoming buffer size

        call    npm_memalloc            ; alloc the memory
        jc      npm5                    ; alloc failed, return error
        mov     es:[di.np_selector2],ax

        mov     bx,npm_size1            ; (bx) = outgoing buffer size
        call    npm_memalloc            ; alloc the memory
        jc      npm5                    ; alloc failed, return error
        mov     es:[di.np_selector1],ax

;*      Find a free area in the user's JFN table.

        CALLFAR JFNFindFree             ; get a free JFN
        jc      npm5                    ; No JFNs, go return error

        mov     npm_pJFN.Offst,di       ; save the JFN offset
        mov     npm_pJFN.Segmt,es       ; save the JFN segment
        mov     npm_JFN,bx              ; save the JFN itself

        mov     ax,npm_omd                      ; get open mode
    .errnz      (open_no_inherit SHL 1)-(JFN_DIH SHL 8)
    .errnz      (open_write_through SHL 1)-(JFN_WRTTHR SHL 8)
        shl     ax,1                    ; put relevant bits in ah
        and     ah,(JFN_DIH+JFN_WRTTHR) ; mask relevant bits
        les     di,JFN_Flg_Ptr          ; get pointer to JFN flags
        mov     byte ptr es:[di+bx],ah  ; set no inherit/writethrough bits

;*      Grab a free SFT and mark it as busy.

        CALLFAR SFNAlloc                ; get a free SFN
        jc      npm4                    ; oops, no free SFT's

;*      The SFT was successfully opened.  Remove busy mark.  Fill in JFN.
;       Make JFN_Table[JFN] -> SFN, since SFT is now valid, and thus safe.

        SaveReg <es,di>
        les     di,npm_pJFN
        mov     es:[di],bx              ; set SFN, it's safe now
        RestoreReg <si,ds>              ; (ds:si) = pointer to SFT
    assume      ds:nothing

        mov     [si].sf_ref_count,1
        jmp     short npm8

npm4:   lds     si,npm_pJFN
        mov     word ptr [si],SFN_NULL  ; free the SFN...

npm5:   les     di,npm_np               ; point to named pipe structure
        push    ax                      ; save error code
        call    npcl_dealloc_all        ; free pipe buffers and data struct
        pop     ax
        jmp     npm_err


;*      Have all the data structures we need.  Finish init.
;       Nothing can fail now.
;
;       (DS:SI) = pointer to SFT

npm8:   les     di,npm_np               ; (es:di) = named_pipe structure
        mov     ds:[si.sf_np.Offst],di
        mov     ds:[si.sf_np.Segmt],es
        mov     es:[di.np_ssft.Offst],si        ; save back pointer to SFT
        mov     es:[di.np_ssft.Segmt],ds
        mov     ax,es:[di.np_pipmod]    ; get pipe mode
        xor     al,al                   ; clear icount bits
        or      ax,NP_SERVER
        mov     ds:[si.sf_pipmod],ax
        or      ds:[si.sf_flags],SF_PIPE + SF_NMPIPE + DEVID_FILE_CLEAN
        mov     ax,npm_omd
        mov     bx,ax
        and     bx,open_access
        or      al,ShareMode[bx]        ; fill in sharing mode from table
        mov     ds:[si.sf_fsi.sfi_mode],ax
        mov     ds:[si.sf_devptr.Offst],0
        mov     ds:[si.sf_devptr.Segmt],0

        mov     ds:[si].sf_FSC.Offst,FSC_LOCAL ; mark named pipe as local
        mov     ds:[si].sf_FSC.Segmt,0

        mov     es:[di.np_scnt],1
        mov     es:[di.np_ccnt],0
;
;       Initialize the data buffers
;
        mov     si,es:[di.np_selector2] ; get incoming data buffer selector
        mov     dl,0
        call    npcr_initbuf
        mov     si,es:[di.np_selector1] ; get outgoing data buffer selector
        mov     dl,1
        call    npcr_initbuf

        mov     es:[di.np_state],NP_DISCONNECTED
        mov     es:[di.np_flags],0
        mov     ax,npm_JFN              ; (AX) = handle
        mov     bx,ax                   ; (BX) = handle

        clc
;### PTR B720732 npmx:  mov     SS:[ptda_pOpenBuf],0    ; Clear ptr to openbuf
;### PTR B720732        mov     SS:[ptda_pCDS],0
npmx:   LeaveProc
        CodeUnlock      NMPIPE_GROUP
        ret
EndProc w_MakeNmPipe



;**     npm_chk_buf_size - check/adjust data buffer size specification
;
;       ENTRY   AL = low bit set if this buffer needed
;               CX = user-specified buffer size
;
;       EXIT    CX = adjusted buffer size
;
;       USES    CX, Flags

Procedure       npm_chk_buf_size,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        test    al,1                    ; buffer needed?
        jz      ncbs5                   ; no, set size to zero
        cmp     cx,MINBUFSIZE           ; buffer big enough?
        jb      ncbs2                   ; no
        cmp     cx,MAXBUFSIZE           ; buffer too big?
        jb      ncbsx                   ; no, just right
        mov     cx,MAXBUFSIZE
ncbsx:  ret

ncbs2:  mov     cx,DEFBUFSIZE           ; set the default size
        jmp     ncbsx

ncbs5:  mov     cx,0                    ; indicate no buffer needed
        jmp     ncbsx
EndProc npm_chk_buf_size


;**     npm_memalloc - allocate memory for a pipe buffer
;
;       The named pipe buffers will be allocated from the kernel heap.
;
;       ENTRY   BX = size of block to allocate
;
;       EXIT    C set - allocation failed
;                       AX = error code
;               C clear - allocation okay (or not needed)
;                       AX = selector (or zero)
;
;       USES    EAX, EBX, ECX, EDX, SI, Flags

Procedure       npm_memalloc,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

LocalVar npma_ac,<size vmac_s>

        EnterProc
        SaveReg <ds,es,di>

        xor     ax,ax
        or      bx,bx                   ; size == 0? if so, don't alloc
                                        ; NOTE carry cleared by or
        jz      npmax
        cmp     bx,MSG_HDR_LEN+1        ; below minimum size?
        jae     npma1                   ; no
        mov     bx,MSG_HDR_LEN+1        ; set minimum

;*      Compute actual size of buffer needed.  Add the size of the
;       control structure and an extra byte to account for the fact
;       that when the buffer is full, one byte is wasted to allow
;       distinguishing between buffer full and empty conditions.
;
;       (bx) = user's desired size of buffer.

npma1:  add     bx,SIZE npb+1           ; account for overhead
        jnc     npma2                   ; no overflow, seg is not too big
        mov     bx,0FFFFh               ; (bx) = max. segment size

;       _SELAllocKHB(BUFFERHEAP,bx,0,0,FSNPIPEBUFOWNER,pAC)

npma2:  CpuMode 386
        FlatContext <ds,es>,cx
        SSToDS  ecx,[npma_ac]
        movzx   ebx,bx
        CCALL32 _SELAllocKHB,<BUFFERHEAP,ebx,PG_CONTIG,0,FSNPIPEBUFOWNER,ecx>
        ASSUME  DS:NOTHING,ES:NOTHING
        or      eax,eax
        jnz     short npmae

        mov     dx,[npma_ac].ac_sel     ; dx = selector for buffer
        mov     ds,dx                   ; ds = selector for buffer
        mov     es,dx                   ; es = selector for buffer

; zero-init the buffer's header fields

        .errnz (size npb - 22)          ; Fix PTR B725523   @ChinhDo
        mov     cx,5
        xor     di,di                   ; es:di -> buffer header
        rep     stosd                   ; zero-init buffer header
        CpuMode 286

        mov     ax,SIZE npb
        mov     ds:[npb_first],ax       ; init. circular buffer pointers
        mov     ds:[npb_in],ax
        mov     ds:[npb_out],ax
        mov     ds:[npb_last],bx
        mov     ds:[npb_rdsem],1        ; readers must wait for data

;*      Other fields have been zero-inited.

        mov     ax,dx                   ; ax = buffer's segment selector
        clc
        jmp     short npmax

npmae:  mov     ax,ERROR_NOT_ENOUGH_MEMORY
        stc

npmax:  RestoreReg <di,es,ds>
        LeaveProc
        ret
EndProc npm_memalloc

;**     npcr_initbuf - bump ref. count on a pipe buffer
;
;       ENTRY   SI = buffer's segment selector
;               DL = 0 if reader of buffer, 1 if writer
;
;       EXIT    NONE
;
;       USES    AX, BX, CX, DX, SI, DS, Flags

Procedure       npcr_initbuf,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        or      si,si                   ; buffer allocated?
        jz      npcix                   ; no, nothing to do
        mov     ds,si                   ; ds:0 -> buffer
        or      dl,dl                   ; incoming or outgoing?
        jnz     npci3                   ; outgoing
        inc     ds:[npb_rdcnt]          ; one more reader of pipe
        jmp     short npcix
npci3:  inc     ds:[npb_wtcnt]          ; one more writer
npcix:  ret
EndProc npcr_initbuf


Break   <Named pipe client connect, disconnect>

;***    w_ConnectNmPipe -- allow connection to a named pipe
;
;       Allow clients to open a named pipe, or check on the
;       open status of a named pipe.  Set the pipe state to
;       LISTENING if appropriate.
;
;       ENTRY   (dx)    = pipe file handle
;
;       EXIT    CARRY CLEAR
;                       no error, pipe in LISTENING state
;               CARRY SET
;                       AX = error code
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags
;
;   Psuedocode:
;
;       w_ConnectNmPipe() {
;           if (local pipe) {
;               if (! (sf_pipmod & SERVER))
;                       return ERROR_INVALID_FUNCTION
;               np = sf_np
;
;               while ((s = np_state) != CONNECTED)
;                       if (s == CLOSING)
;                               return ERROR_CLOSING
;                       if (s == DISCONNECTED)
;                               np_state = LISTENING
;                       if (sf_pipmod & NBLK)
;                               return ERROR_NOT_CONNECTED
;                       ASSERT(np_state == LISTENING ||
;                              np_state == HALF_OPENED)
;                       ProcBlock(np_state)
;               return 0
;           } else
;               pass request to UNC FSD
;       }

        ASSUME  DS:NOTHING,ES:NOTHING
Procedure       w_ConnectNmPipe,FARONLY
LocalVar npc_req,<size npc_param>

        CodeLock NMPIPE_GROUP

        call    np_handle_chk           ; es:di -> sft
        jc      npcoe
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL ; local or remote?
        je      npco1                   ; local

; not local, must be FSD named pipe

        EnterProc
        mov     npc_req.npc_len,size npc_param
        mov     npc_req.npc_dlen,0
        lea     ax,es:[di].sf_fsi       ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to sf_fsi
        lea     ax,es:[di].sf_fsd       ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to sf_fsd
        push    NMP_ConnectPipe         ; Op code
        lea     ax,npc_req              ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        xor     ax,ax                   ; ax = null pointer
        push    ax
        push    ax                      ; push null pData
        push    ax
        push    ax                      ; push null pName
        les     di,es:[di].sf_FSC       ; es:di -> fsc
        call    es:[di].FS_NMPIPE
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret

npco1:  call    check_server_mode       ; ds:si -> sft, es:di -> np
        jc      npcoe

        mov     al,es:[di.np_state]     ; (al) = pipe state
        and     al,NP_STATE_MASK        ; mask internal states
        cmp     al,NP_CLOSING           ; client closed pipe?
        jne     npco2                   ; no
        mov     ax,ERROR_BROKEN_PIPE    ; (ax) = error code
        jmp     short npcoe

npco2:  cmp     al,NP_CONNECTED         ; connected now?
        je      npcox                   ; yes, return
        cmp     al,NP_CLOSING           ; client closed pipe?
        jne     npco4                   ; no
        jmp     short npcox             ; yes, return success

npco4:  cmp     al,NP_DISCONNECTED      ; disconnected state?
        jne     npco6                   ; no

;*      Set pipe state to listening.  Waken anyone blocked in w_WaitNmPipe.

        mov     al,NP_LISTENING
        mov     es:[di.np_state],al     ; set state to LISTENING

        SaveReg <ax,dx>
        mov     ax,es
        mov     bx,es:[di.np_namkey]    ; (ax:bx) = address of npn struct
        CALLFAR ProcRun
        RestoreReg <dx,ax>

npco6:  test    dx,NP_NBLK              ; non-blocking call?
        je      npco8                   ; no, must wait for state change
        mov     ax,ERROR_PIPE_NOT_CONNECTED
        jmp     short npcoe
npco8:

IFDEF PIPESTRICT
        cmp     al,NP_LISTENING         ;; Assert(np_state == LISTENING)
        jne     npco_ier
ENDIF

        SaveReg <dx,di,es>              ; save loop variables
        mov     ax,es
        lea     bx,es:[di.np_state]     ; ax:bx = event ID
        mov     cx,-1
;### B725255 SaveReg <di>
        mov     di,cx                   ; di:cx = timeout (never)
        mov     dh,0                    ; dh = interruptible sleep
        CALLFAR ProcBlock
;### B725255    RestoreReg <di>
;### B725255    SaveReg <ds>
;### B725255    pushf
;### B725255    mov     bx,es
;### B725255    mov     ds,bx
;### B725255    lea     bx,ds:[di.np_semconnect]; tell whoever ran us that we
;### B725255    call    np_sem_sig              ; woke up.
;### B725255    popf
;### B725255    RestoreReg <ds>
        RestoreReg <es,di,dx>
        jc      npco9                   ; see if state changed

        mov     al,es:[di.np_state]     ; (al) = pipe state
        and     al,NP_STATE_MASK        ; mask internal states
        mov     dx,ds:[si.sf_pipmod]    ; 89862 (dx) = pipe mode
        jmp     short npco2             ; see if state changed

npco9:  mov     ax,ERROR_INTERRUPT

npcoe:  stc                             ; error return
        CodeUnlock NMPIPE_GROUP
        ret

npcox:  clc                             ; non-error return
        CodeUnlock NMPIPE_GROUP
        ret

IFDEF PIPESTRICT
npco_ier:
        int     3
        mov     ax,ERROR_PIPE_NOT_CONNECTED
        CodeUnlock NMPIPE_GROUP
        ret
ENDIF
EndProc         w_ConnectNmPipe


;***    w_DisconnectNmPipe -- disconnect a named pipe
;
;       Force clients off a pipe and allow new opens.  Set the
;       pipe state to DISCONNECTED.
;
;       ENTRY   (dx)    = pipe file handle
;
;       EXIT
;
;       USES
;
;   Pseudocode:
;
;       w_DisconnectNmPipe() {
;               if (! (sf_pipmod & SERVER))
;                       return ERROR_INVALID_FUNCTION
;               np = sf_np
;               if (np_ccnt)
;                       np_csft.sf_pipmod |= DISCONNECTED
;                       np_csft = 0
;                       np_ccnt = 0
;                       np_refcnt--
;               ASSERT(np_refcnt == 1)
;               np_state = DISCONNECTED
;               flushpipe(np_selector2, DISCONNECT)
;               flushpipe(np_selector1, DISCONNECT)
;       }


Procedure       w_DisconnectNmPipe,FARONLY
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar npd_req,<size npd_param>

        CodeLock NMPIPE_GROUP

        EnterProc
        call    np_handle_chk           ; es:di -> sft
        LJC     npdx
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL ; local or remote?
        je      npd2                    ; local

; not local, must be FSD named pipe

        SaveReg <es>
        mov     npd_req.npd_len,size npd_param
        mov     npd_req.npd_dlen,0
        lea     ax,es:[di].sf_fsi       ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to sf_fsi
        lea     ax,es:[di].sf_fsd       ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to sf_fsd
        push    NMP_DisconnectPipe      ; Op code
        lea     ax,npd_req              ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        xor     ax,ax                   ; ax = null pointer
        push    ax
        push    ax                      ; push null pData
        push    ax
        push    ax                      ; push null pName
        lds     si,es:[di].sf_FSC       ; ds:si -> fsc
        call    ds:[si].FS_NMPIPE
        RestoreReg <es>
        cmp     ax,ERROR_INVALID_FSD_NAME       ; undo serving redirection?
        jne     npd1                    ; no, it's a bona-fide error
        cmp     es:[di].sf_ref_count,0  ; 111380 - is pipe still around?
        je      npd0                    ; 111380 - no, get out
        mov     es:[di].sf_FSC.Offst,FSC_LOCAL  ; reset to local FSC
        and     es:[di].sf_flags2,(not SF_SRVRDR)
        jmp     short npd2              ; do common stuff

npd0:   xor     ax,ax                   ; 111380 - pipe was closed, so
        clc                             ; 111380   exit and return no
        jmp     short npdx              ; 111380   error

npd1:   xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        jmp     short npdx

; local named pipe

npd2:   call    check_server_mode       ; dx = pmode, es:di = np
        jc      npdx

        test    es:[di.np_ccnt],-1      ; client attached?
        je      npd3                    ; no
        lds     si,es:[di.np_csft]      ; get ptr to client SFT
    assume      ds:nothing
        or      ds:[si.sf_pipmod],NP_SDISCON ; set disconnected state
        mov     ds:[si].sf_FSC.Offst,FSC_LOCAL  ; reset to local FSC
        xor     ax,ax
        mov     es:[di.np_csft.Offst],ax        ; clear SFT pointer
        mov     es:[di.np_csft.Segmt],ax
        mov     es:[di.np_ccnt],al      ; clear client count (assumed = 1)
        dec     es:[di.np_refcnt]       ; decrement reference count
npd3:
IFDEF PIPESTRICT                             ;@73173   if state is not CLOSING
        cmp     es:[di.np_state], NP_CLOSING ;we need to bypass the checking of
        jnz     npd4                         ;ref count due to a racing condition
        cmp     es:[di.np_refcnt],1      ;; Assert (np_refcnt == 1)
        je      npd4
        InternalError <w_DisconnectNmPipe: refcount not 1>
npd4:
ENDIF
        mov     es:[di.np_state],NP_DISCONNECTED
        and     es:[di.np_flags],(not NP_SRVRDR)
        mov     si,es:[di.np_selector2]
        mov     dl,FP_DISCONW
        call    flushpipe               ; flush incoming pipe buffer
        mov     si,es:[di.np_selector1]
        mov     dl,FP_DISCONR
        call    flushpipe               ; flush outgoing pipe buffer
        lea     bx,es:[di.np_csem]      ; (es:bx) = pointer to client sem handle
        call    np_sem_clear
        call    snps_sem_deref          ; get rid of the semaphore
        clc
npdx:   LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret

EndProc         w_DisconnectNmPipe


;***    w_WaitNmPipe - Wait for an available named pipe instance
;
;       Check to see if the pipe name passed exists.  If so,
;       check to see if any pipe instances are in the LISTENING
;       state.  If not, block until some later time when the
;       pipe might be available.
;
;       ENTRY   (ds:dx) = pointer to file name buffer
;               (di:si) = timeout
;
;       EXIT    C flag set if error
;                       (ax) = error code
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags
;

        ASSUME  DS:NOTHING,ES:NOTHING
Procedure w_WaitNmPipe,<hybrid,far16>
LocalVar        wnp_name,DWORD
LocalVar        wnp_timeo,DWORD
LocalVar        wnp_req,<size npq_param>
LocalVar        wnp_openbuf,PMPATHLEN
LocalVar        wnp_dummycds, <size curdir>
LocalVar        wnp_dummy,WORD          ; KLUDGE: see gshare.asm

        CodeLock        NMPIPE_GROUP
        EnterProc       <ds,dx, di,si>  ; initialize wnp_timeo

        CALLFAR Take_Kernel_Sem         ; Take the FS serialization sem
        LJC     wnpx

        mov     bx,CurrTCB              ;### PTR B720732
        lea     di,wnp_dummycds
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,di
        lea     di,wnp_openbuf          ; appropriate buffer
        mov     TASKAREA:[bx].TCBpOpenBuf,di            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],di ; Initialize ptda_pOpenBuf
        push    bp                      ; save frame pointer
        mov     si,dx                   ; (ds:si) = pipe name
;### PTR B720732        mov     bx,CurrTCB
        push    word ptr TASKAREA:[bx].TCBNewFlags
        or      TASKAREA:[bx].TCBNewFlags,TCBNEWFILES ; enable NEWFILES
        CALLFAR TransPath               ; convert the path
        mov     bx,CurrTCB
        pop     word ptr TASKAREA:[bx].TCBNewFlags
    ASSUME   DS:TASKAREA
        pop     bp                      ; restore frame pointer
        jc      wnpe

; Changed 'cmp' to 'test' and subsequent 'jz' to 'jnz'.                  75228
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_NMPIPE ; is it a local named pipe? 75228
        UnSetBP_CurrTCB
        jnz     wnp2                    ; yes, go do it                  75228

        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_UNC ; UNC name?                    75228
        UnSetBP_CurrTCB
                                        ;72417 end
        jnz     wnp1                    ; yes, send it remote            75228

        mov     ax,ERROR_BAD_PIPE
wnpe:   CALLFAR Clear_Kernel_Sem        ; release the FS serialization sem
        stc
        jmp     short wnpx

; callout to UNC FSD. First set up request packet

wnp1:   mov     wnp_req.npq_len,size npq_param
        mov     wnp_req.npq_dlen,0
        mov     ax,wnp_timeo.loword
        mov     wnp_req.npq_timeo.loword,ax
        mov     ax,wnp_timeo.hiword
        mov     wnp_req.npq_timeo.hiword,ax
        mov     dl,ss:[bx].TCBPriLevel
        mov     dh,ss:[bx].TCBPriClass
        mov     wnp_req.npq_prio,dx     ; get priority of running thread.

; call FSD

        xor     ax,ax                   ; ax = null pointer
        push    ax
        push    ax                      ; push null sf_fsi pointer
        push    ax
        push    ax                      ; push null sf_fsd pointer
        push    NMP_WAITPIPE            ; Op code
        lea     ax,wnp_req              ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        xor     ax,ax                   ; ax = null pointer
        push    ax
        push    ax                      ; push null pData
        push    ss
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        push    TASKAREA:[si].TCBWFP_Start  ; push pointer to pipe name
                                        ;72417 end
        lds     si,ss:[bx].TCBThisFSC   ; Get FSC pointer determined by
    assume      ds:nothing              ; TransUNC
        call    ds:[si].FS_NMPIPE
        CALLFAR Clear_Kernel_Sem        ; release the FS serialization sem
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        jmp     short wnpx              ; C set as appropriate

; process local named pipe

wnp2:   call    CopyName                ; es:di -> temp buffer
        SaveReg <es,di>                 ; save temp buffer pointer
        CALLFAR Clear_Kernel_Sem        ; release the FS serialization sem
        mov     cx,wnp_timeol
        mov     di,wnp_timeoh           ; (di:cx) = user-specified timeout
        RestoreReg <si,ds>              ; (ds:si) = pipe name
        SaveReg <ds,si>                 ; save temp buffer address
        call    wnp_sub                 ; try to find a pipe in listen state
        RestoreReg <di, es>             ; es:di = temp buffer address
        pushf
        CALLFAR FreeTempBuffer          ; free tempbuffer
        popf

;### PTR B720732 wnpx:  mov     SS:[ptda_pOpenBuf],0    ; Clear ptr to openbuf
wnpx:   LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret

EndProc w_WaitNmPipe

;***    CopyName - copy name from WFP_Start to temp buffer
;
;       ENTER   WFP_Start contains full path
;
;       EXIT    ES:DI -> tempbuffer containing full path
;               DS = TASKAREA
;
Procedure CopyName,NEAR,LOCAL
        ASSUME  DS:NOTHING,ES:NOTHING

        CALLFAR GetTempBuffer       ; es:di -> buffer
        SaveReg <di>
        TaskContext <ds>,si
                                    ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBWFP_Start  ; (ds:si) = pipe name
                                    ;72417 end
        mov     cx,MAXPATHLEN
        shr     cx,1                ; convert to words + carry if odd
        rep movsw                   ; move words (even number of bytes)
        jnc     cn10                ; even number of bytes, all done
        movsb                       ; move last, odd byte
cn10:   RestoreReg <di>
        ret

EndProc CopyName

;**     wnp_sub - helper subroutine for WaitNmPipe and CallNmPipe
;
;       The guts of WaitNmPipe.  Tries to find a pipe instance in
;       the listen state.  If none is currently available, it will
;       wait for one to become available.
;
;       (ds:si) = pointer to pipe name
;       (di:cx) = timeout value

Procedure       wnp_sub,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        wnps_timeo,DWORD
        EnterProc <di,cx>

;*      See if there is an existing instance of the pipe
;
;       (ds:si) = pointer to pipe name

        call    findpipe
        jc      wnpse                   ; none currently exist
        mov     bx,es:[di.np_namkey]    ; (es:bx) = procblock key

; NOTE - the following variables may be removed from the stack by the
;        LeaveProc.  There are no matching pops in some execution paths.
        push    es:[di.np_timeo].hiword
        push    es:[di.np_timeo].loword ; save default timeout
        push    es                      ; save npn struct segment

wnps4:  cmp     es:[di.np_state],NP_LISTENING   ; ready for open?
        je      wnps9                   ; yes, we got our struct
        call    get_next_instance       ; find next instance of pipe
        jnc     wnps4                   ; see if it matches

        pop     ax                      ; (ax:bx) = procblock key
        mov     cx,wnps_timeol
        mov     di,wnps_timeoh          ; (di:cx) = timeout interval
        mov     dx,cx                   ; check to see if default timeout needed
        or      dx,di
        jne     wnps5
        pop     cx
        pop     di                      ; (di:cx) = default timeout

wnps5:  mov     dh,0                    ; (dh) = interruptible procblock
        CALLFAR ProcBlock
        jnc     wnps9                   ; normal wakeup
        mov     ax,ERROR_INTERRUPT      ; assume interrupted
        jnz     wnpse
        mov     ax,ERROR_SEM_TIMEOUT

wnpse:  stc
        jmp     short wnpsx

wnps9:  clc                             ; success!

wnpsx:  LeaveProc
        ret
EndProc wnp_sub


;***    check_server_mode - check SFT type and mode to assure server end of pipe
;
;       The SFT flags are checked to assure that the file refers to
;       named pipe.  If so, the pipe mode is checked to see if it is
;       the server end of the pipe.  Note that remote named pipes always
;       refer to the client end of the pipe (except pipes for which
;       serving pipe redirection is done).
;
;       ENTRY   (ES:DI) point to SFT
;
;       EXIT    (DS:SI) point to SFT
;               Carry clear, pipe is okay
;                       (ES:DI) point to named_pipe struct
;                       (DX) =  pipe mode
;               Carry set, not a server pipe
;                       (AX) =  error coe
;
;       USES    AX, DX, SI, DI, DS, ES, Flags
;
Procedure       check_server_mode,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL  ; local named pipe?
        jne     csm_eif                         ; not local

; local named pipe

        mov     dx,es:[di.sf_flags]             ; (dx) = sft flags
        and     dx,SF_PIPE+SF_NMPIPE
        cmp     dx,SF_PIPE+SF_NMPIPE            ; really local named pipe?
        jne     csmie                           ; no, strange pipe

; good local named pipe

        mov     dx,es:[di.sf_pipmod]            ; get pipe mode
        test    dx,NP_SERVER
        jz      csm_ebp
        lds     si,es:[di.sf_np]
        CALLFAR XCHGP                           ; swap DS:SI and ES:DI
        clc
csmx:   ret

csm_ebp: mov    ax,ERROR_BAD_PIPE
csm_ec: stc
        jmp     SHORT csmx

csm_eif: mov    ax,ERROR_INVALID_FUNCTION
        jmp     SHORT csm_ec

csmie:  InternalError <check_server_mode: bad pipe>

EndProc         check_server_mode


;***    flushpipe - close one end of a pipe
;
;       FlushPipe will close or disconnect one end of a pipe.  Reader
;       or write counts are adjusted, waiting readers and writers are
;       awoken and the process will wait until readers or writers are
;       clear of one of the lock semaphores.
;
;       ENTRY   SI =    pipe data buffer's segment selector
;               ES:DI   point to named_pipe struct
;               DL =    command code, one of FP_READER, FP_WRITER, FP_DISCONR,
;                               FP_DISCONW
;
;       EXIT    data in pipe flushed, reader/writer count adjusted,
;               waiting writers/readers awoken, process waits until
;               readers and writers are clear of lock semaphore.
;
;       USES    AX, DX, SI, DS, Flags
;
Procedure       flushpipe,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        or      si,si                   ; handle null?
        jz      fpx                     ; yes, nothing to do

        SaveReg <BX,DI>
        mov     ds,si                   ; (ds:0) point to pipe data buffer
        test    dl,FP_READER            ; flushing reader side?
        jz      fp5                     ; no
        lea     bx,ds:[npb_rdlck]       ; (bx) = wait for read lock
        mov     al,0
        test    dl,FP_DISCON            ; disconnecting all?
        jnz     fp2                     ; yes
        mov     al,ds:[npb_rdcnt]
        dec     al
fp2:    mov     ds:[npb_rdcnt],al       ; save new count
        test    al,al                   ; last reader?
        jnz     fp9                     ; no, we're done
        jmp     short fp8

fp5:    mov     al,0
        lea     bx,ds:[npb_wtlck]       ; (bx) = wait for write lock
        test    dl,FP_DISCON            ; disconnecting all?
        jnz     fp7                     ; yes
        mov     al,ds:[npb_wtcnt]
        dec     al
fp7:    mov     ds:[npb_wtcnt],al       ; save new count
        test    dl,FP_DISCON            ; disconnecting?
        jz      fp9                     ; no, retain any buffered data

;*      Flush data remaining in the buffer.

fp8:    xor     si,si                   ; ds:si -> pipe buffer
        call    rd_chk_avail
        mov     ds:[npb_data],ax        ; remaining unread data in pipe
        mov     ax,ds:[npb_first]       ; toss any data
        mov     ds:[npb_in],ax
        mov     ds:[npb_out],ax

;*      Awaken both readers and writers always.  Closing or
;       disconnecting a pipe will generally cause any I/O in
;       progress to be terminated.
;
;       (ds:bx) = semaphore to acquire after waking other threads

fp9:    push    bx
        lea     bx,ds:[npb_wtsem]       ; ds:bx = npb_wtsem
        call    np_sem_sig
        lea     bx,ds:[npb_rdsem]       ; ds:bx = npb_rdsem
        call    np_sem_sig
        pop     bx
; Defect 79822 made it possible for np_sem_wait to return with
; ds=0 and carry flag set if ds selector was no longer valid. Since the
; first thing that np_sem_sig does is reference ds, we should avoid this
; call. This is a hack which fixes this immediate problem...anyone that
; calls np_sem_wait should really be making this same check. The real
; problem is in the serialization which was never fixed in 79822, but
; worked around instead.                                               88284
fp10:                                   ;                              88284
        call    np_sem_wait             ; wait for lock
        jnc     short fp11              ; no errors.                   88284
        push    ax                      ; save ax                      88284
        mov     ax,ds                   ;                              88284
        or      ax,ax                   ; sem freed?                   88284
        pop     ax                      ; restore ax                   88284
        jnz     short fp10              ; not freed, wait again.       88284
        jmp     short fp13              ; skip over call to np_sem_sig 88284
fp11:                                   ;                              88284
        call    np_sem_sig
fp13:                                   ;                              88284
        RestoreReg <DI,BX>
fpx:    ret

EndProc         flushpipe



;***    NmPipeOpen -- client connection to a named pipe
;
;       Open a pipe which is in the LISTENING state.
;
;       ENTRY   WFP_Start       Points to complete pathname
;               SS:BX -> Parameter frame on stack (See Open3Struc, argframe.inc)
;               TCBThisSFT Points to SFT to be filled
;                               in if file created
;                       sf_fsi.sfi_mode field set
;                       sf_flags field set
;               CX = open flags
;
;       EXIT    sf_ref_count is NOT altered
;               (DS) is TASKAREA
;               CARRY CLEAR
;                   ThisSFT filled in
;                   cx = action taken
;               CARRY SET
;                   (AX) is error code
;
;       USES    AX, BX, CX, DX, SI, DI, ES, Flags
;

Procedure       NmPipeOpen,hybrid
        SegAssert ss,<ds>,"NmPipeOpen"
        ASSUME  ES:NOTHING

        LocalVar        npo_parms,DWORD ; open parameter block
        LocalVar        npo_fgenflag,WORD ; == 1 if there are need eas otherwise 0
                                          ; always 0 for named pipes
        LocalVar        npo_ActionTaken,WORD
        LocalVar        npo_UncFsc,DWORD ; temp holder of Unc Fsc pointer

        CodeLock        NMPIPE_GROUP

        EnterProc       <ss,bx,0>

        cmp     cx,nexist_create + exist_fail ; create only if non-existant?
        mov     ax,ERROR_OPEN_FAILED
        je      npo_err                 ; yes, fail the open

; set default action code

        mov     ax,file_replace
        test    cx,exist_replace
        jnz     npo0
        mov     ax,file_exist

npo0:   mov     npo_ActionTaken,ax
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        mov     si,TASKAREA:[si].TCBWFP_Start
                                        ;72417 end
        call    findpipe                ; find first instance
    assume      ds:nothing
        jc      npo_err                 ; if C set, (ax) = ERROR_PATH_NOT_FOUND
npo1:   cmp     es:[di.np_state],NP_LISTENING   ; ready for open?
        je      npo2                    ; yes, we got our struct
        call    get_next_instance       ; find next instance of pipe
        jnc     npo1                    ; see if it matches
        mov     ax,ERROR_PIPE_BUSY      ; return error
npo_err:
        stc
        jmp     npox

;**     Found a suitable instance of a pipe.  See if open mode is okay.
;       The following code depends on the bit encoding of the open mode
;       field; if incremented, it becomes a two bit field, one bit indicating
;       the desire to read, the other indicating write.
;
;       (es:di) = NP structure

npo2:
    assume      ds:nothing

    .errnz      (open_for_read-0)
    .errnz      (open_for_write-1)
    .errnz      (open_for_both-2)
        mov     si,CurrTCB
        lds     si,ss:[si.TCBThisSFT]   ; (ds:si) = SFT
        mov     ax,ds:[si.sf_fsi.sfi_mode]      ; get access mode
        and     al,open_access
        cmp     al,open_for_both        ; one of expected access modes?
        ja      npo_ead                 ; no
        inc     al
        test    al,(open_for_read+1)    ; want to read?
        jz      npo4                    ; no
        test    es:[di.np_selector1],-1 ; readable buffer available?
        jne     npo4                    ; yes, no problem
npo_ead:
        mov     ax,ERROR_ACCESS_DENIED
        jmp     npo_err

npo4:   test    al,(open_for_write+1)   ; want to write?
        jz      npo5                    ; no
        test    es:[di.np_selector2],-1 ; writable buffer available?
        je      npo_ead                 ; yes, no problem

npo5:   mov     ds:[si.sf_np.Offst],di          ; save pointer to pipe
        mov     ds:[si.sf_np.Segmt],es
        mov     es:[di.np_csft.Offst],si        ; save back pointer to SFT
        mov     es:[di.np_csft.Segmt],ds

        mov     ax,es:[di.np_pipmod]    ; get pipe mode
        and     ax,NOT (NP_NBLK or NP_SERVER or NP_ICOUNT) ; Don't clear Read Message Flag! @DB73969
        mov     ds:[si.sf_pipmod],ax
        or      ds:[si.sf_flags],SF_PIPE + SF_NMPIPE + DEVID_FILE_CLEAN
        mov     ds:[si.sf_devptr.Offst],0
        mov     ds:[si.sf_devptr.Segmt],0

;       TCBOpCookie contains server per-file cookie.
;       Save the file server's cookie in new SFT.

        mov     bx,CurrTCB
        mov     ax,TASKAREA:[bx].TCBOpCookie.loword ; (AX) = server's cookie
        mov     ds:[si].sf_cookie.loword,ax ; save cookie in SFT
        mov     ax,TASKAREA:[bx].TCBOpCookie.hiword ; (AX) = server's cookie
        mov     ds:[si].sf_cookie.hiword,ax ; save cookie in SFT

        mov     ds:[si].sf_FSC.Offst,FSC_LOCAL ; mark pipe as local
        mov     ds:[si].sf_FSC.Segmt,0

        inc     es:[di.np_ccnt]         ; add a reference
        inc     es:[di.np_refcnt]

;       Set the pipe state to something other than LISTENING and CONNECTED
;       while the pipe open completes.  Some of the operations below could
;       block.

        mov     es:[di.np_state],NP_LISTEN2

;       Check to see if serving side pipe redirection is being requested.
;       If so, call the UNC FSD to validate the open and do its processing.

        test    TASKAREA:[bx].TCBfSharing,-1 ; do serving side pipe redir?
        LJZ     npo8                            ; no
;;; Added code to find the IBM/Microsoft UNC FSD so it can do remote pipe open
;;; 11/14/91 Phil Doragh
        push    ds                            ; Save contents of ds

        DosContext <ds>                 ; get access to UNC FSC
        mov     si,offset ds:UNCfscList
        cmp     ds:[si].UNCfsc_Active,FSC_BAD  ; Is this UNC holder being used?
        je      npo6c                   ; assumption correct; go error exit
        cmp     ds:[si].UNCfsc_2.UNCfsc_Active,FSC_BAD ; more than 1 FSD??
        je      npo6b
npo6a:  cmp     ds:[si].UNCfsc_Active,FSC_BAD ; check current entry for a
                                              ; loaded UNC FSC
        jne     npo6a2                        ; If we have a winner, jump to
npo6a1:                                       ; check its ATTRIBUTE DWORD
        add     si,size uncfscentrstruc       ; else get offset to next entry
        cmp     si,offset ds:UNCfscLast       ; Past last entry??
        jbe     npo6a                         ; No, jump to top of the entry
                                              ; transversal loop
        jmp short npo6c                       ; Yes, error path
npo6a2:
        push    es                            ; Save contents of es:si
        push    di
        les     di,ds:[si].UNCfsc_FSCptr      ; load current FSC ptr into es:di
        les     di,DWORD PTR es:[di]    ; GK FS_ATTRIBUTE Defect #69902
        test    byte ptr es:[di],FS_ATTR_PIPESVR ; Does this FSD do Server pipe stuff??
        pop     di                            ; Restore contents of es:si
        pop     es
        jnz     npo6b                         ; If bit set, load FSC ptr into temp variable
        jmp short npo6a1                      ; else jump to top of the loop

npo6b:  mov     ax,ds:[si].UNCfsc_FSCptr.Offst ; Save the FSC ptr currently
        mov     [npo_UncFsc].Offst,ax          ; pointed to by ds:si into a
        mov     ax,ds:[si].UNCfsc_FSCptr.Segmt ; local variable so it can be
        mov     [npo_UncFsc].Segmt,ax          ; retrieved later to do far call
                                               ; into the UNC FSD

        pop     ds
        jmp short npo7                        ; restore contents of ds
                                              ; and contine
npo6c:  stc                                   ; and continue open operation

        pop     ds                            ; else fsharing was set illegally
        mov     ax,ERROR_OPEN_FAILED          ; so lets fail this open operation
        jmp     npox                          ;
;;; Added code to find the IBM/Microsoft UNC FSD so it can do remote pipe open
;;; 11/14/91 Phil Doragh
npo7:   SaveReg <es,di>
        les     di,TASKAREA:[bx].TCBThisCDS     ; (ES:DI) = CDS pointer  ;### PTR B720732
;### PTR B720732 les    di,TASKAREA:[ThisCDS]   ; ES:DI -> CDS
        lea     ax,es:[di].cd_fsi
        push    es
        push    ax                      ; pcdfsi
        lea     ax,es:[di].cd_fsd
        push    es
        push    ax                      ; pcdfsd
        push    ss
                                        ;72417 moved from PTDA to TCB
        mov     di,CurrTCB
        push    TASKAREA:[DI].TCBWFP_Start ; pName (Ptr to canonicalized name)
        mov     ax,-1
        push    ax                      ; iCurDirEnd (Offset of current dir in pName)
                                        ;72417 end
        les     di,ss:[di.TCBThisSFT]   ; (es:di) = SFT
        mov     bx,npo_parms.Offst      ; SS:BX -> open parameter block
;
; Set up sfi_size with the size of file requested by the user
;
        mov     ax,TASKAREA:[bx].opn3s_Size.HighWord
        mov     es:[di].sf_fsi.sfi_size.HighWord,ax
        mov     ax,TASKAREA:[bx].opn3s_Size.LowWord
        mov     es:[di].sf_fsi.sfi_size.LowWord,ax
        lea     ax,es:[di].sf_fsi       ; ES:AX -> sf_fsi
        push    es
        push    ax                      ; psffsi
        lea     ax,es:[di].sf_fsd
        push    es
        push    ax                      ; psffsd
        push    TASKAREA:[bx].opn3s_Mode2 ; High word of fhandflag - (OpenMode)
        push    TASKAREA:[bx].opn3s_Mode ; fhandflag (sharing and access modes)
        mov     ax,TASKAREA:[bx].opn3s_Flag ; openflag (actions to be taken)
    .erre       (08000h and o_flag_reserved)
        or      ax,08000h               ; indicate client side redirection
        push    ax                      ; openflag
        lea     ax,[npo_ActionTaken]
        push    ss
        push    ax                      ; pAction (Location to place action taken)
        push    TASKAREA:[bx].opn3s_Attrs    ; attr (file attributes for CREATE)
        push    TASKAREA:[bx].opn3s_EABufPtr.Segmt
        push    TASKAREA:[bx].opn3s_EABufPtr.Offst    ; pEABuf (pointer to EABuf)
;;; Added code to find the IBM/Microsoft UNC FSD so it can do remote pipe open
;;; 11/14/91 Phil Doragh
        lds     si,[npo_UncFsc]         ; Get FSC pointer determined by code
    assume      ds:nothing              ; above
;;; Added code to find the IBM/Microsoft UNC FSD so it can do remote pipe open
;;; 11/14/91 Phil Doragh
        lea     ax,npo_fgenflag
        push    ss
        push    ax
        call    ds:[si].FS_OPENCREATE   ; Call the FSD
        dec     ax
        sub     ax,-1                   ; Set carry if ax != 0
        mov     cx,[npo_ActionTaken]    ; CX = Action taken by FSD
        mov     bx,CurrTCB
        lds     si,ss:[bx].TCBThisSFT   ; (ds:si) = SFT
    assume      ds:nothing

        RestoreReg <di,es>
        LJC     npo8                    ; Error. File not subject to redirection

;;; Added code to find the IBM/Microsoft UNC FSD so it can do remote pipe open
;;; 11/14/91 Phil Doragh
        mov     cx,[npo_UncFsc].Offst
        mov     dx,[npo_UncFsc].Segmt
        mov     ds:[si].sf_FSC.Offst,cx ; mark pipe as remote
        mov     ds:[si].sf_FSC.Segmt,dx
;;; Added code to find the IBM/Microsoft UNC FSD so it can do remote pipe open
;;; 11/14/91 Phil Doragh
        or      ds:[si.sf_flags2],SF_SRVRDR

;  Call the UNC FSD to redirect the serving side SFT also.  It is
;  assumed this cannot fail.

        SaveReg <es,di>

        TaskContext <ds>
        lds     si,TASKAREA:[bx].TCBThisCDS     ; (ds:si) = CDS pointer  ;### PTR B720732
    assume      ds:nothing
;### PTR B720732 lds    si,TASKAREA:[ThisCDS]   ; DS:SI -> CDS
        lea     ax,ds:[si].cd_fsi
        push    ds
        push    ax                      ; pcdfsi
        lea     ax,ds:[si].cd_fsd
        push    ds
        push    ax                      ; pcdfsd
        push    ss
                                        ;72417 moved from PTDA to TCB
        mov     bx,CurrTCB
        push    TASKAREA:[bx].TCBWFP_Start    ; pName (Ptr to canonicalized name)
                                        ;72417 end
        mov     ax,-1
        push    ax                      ; iCurDirEnd (Offset of current dir in pName)
        les     di,es:[di.np_ssft]      ; ES:DI -> SFT
        mov     bx,npo_parms.Offst      ; SS:BX -> open parameter block
;
; Set up sfi_size with the size of file requested by the user
;
        mov     ax,TASKAREA:[bx].opn3s_Size.HighWord
        mov     es:[di].sf_fsi.sfi_size.HighWord,ax
        mov     ax,TASKAREA:[bx].opn3s_Size.LowWord
        mov     es:[di].sf_fsi.sfi_size.LowWord,ax
        lea     ax,es:[di].sf_fsi       ; ES:AX -> sf_fsi
        push    es
        push    ax                      ; psffsi
        lea     ax,es:[di].sf_fsd
        push    es
        push    ax                      ; psffsd
        push    TASKAREA:[bx].opn3s_Mode2 ; High word of fhandflag - (OpenMode)
        push    TASKAREA:[bx].opn3s_Mode ; fhandflag (sharing and access modes)
        mov     ax,TASKAREA:[bx].opn3s_Flag ; openflag (actions to be taken)
    .erre       (04000h and o_flag_reserved)
        or      ax,0C000h               ; indicate serving side redirection
        push    ax                      ; openflag
        lea     ax,[npo_ActionTaken]
        push    ss
        push    ax                      ; pAction (Location to place action taken)
        push    TASKAREA:[bx].opn3s_Attrs    ; attr (file attributes for CREATE)
        push    TASKAREA:[bx].opn3s_EABufPtr.Segmt
        push    TASKAREA:[bx].opn3s_EABufPtr.Offst    ; pEABuf (pointer to EABuf)
        mov     es:[di].sf_FSC.Offst,cx ; mark pipe as remote
        mov     es:[di].sf_FSC.Segmt,dx
        or      es:[di.sf_flags2],SF_SRVRDR
        mov     si,cx
        mov     ds,dx                   ; (ds:si) = UNC FSC
    assume      ds:nothing
        lea     ax,npo_fgenflag         ;
        push    ss
        push    ax
        call    ds:[si].FS_OPENCREATE   ; Call the FSD
        RestoreReg <di,es>              ; ES:DI -> NP structure
        or      es:[di.np_flags],NP_SRVRDR

;
;       Initialize the data buffers
;
npo8:   mov     si,es:[di.np_selector1] ; get outgoing data buffer's selector
        mov     dl,0
        call    npcr_initbuf
        mov     si,es:[di.np_selector2] ; get incoming data buffer's selector
        mov     dl,1
        call    npcr_initbuf

;       Wake up any process waiting for the open to complete

        mov     es:[di.np_state],NP_CONNECTED
;### B725255    mov     es:[di.np_semconnect],1              ; set connect semaphore
        lea     bx,es:[di.np_state]
        mov     ax,es
        CALLFAR ProcRun

;### B725255    SaveReg <ds>
;### B725255    mov     bx,es
;### B725255    mov     ds,bx
;### B725255    lea     bx,ds:[di.np_semconnect]
;### B725255    call    np_sem_wait
;### B725255    RestoreReg  <ds>
        clc
npox:   mov     cx,npo_ActionTaken
        LeaveProc
        TaskContext DS

        CodeUnlock NMPIPE_GROUP

        ret
EndProc NmPipeOpen


;***    NmPipeClose -- close reference to one end of a named pipe
;
;       Close a pipe.
;
;       ENTRY   TCBThisSFT = ES:DI  Points to associated SFT.
;
;       EXIT    NC: ES:DI = points to SFT
;                   DS = TASKAREA
;               Carry set if error, ax = code.
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags
;

Procedure       NmPipeClose,near
        ASSUME  DS:NOTHING,ES:NOTHING

        CodeLock NMPIPE_GROUP

        dec     es:[di.sf_ref_count]    ; dec. ref. count, is it zero?
        jnz     npclx                   ; no, just return
        mov     dx,es:[di.sf_pipmod]    ; get pipe mode
        test    dx,NP_SDISCON           ; disconnected?
        jnz     npcl8                   ; yes, nothing to be done
        les     di,es:[di.sf_np]
        test    dx,NP_SERVER            ; server close?
        jnz     npcl3                   ; yes
        dec     es:[di.np_ccnt]         ; bump client ref. count
        jnz     npcl8                   ; nothing to do if more clients

        mov     si,es:[di.np_selector2]
        mov     dl,FP_WRITER
        call    flushpipe               ; flush incoming buffer
        mov     si,es:[di.np_selector1]
        mov     dl,FP_READER
        call    flushpipe               ; flush outgoing buffer

        lea     bx,es:[di.np_csem]      ; (es:bx) = pointer to client semaphore
        call    snps_sem_deref          ; get rid of the semaphore
        lea     bx,[di.np_ssem]         ; (es:bx) = pointer to server semaphore

        cmp     es:[di.np_state], NP_LISTENING    ;@73173
        jz      npcl51                            ;if LISTENING or DISCONNECTED,
        cmp     es:[di.np_state], NP_DISCONNECTED ;don't reset it to CLOSING
        jz      npcl51                            ;state, if neither of them
        jmp     short npcl5                       ;jump to set CLOSING

npcl3:  dec     es:[di.np_scnt]         ; bump server ref. count
        jnz     npcl8                   ; nothing to do if more servers

        mov     si,es:[di.np_selector1]
        mov     dl,FP_WRITER
        call    flushpipe               ; flush outgoing buffer
        mov     si,es:[di.np_selector2]
        mov     dl,FP_READER
        call    flushpipe               ; flush incoming buffer

        lea     bx,es:[di.np_ssem]      ; (es:bx) = pointer to server semaphore
        call    snps_sem_deref          ; get rid of the semaphore
        lea     bx,[di.np_csem]         ; (es:bx) = pointer to client semaphore

npcl5:                                           ;@73173  this is a server
        mov     es:[di.np_state], NP_CLOSING     ;no racing problem, set CLOSING
npcl51:
        call    np_sem_clear            ; notify the other guy of close
        dec     es:[di.np_refcnt]       ; bump total ref. count
IFDEF PIPESTRICT
        jns     npcl6                   ;; Assert(np_refcnt >= 0)
        InternalError <NmPipeClose: ref count underflow>
npcl6:
ENDIF
        jnz     npcl8                   ; there are still references to the pipe

;*      Need to deallocate pipe buffers; temporarily make the pipe
;       appear busy to avoid reallocing the structure while we're
;       using it.

        dec     es:[di.np_refcnt]       ; np_refcnt == -1
        call    npcl_dealloc_all

npcl8:  TaskContext DS                  ; set exit conditions
        mov     di,CurrTCB
        les     di,[di.TCBThisSFT]
        test    es:[di].sf_flags2, SFF2_SPOOLED
        jz      npcl9
        SaveReg <es, di>
        call    npcl_detach_spooled     ; pipe used for spooling, detach
                                        ; object records
        RestoreReg <di, es>

npcl9:  CALLFAR SFLinkFree              ; free the SFT
        clc

        CodeUnlock NMPIPE_GROUP

npclx:  ret

EndProc NmPipeClose


;**     npcl_dealloc_all - deallocate a named pipe and its buffers
;
;       ENTRY   ES:DI point to named_pipe structure
;
;       EXIT
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags

Procedure       npcl_dealloc_all,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        xor     ax,ax
        xchg    ax,es:[di.np_selector2] ; ax = buffer's segment selector
        call    npcl_dealloc_buf

        xor     ax,ax
        xchg    ax,es:[di.np_selector1] ; ax = buffer's segment selector
        call    npcl_dealloc_buf

        call    dealloc_np              ; free the named_pipe struct
        ret
EndProc         npcl_dealloc_all


;**     npcl_dealloc_buf - deallocate a pipe buffer
;
;       ENTRY   AX = segment selector of pipe buffer
;               ES:DI point to named_pipe structure
;
;       EXIT    ES:DI point to named_pipe structure
;
;       USES    ECX, EDX, DS, Flags

Procedure       npcl_dealloc_buf,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        or      ax,ax                   ; does buffer exist?
        jz      ndbx                    ; no, don't try to deallocate it.

IFDEF PIPESTRICT
        mov     ds,ax
    .errnz ((npb_wtcnt-npb_rdcnt)-1)
        test    word ptr ds:[npb_rdcnt],-1
        jz      ndb1
        InternalError <npcl_dealloc_buf: refcnt non-zero>
ndb1:
ENDIF

; _SELFreeKHB(BUFFERHEAP,0,selector)

        CpuMode 386
        SaveReg <eax,es>
        FlatContext <ds,es>,,noassume
        movzx   eax,ax                  ; eax contains selector to free
        CCALL32 _SELFreeKHB,<BUFFERHEAP,0,eax>
        RestoreReg <es,eax>
        CpuMode 286
ndbx:   ret
EndProc npcl_dealloc_buf



;**     npcl_detach_spooled - detach spool objects
;
;       ENTRY   ES:DI point to SFT
;
;       EXIT
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags

Procedure npcl_detach_spooled, NEAR, local
        ASSUME  DS:NOTHING,ES:NOTHING
LocalVar    nds_SFN, WORD

        EnterProc

        mov     ax, es:[di].sf_fsi.sfi_selfsfn
        mov     [nds_SFN], ax                    ; Save SFN

;   Request SpoolDev Semaphore
;
        CpuMode 386
        FlatContext <ds, es>,bx
        mov     ebx, offset FLAT:SpoolDevSem
        CCALL32   _KSEMRequestMutex <ebx,0ffffffffh> ; max timeout value
        CpuMode 286

        xor     ax, ax
        mov     dx, ax                          ; Start from beginning
nds10:  mov     ds, ax
        mov     si, ax
        mov     cx, ax                          ; Get any record
        mov     bx, (SDE_Name - SDE_Sfn)        ; BX    -> Offset
        DosContext <ES>, DI
        mov     di, Offset DOSGROUP:SpoolDevRMPRec   ; ES:DI -> RMP segment data
        CALLFAR Rmp_FindN
        jc      nds90                           ; We're done

; Got a record, see if it corresponds to this SFN

        mov     ax, [nds_SFN]
        cmp     es:[di].SDE_Sfn, ax             ; Same SFN ?
        jnz     nds50                           ; No, continue

; We must remove (detach) this record.
; ES:DI -> record

        push    di
        DosContext <DS>, BX
        mov     bx, Offset DOSGROUP:SpoolDevRMPRec   ; DS:BX -> RMP segment data
        CALLFAR Rmp_Rem                              ; Remove Entry
        pop     dx

nds50:  xor     ax, ax
        jmp short nds10                         ; Get next record

;   Release SpoolDev Semaphore
;
        CpuMode 386
nds90:  FlatContext <ds, es>,bx
        mov     ebx, offset FLAT:SpoolDevSem
        CCALL32   _KSEMReleaseMutex <ebx>
        CpuMode 286

        LeaveProc
        ret

EndProc npcl_detach_spooled

Break   <Named Pipe status and info routines>

;***    w_QNmPHandState - get pipe handle state bits (pipe mode)
;
;       ENTRY   (dx)    = named pipe file handle
;
;       EXIT    C flag clear:
;                       (ax) = pipe handle state
;               C flag set if error
;                       (ax) = error code
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags

Procedure       w_QNmPHandState,<FARONLY,far16>
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        phs_pb,<size phs_param>

        CodeLock NMPIPE_GROUP

        EnterProc

        call    np_handle_chk           ; es:di -> sft
        jc      gphse
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL  ; what file system?
        je      gphs30                  ; local pipe, handle it

; setup parameter block for FSD

        mov     phs_pb.phs_len,size phs_param
        mov     phs_pb.phs_dlen,0

                                        ; fall thru to UNC FSD worker
; UNC named pipe

        lea     ax,[di].sf_fsi          ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to sf_fsi
        lea     ax,[di].sf_fsd          ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to sf_fsd
        push    NMP_GetPHandState       ; Op code
        lea     ax,phs_pb               ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        xor     ax,ax                   ; ax = null pointer
        push    ax
        push    ax                      ; push null pData
        push    ax
        push    ax                      ; push null pName
        lds     si,es:[di].sf_FSC       ; ds:si -> fsc
        call    ds:[si].FS_NMPIPE
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        jc      gphse
        mov     ax,phs_pb.phs_pmode     ; (ax) = pipe mode
        jmp     short gphsx

; local named pipe

gphs30: mov     ax,dx                   ; ax = pipe mode
        test    ax,NP_SDISCON           ; disconnected by server?
    .errnz      (NP_ICOUNT-0FFh)        ; assume Icount is low byte
        mov     al,0                    ; clear icount bits
        jnz     gphsx                   ; disconnected, no Icount
        lds     si,es:[di.sf_np]
        mov     al,byte ptr ds:[si.np_pipmod]   ; al = Icount

gphsx:  clc
gphse:  LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret
EndProc         w_QNmPHandState


;***    w_SetNmPHandState - Set pipe handle state bits (pipe mode)
;
;       ENTRY   (dx)    = named pipe file handle
;               (bx)    = new pipe handle state
;
;       EXIT    C flag clear:
;                       (dx) = pipe handle state
;               C flag set if error
;                       (ax) = error code
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags

Procedure       w_SetNmPHandState,<FARONLY,far16>
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        phs_pb,<size phs_param>

        CodeLock NMPIPE_GROUP

        EnterProc

        push    bx
        call    np_handle_chk           ; do access checks on file handle
        pop     dx                      ; dx = new pipe handle state
        LJC     sphsx                   ; error detected, (ax) is error code
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL ; what file system?
        je      sphs2                   ; local, handle it

; set up parameter for FSD

        mov     phs_pb.phs_len,size phs_param
        mov     phs_pb.phs_dlen,0
        mov     phs_pb.phs_pmode,dx

; callout to FSD

        lea     ax,[di].sf_fsi          ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to sf_fsi
        lea     ax,[di].sf_fsd          ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to sf_fsd
        push    NMP_SetPHandState       ; Op code
        lea     ax,phs_pb               ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        xor     ax,ax                   ; ax = null pointer
        push    ax
        push    ax                      ; push null pData
        push    ax
        push    ax                      ; push null pName
        lds     si,es:[di].sf_FSC       ; ds:si -> fsc
        call    ds:[si].FS_NMPIPE
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        jmp     short sphsx

; local

sphs2:  mov     ax,dx                   ; (ax) = new handle state
        test    ax,NOT SETPMODEBITS     ; illegal bits to be set?
        jnz     sphs_eip                ; yes, give error

        mov     bx,es:[di.sf_pipmod]    ; (bx) = pipe mode
        SaveReg <ax,bx>
        test    ax,NP_NBLK              ; going to non-blocking mode?
        jz      sphs4                   ; no
        xor     ax,bx                   ; state change in blocking bit?
    .errnz      (NP_NBLK-8000h)
        jns     sphs4                   ; no change

        SaveReg <es,di>                 ; save SFT pointer
        call    np_sft_io
        jc      sphs3                   ; error

        push    bx
        mov     bx,npb_rdlck            ; (bx) = offset of read lock
        call    sphs_check_block
        pop     bx
        jc      sphs3

        mov     ax,bx                   ; (ax) = write buffer selector
        mov     bx,npb_wtlck            ; (bx) = offset of write lock
        call    sphs_check_block

sphs3:  RestoreReg <di,es>
sphs4:  RestoreReg <bx,dx>              ; (bx) = current mode, (dx) = new mode
        jc      sphse                   ; can't change state, (ax) = error code

        mov     ax,dx                   ; (ax) = new mode
        and     bx,NOT SETPMODEBITS     ; clear user-settable bits
        or      ax,bx                   ;   and set others
        call    sphs_check_pmode
        jc      sphs_eip
        mov     es:[di.sf_pipmod],ax    ; save the new mode
        mov     dx,ax                   ; (dx) = new mode
        clc
        jmp     short sphsx

sphs_eip:
        mov     ax,ERROR_INVALID_PARAMETER
sphse:  stc

sphsx:  LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret

;**     sphs_check_pmode - check Pmode bits for consistency
;
;       Check that any reserved bits are zero and that message read
;       mode is not set if message write mode is not.
;
;       ENTRY   (AX) = Pmode bits to be tested
;
;       EXIT    Carry clear - no error
;               Carry set - inconsistent bit setting
;
;       USES    Flags

Procedure       sphs_check_pmode,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

;        test    ax,NOT (NP_NBLK+NP_SERVER+NP_WMESG+NP_RMESG+NP_ICOUNT)           @6535
        test    ax,NOT (NP_NBLK+NP_TIMOUT+NP_SERVER+NP_WMESG+NP_RMESG+NP_ICOUNT) ;@6535
        jnz     scpe            ; reserved bit set
        SaveReg <AX>
        and     ax,NP_WMESG+NP_RMESG    ; don't allow RMESG w/o WMESG
        cmp     ax,NP_RMESG
        RestoreReg <AX>
        je      scpe            ; error - bad combination
        clc
        ret

scpe:   stc
        ret
EndProc sphs_check_pmode

;**     sphs_check_block - check for blocked threads
;
;       Check that no threads are blocked on the named pipe buffer
;       passed in when a blocking/non-blocking state change is made.
;
;       ENTRY   (AX) = pipe buffer's segment selector
;               (BX) = offset of lock semaphore in buffer
;
;       EXIT    Carry clear - no error
;               Carry set - lock semaphore is set
;                       (AX) = ERROR_PIPE_BUSY
;
;       USES    AX, BX, Flags

Procedure       sphs_check_block,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        or      ax,ax                   ; is buffer allocated?
        jz      scbx                    ; no, nothing to do (carry clear)
        SaveReg <DS>
        mov     ds,ax                   ; (si) = pipe buffer mem. handle
        mov     bx,ds:[bx]              ; (bx) = semaphore value
        RestoreReg <DS>
        or      bx,bx                   ; anyone waiting?  (clears carry)
        jz      scbx                    ; no, no one is waiting
        mov     ax,ERROR_PIPE_BUSY      ; yes, pipe is busy
        stc
scbx:   ret
EndProc sphs_check_block

EndProc         w_SetNmPHandState


;***    w_QNmP542ipeInfo - return information about a named pipe
;
;       Two levels of information are currently defined, level
;       one gives miscellaneous information about the pipe, level
;       two gives information about the identity of the client
;       attached to the pipe.
;
;       NOTE:   If ERROR_BUFFER_OVERFLOW is returned, the buffer
;               will be filled to the size requested.
;
;       ENTRY   (dx)    = named pipe file handle
;               (ax)    = information level desired
;               (cx)    = length of buffer
;               (ss:bx) = argument frame.  Contains pointer to buffer.
;
;       EXIT    C flag set if error
;                       (ax) = error code
;
;       USES    AX, EBX, CX, DX, SI, DI, DS, ES, Flags

Procedure       w_QNmPipeInfo,<FARONLY,far16>
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        npqi_len,WORD   ; length of user buffer
        LocalVar        npqi_buf,DWORD  ; pointer to user buffer
        LocalVar        npqi_sft,DWORD  ; Pointer to SFT
        LocalVar        npi,<SIZE npi_data1>    ; temp. buffer

        CodeLock        NMPIPE_GROUP
        EnterProc       <cx>            ; save entry params

        CpuMode 386
        mov     ebx,ss:[bx].qnpi_InfoBuf ; get pointer to buffer
        mov     npqi_buf,ebx            ; save for later
        CpuMode 286

        jcxz    npqi_eip                ; zero buffer size given

        call    np_handle_chk           ; do access checks on file handle
        jc      npqie_j

        mov     npqi_sft.Segmt,es
        mov     npqi_sft.Offst,di       ; Store pointer to SFT for later use
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL ; what file system?
        je      npqi2                   ; local

; set up parameter for fsd

        mov     npi.npi_len,size npi_param
        mov     npi.npi_dlen,cx         ; save buffer length
        mov     npi.npi_level,ax        ; save info level

; UNC named pipe

        lea     ax,[di].sf_fsi          ; es:ax -> sf_fsi
        push    es
        push    ax                      ; push pointer to sf_fsi
        lea     ax,[di].sf_fsd          ; es:ax -> sf_fsd
        push    es
        push    ax                      ; push pointer to sf_fsd
        push    NMP_PipeQInfo           ; Op code
        lea     ax,npi                  ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        mov     ax,npqi_buf.Segmt
        push    ax
        mov     ax,npqi_buf.Offst
        push    ax                      ; push pointer to user buffer
        xor     ax,ax
        push    ax
        push    ax                      ; push null pName
        lds     si,es:[di].sf_FSC       ; ds:si -> fsc
        call    ds:[si].FS_NMPIPE
;
; If the FSD does not support this call, and it is infolevel 1, then we have
; the information locally and can return that to the caller.
;
        cmp     ax,ERROR_NOT_SUPPORTED  ; Is it supported?
        jne     npqi1                   ; Yes. Return error to caller.
        cmp     npi.npi_level,QNPI_HANDINFO ; No. Was this infolevel 1?
        jne     npqi1                   ; No. Return the error.
        les     di,npqi_sft             ; (es:di) -> SFT
        les     di,es:[di.sf_np]        ; (es:di) = named_pipe struct
        mov     cx,npi_dlen             ; (cx) = length of buffer
        jmp     short npqi10            ; Return the information from local structures

;
; The FSD returned an error. We should percolate it back to the caller.
;
npqi1:  xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        jmp     npqix

npqi_eip:
        mov     ax,ERROR_INVALID_PARAMETER
npqie_j: jmp    npqie

npqi2:  test    dx,NP_SDISCON           ; disconnected by server?
        jnz     npqi_epnc               ; yes, can't return info
        les     di,es:[di.sf_np]        ; (es:di) = named_pipe struct

        dec     ax                      ; info level 1?
    ERRNZ <QNPI_HANDINFO - 1>
        je      npqi10                  ; yes
        dec     ax                      ; info level 2?
    ERRNZ <QNPI_COOKIE - 2>
        LJE     npqi20                  ; yes

        mov     ax,ERROR_INVALID_LEVEL
        jmp     short npqie_j

npqi_epnc:
        mov     ax,ERROR_PIPE_NOT_CONNECTED
        jmp     npqie_j


;*      Return info. level 1 stuff
;
;       (es:di) = NP struct for file
;       (cx)    = length of user data buffer

npqi10: mov     ax,es:[di.np_selector1]
        call    npqi_bufsize            ; compute size of outgoing buffer
        mov     npi.npi_obuflen,ax      ;   and save it

        mov     ax,es:[di.np_selector2]
        call    npqi_bufsize            ; compute size of incoming buffer
        mov     npi.npi_ibuflen,ax      ;   and save it

        mov     al,byte ptr es:[di.np_pipmod]   ; al = Icount
        mov     npi.npi_maxicnt,al      ;   save it

        call    find_npn                ; (ds:si) = pointer to npn struct
        mov     cx,npqi_len
        cmp     cx,npi_namlen
        mov     dh,1                    ; indicate buffer overflow error
        jbe     npqi16                  ; room for cur. icount only, compute it

;*      Save the pipe name in the user's buffer, prefixing it with the
;       common pipe directory name.  It's known that at least the length
;       of the name will fit in the buffer.  In the main loop below:
;
;       (cx) = number of bytes remaining in user buffer
;       (dl) = current name length
;       (dh) = indicator, whether ERROR_BUFFER_OVERFLOW should be returned
;       (ds:si) = point to next char. in name
;       (bx:di) = point to next char. in user buffer

        SaveReg <cx,si>                 ; save current pointers
        sub     cx,npi_name             ; account for beginning of buffer

        mov     dx,ds:[si.npn_len]      ; (dl) = length of npn struct
        add     dx,(PIPEDIRLEN-npn_name) ; (dl) = name length with prefix
;
; If the length of the name is greater than 255, then it won't fit in the byte
; field in npi_namlen. In this case, we return a length of zero.
;
        mov     npi.npi_namlen,dl       ; Save name length
        or      dh,dh                   ; Overflow into high byte?
        jz      npqi11                  ; No. Length is OK.
        mov     npi.npi_namlen,0        ; Return length of zero

npqi11: mov     di,npqi_buf.Offst
        mov     bx,npqi_buf.Segmt       ; (bx:di) = user buffer
        add     di,npi_name             ; (bx:di) = pointer to name
        SaveReg <bx,cx,si,ds>
        mov     si,offset PipeDir
        push    cs
        pop     ds                      ; (ds:si) = pipe prefix
        cmp     cx,PIPEDIRLEN           ; will it fit entirely?
        jbe     npqi12                  ; no, just copy what we can
        mov     cx,PIPEDIRLEN           ; copy it all
npqi12: CALLFAR SuBuff
        RestoreReg <ds,si,cx,bx>
        jc      npqie                   ; error - LeaveProc will clean stack

        sub     cx,PIPEDIRLEN
        jbe     npqi14                  ; no more room, don't transfer name
        lea     si,ds:[si.npn_name]     ; point to respective name buffers
        sub     dx,PIPEDIRLEN
        mov     al,1                    ; (al) = buffer overflow indicator
        cmp     cx,dx                   ; will entire name fit?
        jb      npqi13
        mov     cx,dx                   ; only transfer what will fit
        xor     al,al                   ; indicate no overflow

npqi13: mov     dh,al
        SaveReg <ds>
        CALLFAR SuBuff                  ; transfer the name
        RestoreReg <ds>
        jc      npqie                   ; error - LeaveProc will clean stack
        jmp     short npqi15

npqi14: mov     dh,1                    ; indicate whether to return error
npqi15: RestoreReg <si,cx>

;*      Compute current instance count and save it.  It's already been
;       determined that it will fit in the buffer.
;
;       (cx)    = length of user buffer
;       (dh)    = 0 -> name fit in buffer, 1 -> name didn't fit
;       (ds:si) = pointer to NPN structure
;

npqi16: les     di,ds:[si.npn_link]     ; (es:di) = list of np structs
        mov     dl,1                    ; count first struct
npqi17: call    get_next_instance       ; find next instance of pipe
        jc      npqi18
        inc     dl
        jz      npqi17a                 ; in case of overflow
        jmp     npqi17

npqi17a:
        dec     dl                      ; (dl) = 0ffh (a lot of them)
npqi18: mov     npi.npi_curicnt,dl

;*      Copy the information buffer on the stack to user space
;
;       (cx)    = length of user buffer
;       (dh)    = 0 -> name fit in buffer, 1 -> name didn't fit
;
        cmp     cx,npi_name             ; user request more than on stack?
        jbe     npqi19                  ; no
        mov     cx,npi_name

npqi19: mov     di,npqi_buf.Offst
        mov     bx,npqi_buf.Segmt       ; (bx:di) = user buffer
        push    ss
        pop     ds
        lea     si,npi                  ; (ds:si) = buffer on stack
        CALLFAR SuBuff
        test    dh,1                    ; buffer overflow?
        jz      npqix                   ; no (carry cleared by test)
npqi_ebo:
        mov     ax,ERROR_BUFFER_OVERFLOW
npqie:  stc

npqix:  LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret

;*      Return info. level 2 stuff
;
;       (es:di) = NP struct for file
;       (cx)    = length of user data buffer

npqi20:
        cmp     cx,4                    ; user specify bigger buffer?
        jb      npqi_ebo                ; no
        mov     cx,4                    ; only return what we have
        mov     word ptr npi,0
        mov     word ptr npi+2,0        ; return file server cookie, unique ID
        lds     si,es:[di.np_csft]      ; (ds:si) = client (file server) SFT
        mov     ax,ds
        or      ax,ax                   ; valid pointer?
        jz      npqi22                  ; no
        mov     ax,ds:[si].sf_cookie.loword     ; get cookie from SFT
        mov     word ptr npi,ax
        mov     ax,ds:[si].sf_cookie.hiword     ; get cookie from SFT
        mov     word ptr npi+2,ax       ; return file server cookie, unique ID
npqi22:
        mov     dh,0
        jmp     npqi19

EndProc         w_QNmPipeInfo


;**     npqi_bufsize - get I/O buffer size
;
;       ENTRY   AX = segment selector for I/O buffer
;
;       EXIT    AX = size of I/O buffer, excluding control structure
;
;       USES    AX, Flags
;

Procedure       npqi_bufsize,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        or      ax,ax                   ; buffer allocated?
        jz      npibsx                  ; no, size is zero
        SaveReg <ds>
        mov     ds,ax
        mov     ax,ds:[npb_last]        ; (ax) = end+1 of buffer
        sub     ax,size npb+1           ; deduct size of control struct and
        RestoreReg <ds>
npibsx:
        ret
EndProc         npqi_bufsize


;***    w_CallNmPipe - Perform a transaction on a named pipe by name
;
;       NOTE:   It is not expected that the file server will request
;               serving side pipe redirection on this call.
;
;       ENTRY   (ds:dx) = pointer to file name buffer
;               (cx)    = length of InBuffer
;               [TCB_TmpNameBuf] = pointer to OutBuffer
;               [TCB_TmpNameBufL] = length of OutBuffer
;               (di:ax) = timeout
;               (ss:bx) = argument frame; contains pointer to InBuffer
;
;       EXIT    C flag clear:
;                       (ax) = number of bytes returned
;               C flag set:
;                       (ax) = error code
;

        ASSUME  DS:NOTHING,ES:NOTHING
Procedure       w_CallNmPipe,<FARONLY,far16>
LocalVar        cnp_name,DWORD
LocalVar        cnp_ilen,WORD
LocalVar        cnp_tmo,DWORD
LocalVar        cnp_ibuf,DWORD
LocalVar        cnp_req,<size npx_param>
LocalVar        cnp_opnarg,<size Open3Struc>
LocalVar        cnp_openbuf,PMPATHLEN
LocalVar        cnp_dummy,WORD                  ; KLUDGE: see gshare.asm
LocalVar        cnp_DummyCDS, <size curdir>

        CodeLock NMPIPE_GROUP
        EnterProc       <ds,dx,cx,di,ax>

        CpuMode 386
        mov     eax,ss:[bx].cnmp_InBuffer
        mov     cnp_ibuf,eax            ; save pointer to input buffer
        CpuMode 286

        CALLFAR Take_Kernel_Sem         ; Take the FS serialization sem
        LJC     cnpx

        lea     di,cnp_DummyCDS
        push    bx                                      ;### PTR B720732
        mov     bx,TASKAREA:[CurrTCB]                   ;### PTR B720732
        mov     TASKAREA:[bx].TCBpTmpCDS.Offst,di       ; Initialize TCBpTmpCDS ;### PTR B720732
;       mov     TASKAREA:[bx].TCBpTmpCDS.Segmt,ss       ; Initialize TCBpTmpCDS ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pCDS],di ; Initialize ptda_pCDS
        lea     di,cnp_openbuf          ; appropriate buffer
        mov     TASKAREA:[bx].TCBpOpenBuf,di            ; Initialize TCBpOpenBuf ;### PTR B720732
;### PTR B720732        mov     TASKAREA:[ptda_pOpenBuf],di ; Initialize ptda_pOpenBuf
        pop     bx                                      ;### PTR B720732


        push    bp                      ; save frame pointer
        mov     si,dx                   ; (ds:si) = pipe name
        mov     bx,CurrTCB
        push    word ptr TASKAREA:[bx].TCBNewFlags
        or      TASKAREA:[bx].TCBNewFlags,TCBNEWFILES ; enable NEWFILES
        CALLFAR TransPath               ; convert the path
        mov     bx,CurrTCB
        pop     word ptr TASKAREA:[bx].TCBNewFlags
    ASSUME   DS:TASKAREA
        pop     bp                      ; restore frame pointer
        jnc     cnpe_y
        cmp     ax,ERROR_FILENAME_EXCED_RANGE   ; D151989
        je      cnpe_z                          ; D151989
        jmp     cnpe_x                          ; D151989

; Changed 'cmp' to 'test' and subsequent 'jz' to 'jnz'.                  75228
                                        ;72417 moved from PTDA to TCB
cnpe_y: SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_NMPIPE ; is it a local named pipe? 75228
        UnSetBP_CurrTCB
        jnz     cnp2                    ; yes, go do it                  75228

        SetBP_CurrTCB
        test    [bp].TCBPathNameType,PNType_UNC ; UNC name?                    75228
        UnSetBP_CurrTCB
                                        ;72417 end
        jnz     cnp1                    ; yes, go do it                  75228

cnpe_z: mov     ax,ERROR_BAD_PIPE
cnpe_x: CALLFAR Clear_Kernel_Sem        ; release the FS serialization sem
        stc
        jmp     cnpx

; dispatch to UNC FSD. First set up request packet

cnp1:   mov     cnp_req.npx_len,size npx_param
        mov     ax,cnp_ilen
        mov     cnp_req.npx_ilen,ax
        mov     bx,CurrTCB
        mov     ax,TASKAREA:[bx].TCB_TmpNameBuf.Offst
        mov     cnp_req.npx_obuf.Offst,ax
        mov     ax,TASKAREA:[bx].TCB_TmpNameBuf.Segmt
        mov     cnp_req.npx_obuf.Segmt,ax
        mov     ax,TASKAREA:[bx].TCB_TmpNameBufL
        mov     cnp_req.npx_olen,ax
        mov     ax,cnp_tmo.loword
        mov     cnp_req.npx_timeo.loword,ax
        mov     ax,cnp_tmo.hiword
        mov     cnp_req.npx_timeo.hiword,ax

; call FSD

        xor     ax,ax                   ; ax = null pointer
        push    ax
        push    ax                      ; push null sf_fsi pointer
        push    ax
        push    ax                      ; push null sf_fsd pointer
        push    NMP_CALLPIPE            ; Op code
        lea     ax,cnp_req              ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        push    cnp_ibuf.Segmt          ; push pData -> inbuffer
        push    cnp_ibuf.Offst

        push    ss
                                        ;72417 moved from PTDA to TCB
        mov     si,CurrTCB
        push    TASKAREA:[si].TCBWFP_Start    ; push pointer to pipe name
                                        ;72417 end
        lds     si,TASKAREA:[bx].TCBThisFSC   ; Get FSC pointer determined by
    CALLFAR Clear_Kernel_Sem   ; release the FS serialization sem @80655
    assume      ds:nothing                    ; TransUNC
        call    ds:[si].FS_NMPIPE
;       CALLFAR Clear_Kernel_Sem        ; moved up @80655
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        LJC     cnpx                    ; if error, ax = error code
        mov     ax,cnp_req.npx_nbyt     ; (ax) = number of bytes read
        jmp     cnpx

; do local named pipe

cnp2:   call    CopyName                ; es:di -> temp buffer
    assume ds:TASKAREA                  ; set by CopyName
        SaveReg <es,di>                 ; save temp buffer pointer
        CALLFAR Clear_Kernel_Sem        ; release the FS serialization sem
        mov     cx,[cnp_tmol]
        mov     di,[cnp_tmoh]           ; (cx:di) = timeout
    assume ds:nothing
        RestoreReg <si,ds>              ; (ds:si) = pipe name
        SaveReg <si,ds>                 ; save temp buffer offset
        call    wnp_sub                 ; look for, wait for listen state NP
        LJC     cnpe                    ; error, open will fail

;*      Grab a free SFT and mark it as busy, after copying name back
;*      into cnp_openbuf.

        CALLFAR Take_Kernel_Sem         ; take the FS serialization sem again
        LJC     cnpe                    ; exit if error

        RestoreReg <ds,si>              ; ds:si -> temp buffer
        SaveReg <ds, si>                ; si = offset of temp buffer
        TaskContext <es>
        lea     di,cnp_openbuf          ; appropriate buffer
;restore wfp_start since we blocked in wnp_sub which could nuke any ptda_*.*
                                        ;72417 moved from PTDA to TCB
        SetBP_CurrTCB
        mov     [bp].TCBWFP_Start,di
        UnSetBP_CurrTCB
                                        ;72417 end
        mov     cx,MAXPATHLEN
        shr     cx,1                    ; convert to words + carry if odd
        rep movsw                       ; move words (even number of bytes)
        jnc     cnp3                    ; even number of bytes, all done
        movsb                           ; move last, odd byte
cnp3:   RestoreReg <di, es>             ; es:di = address of temp buffer
        CALLFAR FreeTempBuffer
    assume es:nothing
        CALLFAR SFNAlloc                ; get a free SFN
        LJC     cnpe_x                  ; oops, no free SFT's
        or      es:[di].sf_flags,SF_NOJFN       ;

        TaskContext <DS>
        mov     si,CurrTCB
        mov     TASKAREA:[si.TCBThisSFT.Offst],di
        mov     TASKAREA:[si.TCBThisSFT.Segmt],es
        mov     es:[di.sf_ref_count],1
        mov     es:[di.sf_fsi.sfi_mode],sharing_deny_none+open_for_both
        mov     cx,nexist_fail+exist_open
        lea     bx,cnp_opnarg           ; (ss:bx) -> open argument structure
        xor     ax,ax
        mov     TASKAREA:[bx].opn3s_Size.hiword,ax
        mov     TASKAREA:[bx].opn3s_Size.loword,ax
        mov     TASKAREA:[bx].opn3s_EABufPtr.Offst,ax
        mov     TASKAREA:[bx].opn3s_EABufPtr.Segmt,ax
        mov     TASKAREA:[bx].opn3s_Flag,cx
        mov     TASKAREA:[bx].opn3s_Mode,sharing_deny_none+open_for_both
        mov     TASKAREA:[bx].opn3s_Mode2,ax
        mov     es:[di.sf_fsi.sfi_mode2],ax     ;should have same Mode2 value
        call    NmPipeOpen
        jnc     cnp4

        mov     di,CurrTCB
        les     di,[di.TCBThisSFT]
        mov     es:[di.sf_ref_count],0  ; Make sure it gets freed
        CALLFAR SFLinkFree              ; free the SFT
        jmp     cnpe_x

cnp4:   CALLFAR Clear_Kernel_Sem        ; release the FS serialization sem
        push    cnp_ibuf.Segmt
        push    cnp_ibuf.Offst                  ; pointer to InBuffer
        push    cnp_ilen                        ; length of InBuffer
        mov     di,CurrTCB
        push    TASKAREA:[di.TCB_TmpNameBuf].Segmt
        push    TASKAREA:[di.TCB_TmpNameBuf].Offst  ; pointer to OutBuffer
        push    TASKAREA:[di.TCB_TmpNameBufL]       ; length of OutBuffer

        call    np_trans_sft                    ; do the transaction,
    assume      ds:nothing,es:nothing           ;    stack cleaned by LeaveProc

        pushf
        SaveReg <ax,cx>
        mov     bx,-1                   ; no handle for this sft
        CALLFAR FSD_Close               ; free the SFT
        RestoreReg <cx,ax>
        popf                            ; recover error indicator
        jc      cnpx                    ; If error, AX already has error code
        mov     ax,cx                   ; No error, CX contains xfer count.
;### PTR B720732 cnpx:  mov     SS:[ptda_pOpenBuf],0    ; Clear ptr to openbuf
;### PTR B720732        mov     SS:[ptda_pCDS],0        ; Clear ptr to CDS
cnpx:   LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret

cnpe:   pop     es      ; es = segment of tempbuffer
        pop     di              ; di = offset of tempbuffer
        CALLFAR FreeTempBuffer
        stc
        jmp short  cnpx

EndProc         w_CallNmPipe

;***    w_SetNmPipeSem - Attach a system semaphore to a named pipe
;
;       ENTRY   (ax:cx) = semaphore handle
;               (dx)    = file handle of named pipe
;               (bx)    = key value to be attached to named pipe
;
;       EXIT    C flag set:
;                       (ax) = error code
;
;       USES    EAX, BX, ECX, EDX, SI, DI, DS, ES, Flags

Procedure       w_SetNmPipeSem,FARONLY
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        snps_semhnd,DWORD
        LocalVar        snps_keyval,WORD

        CodeLock        NMPIPE_GROUP

        EnterProc       <ax,cx, bx>

        call    np_handle_chk           ; look up file handle
        jc      snpsx
        cmp     es:[di].sf_FSC.Offst,FSC_LOCAL  ; what file system?
        je      snps0                   ; valid if not remote pipe
        test    es:[di.sf_flags2],SF_SRVRDR     ; serving pipe redir?
        jz      snpse                   ; no, not allowed

snps0:  test    dx,NP_SDISCON
        jz      snps1
        mov     ax,ERROR_PIPE_NOT_CONNECTED
        stc
        jmp     snpsx

snps1:  mov     ax,snps_semhndh
        CpuMode 386
        shl     eax,16
        mov     ax,snps_semhndl         ; eax = semaphore handle
        SaveReg <dx,es,ds>
        FlatContext <ds,es>,,noassume
        CCALL32 _SEMFindType,<eax>      ; check if valid 32-bit sempahore
        RestoreReg <ds,es,dx>
        or      eax,eax
        jz      snps1a                  ; not valid 32-bit sem. Assume 16-bit.
        cmp     eax,DCE_EVENT_SEM OR DC_SEM_SHARED
        CpuMode 286
        je      snps2                   ; 32-bit shared event sem.  Allow it.
        mov     ax,ERROR_WRONG_TYPE     ; wrong type of 32-bit sempahore.
        stc                             ; --error exit.
        jmp     short snpsx

snps1a: SaveReg <es,di,dx>
        mov     bx,snps_semhndl
        mov     ax,snps_semhndh         ; (ax:bx) = semaphore handle
        mov     dh,IN_USE
        CALLFAR SemHandle               ; register use of semaphore
        RestoreReg <dx,di,es>
        jc      snpsx                   ; error occurred, return it
        mov     snps_semhndl,bx
        mov     snps_semhndh,ax         ; save system handle

        cmp     ax,SYSHANDLE_HIGHWORD   ; system semaphore handle?
        je      snps2a                  ; yes, it's a good one
        mov     dh,NOT_IN_USE
        CALLFAR SemHandle
snps1b: mov     ax,ERROR_SEM_NOT_FOUND
        stc
        jmp     short snpsx

snps2:  CpuMode 386
        mov     eax, snps_semhnd

        CALLFAR dh_OpenEventSem         ; Open 32-bit shared event semaphore
        CpuMode 286

snps2a: les     di,es:[di.sf_np]        ; (es:di) = NP structure
        lea     bx,[di.np_ssem]         ; (es:bx) points to server sem
        test    dx,NP_SERVER            ; is this SFT server?
        jnz     snps3                   ; yes, pointing right place
        lea     bx,[di.np_csem]         ; (es:bx) points to client sem
snps3:  call    snps_sem_deref          ; get rid of previous ref.
        mov     di,bx                   ; (es:di) points to sem field
        mov     ax,snps_semhndl
        stosw
        mov     ax,snps_semhndh
        stosw                           ; store new semaphore
        mov     ax,snps_keyval
        stosw                           ; store key
        clc
snpsx:  LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret

snpse:  mov     ax,ERROR_INVALID_FUNCTION
        stc
        jmp     snpsx

;**     snps_sem_deref - dereference a user semaphore handle
;
;       ENTRY   (es:bx) = pointer to semaphore to be deregistered
;
;       EXIT    none
;
;       USES    AX, CX, DX, SI, DS
;

Procedure       snps_sem_deref,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        CpuMode 386
        mov     eax, es:[bx]            ; eax = semaphore handle
        or      eax, eax                ; existing associated sem?
        jz      ssd4                    ; no
        push    eax
        SaveReg <dx,es,ds>
        FlatContext <ds,es>,,noassume
        CCALL32 _SEMFindType,<eax>      ; check if valid 32-bit sempahore
        RestoreReg <ds,es,dx>
        or      eax, eax
        pop     eax
        jz      ssd2                    ; assume 16 bit semaphore

;   32-bit shared event semaphore, close it
        CALLFAR dh_CloseEventSem
        xor     eax, eax
        mov     es:[bx],eax             ; clear reference to semaphore
        jmp     ssd4
        CpuMode 286

ssd2:   SaveReg <bx,es,di>
        xor     ax,ax
        xchg    ax,es:[bx.hiword]
        xor     di,di
        xchg    di,es:[bx.loword]
        mov     bx,di                   ; (ax:bx) = currently assigned sem.
        mov     dh,NOT_IN_USE
        CALLFAR SemHandle               ; deregister use, ignore errors
IFDEF PIPESTRICT
        jnc     ssd3
        InternalError <snps_sem_deref: SemHandle returned error>
ssd3:
ENDIF
        RestoreReg <di,es,bx>
ssd4:   ret

EndProc snps_sem_deref

EndProc         w_SetNmPipeSem


;***    w_QNmPipeSemState - Return state of pipes attached to a semaphore
;
;       ENTRY   (ax:dx) = semaphore handle
;               (cx)    = length of info buffer
;               (ss:bx) = argument frame; contains pointer to info buffer
;
;       EXIT    C flag set:
;                       (ax) = error code
;
;       USES    EAX, EBX, ECX, EDX, SI, DI, DS, ES, Flags

Procedure       w_QNmPipeSemState,FARONLY
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        qnps_semhnd,DWORD
        LocalVar        qnps_ilen,WORD
        LocalVar        qnps_ibuf,DWORD
        LocalVar        qnps_irec,<size npss>
        LocalVar        qnps_req,<size qnps_param>

        CodeLock        NMPIPE_GROUP
        EnterProc       <ax,dx,cx>

        CpuMode 386

        mov     ebx,ss:[bx].qnps_InfoBuf
        mov     qnps_ibuf,ebx           ; save pointer to info buffer

        jcxz    short qnps_eip          ; jump with error if buffer size = zero

qnps0:  shl     eax,16                  ; high 16 bits of sem handle
        mov     ax,dx                   ; eax = semaphore handle
        SaveReg <es,ds>
        FlatContext <ds,es>,,noassume
        CCALL32 _SEMFindType,<eax>      ; check if valid 32-bit sempahore
        RestoreReg <ds,es>              ; (bx is preserved by C conventions)
        or      eax,eax                 ; 32-bit semaphore?
        jz      short qnps0a            ; no, assume 16-bit.
        cmp     eax,DCE_EVENT_SEM OR DC_SEM_SHARED      ; correct type of sem?
        jne     short qnps0a            ; no, assume 16-bit
        mov     cx,qnps_semhndh
        mov     bx,qnps_semhndl         ; cx:bx is semaphore handle
        jmp     short qnps1             ; valid 32-bit sem, continue

        CpuMode 286

qnps0a: mov     dh,IN_USE
        mov     ax,qnps_semhndh         ; fetch semaphore handle in ax:bx
        mov     bx,qnps_semhndl
        CALLFAR SemHandle               ; register use of semaphore
        jc      qnpsx_j                 ; error occurred, return it
        mov     qnps_semhndl,bx
        mov     qnps_semhndh,ax         ; save system handle
        mov     cx,ax                   ; (cx:bx) = Sem. handle
        or      ax,bx                   ; valid semaphore handle?
        jnz     qnps1                   ; yes, non-zero
        mov     ax,ERROR_SEM_NOT_FOUND
        stc
        jmp     qnpse

qnps_eip:
        mov     ax,ERROR_INVALID_PARAMETER
        stc
qnpsx_j: jmp    qnpsx

qnps1:  DosContext ds
        mov     di,ActiveNPList
        mov     es,NmpBmpSel            ; (es:di) = first NP on list

qnps2:  or      di,di                   ; end of list?
        jz      qnps8                   ; yes, write EOI record

        cmp     bx,es:[di.np_ssem.loword]       ; matching server sem?
        jne     qnps3
        cmp     cx,es:[di.np_ssem.hiword]       ; matching server sem?
        jne     qnps3
        mov     dx,es:[di.np_ssemkey]
        mov     qnps_irec.npss_key,dx
        mov     dx,NP_SERVER
        call    qnps_check_space
        LJC     qnpse                   ; error, probably no space in buffer

qnps3:  cmp     bx,es:[di.np_csem.loword]       ; matching client sem?
        jne     qnps4
        cmp     cx,es:[di.np_csem.hiword]       ; matching client sem?
        jne     qnps4
        mov     dx,es:[di.np_csemkey]
        mov     qnps_irec.npss_key,dx
        mov     dx,0                    ; indicate client side
        call    qnps_check_space
        jc      qnpse                   ; error, probably no space in buffer

qnps4:  mov     di,es:[di.np_next]
        jmp     qnps2

;       Build and set the final record indicating no more data.

qnps8:
        mov     qnps_irec.npss_status,0 ; indicate no more info
        mov     cx,1
        call    qnps_xfer_rec
;;      jc      qnpse
;;      clc

qnpse:  pushf                           ; save error state
        push    ax
        mov     bx,qnps_semhndl
        mov     ax,qnps_semhndh         ; (ax:bx) = semaphore handle
        mov     dh,NOT_IN_USE
        CALLFAR SemHandle               ; deregister use, ignore errors
        pop     ax
        popf
qnpsx:  LeaveProc
        CodeUnlock NMPIPE_GROUP
        ret
EndProc         w_QNmPipeSemState

;**     qnps_xfer_rec - transfer an information record to the user
;
;       A information record is transferred to the user buffer.
;       This subroutine uses stack frame variables declared in
;       w_QNmPipeSemState.
;
;       ENTRY   (cx) = number of bytes to be transferred
;               qnps_irec = data record to be transferred
;               qnps_ibuf = user data buffer pointer
;               qnps_ilen = length of user data buffer
;
;       EXIT    C set if error
;                       (ax) = error code
;               otherwise,
;                       qnps_ibuf and qnps_ilen updated to reflect transfer
;
;       USES    AX, BX, CX, SI, DI, DS, ES, Flags

Procedure       qnps_xfer_rec,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        sub     qnps_ilen,cx            ; room for one more record?
        jc      qxr_ebo                 ; no, return error_buffer_overflow
        lea     si,qnps_irec
        push    ss
        pop     ds                      ; (ds:si) = info record to be given user
        mov     di,qnps_ibuf.Offst
        mov     bx,qnps_ibuf.Segmt      ; (bx:di) = pointer to user's buffer
        CALLFAR SuBuff                  ; transfer user data
        mov     qnps_ibuf.Offst,di      ; update user buffer pointer
        ret

qxr_ebo:
        mov     ax,ERROR_BUFFER_OVERFLOW
        stc
        ret
EndProc qnps_xfer_rec


;**     qnps_check_space - check an NP structure for space/data
;
;       Checks on the NP structure are made to determine if there
;       is data available in the input buffer, space available in
;       the output buffer or if the named pipe is in closing state.
;       For each condition which holds, a record is put into the
;       user data buffer.
;
;       This subroutine uses stack frame variables declared in
;       w_QNmPipeSemState.
;
;       ENTRY   (es:di) = NP structure to be checked
;               (dx)    = NP_SERVER if server side to be checked, else client
;               qnps_irec = data record structure, npss_keyval is filled in
;               qnps_ibuf = user data buffer pointer
;               qnps_ilen = length of user data buffer
;
;       EXIT    C set if error
;                       (ax) = error code
;               otherwise,
;                       qnps_ibuf and qnps_ilen updated to reflect transfer
;
;       USES    AX, SI, DS, Flags

Procedure       qnps_check_space,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        SaveReg <bx,cx>
        test    es:[di.np_flags],NP_SRVRDR  ; serving redirection in effect?
        je      qcs1                    ; no, handle locally

        SaveReg <es,di>
        mov     qnps_req.qnps_len,size qnps_param
        mov     ax,qnps_ilen
        mov     qnps_req.qnps_dlen,ax
        mov     ax,qnps_semhnd.loword
        mov     qnps_req.qnps_semh.loword,ax
        mov     ax,qnps_semhnd.hiword
        mov     qnps_req.qnps_semh.hiword,ax
        mov     ax,qnps_irec.npss_key
        mov     qnps_req.qnps_semkey,ax
        mov     qnps_req.qnps_nbyt,0

        test    dx,NP_SERVER            ; server end of pipe?
        lds     si,es:[di.np_ssft]      ; (ds:si) = SFT pointer
        jnz     qcs0                    ; if test was true(non null) skip
                                        ; loading of the client sft pointer
        lds     si,es:[di.np_csft]      ; (ds:si) = SFT pointer

qcs0:   lea     ax,[si].sf_fsi          ; ds:ax -> sf_fsi
        push    ds
        push    ax                      ; push pointer to sf_fsi
        lea     ax,[si].sf_fsd          ; ds:ax -> sf_fsd
        push    ds
        push    ax                      ; push pointer to sf_fsd
        push    NMP_QNmPipeSemState     ; Op code
        lea     ax,qnps_req             ; ss:ax -> parameter block
        push    ss
        push    ax                      ; push pointer to parameter block
        mov     ax,qnps_ibuf.Segmt
        push    ax
        mov     ax,qnps_ibuf.Offst
        push    ax                      ; push pointer to user buffer
        xor     ax,ax                   ; ax = null pointer
        push    ax
        push    ax                      ; push null pName
; ds:si => server sft
        lds     di,[si].sf_FSC          ; get the fsc pointer for the redir
    assume      ds:nothing              ; from the sft
        call    ds:[di].FS_NMPIPE
        RestoreReg <di,es>
        xor     bx,bx
        cmp     bx,ax                   ; set carry if ax != 0
        jc      qcsx_j                  ; error, go clean up and exit
        mov     cx,qnps_req.qnps_nbyt   ; cx = number of bytes returned
        jcxz    qcsx_j                  ; nothing returned

        sub     qnps_ilen,cx            ; update remaining space in buffer
        add     qnps_ibuf.Offst,cx      ; update buffer pointer
        mov     ax,ERROR_BUFFER_OVERFLOW ; assume error
        jmp     qcsx                    ; C set if address overflowed

qcs1:   mov     ax,es:[di.np_selector2] ; (ax) = incoming buffer's selector
        mov     bx,es:[di.np_selector1] ; (bx) = outgoing buffer's selector
        test    dx,NP_SERVER            ; server end of pipe?
        jnz     qcs1a                   ; yes, we're set up
        xchg    ax,bx                   ; swap ax, bx

qcs1a:  cmp     es:[di.np_state],NP_CLOSING     ; in closing state?
        jne     qcs2
        mov     qnps_irec.npss_status,03h
        mov     qnps_irec.npss_flag,0
        mov     qnps_irec.npss_avail,0
        mov     cx,size npss
        SaveReg <es,di>
        call    qnps_xfer_rec
        RestoreReg <di,es>
        jnc     qcs6                    ; check for available read data
qcsx_j: jmp     qcsx


qcs2:   cmp     es:[di.np_state],NP_CONNECTED   ; in connected state?
        clc
        jne     qcsx_j                  ; no, can't be any buffered data
        or      bx,bx                   ; is outgoing buffer allocated?
        jz      qcs6                    ; no, check for available read data
        SaveReg <ax>
        mov     ds,bx
        xor     si,si                   ; (ds:si) = outgoing NPB
        push    bx
        call    wrt_chk_avail
        pop     bx
        mov     qnps_irec.npss_avail,ax
        mov     qnps_irec.npss_flag,0
        test    ds:[npb_rdlck],-1       ; process waiting?
        jz      qcs3                    ; no, flag is okay
        mov     qnps_irec.npss_flag,1   ; indicate waiting process
qcs3:   RestoreReg <ax>
        cmp     qnps_irec.npss_avail,MSG_HDR_LEN    ; space to start write?
        jbe     qcs6

        SaveReg <ax,di,es>
        mov     qnps_irec.npss_status,02h
        mov     cx,size npss
        call    qnps_xfer_rec           ; output another record
        RestoreReg <es,di,bx>           ; (bx) = incoming buffer selector
        jc      qcsx
        mov     ax,bx

qcs6:   or      ax,ax                   ; is there an incoming buffer?
        jz      qcsx                    ; no, can't be any read data
        SaveReg <di,es>
        mov     ds,ax
        xor     si,si                   ; (ds:si) = outgoing NPB
        call    rd_chk_avail            ; (ax) = read data available
        mov     qnps_irec.npss_avail,ax
        mov     qnps_irec.npss_flag,0
        test    ds:[si.npb_wtlck],-1    ; process waiting?
        jz      qcs7                    ; no, flag is okay
        mov     qnps_irec.npss_flag,1   ; indicate waiting process

qcs7:   cmp     qnps_irec.npss_avail,0  ; any read data available?
        je      qcs8

        mov     qnps_irec.npss_status,01h
        mov     cx,size npss
        call    qnps_xfer_rec           ; output another record

qcs8:   RestoreReg <es,di>
qcsx:   RestoreReg <cx,bx>
        ret
EndProc qnps_check_space



;***    np_handle_chk - lookup SFT and check file type
;
;       ENTRY   (dx)    = file handle
;
;       EXIT    (es:di) point to SFT
;               Carry clear, pipe is okay
;                   TCBThisSFT set
;                   (dx) = pipe mode
;               Carry set, not a named pipe
;                   (ax) = error code
;
;       USES    AX, BX, DX, SI, DI, ES, Flags

Procedure       np_handle_chk,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        mov     bx,dx
        CALLFAR SFFromHandle            ; es:di -> sft
        jc      nhcex
        mov     si,CurrTCB
        mov     ss:[si.TCBThisSFT.Offst],di     ; save pointer to SFT
        mov     ss:[si.TCBThisSFT.Segmt],es     ; save pointer to SFT

        test    es:[di.sf_flags],SF_PIPE        ; is it a pipe?
        jz      nhce                            ; no, error
        test    es:[di.sf_flags],SF_NMPIPE      ; is it a named pipe?
        jz      nhce                            ; no, error

        mov     dx,es:[di.sf_pipmod]            ; get pipe mode
        clc
        ret

nhce:   mov     ax,ERROR_INVALID_FUNCTION
        stc
nhcex:  ret

EndProc         np_handle_chk


Break   <Named pipe structure management>

;***    Two data structures are inportant in the allocation of named
;       pipes.  The first is the Named Pipe Name structure which is
;       a variable length structure allocated via RMP.
;
;       The other important data structure is the Named Pipe structure.
;       These structures are fixed length and are allocated via BMP.
;       Np structures contain information about a particular instance of a
;       named pipe.  Multiple instances of a particular named pipe
;       are kept in a doubly-linked list headed by the npn structure.
;


DATA segment

        PUBLIC  _NmpBmpSel, NmpBmpSel, _NmpRmpHand, NmpRmpHand
_NmpBmpSel      label   WORD            ; D150371
NmpBmpSel       dw      0               ; BMP selector for NP structs
_NmpRmpHand     label   WORD            ; D150371
NmpRmpHand      dw      0,0             ; RMP handle, selector for NPN structs

NmpSerial       dw      0               ; NPN serial number (np_namkey, npn_key)

ActiveNPList    dw      0               ; list of active NP structs

DATA ends

;***    findpipe -- look up a named pipe structure by name
;
;       Find a named pipe structure corresponding to a particular
;       file name.  uppercases name.
;
;       ENTRY   DS:SI   file name
;
;       EXIT    DS:SI   points to significant part of file name
;               'C' clear - structure found
;                       ES:DI   point to named_pipe
;               'C' set - structure not found
;                       AX = ERROR_PATH_NOT_FOUND
;
;       USES    AX, SI, DI, ES, Flags
;

Procedure       findpipe,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        SaveReg <cx>
        call    CheckPipeDir            ; name start with /pipe/ ?
        jne     fipe                    ; no, return error
        add     si,PIPEDIRLEN           ; skip name prefix

        DosContext es
        lea     di,NmpRmpHand           ; (es:di) = pointer to RMP handle
        mov     bx,npn_name             ; (bx) = offset of pipe name in struct
        CALLFAR DStrLen                 ; (cx) = length of pipe name
        CALLFAR Rmp_Find                ; try to find the name
        jc      fipe

        test    word ptr es:[di.npn_link].Segmt,-1 ; make sure sel is non-null
        jz      fipe
        les     di,es:[di.npn_link]
        clc
fipx:   RestoreReg <cx>
        ret

fipe:   mov     ax,ERROR_PATH_NOT_FOUND
        stc
        jmp     short fipx

EndProc findpipe


;***    get_next_instance -- find next matching named pipe structure
;
;       Given a named_pipe structure, find the next instance of
;       the pipe.
;
;       ENTRY   DS:SI   file name
;               ES:DI   current named_pipe structure
;
;       EXIT    'C' clear - structure found
;                       ES:DI   point to named_pipe
;               'C' set - structure not found
;                       AX = ERROR_FILE_NOT_FOUND
;
;       USES    AX, DI, ES, Flags
;

Procedure       get_next_instance,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        mov     di,es:[di.np_flink]     ; (es:di) = next instance of pipe
        or      di,di                   ; is there one?
        je      gni2                    ; no, return error
        clc
        ret

gni2:   mov     ax,ERROR_FILE_NOT_FOUND
        stc
        ret

EndProc get_next_instance



;***    get_free_npn -- get a free named pipe name structure
;
;       Allocate and initialize a named pipe name structure.
;
;       ENTRY   (DS:SI) = pointer to name of pipe
;
;       EXIT    'C' clear - free structure found
;                       (ES:DI) point to npn struct
;                       (BX) = unique serial number of name
;
;               'C' set - no free structure found
;                       (AX) = ERROR_OUT_OF_STRUCTURES
;
;       USES    AX, BX, CX, DI, ES, Flags
;

Procedure       get_free_npn,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        LocalVar        gfnpn_npn,<size npn>
        EnterProc
        SaveReg <dx,si,ds>

        CALLFAR DStrLen                 ; (cx) = length of pipe name
        cmp     cx,MAXNPIPENAME         ; name too big?
        ja      gfnpn_efnf              ; yes, return error
        cmp     cx,1                    ; null file name?
        jbe     gfnpn_eip               ; yes, return error
        mov     al,ds:[si]              ; (al) = first char of name
        JPathChar <gfnpn_eip>           ; jump if name start with '/pipe'...

        push    ss
        pop     es
        lea     di,gfnpn_npn.npn_name   ; (es:di) points to name field
        SaveReg <cx>
    rep movsb                           ; copy the name
        RestoreReg <cx>
        add     cx,(size npn)-MAXNPIPENAME      ; (cx) = length of npn
        mov     gfnpn_npn.npn_len,cx

gfnpn2: DosContext <es,ds>,ax
        lea     di,NmpRmpHand           ; (es:di) = RMP handle for NPN structs
        lea     si,NmpSerial            ; (ds:si) = NMP serial number
gfnpn3: inc     word ptr ds:[si]        ; bump the serial number
        jz      gfnpn3                  ; don't use zero
        mov     bx,npn_key              ; (bx) = offset of serial number field
        mov     cx,2
        CALLFAR Rmp_Find
        jnc     gfnpn2                  ; found one, need to bump serial number

;*      Found a unique serial number, put it into our RMP record and
;       add the record.
;
;       (ds:si) points to serial number

        lodsw                           ; (ax) = serial number
        mov     gfnpn_npn.npn_key,ax
        xor     ax,ax
        mov     gfnpn_npn.npn_link.Offst,ax
        mov     gfnpn_npn.npn_link.Segmt,ax     ; set link field to NULL

        push    ss
        pop     ds
        lea     si,gfnpn_npn            ; (ds:si) = pointer to our npn record
        mov     cx,gfnpn_npn.npn_len    ; (cx) = length of record
        DosContext <es>
        lea     di,NmpRmpHand           ; (es:di) = RMP handle for NPN structs
        CALLFAR Rmp_Add
    assume      es:nothing
        jc      gfnpn_eoos              ; error, out of memory
        mov     bx,es:[di.npn_key]

gfnpnx: RestoreReg <ds,si,dx>
        LeaveProc
        ret

gfnpn_eip:
        mov     ax,ERROR_INVALID_PARAMETER
        jmp     short gfnpne

gfnpn_eoos:
        mov     ax,ERROR_OUT_OF_STRUCTURES
        jmp     short gfnpne

gfnpn_efnf:
        mov     ax,ERROR_FILE_NOT_FOUND
gfnpne: stc                             ; couldn't get a structure
        jmp     short gfnpnx

EndProc get_free_npn



;***    find_npn - look up an NPN structure from an NP structure
;
;       Given a pointer to an NP structure, look up the corresponding
;       NPN structure.
;
;       ENTRY   (ES:DI) = pointer to np
;
;       EXIT    (DS:SI) = pointer to npn
;
;       USES    AX, BX, CX, SI, DS, Flags
;

Procedure       find_npn,near,local
        ASSUME  DS:NOTHING,ES:NOTHING

        SaveReg <es,di>
        push    es
        pop     ds
        lea     si,[di.np_namkey]       ; (ds:si) = pointer to npn key
        mov     bx,npn_key              ; (bx) = offset of key
        mov     cx,2                    ; (cx) = length of key
        DosContext es
        lea     di,NmpRmpHand           ; (es:di) = RMP handle
        CALLFAR Rmp_Find
IFDEF PIPESTRICT
        jnc     gfn3
        InternalError <find_npn: cannot find NPN>
gfn3:
ENDIF
        RestoreReg <si,ds>
        CALLFAR XCHGP
        ret
EndProc find_npn


;***    get_free_np -- get a free named pipe structure
;
;       Allocate an unused named pipe structure.  Link it to
;       other np structures associated with the same name.
;
;       ENTRY   (BX)    = named pipe name key value
;
;       EXIT    'C' clear - free structure found
;                       (ES:DI) point to named_pipe
;               'C' set - no free structure found
;                       (AX) = ERROR_OUT_OF_STRUCTURES
;
;       USES    AX, CX, DI, ES, Flags
;

Procedure       get_free_np,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        SaveReg <bx,dx,si,ds>
        DosContext es
        mov     es,NmpBmpSel
        CALLFAR Bmp_Get
    assume      es:nothing
        jc      gfne                    ; no memory, return error
        mov     es:[di.np_refcnt],-1    ; mark struct as being used
        mov     es:[di.np_flink],0      ; clear link pointers
        mov     es:[di.np_blink],0
        mov     es:[di.np_namkey],bx

        call    find_npn                ; (ds:si) = npn struct

        mov     ax,di
        xchg    ds:[si.npn_link].Offst,ax       ; (ax) = old link value
        mov     ds:[si.npn_link].Segmt,es
        mov     es:[di.np_flink],ax
        or      ax,ax                   ; first NP on list?
        jz      gfn8                    ; yes, don't need to fix back link
        mov     si,ax
        mov     es:[si.np_blink],di

gfn8:   DosContext ds
        mov     ax,di
        xchg    ax,ActiveNPList         ; put new struct at head of active list
        mov     es:[di.np_next],ax      ;   and chain in the next struct
        clc
gfnx:   RestoreReg <ds,si,dx,bx>
        ret

gfne:   stc                             ; couldn't find in entire array
        mov     ax,ERROR_OUT_OF_STRUCTURES
        jmp     short gfnx

EndProc get_free_np


;***    dealloc_np -- free a named pipe structure
;
;       Deallocate an unused named pipe structure.  This might also
;       cause the related npn structure to be freed.
;
;       ENTRY   (ES:DI) point to named_pipe
;
;       EXIT    none
;
;       USES    DI, ES, Flags
;

Procedure       dealloc_np,NEAR,local
        ASSUME  DS:NOTHING,ES:NOTHING

        SaveReg <ax,bx,cx,si,ds>

IFDEF PIPESTRICT
        mov     bx,es:[di.np_selector2] ; still have memory assigned?
        or      bx,es:[di.np_selector1]
        jz      danp1
        InternalError <dealloc_np: memory not freed>
danp1:
ENDIF
        call    find_npn                ; (ds:si) = npn struct

        mov     ax,es:[di.np_flink]     ; (es:ax) = next np struct
        cmp     ds:[si.npn_link].Offst,di       ; our np at head of list?
        jne     danp2                   ; no
        mov     ds:[si.npn_link].Offst,ax

danp2:  mov     bx,es:[di.np_blink]     ; (es:bx) = previous np struct
        or      bx,bx                   ; head of list?
        je      danp4                   ; yes
        mov     es:[bx.np_flink],ax
danp4:

        xchg    ax,bx                   ; swap previous, next pointers
        or      bx,bx                   ; end of list?
        je      danp5                   ; yes
        mov     es:[bx.np_blink],ax
danp5:

;*      Remove the structure from the list of all allocated NPs
;
;       (es:di) = NP struct to be removed
;       (ds:si) = associated NPN structure

        SaveReg <ds,si>
        DosContext ds
        mov     ax,es:[di.np_next]      ; (es:ax) = next in list
        mov     bx,ActiveNPList         ; (es:bx) = first struct on NP list
        cmp     bx,di                   ; is it our struct?
        jne     danp7                   ; no, scan list looking for it
        mov     ActiveNPList,ax         ; set list to our successor
        jmp     short danp8

danp6:  mov     bx,es:[bx.np_next]
        or      bx,bx                   ; end of list?

IFDEF PIPESTRICT
        jnz     danp7
        InternalError <dealloc_np: freed NP not in active list>
ENDIF

danp7:  cmp     es:[bx.np_next],di      ; looking at our predecessor?
        jne     danp6                   ; no
        mov     es:[bx.np_next],ax      ; close up the list

danp8:  RestoreReg <si,ds>

;*      Now deallocate the NPN structure if we just removed the last reference
;       to it.  Note that we shouldn't block between the call to find_npn
;       which looked up the structure and the call to Rmp_Rem below, to
;       prevent other threads from having access to the NPN segment.
;
;       (ds:si) = NPN structure

        cmp     ds:[si.npn_link].Offst,0 ; are we the last np attatched to npn?
        jne     danp9                   ; no

        SaveReg <es,di>
        push    ds
        pop     es
        mov     di,si                   ; (es:di) = NPN record to be deleted
        DosContext ds
        lea     bx,NmpRmpHand           ; (ds:bx) = RMP handle
        CALLFAR Rmp_Rem
    assume      ds:nothing
        RestoreReg <di,es>

danp9:  CALLFAR Bmp_Free                ; free the np structure

        RestoreReg <ds,si,cx,bx,ax>
        ret
EndProc dealloc_np

        public endofnampipe1
endofnampipe1 label byte

NMPIPESWAPCODE   ENDS

        FARCODE HIGH4
HIGH4CODE SEGMENT

;***    CheckPipeDir - check for the pipe pseudo directory name
;
;       A check is made to see if the file name passed in begins with
;       \PIPE\.  It is assumed that the name starts with a `\'.
;
;       ENTRY   DS:SI   points to path name to be checked
;
;       EXIT    Z set if path begins with \PIPE\
;               Z clear otherwise
;
;       USES    AX, Flags

Procedure       CheckPipeDir,hybrid
        ASSUME  DS:NOTHING,ES:NOTHING

        SaveReg <CX,SI,DI>
        mov     di,offset cs:PipeDir    ; get pointer to pseudo-dir name
        mov     cx,PIPEDIRLEN

cpd1:   lodsb                           ; get next character
cpd2:   CALLFAR UCase                   ; upper case the character
        cmp     al,cs:[di]              ; character match?
        jnz     cpdx                    ; yes
        inc     di
        loop    cpd1

        xor     ax,ax                   ; set Z flag
cpdx:   RestoreReg      <DI,SI,CX>
        ret
EndProc         CheckPipeDir

HIGH4CODE ENDS

        FARCODE INIT
INITCODE SEGMENT

;***    NmpInit - initialize the Nmp selector/segment.
;
;       NmpInit allocates two segments mapped to GDT selectors.
;       One is for storage of the NPN structures and is managed
;       by RMP.  The other is for storage of NP structures and
;       is managed by BMP.
;
;       ENTRY   NONE
;
;       EXIT    [NmpBmpSel] initialized to selector of BMP segment.
;               [NmpRmpHand] initialized to point to RMP segment.
;
;       USES    AX, BX, CX, DX, SI, DI, DS, ES, Flags
;

Procedure NmpInit,NEAR
    ASSUME ds:NOTHING, es:NOTHING, ss:NOTHING

        PMONLY

        xor     ax,ax
        mov     bx,Bmp_SegSize
        mov     cx,(D_DATA0 SHL 8) + SA_GDT
        mov     si, (HF_MOVABLE or HF_NONCONTIG)
        mov     di,FSNPIPENPOWNER
        CALLFAR SegAlloc
        jc      nmpie1

        DosContext ds
        mov     NmpBmpSel,bx            ; Save selector returned by SegAlloc
        mov     cx,size np
        mov     dx,3*(size np)

;*      Initialize the segment to be a BMP managed segment.
;
;       (ax) = segment handle
;       (bx) = segment selector
;       (cx) = block size
;       (dx) = grow delta (in blocks)

        CALLFAR Bmp_Init

;       Rmp_Alloc(ax=hobowner,bx=hobmte,esi=PG flags,es:di->rmpdata)

        mov     ax,FSNPIPENPNOWNER
        xor     bx,bx
        mov     esi,PG_SWAPPABLE
        DosContext es
        mov     di,offset NmpRmpHand
        CALLFAR Rmp_Alloc
        jc      nmpie2
        ret

nmpie1: Panic <'NmpInit: BMP_Init'>
nmpie2: Panic <'NmpInit: RMP_Alloc'>
EndProc NmpInit

INITCODE ENDS
        END
