
;
; HISTORY:
;
; 10/06/93 ChangeTeam D74380 procedure NmpHandleCheck                    ;74380
;          was returning ERROR_INVALID_FUNCTION if the handle in BX was  ;74380
;          for StdIn, StdOut, or StdErr.  Changed to return this error   ;74380
;          if and only if the handle truly points to Stdin, StdOut, or   ;74380
;          StdErr (points to console).                                   ;74380
;
; 10/21/93 ChangeTeam D74997 procedure NmpHandleCheck:  change const     ;74997
;          to newly added constant for con device                        ;74997
;          Ref D74997 in src\dos\dosinc\dos3dev.inc                      ;74997
;
        title   Macro.asm
        page    ,132

INCL_DOSERRORS  EQU     1
.xlist
include v86seg.inc                      ;get appropriate segment definitions
.xcref
include bseerr.inc                      ;{INC}
include demcd.inc
include struc30.inc
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include demmac.inc
include dossymc.inc
include filemode.inc
include opncreat.inc
include dos3dev.inc                                                      ;74380
include error.inc
include mi.inc
include demsvc.inc
include mvdmexp.inc
include dirent.inc
include demdbcs.inc
include devhdr.inc
include syscalls.inc
include demmult.inc
.cref
.list
        CpuMode 386

        ReferV86    TempBX,WORD
        EXTRN   MyName: byte
        EXTRN   DIFFNAM: byte
;---------------------------------------
V86TABLE SEGMENT
        EXTRN   internal_val_seg: word
V86TABLE ENDS
;---------------------------------------
V86LoL  SEGMENT
        EXTRN   MyNum: word
V86LoL  ENDS
;---------------------------------------
V86SDA  SEGMENT
        EXTRN   THISCDS: Dword
        EXTRN   CurDrv: byte
        EXTRN   fSharing: byte
        EXTRN   DummyCDS: byte
        EXTRN   EXTERR_LOCUS: byte
        EXTRN   DrvErr: byte
V86SDA  ENDS
;---------------------------------------
V86LoL  SEGMENT
        EXTRN   NUMIO: byte
        EXTRN   CDSCount: byte
        EXTRN   CDSAddr: Dword
V86LoL  ENDS
;---------------------------------------
V86CODE SEGMENT
        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

        EXTRN   VSFNFromHandle:NEAR
        EXTRN   RMDevCheck:NEAR
        EXTRN   Sys_Ret_OK:NEAR
        EXTRN   Sys_Ret_Err:NEAR
        EXTRN   Sys_Ret_Err_AL:NEAR
        EXTRN   get_user_stack:NEAR

BREAK <$AssignOper - Int 21 Fun 5f dispatcher>
; Input:
;       AH = 5Fh
;       AL = Name Pipe Subfunction
;             0..6 = DOS 4 functions
;             7..8 = new DOS 5 functions
;             32h..3Ah = Named pipe functions
;             F2h..FAh = Named pipe functions
;       other registers depending on al
;
; Output:
;       Carry Clear - Success; Resulting registers depend on al
;       Carry Set   - Failure;
;
; Uses

        PUBLIC  apNPipeFunc
BeginNPipeFunc  equ     032h
BeginNPipeFunc2 equ     0F2h
apNPipeFunc     label   word
        dw      OFFSET V86GROUP:QnmPipeInfo          ; 5F32 F2
        dw      OFFSET V86GROUP:QNmpHandState        ; 5F33 F3
        dw      OFFSET V86GROUP:SetNmpHandState      ; 5F34 F4
        dw      OFFSET V86GROUP:PeekNmPipe           ; 5F35 F5
        dw      OFFSET V86GROUP:TransactNmPipe       ; 5F36 F6
        dw      OFFSET V86GROUP:CallNmPipe           ; 5F37 F7
        dw      OFFSET V86GROUP:WaitNmPipe           ; 5F38 F8
        dw      OFFSET V86GROUP:RawReadNmPipe        ; 5F39 F9
        dw      OFFSET V86GROUP:RawWriteNmPipe       ; 5F3a Fa
