Metropoli BBS
VIEWER: putenv.asm MODE: TEXT (ASCII)
        name    putenv
        page    55,132
        title   PUTENV modify environment variable
;
; PUTENV.ASM --- add or modify environment variable
;
; (C) 1987 Ziff Communications Co, by Ray Duncan
;
; Call with:    DS:SI = new ASCIIZ environment variable 
;               ES    = segment of program segment prefix
;
; Returns:      AX    = 0 if successful, -1 if failed

DGROUP  group   _DATA

_TEXT   segment word public 'CODE'

        assume  cs:_TEXT

        public  putenv          ; make visible to Linker

        extrn   getenv:near

                                ; names for working storage
oldenv  equ     [bp-2]          ; segment of old environment
envlen  equ     [bp-4]          ; length of old environment
newenv  equ     [bp-6]          ; segment of new environment
pspseg  equ     [bp-8]          ; segment of program segment prefix
oldlen  equ     [bp-10]         ; length of existing variable
newlen  equ     [bp-12]         ; length of new env. variable
newname equ     [bp-14]         ; offset of new name portion
newvar  equ     [bp-16]         ; offset of new param. portion

putenv  proc    near            ; modify/add environment variable

        push    bp              ; set up stack frame for
        mov     bp,sp           ;  working storage
        sub     sp,16

        mov     newname,si      ; save offset of new variable
        mov     pspseg,es       ; save PSP segment
        mov     es,es:[002ch]   ; pick up segment of old 
        mov     oldenv,es       ;  environment and save it

        push    cx              ; save other registers
        push    si
        push    di
        
                                ; find old environment length
        mov     cx,8000h        ; assume max = 32 KBytes
        xor     di,di           ; ES:DI = environment base
        xor     al,al
put1:   repne scasb             ; scan for double null (can't use
        scasb                   ; SCASW since might be on odd addr)
        jne     put1

        mov     envlen,di       ; save length of old environment

        mov     di,si           ; find length of new env. variable
        mov     ax,ds           ; ES:DI = addr of new variable  
        mov     es,ax
        mov     cx,-1
        xor     al,al
        repne scasb             ; scan for terminating null byte
        not     cx              ; now CX = length including null
        mov     newlen,cx       ; save length of new variable

                                ; attempt to allocate memory
                                ; block for new environment...  
        mov     bx,cx           ; length of old environment
        add     bx,envlen       ; + length of new variable
        mov     cl,4            ; divide by 16 and round
        shr     bx,cl           ; up to find paragraphs to 
        inc     bx              ; allocate for new environment
        mov     ah,48h          ; Fxn 48h=allocate memory block
        int     21h             ; transfer to MS-DOS
        jnc     put2            ; jump if allocation succeeded

        mov     ax,-1           ; otherwise, return error flag
        jmp     put6

put2:   mov     newenv,ax       ; save new environment segment

        mov     di,offset envar ; copy name portion of new 
                                ; variable to local storage

put3:   movsb                   ; copy characters up to '='
        cmp     byte ptr [si],'='
        jne     put3            ; loop until end of name found
        xor     al,al           ; append null byte to name
        stosb

        inc     si              ; save address of param.
        mov     newvar,si       ; portion of new variable

        mov     si,offset envar ; check if new environment 
        mov     es,oldenv       ; variable has a previous value
        call    getenv          ; in old environment
        mov     oldlen,ax       ; save length of value, if any

        or      ax,ax           ; was it present in old block?
        jnz     put4            ; yes, PUTENV the hard way

        push    ds              ; no just copy existing environment
                                ; and add the new variable

        mov     ds,oldenv       ; DS:SI = old env. block
        mov     es,newenv       ; ES:DI = new env. block
        mov     cx,envlen       ; CX = length of old environment 
        dec     cx              ; less the extra null byte
        xor     si,si
        xor     di,di
        rep movsb               ; copy the old stuff

        pop     ds              ; DS:SI = address of new variable 
        mov     si,newname      ; ES:DI = end of new environment
        mov     cx,newlen       ; CX = length of new variable
        rep movsb               ; append the new variable
        xor     al,al           ; and the extra null byte
        stosb                   ; marking end of environment
        jmp     put5            ; go update PSP env. pointer

put4:                           ; come here on the messy case,
                                ; env. variable already exists
                                ; ES:DI = offset+1 of '=' in
                                ; old variable from 'getenv'

        push    ds              ; copy old environment to new
        mov     ax,es           ; up through the '=' of the
        mov     ds,ax           ; variable we are changing
        mov     cx,di           ; DS:SI = old environment
        mov     es,newenv       ; ES:DI = new environment
        xor     si,si           ; CX = offset+1 of '=' 
        xor     di,di           ;      in old variable
        rep movsb

        pop     ds              ; now let DS:SI = offset+1 
        push    si              ; of '=' in new variable
        mov     si,newvar       ; and CX = length of portion
        mov     cx,newname      ; following the '='
        add     cx,newlen
        sub     cx,newvar       ; copy the new parameter portion
        rep movsb               ; to the new environment

        pop     si              ; skip over parameter portion
        add     si,oldlen       ; of the old variable
        inc     si              ; and its null byte

        push    ds              ; now copy remainder of
        mov     ds,oldenv       ; old environment to new one
        mov     cx,envlen       ; total length less portion
        sub     cx,si           ; already copied and length
        rep movsb               ; of old environment variable
        pop     ds

put5:                           ; PUTENV function successful,
        mov     es,oldenv       ; release old environment block
        mov     ah,49h
        int     21h             ; transfer to MS-DOS

        mov     es,pspseg       ; update pointer to new 
        mov     ax,newenv       ; environment in caller's PSP
        mov     es:[002ch],ax

        xor     ax,ax           ; return success code

put6:   pop     di              ; restore registers
        pop     si
        pop     cx
        mov     sp,bp           ; discard stack frame
        pop     bp
        ret                     ; back to caller

putenv  endp

_TEXT   ends


_DATA   segment word public 'DATA'

envar   db      80 dup (0)      ; name of new environment var.
                                ; for call to 'getenv'
_DATA   ends

        end

[ RETURN TO DIRECTORY ]