cNPipeFunc  equ ($-apNPipeFunc) / 2

;Function 5F00h  Get Redirection mode
;                input:  AX=5F00h
;                        BL=redirection type (BL=3 for printer, =4 disk drive)
;                output: CY, AX=error code, else NC
;                        BH=redirection state (0=off, 1=on)
;Function 5F01h  Set Redirection mode
;                input:  AX=5F01h
;                        BL=redirection type (BL=3 for printer, =4 disk drive)
;                        BH=redirection state (0=off, 1=on)
;                output: CY, AX=error code, else NC
;Function 5F02h  Get Redirection list entry
;                input:  AX=5F02h
;                        BX=redirection list index
;                        DS:SI=addr of 16-byte buffer(for asciiz device name)
;                        ES:DI=addr of 128-byte buffer(for asciiz network name)
;                output: CY, AX=error code, else NC
;                        BH=device status (0=valid, 1=invalid)
;                        BL=device type (3=printer, 4=disk drive)
;                        CX=user data previously set by 5F03h
;                        DS:SI=buffer filled in
;                        ES:DI=buffer filled in
;Function 5F03h  Redirect device
;                input:  AX=5F03h
;                        BL=redirection type
;                           0=alias
;                           1=file/device
;                           2=drive
;                           3=char device, network
;                           4=file device, network
;                        CX=user data to save
;                        DS:SI=addr of 16-byte buffer with asciiz device name
;                        ES:DI=addr of 128-byte buffer with asciiz network name
;                output: CY, AX=error code, else NC
;Function 5F04h  Cancel redirection
;                input:  AX=5F04h
;                        DS:SI=addr of asciiz device name
;                output: CY, AX=error code, else NC
;Function 5F05h  Get redirection list extended entry
;                input:  AX=5F05h
;                        BX=redirection list index
;                        DS:SI=addr buffer for asciiz source device name
;                        ES:DI=addr buffer for destination asciiz network path
;                output: CY, AX=error code, else NC
;                        BH=device status flag (bit 0 clear if valid)
;                        BL=redirection type (BL=3 for printer, =4 disk drive)
;                        CX=stored parameter value (user data)
;                        BP=NETBIOS local session number
;                        DS:SI=buffer filled in
;                        ES:DI=buffer filled in
;Function 5F06h  Get IFSFUNC item
;                input:  AX=5F06h
;                output: CY, AX=error code, else NC
;Function 5F07h  Enable drive (set in use of a drive's CDS)
;                input:  AX=5F06h
;                        DL=drive number (0=A:, 1=B:,...)
;Function 5F08h  Disable drive (reset in use of a drive's CDS)
;                input:  AX=5F06h
;                        DL=drive number (0=A:, 1=B:,...)

procedure $AssignOper,NEAR

        .select

        .when   <AL eq 7> or
        .when   <AL eq 8>
;         push    ax
;         mov     al,dl
;         call    GetCDSFromDrv
;         pop     ax
;         .if     nc and
;         .if     <<word ptr [si].CurDir_DevPtr> ne 0>
;           .if     <AL eq 7>
;             or      [si].CurDir_flags,CurDir_InUse
;           .else
;             and     [si].CurDir_flags,NOT CurDir_InUse
;           .endif
;           jmp     SYS_RET_OK
;         .endif
          mov     ax,ERROR_INVALID_DRIVE

        .when   <AL ae BeginNPipeFunc> and
        .when   <AL b <BeginNPipeFunc+cNPipeFunc>>
          ;*named pipe 32..3A
          sub     al,BeginNPipeFunc
Named_pipe:
          xor     ah,ah
          mov     [TempBX],bx
          mov     bx,ax
          shl     bx,1
          push    apNPipeFunc[bx]
          mov     bx,[TempBX]
          ret

        .when   <AL ae BeginNPipeFunc2> and
        .when   <AL b <BeginNPipeFunc2+cNPipeFunc>>
          ;*named pipe F2..FA
          sub     al,BeginNPipeFunc2
          jmp     short Named_pipe

        .otherwise
          push    ax
          mov     ax,111Eh    ;=(multnet shl 8) or 30
          int     2Fh
          pop     bx
          .if     nc
            jmp     sys_ret_ok
          .endif

        .endselect

        jmp     sys_ret_err

EndProc $AssignOper

BREAK <NmpHandleCheck - Check file handle for named pipe calls>
; Input
;       BX is handle
; Output
;       CY clear if valid handle
;       CY set if invalid
;
; Uses  AX= error code if cy set
;       BX= VSFN if no cy

procedure NmpHandleCheck,NEAR              ;local subroutine

        push    cx                                                       ;74380
        mov     cx,bx                      ; copy handle to CX           ;74380
        call    VSFNFromHandle             ; get the VSFN into BX        ;74380
        .if     nc                         ; exit if bad handle          ;74380
           .if     <CX be stderr>          ; StdIn, StdOut, or StdErr?   ;74380
                                                                         ;74380
;D74997       .if     <BL eq VSFN_CONSOLE>                               ;74380
              .if     <BL eq DOS3DEV_CON>                                ;74997
              ; StdIn,Out,Err pointing to CON?                           ;74380
                  ; These may not be pointing to CON - i.e. I/O          ;74380
                  ; redirection or the device could have been            ;74380
                  ; closed and another file given its old handle.        ;74380
                  ; Retn error only if truly pointing to CON.            ;74380
                 mov     ax, ERROR_INVALID_FUNCTION                      ;74380
                 pop     cx                                              ;74380
                 stc                                                     ;74380
                 ret                                                     ;74380
              .endif                                                     ;74380
           .endif                                                        ;74380
           call    RMDevCheck
           .if     z                       ; error if Real Mode device
              pop     cx
              clc
              ret
           .endif
        .endif

        pop     cx                                                       ;74380
        stc
        ret
EndProc NmpHandleCheck

BREAK <QNmPipeInfo - Get info on a named pipe>
; Inputs:
;       BX is handle of pipe (must be checked)
;       CX is size of buffer for info
;       DX is Level of info to get
;       DS:SI is address of buffer
; Function:
;       Get pipe info
; Returns:
;       Carry set
;           AX = error
;       Carry Clear
;           OK
; Uses all

        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
procedure   QnmPipeInfo,NEAR            ;function 5F32h
        call    NmpHandleCheck          ; Handle sanity check
        .if     nc
          SVC     SVC_DEMQNMPINFO       ; let OS/2 do the work
          .if     nc                    ; skip if error
            jmp     Sys_Ret_OK          ; return OK
          .endif
        .endif
        jmp     Sys_Ret_Err             ; return error
EndProc QnmPipeInfo

BREAK <QNmpHandState - Get pipe mode information>
; Inputs:
;       BX is handle of pipe (must be checked)
; Function:
;       Return mode of pipe
; Returns:
;       Carry clear
;           AX = Pipe mode word
;       Carry set
;           AX = error
; Uses all

        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
procedure   QNmpHandState,NEAR          ;function 5F33h
        call    NmpHandleCheck          ; Handle sanity check
        .if     nc
          SVC     SVC_DEMQNMPHANDSTATE  ; let OS/2 do the work
          .if     nc                    ; skip if error
            call    get_user_stack
            ASSUME DS:NOTHING
            mov     [SI.user_AX],ax     ; Save Pipe Mode
            jmp     Sys_Ret_OK          ; return OK
          .endif
        .endif
        jmp     Sys_Ret_Err             ; return error

EndProc QNmpHandState

BREAK <SetNmpHandState - Set pipe mode>
; Inputs:
;       BX is handle of pipe (must be checked)
;       CX is pipe mode word to set
; Function:
;       Set mode of pipe
; Returns:
;       Carry Clear
;           Indicated mode set
;       Carry set
;           AX = error
; Uses all

        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
procedure   SetNmpHandState,NEAR        ;function 5F34h
        call    NmpHandleCheck          ; Handle sanity check
        .if     nc
          SVC     SVC_DEMSETNMPHANDSTATE  ; let OS/2 do the work
          .if     nc                    ; skip if error
            jmp     Sys_Ret_OK          ; return OK
          .endif
        .endif
        jmp     Sys_Ret_Err             ; return error
EndProc SetNmpHandState


BREAK <PeekNmPipe - Peek on a named pipe>
;***    PeekNmPipe - Peek on a named pipe
;
; Inputs:
;       BX is handle of pipe (must be checked)
;       CX is size of buffer for peek
;       DS:SI is address of peek buffer
; Function:
;       Peek from pipe
; Returns:
;       Carry set
;           AX = error code. IF ERROR IS ERROR_MORE_DATA Carry
;                clear returns are also set.
;       Carry Clear
;           CX = bytes peeked from pipe
;           User DX:SI is available data
;           AX is pipe status
; Uses all
;
        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
procedure   PeekNmPipe,NEAR             ;function 5F35h
        call    NmpHandleCheck          ; Handle sanity check
        .if     nc
          SVC     SVC_DEMPEEKNMP        ; let OS/2 do the work
          mov     bx,si
          call    get_user_stack
          .if     nc                    ; skip if error
            ASSUME  ds:NOTHING
            mov     [si.user_ax],ax
            mov     [si.user_cx],cx
            mov     [si.user_dx],dx
            mov     [si.user_si],bx
            jmp     Sys_Ret_OK          ; return OK
          .endif
        .endif
        .if     <AX eq ERROR_MORE_DATA>
          mov     [si.user_cx],cx
          mov     [si.user_dx],dx
          mov     [si.user_si],bx
        .endif
        jmp     Sys_Ret_Err             ; return error
EndProc PeekNmPipe

BREAK <TransactNmPipe - Do a transact named pipe>
; Inputs:
;       BX is handle of pipe (must be checked)
;       CX is source (write) buffer length
;       DX is destination (read) buffer length
;       DS:SI is address of source (write) buffer
;       ES:DI is address of destination (read) buffer
; Function:
;       Write the data part of the transaction
;       NOTE: IT IS ASSUMED THE RESPONSE SMB HAS ONE PARAMETER WHICH IS THE
;             AMOUNT WRITTEN.
; Returns:
;       Carry set
;           AX = error code. IF ERROR IS ERROR_MORE_DATA Carry
;                clear returns are also set.
;       Carry Clear
;           CX = bytes read into destination (read) buffer address
; Uses all
        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
procedure   TransactNmPipe,NEAR         ;function 5F36h
        call    NmpHandleCheck          ; Handle sanity check
        .if     nc
          SVC     SVC_DEMTRANSACTNMP    ; let OS/2 do the work
          call    get_user_stack
          .if     nc                    ; skip if error
            ASSUME  DS:NOTHING
            mov     [si.user_cx],cx
            jmp     Sys_Ret_OK          ; return OK
          .endif
        .endif
        .if     <AX eq ERROR_MORE_DATA>
          mov     [si.user_cx],cx
        .endif
        jmp     Sys_Ret_Err             ; return error
EndProc TransactNmPipe

BREAK <CallNmPipe - Do a call named pipe>
; Inputs:
;       DS:SI -> Call named pipe parameter structure
; Function:
;       Do a call named pipe
; Returns:
;       Carry set
;           AX = error code. IF ERROR IS ERROR_MORE_DATA Carry
;                clear returns are also set.
;       Carry Clear
;           CX = Bytes read
; Uses all

        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
procedure   CallNmPipe,NEAR             ;function 5F37h
        SVC     SVC_DEMCALLNMP          ; let OS/2 do the work
        call    get_user_stack
        .if     nc                      ; skip if error
          ASSUME  DS:NOTHING
          mov     [si.user_cx],cx
          jmp     Sys_Ret_OK            ; return OK
        .endif
        .if     <AX eq ERROR_MORE_DATA>
          mov     [si.user_cx],cx
        .endif
        jmp     Sys_Ret_Err             ; return error
EndProc CallNmPipe

BREAK <WaitNmPipe - Do a wait named pipe>
; Inputs:
;       DS:DX -> Pipe name
;       BX:CX = Timeout value
; Function:
;       Do a wait named pipe
; Returns:
;       Carry set
;           AX = error
;       Carry Clear
;
; Uses all
procedure   WaitNmPipe,NEAR             ;function 5F38h
        SVC     SVC_DEMWAITNMP          ; let OS/2 do the work
        .if     nc                      ; skip if error
          jmp     Sys_Ret_OK            ; return OK
        .endif
        jmp     Sys_Ret_Err             ; return error
EndProc WaitNmPipe


BREAK <RawReadNmPipe/WriteNmPipe - Do raw I/O on a pipe>
;
; Notes about RAW read write.
;
;       Since we only buffer on pipes that are BYTE mode at both ends,
;           the RA and WB buffer data is the same as the RAW data. Thus the
;           "buffered" case is the same as normal read and write.
;
;       These calls are not high volume, they aren't even supposed to be
;           documented. Thus we will sort of PUNT on the efficiency issue as
;           far as "buffering" is concerned.
;
;           Both Read and Write simply FLUSH the WB buffer first. On read if
;               there is a RA buffer, we will satisfy the first part of the read
;               from the buffer, flush the buffer (if we suck up all of it) and
;               then complete the read from the server WITHOUT doing any
;               buffering.
;
;           In both of the unbuffered cases we simply send the RW to the server
;               with NO check on its size. This will be gag me slow if the user
;               is stupid enough to do it in repeated small pieces.
;
; Inputs:
;       BX is handle of pipe (must be checked)
;       CX is size to read
;       DS:DX is address of buffer
; Function:
;       Do a Raw read transaction on the pipe
; Returns:
;       Carry set
;           AX = error code. IF ERROR IS ERROR_MORE_DATA Carry
;                clear returns are also set.
;       Carry Clear
;           CX = bytes read into transfer address
; Uses all

        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
procedure   RawReadNmPipe,NEAR          ;function 5F39h
        call    NmpHandleCheck          ; Handle sanity check
        .if     nc
          SVC     SVC_DEMRAWREADNMP     ; let OS/2 do the work
          call    get_user_stack
          .if     nc                    ; skip if error
            ASSUME  DS:NOTHING
            mov     [si.user_cx],cx
            jmp     Sys_Ret_OK          ; return OK
          .endif
        .endif
        .if     <AX eq ERROR_MORE_DATA>
          mov     [si.user_cx],cx
        .endif
        jmp     Sys_Ret_Err             ; return error
EndProc RawReadNmPipe


; Inputs:
;       BX is handle of pipe (must be checked)
;       CX is size to write
;       DS:DX is address of buffer
; Function:
;       Do a Raw write transaction on the pipe
; Returns:
;       Carry set
;           AX = error
;       Carry Clear
;           CX = bytes written from transfer address
; Uses all

        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
procedure   RawWriteNmPipe,NEAR         ;function 5F3Ah
        call    NmpHandleCheck          ; Handle sanity check
        .if     nc
          SVC     SVC_DEMRAWWRITENMP    ; let OS/2 do the work
          .if     nc                    ; skip if error
            jmp     Sys_Ret_OK          ; return OK
          .endif
        .endif
        jmp     Sys_Ret_Err             ; return error
EndProc RawWriteNmPipe


        assume  cs:V86GROUP,ds:nothing,es:nothing,ss:nothing

        subttl  InitCDS - set up an empty CDS
        page

;**     InitCDS - Setup an Empty CDS
;
; [THISCDS] := &CDS to create
; mov  al,"C"  drive letter
; push ax
; mov  ax,121Fh
; int  2Fh
;
;       ENTRY   [ThisCDS]=points to CDS to create
;               AL=has uppercase drive letter
;       EXIT    ES:DI=created CDS
;               CY=no DPB asscoiated with drive
;                  flags=0
;               NC=good CDS create
;                  text="A:\"0
;                  flags=4000h (in-use)
;                  ID=0,0
;                  END=2
;                  DEVPTR=-1 (no DPB support yet)
;       USES    AH,ES,DI, Flags

public  InitCDS
InitCDS proc    near
        assume  cs:V86GROUP,ds:nothing,es:nothing,ss:nothing

        push    ax                      ; save (AL) for caller
        LES     DI,cs:[THISCDS]         ; (es:di) = CDS address
        mov     es:[di].curdir_flags,0  ; "free" CDS
        sub     al,"A"-1                ; A=1, B=2, ...
        cmp     cs:[NumIO],AL
        pop     ax
        .if     ae
          mov     byte ptr es:[di].curdir_text+0,al      ; set "x:"
          mov     byte ptr es:[di].curdir_text+1,":"     ; set "x:"
          mov     word ptr es:[di].curdir_text+2,"\"   ; NUL terminate
          or      byte ptr ES:[DI].curdir_flags+1,curdir_inuse SHR 8
          mov     es:[DI].curdir_ID,0
          mov     es:[DI].curdir_ID+2,0
          mov     es:[DI].curdir_END,2
          mov     word ptr es:[di].curdir_devptr,-1
          mov     word ptr es:[di].curdir_devptr+2,-1
        .endif

        ret

InitCDS endp

        subttl  Getthisdrv - map a drive designator (0=def, 1=A...)
        page

;
;   GetThisDrv - look through a set of macros and return the current drive
;                and macro pointer
;
;   Inputs:     AL has drive identifier (0=default, 1=A, 2=B,...)
;   Outputs:
;               Carry Set - invalid drive/macro
;               Carry Clear - AL has physical drive (0=A)
;                  ThisCDS points to macro
;   Registers modified: AL

public  GetThisDrv
GetThisDrv proc near
        assume  cs:V86GROUP,ds:nothing,es:nothing,ss:nothing

        .if     <ZERO AL>               ; are we using default drive?
          mov     al,cs:[CurDrv]        ; get the current drive
          inc     ax                    ; Counteract next instruction
        .endif
        dec     ax                      ; 0 = A

        push    ds
        push    si

        mov     cs:[EXTERR_LOCUS],errLOC_Disk

        .if     <<cs:[fSharing]> ne 0>     ; Logical or Physical?

          ;fSharing==TRUE, means no redirection
          push    ax
          push    es
          push    di

          mov     word ptr cs:[ThisCDS],offset V86GROUP:DummyCDS
          mov     word ptr cs:[ThisCDS]+2,cs    ;[ThisCDS] = &DummyCDS
          add     AL,'A'
          call    InitCDS                              ;InitCDS(c);
          TEST    es:[di].curdir_flags,curdir_inuse    ; Clears carry
          ;ZR=drive letter is not InUse
          ;NZ=drive letter InUse

          pop     di
          pop     es
          pop     ax

          JZ      short gtd30           ; Not a physical drive, drive not InUse
                                        ; carry clear
        .else

          ;fSharing==FALSE, means redirection can exist

;;;;;;;;;;invoke  GetCDSFromDrv
;;;;;;;;;;.if     c or                  ; Unassigned CDS -> return error already set
          .if     <BIT <ds:[si].curdir_flags> nand curdir_inuse>
gtd30:
            mov     AL,error_invalid_drive      ; invalid FAT drive
            mov     cs:[DrvErr],AL              ; save this for IOCTL
            mov     cs:[EXTERR_LOCUS],errLOC_UNK
            stc
          .endif
        .endif
        pop     si
        pop     ds
        ret

GetThisDrv endp


subttl  GetCDSFromDrv - convert a drive number to a CDS pointer
page

;
;   GetCDSFromDrv - given a physical drive number, convert it to a CDS
;       pointer, returning an error if the drive number is greater than the
;       number of CDS's
;
;   Inputs:     AL is physical unit # A=0...
;   Outputs:    Carry Set if Bad Drive
;               Carry Clear
;                   DS:SI -> CDS
;                   [THISCDS] = DS:SI
;   Registers modified: DS,SI

public  GetCDSFromDrv
GetCDSFromDrv proc near
        assume  cs:V86GROUP,ds:nothing,es:nothing,ss:nothing

        ; is this a valid designator
        ; yes, go get the macro
        .if     <AL ae <cs:[CDSCount]>>
          stc                           ; signal error
          ret
        .endif
        push    bx
        push    ax
        LDS     SI,cs:[CDSAddr]         ; get pointer to table
        mov     bl,SIZE CurDir_list     ; size in convenient spot
        mul     bl                      ; get net offset
        add     si,ax                   ; convert to true pointer
        mov     word ptr cs:[ThisCDS],si   ; store convenient offset
        mov     word ptr cs:[ThisCDS+2],ds ; store convenient segment
        pop     ax
        pop     bx
        clc                             ; no error
        ret
GetCDSFromDrv endp



        subttl  Int 21h, AH=5Eh
        page
        subttl  dos5=macro.asm $UserOper - get/set current user ID (for net)>

;
;   $UserOper - retrieve or initiate a user id string.  MSDOS will only
;       maintain this string and do no verifications.
;
;   Inputs:     AL has function type (0-get 1-set 2-printer-set 3-printer-get
;                                     4-printer-set-flags,5-printer-get-flags)
;               DS:DX is user string pointer (calls 1,2)
;               ES:DI is user buffer (call 3)
;               BX is assign index (calls 2,3,4,5)
;               CX is user number (call 1)
;               DX is flag word (call 4)
;   Outputs:    If AL = 0 then the current user string is written to DS:DX
;                       and user CX is set to the user number
;               If AL = 3 then CX bytes have been put at input ES:DI
;               If AL = 5 then DX is flag word

        PUBLIC  $UserOper
        assume  cs:V86CODE,ss:V86GROUP,ds:nothing,es:nothing
$UserOper       proc    near

        .select
        .when   <ZERO AL>
          ;------ Get user, transfer MYNAME to DS:DX, set return CX to MYNUM
          assume  ds:nothing,es:nothing
          push    ds
          pop     es
          mov     di,dx                   ;ES:DI=user string
          push    ds                                                    ;55160;
          mov     ds,cs:[Internal_val_seg]                              ;55160;
          assume  ds:V86GROUP                                           ;55160;
          mov     cx,ds:[MYNUM]           ; Get number                  ;55160;
          pop     ds                                                    ;55160;
          assume  ds:nothing                                            ;55160;
          call    get_user_stack
          mov     [SI].User_CX,CX         ; Set number return
          push    cs
          pop     ds
          assume  ds:V86GROUP
          mov     si,offset V86GROUP:MyName   ; point source to user string
UserMove:
          mov     cx,15
          rep     movsb
          xor     ax,ax                   ; 16th byte is 0
          stosb
          jmp     sys_ret_ok

        .when   <AL eq 1>
          assume  ds:nothing,es:nothing
          ;------ Set user, transfer DS:DX to MYNAME, CX to MYNUM
          push    ds                                                    ;55160;
          mov     ds,cs:[Internal_val_seg]                              ;55160;
          assume  ds:V86GROUP                                           ;55160;
          mov     ds:[MYNUM],cx                                         ;55160;
          pop     ds                                                    ;55160;
          assume  ds:nothing                                            ;55160;
          ;------
          mov     si,dx                 ; user space has source
          push    cs
          pop     es
          assume  es:V86GROUP
          mov     di,offset V86GROUP:MyName     ; point dest to user string
          inc     ss:[DiffNam]          ; signal change
          jmp     short UserMove

        .when   <AL be 5>
          assume  ds:nothing,es:nothing
          push    ax
          mov     ax,(multNET SHL 8) OR 31     ;=111Eh  "Printer_set_string"
          int     2Fh
          pop     dx                      ; Clean stack
          .if     c
            jmp      SYS_RET_ERR
          .endif
          jmp      SYS_RET_OK

        .otherwise
          mov     ss:[EXTERR_LOCUS],errLoc_Unk  ;Extended Error Locus
          mov     al,ERROR_INVALID_FUNCTION
          jmp     SYS_RET_ERR_AL
        .endselect

$UserOper       endp

;--------------------------------------

V86CODE ENDS

        END
