                page    66,132
;============================================================================
; ALIAS.COM adds a command line stack, a command line editor, and
; an alias function to COMMAND.COM.  Syntax is:
;
;       ALIAS [alias [command]] [/F filename] [/S nn] [/U]
;             [/B] [/E] [/D] [/L] [/U] [/*] [//]
;
; where /B = Set size of buffer for future alias commands
;       /D = Disable alias translation
;       /E = Enable alias translation
;       /F = Filename of file with list of commands
;       /L = List aliases currently in list
;       /M = Set minimum command length to save
;       /S = Size of command line stack where "nn" is the number of commands
;            to save
;       /U = Uninstall the program
;       /* or // = Comment. Ignore remainder of the line.
;
;
; Revision History:
;
;       Version 1.0     Initial Release         PC Magazine Vol 8 Num 22
;
;       Version 1.1     Fixed %% bug            January 3, 1989
;                       Fixed DOS 3.2 bug
;                       Now works in OS/2 DOS box
;                       Support for COMMAND.COM shelling
;                       Reduced installation memory requirments
;
;============================================================================

                code    segment
                assume  cs:code

                org     2ch
env_segment     dw      ?                       ;Word containing the segment
                                                ;  of the program's env. block.
                org     80h
command_tail    db      ?                       ;Offset of the command tail.

                org     100h

main:           jmp     initialize
program         db      13,10,"ALIAS 1.1 "
copyright       db      "(c) 1990 Ziff Communications Co.",10,13
authors         db      "PC Magazine ",254," Doug Boling and Jeff Prosise"
                db      10,13,"$",1Ah
;
;Change the following two numbers to adjust the size of alias on installation.
;
filebuf_size    dw      0400h                   ;Size of input file buffer.
max_code_size   dw      0AF0h                   ;Max size of installed code.

aliaslist_ptr   dd      ?                       ;Pointer to alias list.
aliaslist_size  dw      ?                       ;Pointer to end of list seg
work_buff_ptr   dw      ?                       ;Pointer to alias working buff

chk_alias       db      1                       ;Alias enable flag.
minlength       db      1                       ;Minimum len of cmd to stack
cmdstack_base   dw      ?                       ;Offset of command stack
cmdstack_size   dw      16                      ;Size of command stack.

cmdcom_psp      dw      0                       ;Segment of COMMAND.COM PSP
master_env      dw      0                       ;Segment of master environment

cef_pointer     dd      ?                       ;Pointer to critical err flag
dos_version     dw      0                       ;DOS Version number

mystack_ptr     dw      offset end_of_resident + 512 ;Ptr to internal stack
saved_ss        dw      ?
saved_sp        dw      ?
int21h          dd      -1                      ;Int 21 vector (DOS)
int2fh          dd      -1                      ;Int 2f vector (DOS MULTIPLEX)

multiplex_id    db      0dbh                    ;Program ID for multiplex int

points          dw      ?                       ;Scan lines per character
columns         db      ?                       ;Number of screen columns
cursor_mode     dw      ?                       ;Cursor mode
bufferptr       db      ?                       ;Input buffer pointer
next_ptr        dw      0                       ;Address where next command
                                                ;  will be stored
cmd_ptr         dw      0                       ;Address of current command
                                                ;  in command stack
insert_flag     db      0                       ;0 = insert off, 1 = on
exkeys          db      71,72,75,77,79,80       ;Extended keycode list
                db      82,83,115,116,117
exkeys_end      =       $

ex_entry        dw      offset ctrl_end         ;Jump table for extended
                dw      offset ctrl_right       ;  keycodes
                dw      offset ctrl_left
                dw      offset delete
                dw      offset toggle_ins
                dw      offset next_cmd
                dw      offset eol
                dw      offset move_right
                dw      offset move_left
                dw      offset prev_cmd
                dw      offset home

;============================================================================
; DOSINT processes calls to interrupt 21h
;============================================================================
dosint          proc    far
                assume  cs:code,ds:nothing,es:nothing
                cmp     ah,0ah                  ;Check for char input
                je      dosint_1                ;If so, continue
goto_dos:
                jmp     cs:[int21h]             ;else pass the call to DOS
;
;Compare the active PSP with COMMAND.COM PSP.
;
dosint_1:
                push    ax                      ;Save registers
                push    bx
                mov     ah,51h                  ;Get active PSP segment
                cmp     word ptr cs:[dos_version],310h
                jb      early_dos
                pushf
                call    cs:[int21h]
                jmp     short chkpsp1
early_dos:
                push    es                      ;If before DOS 3.1, set
                les     bx,cs:[cef_pointer]     ;  critical error flag to
                inc     byte ptr es:[bx]        ;  force DOS to use the aux
                pushf                           ;  stack.
                call    cs:[int21h]             ;Get active PSP segment
                mov     ax,bx
                les     bx,cs:[cef_pointer]     ;Reset critical error flag
                dec     byte ptr es:[bx]
                mov     bx,ax
                pop     es
chkpsp1:
                cmp     bx,cs:[cmdcom_psp]      ;See if COMMAND.COM active.
                je      dosint_2

                push    es                      ;If the active PSP has an
                mov     es,bx                   ;  environment block at a
                mov     ax,es:[2ch]             ;  higher memory address than
                dec     ax                      ;  the PSP, assume that the
                mov     es,ax                   ;  the active program is a
                cmp     byte ptr es:[0],"M"     ;  shelled copy of COMMAND.COM
                pop     es
                jne     chkpsp2                 ;Valid memory block?
                cmp     ax,bx                   ;Env seg > PSP seg?
                jae     dosint_2                ;Yes, must be shelled copy
chkpsp2:
                pop     bx                      ;Cleanup and goto DOS
                pop     ax
                jmp     short goto_dos
dosint_2:
                cld                             ;Set direction flag
                mov     cs:[saved_ss],ss        ;Save SS:SP
                mov     cs:[saved_sp],sp
                cli
                push    cs                      ;Move to internal stack
                pop     ss
                mov     sp,cs:[mystack_ptr]
                sti
                push    bp                      ;Save remaining registers.
                mov     bp,sp                   ;Set up stack access
                push    cx
                push    dx
                push    di
                push    si
                push    ds
                push    es
;
;The call is from COMMAND.COM. Invoke line editor.
;
                push    dx
                call    cmd_input
                pop     dx
                cmp     cs:[cmdstack_size],0
                je      dosint_3
                call    cmd_record
;
;Check for alias before returning to COMMAND.COM
;
dosint_3:
                cmp     cs:[chk_alias],0        ;See if alias translation
                je      dosint_exit             ;  is enabled.
                mov     si,[bp-4]               ;Get pointer to input buffer
                inc     si
                xor     cx,cx
                or      cl,ds:[si]              ;Get length of buffer
                je      dosint_exit             ;If buffer empty, exit.
                inc     si                      ;Point to 1st char in buffer
                mov     ax,cs
                mov     es,ax                   ;Set ES to installed code.
                call    searchalias             ;See if an alias is found.
                jc      dosint_exit             ;No, exit.
;
;If alias found, copy it from alias list to internal buffer.
;
                mov     si,di                   ;Load SI with alias pointer
                mov     ax,es
                mov     ds,ax                   ;Point DS to alias list seg
                xor     cx,cx
                mov     cl,ds:[si+3]            ;Get size of alias
                call    getalias                ;Get alias from list
;
;Append remainder of command line to alias if no cmd line parameters were used.
;
                or      dx,dx                   ;See if any command line
                jne     dosint_6                ;  parameters were used.
                push    si                      ;Save pointer to buffer.
                push    ds
                mov     di,si
                add     di,cx                   ;Point DI to end of alias
                mov     dx,cx                   ;Save length of alias
                mov     si,[bp-4]               ;Point DS:SI to command.com
                mov     ds,[bp-10]              ;  data buffer.
                xor     cx,cx
                inc     si
                mov     cl,[si]                 ;Get length of command line.
                cmp     ah,cl                   ;See if enough space in
                ja      dosint_4                ;  internal buffer. If not
                mov     cl,ah                   ;  copy only until buff full.
dosint_4:
                inc     si
                mov     bl,1                    ;Skip past alias.
                call    scan4char
                jc      dosint_5
                dec     si                      ;Back up 1 char
                inc     cx
                add     dx,cx                   ;Add length of command line.
                rep     movsb                   ;Copy command line
dosint_5:
                mov     cx,dx                   ;Restore length of command
                pop     ds                      ;Restore pointer
                pop     si
;
;Copy alias from internal buffer to COMMAND.COM data buffer.
;
dosint_6:
                mov     di,[bp-4]               ;Point ES:DI to command.com
                mov     es,[bp-10]              ;  data buffer.
                mov     al,es:[di]              ;Get size of data buffer
                dec     al                      ;If alias longer than buffer,
                cmp     al,cl                   ;  copy only the enough
                ja      dosint_7                ;  characters to fill the
                xor     cx,cx                   ;  buffer.
                mov     cl,al
dosint_7:
                inc     di                      ;Move DI past length bytes
                mov     es:[di],cl              ;Save length of command
                inc     di
                rep     movsb                   ;Copy alias.
                mov     byte ptr es:[di],13     ;Append carriage return
dosint_exit:
                mov     cx,cs:[cursor_mode]     ;Set default cursor
                mov     ah,1
                int     10h
                pop     es
                pop     ds
                pop     si
                pop     di
                pop     dx
                pop     cx
                pop     bp
                cli
                mov     ss,cs:[saved_ss]        ;Restore stack pointer
                mov     sp,cs:[saved_sp]
                pop     bx
                pop     ax
                iret                            ;Return to COMMAND.COM
dosint          endp

;-----------------------------------------------------------------------------
; GETALIAS Copies an alias from the alias list while substituting any
;          environment variables and command line parameters.
; Entry:  DS:SI - pointer to alias
; Exit:   DS:SI - pointer to buffer containing translated alias.
;            AH - free space in buffer
;            CX - length of the alias
;-----------------------------------------------------------------------------
getalias        proc    near
                mov     di,work_buff_ptr        ;Point DI to internal buffer
                xor     ax,ax                   ;Point to command by adding
                mov     al,ds:[si+2]            ;  the size of the alias to
                add     si,ax                   ;  pointer to the entry.
                add     si,4                    ;Move past list data.
                mov     ah,126                  ;AH contains max size of buff
                xor     dx,dx                   ;Clear flag for line params
alias_1:
                lodsb                           ;Get byte from alias
                cmp     al,"%"                  ;See if special character
                je      alias_3                 ;Yes, process special char.
alias_2:
                stosb                           ;Store byte from alias
                dec     ah                      ;Dec buffer size counter
                jz      alias_6                 ;If internal buffer full, done
                loop    alias_1
                jmp     short alias_6           ;If at end of alias, done
;
;A percent sign has been found indicating a 'soft' parameter.
;
alias_3:
;               mov     al,ds:[si]              ;Get character after %
                lodsb
                dec     cx
                cmp     al,"%"                  ;If double %, include in one
                je      alias_2                 ;  % in alias.
                mov     bh,al                   ;Copy and check to see if
                sub     bh,"0"                  ;  the next char is a number.
                jb      alias_5                 ;  If so, assume a line
                cmp     bh,9                    ;  parameter.
                ja      alias_5
                call    sublineparam            ;Substitute a line paramter
                inc     dx
alias_4:
                loop    alias_1
                jmp     short alias_6           ;If at end of alias, done
alias_5:
                dec     si                      ;Backup to 1st character
                inc     cx
                call    subenvvar               ;Substitute an environment var
                loop    alias_1
alias_6:
                mov     si,work_buff_ptr        ;Point SI to internal buffer
                mov     cx,di                   ;Compute size of completed
                sub     cx,si                   ;  alias.
                ret
getalias        endp

;-----------------------------------------------------------------------------
; SUBLINEPARAM substitutes a parameter from the command line into the alias.
; Entry:  DS:SI - pointer to alias
;         ES:DI - pointer to buffer to copy the line parameter
;            AH - remaining space in the internal buffer
;            BH - binary number of the line parameter
;            CX - length of the alias
; Exit:   ES:DI - pointer to byte after the parameter in the buffer
;         DS:SI - pointer to the character after the line parameter number
;            CX - remaining length of the alias
;-----------------------------------------------------------------------------
sublineparam    proc    near
                push    cx
                push    si
                push    ds
                mov     si,[bp-4]               ;Get pointer to command.com
                mov     ds,[bp-10]              ;  data buffer.
                xor     cx,cx
                mov     cl,ds:[si+1]            ;Get number of chars in buffer.
                inc     si                      ;Point to the first byte of
                inc     si                      ;  data.
sublineparam_1:
                or      bh,bh                   ;Check count of param to find.
                jz      sublineparam_2
                mov     bl,1
                call    scan4char               ;Find next space
                jc      sublineparam_exit
                dec     bl
                call    scan4char               ;Find next word
                jc      sublineparam_exit
                dec     bh                      ;Dec parameter count
                jne     sublineparam_1          ;If not done, loop back.
                dec     si                      ;Backup to 1st char in word.
sublineparam_2:
                lodsb                           ;Get character from parameter
                cmp     al," "                  ;If space, parameter done
                jbe     sublineparam_exit
                stosb
                dec     ah                      ;Dec buffer size counter
                jnz     sublineparam_2
sublineparam_exit:
                pop     ds
                pop     si
                pop     cx
                ret
sublineparam    endp

;-----------------------------------------------------------------------------
; SUBENVVAR substitutes an environment variable into alias.
; Entry:  DS:SI - pointer to variable to substitute
;         ES:DI - pointer to buffer to copy the contents of the variable
;            AH - remaining space in internal buffer
;            CX - length of alias string
; Exit:   DS:SI - pointer to the byte after the variable name
;         ES:DI - pointer to the byte after the variable contents
;            CF - set if variable not found
;-----------------------------------------------------------------------------
subenvvar       proc    near
                push    dx
                push    ds
                push    es
;
;Compute the length of the variable name.
;
                mov     bx,di                   ;Save pointer to internal buff
                mov     di,si                   ;Compute the length of the
                mov     dx,cx                   ;  environment variable by
                mov     al,"%"                  ;  searching for the trailing
                repne   scasb                   ;  % sign.
                sub     dx,cx                   ;Compute length of variable.
                dec     dx                      ;Subtract % byte fron length.
                push    di                      ;Save ptr to end of env var.
;
;Search the Master Environment block for the variable pointed to by DS:SI
;
                mov     es,cs:[master_env]      ;Get segment of master env blk
                xor     di,di                   ;Point ES:DI to environment.
                push    cx                      ;Save alias size.
                push    bx                      ;Save ptr to internal buffer
                mov     bx,si                   ;Save pointer to var name
subenvvar_1:
                mov     si,bx                   ;Get back ptr to var name.
                mov     cx,dx                   ;Compare env var to var in
                repe    cmpsb                   ;  alias.
                je      subenvvar_2             ;Variable found, exit loop
                xor     al,al                   ;Find next environment var.
                mov     cx,-1                   ;Scan the entire segment.
                repne   scasb
                cmp     byte ptr es:[di],0      ;If double zero, end of env
                jne     subenvvar_1             ;  block. else, loop back.
                pop     di                      ;Restore DI and exit
                jmp     short subenvvar_exit
;
;Environment variable found. Substitute into alias.
;
subenvvar_2:
                mov     si,es                   ;DS:SI points to env string
                mov     ds,si                   ;ES:DI points to internal buff
                mov     si,cs
                mov     es,si
                mov     si,di                   ;Copy pointer to var contents.
                pop     di                      ;Restore ptr to internal buff
subenvvar_3:
                lodsb                           ;Move environment pointer past
                cmp     al,"="                  ;  the equals sign.
                jne     subenvvar_3
subenvvar_4:
                lodsb                           ;Move pointer to first
                cmp     al," "                  ;  non-space character.
                jb      subenvvar_4
subenvvar_5:
                or      al,al                   ;See if at the end of variable
                je      subenvvar_exit
                stosb                           ;Save character in command
                lodsb                           ;Get next character
                dec     ah                      ;Dec buffer size count.
                jne     subenvvar_5             ;If buffer not full, continue
subenvvar_exit:
                pop     cx                      ;Restore alias length
                pop     si                      ;Restore alias pointer
                pop     es                      ;Restore segment registers
                pop     ds
                pop     dx
                ret
subenvvar       endp

;-----------------------------------------------------------------------------
; SUBKEY searches the alias list for a key substitution
; Entry:  AL - extended key code
; Exit:   CF - clear if key found, set if not found
;         DX - offset address of matching entry in alias list (if CF = 0)
;         CX - length of matching entry (if CF = 0)
;-----------------------------------------------------------------------------
subkey          proc    near
                push    bx
                push    di
                push    si
                push    ds
                push    es
                xor     ah,ah                   ;Indicate extended code.
                les     di,cs:[aliaslist_ptr]   ;Get pointer to alias list.
subkey_1:
                mov     dx,es:[di]              ;Get next entry offset
                cmp     dx,-1                   ;See if at the end of the list
                je      subkey_notfound
                cmp     es:[di+4],ax            ;Check for key code.
                je      subkey_found            ;If found, exit loop
subkey_2:
                add     di,dx                   ;Else, point to next entry.
                jmp     short subkey_1
;
;Copy alias into internal buffer.
;
subkey_found:
                mov     si,di                   ;Load SI with alias pointer
                mov     ax,es
                mov     ds,ax                   ;Point DS to alias list seg
                xor     cx,cx
                mov     cl,ds:[si+3]            ;Get size of alias
                call    getalias                ;Get alias from list
                mov     dx,si                   ;Copy pointer to buffer
                clc                             ;Set key found flag
subkey_exit:
                pop     es
                pop     ds
                pop     si
                pop     di
                pop     bx
                ret
subkey_notfound:
                stc                             ;Set key not found flag
                jmp     subkey_exit
subkey          endp

;-----------------------------------------------------------------------------
; SEARCHALIAS searches the alias list for a matching alias.
; Entry:  DS:SI - pointer to alias
;            ES - segment of installed code
;            CX - length input buffer
; Exit:      CF - clear if alias found
;         ES:DI - pointer to matching entry in alias list, if CF is clear
;-----------------------------------------------------------------------------
searchalias     proc    near
                push    bx                      ;Save registers
                push    cx
                push    si
                xor     bx,bx
searchalias_1:
                lodsb                           ;Compute the length of the
                or      al,al                   ;  length of the alias by
                je      searchalias_2           ;  finding the next space.
                cmp     al," "                  ;Allow zero byte in alias
                jbe     searchalias_3           ;  for function key labels.
searchalias_2:
                inc     bx
                loop    searchalias_1
searchalias_3:
                pop     si
                les     di,es:[aliaslist_ptr]   ;Get pointer to alias list.
                mov     cx,bx                   ;Get length of alias
searchalias_4:
                mov     bx,es:[di]
                cmp     bx,-1                   ;See if at the end of the list
                je      searchalias_notfound
                cmp     es:[di+2],cl            ;Compare lengths
                jne     searchalias_5
                push    cx                      ;Save size and starting
                push    di                      ;  pointers.
                push    si
                add     di,4                    ;Point to start of alias field.
                repe    cmpsb                   ;Compare alias to input string
                pop     si
                pop     di
                pop     cx
                je      searchalias_6           ;If found, exit loop
searchalias_5:
                add     di,bx                   ;Else, point to next entry.
                jmp     short searchalias_4
searchalias_6:
                clc                             ;Set alias found flag
searchalias_exit:
                pop     cx
                pop     bx
                ret
searchalias_notfound:
                stc
                jmp     short searchalias_exit
searchalias     endp

;-----------------------------------------------------------------------------
; SCAN4CHAR scans a string to find the first character.
; Entry:  SI - pointer to ASCII string
;         BL - 0 = find next char, 1 = find next space
;         CX - file length
; Exit:   AL - first nonspace character
;         CF - set if carriage return found
;-----------------------------------------------------------------------------
scan4char       proc near
                assume  ds:nothing,es:nothing
scan4loop:
                jcxz    scan4_eol               ;See if at the end of the file.
                lodsb
                dec     cx                      ;Decrement file length counter.
                cmp     al,13                   ;Check for carriage return.
                jne     scan4_1
scan4_eol:
                stc
                jmp     short scan4_exit1
scan4_1:
                or      bl,bl                   ;Check if searching for space
                jne     scan4_2                 ;  or character.
                cmp     al," "                  ;Check for space or other
                jbe     scan4loop               ;  'white' characters.
                jmp     short scan4_exit
scan4_2:
                cmp     al," "                  ;Check for characters.
                ja      scan4loop
scan4_exit:
                clc
scan4_exit1:
                ret
scan4char       endp

;----------------------------------------------------------------------------
; CMD_INPUT replaces DOS' 0Ah text input function.
;----------------------------------------------------------------------------
cmd_input       proc    near
                push    ds                      ;Save DS
                xor     ax,ax                   ;Then zero it
                mov     ds,ax
                mov     ax,ds:[0485h]           ;Get number of scan lines
                cmp     ax,cs:[points]          ;  per character and branch
                je      cmd1                    ;  if it hasn't changed

                mov     cs:[points],ax          ;Record new number of scan
                mov     ax,ds:[0460h]           ;  lines per character and
                mov     cs:[cursor_mode],ax     ;  cursor mode
cmd1:
                pop     ds                      ;Restore DS
                call    set_cursor              ;Set cursor mode

                mov     ah,15                   ;Get video page and columns
                int     10h
                dec     ah                      ;Calculate max column number
                mov     cs:[columns],ah         ;Save it
                mov     ax,ds                   ;Point ES:DI to buffer
                mov     es,ax
                mov     di,dx
                add     di,2
                mov     si,dx                   ;Point DS:SI to character count
                inc     si
                mov     byte ptr [si],0         ;Zero initial count
                mov     cs:[bufferptr],1        ;Set initial index value
                cld                             ;Clear DF
;
;Wait for a keycode to appear in the keyboard buffer.
;
getkey:         mov     ah,0Bh                  ;Check buffer for keycode
                int     21h
                or      al,al                   ;Anything there?
                jne     getchar                 ;Yes, then go get it
                int     28h                     ;No, then execute interrupt 28h
                jmp     getkey                  ;Loop back for another try
;
;Read the keycode and process it if it's not an extended code.
;
getchar:
                mov     ah,8                    ;Read character from buffer
                int     21h
                or      al,al                   ;Is it an extended code?
                je      excode                  ;Yes, then branch

                cmp     al,8                    ;Backspace key?
                jne     getc1                   ;No, then branch
                call    backspace               ;Rub out a character
                jmp     getkey                  ;Return to loop
getc1:
                cmp     al,9                    ;Tab key?
                jne     getc2                   ;No, then branch
                call    tab                     ;Tab to next tab boundary
                jmp     getkey                  ;Return to loop
getc2:
                cmp     al,27                   ;ESC key?
                jne     getc3                   ;No, then branch
                call    clear_line              ;Yes, then clear input line
                mov     cs:[cmd_ptr],0          ;Zero current command pointer
                jmp     getkey
getc3:
                cmp     al,7Fh                  ;Ctrl-Backspace?
                jne     getc4                   ;No, then branch
                call    ctrl_bs                 ;Yes, then delete word
                jmp     getkey
getc4:
                cmp     al,13                   ;ENTER key?
        je  entr            ;Yes, then branch
                call    printchar               ;No, then print the character
getc5:
                jmp     getkey
entr:
                call    eol                     ;Place cursor at end-of-line
                mov     byte ptr es:[di],13     ;Insert carriage return code
                mov     ah,2                    ;Advance to next line
                mov     dl,13
                int     21h
get_exit:
                ret
;
;Process extended keycodes.
;
excode:
                mov     ah,8                    ;Read extended code
                int     21h

                cmp     al,3Bh                  ;See if below F1
                jb      excode3
                cmp     al,71h                  ;See if above Alt-F10
                ja      excode3
                cmp     al,54h                  ;See if above Shift-F1
                jae     excode1
                cmp     al,44h                  ;See if above F10
                ja      excode3
excode1:
                call    subkey                  ;Scan alias list for match
                jc      getkey                  ;Exit if no match found
                push    cx
                push    dx
                call    clear_line              ;Clear command line
                pop     dx
                pop     cx
excode2:
                push    si
                mov     si,dx                   ;Copy offset address into SI
                mov     al,byte ptr cs:[si]     ;Get next character
                pop     si
                cmp     al,13                   ;Exit and execute command
        je  entr            ;  on carriage return
                inc     dx
                push    cx
                push    dx
                call    printchar               ;Print it
                pop     dx
                pop     cx
                jc      getc5
                loop    excode2                 ;Loop until done
                jmp     getkey                  ;Return to input loop
excode3:
                push    es                      ;Save buffer address
                push    di
                mov     cx,cs                   ;Point ES:DI to list of
                mov     es,cx                   ;  supported keycodes
                mov     di,offset exkeys
                mov     cx,offset exkeys_end - offset exkeys
                repne   scasb                   ;Scan list
                pop     di                      ;Clear the stack
                pop     es
                jne     excode4                 ;Ignore if key not found
                shl     cx,1                    ;Convert CX to address
                add     cx,offset ex_entry
                push    bx                      ;Save page number in BH
                mov     bx,cx                   ;Get entry address from table
                mov     ax,cs:[bx]
                pop     bx                      ;Restore BH
                call    ax                      ;Call handling routine
excode4:
                jmp     getkey                  ;Return to input loop
cmd_input       endp

;------------------------------------------------------------------------------
; PREV_CMD outputs the previous command in the command stack.
;------------------------------------------------------------------------------
prev_cmd        proc    near
                mov     dx,cs:[cmd_ptr]         ;Get current stack index
                cmp     dx,cs:[cmdstack_size]   ;Exit if at top of command
                je      prev_exit               ;  stack
                or      dx,dx                   ;Branch if not at bottom of
                jnz     prev1                   ;  command stack
;
;Copy the current contents of the command line to the search buffer.
;
                mov     cl,[si]                 ;Get count of characters on
                xor     ch,ch                   ;  command line in CX
                inc     cx
                push    es                      ;Save registers
                push    di
                push    si
                mov     ax,cs                   ;Point ES:DI to search text
                mov     es,ax                   ;  buffer
                mov     di,offset command_tail
                rep     movsb                   ;Copy command line text
                pop     si                      ;Restore registers
                pop     di
                pop     es
;
;Search for the previous command and output it.
;
prev1:
                inc     dx                      ;Increment stack index
                mov     ax,cs:[next_ptr]        ;Get stack base index
                sub     ax,dx                   ;Calculate address of previous
                cmp     ax,0                    ;  command
                jge     prev2
                add     ax,cs:[cmdstack_size]
prev2:
                mov     cl,7                    ;Calculate offset address of
                shl     ax,cl                   ;  the command
                add     ax,cs:[cmdstack_base]
                cmp     byte ptr cs:[command_tail],0    ;Output command if there is
                je      prev3                   ;  no search criterion
                call    check_string            ;Compare strings
                jz      prev3                   ;Output command if the strings
                cmp     dx,cs:[cmdstack_size]   ;  match
                jne     prev1
                ret                             ;Exit if at top of stack
prev3:
                push    si                      ;Save SI
                mov     si,ax                   ;Transfer address to SI
                cmp     byte ptr cs:[si],0      ;Valid command?
                pop     si                      ;Restore SI
                je      prev_exit               ;No, then ignore keypress
                mov     cs:[cmd_ptr],dx         ;Save new command pointer
                call    write_command           ;Output the command string
prev_exit:
                ret
prev_cmd        endp

;------------------------------------------------------------------------------
; NEXT_CMD outputs the next command in the command stack.
;------------------------------------------------------------------------------
next_cmd        proc    near
                mov     dx,cs:[cmd_ptr]         ;Get current stack index
                or      dx,dx                   ;Exit if it's zero
                jz      next_exit
next1:
                dec     dx                      ;Decrement stack index
                mov     cs:[cmd_ptr],dx         ;Save command pointer
                or      dx,dx                   ;Clear line and exit if
                jz      next3                   ;  the result is zero
                mov     ax,cs:[next_ptr]        ;Get stack base index
                sub     ax,dx                   ;Calculate address of next
                cmp     ax,0                    ;  command
                jge     next2
                add     ax,cs:[cmdstack_size]
next2:
                mov     cl,7                    ;Calculate offset address of
                shl     ax,cl                   ;  command
                add     ax,cs:[cmdstack_base]
                cmp     byte ptr cs:[command_tail],0    ;Output command if there is
                je      next4                   ;  no search criterion
                call    check_string            ;Compare strings
                jz      next4                   ;Output command if the strings
                jmp     next1                   ;  match
next3:
                call    clear_line
                cmp     byte ptr cs:[command_tail],0
                je      next_exit
                mov     ax,offset command_tail
next4:
                call    write_command           ;Output the command string
next_exit:
                ret
next_cmd        endp

;------------------------------------------------------------------------------
; CHECK_STRING compares the string in the search buffer with another string.
; Entry:  AX - string address
; Exit:   ZF - set if strings are equivalent, clear if they are not
;------------------------------------------------------------------------------
check_string    proc    near
                push    ds                      ;Save registers
                push    si
                push    es
                push    di
                mov     cx,cs                   ;Point DS and ES to
                mov     ds,cx                   ;  code segment
                mov     es,cx
                mov     si,offset command_tail  ;Point DS:SI to text in
                mov     cl,[si]                 ;  search buffer
                xor     ch,ch
                inc     si
                mov     di,ax                   ;Point DI to other string
                inc     di                      ;
                repe    cmpsb                   ;Compare strings
                pop     di                      ;Restore registers
                pop     es
                pop     si
                pop     ds
                ret
check_string    endp

;------------------------------------------------------------------------------
; PRINT_STRING writes an ASCII string to the command line.
; Entry:  DS:SI - string address
;            CX - number of characters
;------------------------------------------------------------------------------
print_string    proc    near
                jcxz    ps_exit                 ;Exit if no characters
                push    dx
                cld
                mov     ah,2                    ;Print the character
ps1:
                lodsb                           ;Get a byte
                mov     dl,al                   ;Transfer it to DL
                int     21h                     ;Output character
                loop    ps1                     ;Loop until done
                pop     dx
ps_exit:
                ret
print_string    endp

;------------------------------------------------------------------------------
; WRITE_COMMAND outputs a command string.
; Entry:  AX - string offset address
; Exit:   AL - character after string
;------------------------------------------------------------------------------
write_command   proc    near
                push    ax                      ;Save address
                call    clear_line              ;Clear input line
                pop     ax                      ;Retrieve string address
                push    ds                      ;Save DS and SI
                push    si
                push    cs                      ;Point DS to string segment
                pop     ds
                mov     si,ax                   ;Point SI to the string
                mov     cl,[si]                 ;Get string length
                xor     ch,ch
                mov     byte ptr es:[di-1],cl   ;Store string length
                inc     si                      ;Advance SI to string text
write1:
                mov     ah,2                    ;Print one character
                mov     dl,[si]
                int     21h
                movsb                           ;Transfer character to buffer
                inc     cs:[bufferptr]          ;Advance pointer
                loop    write1                  ;Loop until done
                lodsb                           ;Get first character after string
                pop     si                      ;Restore registers
                pop     ds
                ret
write_command   endp

;------------------------------------------------------------------------------
; TOGGLE_INS toggles the insert flag.
;------------------------------------------------------------------------------
toggle_ins      proc    near
                xor     cs:[insert_flag],1      ;Toggle insert flag
                call    set_cursor              ;Set cursor mode
                ret
toggle_ins      endp

;------------------------------------------------------------------------------
; TAB tabs to the next tab boundary.
;------------------------------------------------------------------------------
tab             proc    near
                mov     al,cs:[bufferptr]       ;Calculate number of
                dec     al                      ;  spaces to insert for
                xor     ah,ah                   ;  soft tab
                mov     bl,8
                div     bl
                mov     cx,8
                sub     cl,ah
tab1:
                push    cx                      ;Print spaces
                mov     al,32
                call    printchar
                pop     cx
                jc      tab_exit
                loop    tab1
tab_exit:
                ret
tab             endp

;------------------------------------------------------------------------------
; BACKSPACE deletes the character left of the cursor.
;------------------------------------------------------------------------------
backspace       proc    near
                cmp     cs:[bufferptr],1        ;At beginning of command line?
                je      bs_exit                 ;Yes, then ignore it
                mov     cl,[si]                 ;Get count
                sub     cl,cs:[bufferptr]       ;Calculate distance to end-of-line
                inc     cl
                xor     ch,ch
                push    cx                      ;Save it
                jcxz    bs1                     ;Branch if at end-of-line
;
;Shift all characters right of the cursor in the buffer one slot left.
;
                push    si                      ;Save SI and DI
                push    di
                mov     si,di                   ;Position them for shifts
                dec     di
                rep     movsb                   ;Shift characters right of cursor
                pop     di                      ;Restore registers
                pop     si
;
;Display the new string and update input parameters.
;
bs1:
                call    move_left               ;Move cursor left
bs2:
                pop     cx                      ;Retrieve shift count
                push    dx                      ;Save cursor position
                push    si                      ;Save SI
                mov     si,di                   ;Point SI to new part of string
                call    print_string            ;Print the new part
                mov     ah,2                    ;Blank the last character
                mov     dl,32
                int     21h
                pop     si                      ;Restore registers
                pop     dx                      ;Restore cursor address
                mov     ah,2                    ;Reset the cursor
                int     10h
                dec     byte ptr [si]           ;Decrement character count
bs_exit:
                ret
backspace       endp

;------------------------------------------------------------------------------
; PRINTCHAR writes a new character to the input buffer and echoes it.
; Entry:  AL - character to print
; Exit:   CF clear if character printed
;         CF set if buffer full
;------------------------------------------------------------------------------
printchar       proc    near
                cmp     cs:[insert_flag],0      ;Insert state on?
                jne     print3                  ;Yes, then branch
;
;Print a character in overstrike mode.
;
                mov     cl,[si]                 ;Get count
                cmp     cl,cs:[bufferptr]       ;End-of-line?
                jae     print2                  ;No, then branch
                mov     cl,[si-1]               ;Get maximum length
                sub     cl,[si]                 ;Subtract current length
                cmp     cl,1                    ;Buffer full?
                je      beep                    ;Yes, then branch
print1:
                inc     byte ptr [si]           ;Increment count
print2:
                stosb                           ;Deposit new character
                mov     ah,2                    ;Then print it
                mov     dl,al
                int     21h
                inc     cs:[bufferptr]          ;Advance buffer pointer
print_exit:
                clc                             ;Clear CF and exit
                ret
beep:
                mov     ax,0E07h                ;Print ASCII 7 thru BIOS
                int     10h
                stc
                ret
;
;Print a character in insert mode.
;
print3:
                mov     cl,[si-1]               ;Get maximum length
                sub     cl,[si]                 ;Subtract current count
                cmp     cl,1                    ;Buffer full?
                je      beep                    ;Yes, then branch
                mov     cl,[si]                 ;Get count
                cmp     cl,cs:[bufferptr]       ;End-of-line?
                jb      print1                  ;Yes, then branch
                sub     cl,cs:[bufferptr]       ;Calculate number of shifts
                inc     cl
                xor     ch,ch
                push    cx                      ;Save shift count
                push    si                      ;Save SI
                add     di,cx                   ;Position DI to end-of-line
                mov     si,di                   ;Position SI just before it
                dec     si
                std                             ;Set DF for now
                rep     movsb                   ;Make room for new character
                cld                             ;Clear DF
                pop     si                      ;Restore SI
                mov     es:[di],al              ;Deposit new character
                mov     ah,3                    ;Get cursor position
                int     10h
                pop     cx                      ;Retrieve shift count
                inc     cx                      ;Increment it
                push    dx                      ;Save cursor position
                push    si                      ;Save SI
                mov     si,di                   ;Point SI to current location
                call    print_string            ;Print new part of string
                pop     si                      ;Restore SI and DX
                pop     dx
                mov     ah,2                    ;Reset cursor position
                int     10h
                inc     byte ptr [si]           ;Add to character count
                call    move_right              ;Move cursor right
                jmp     print_exit
printchar       endp

;------------------------------------------------------------------------------
; DELETE deletes the character at the cursor.
;------------------------------------------------------------------------------
delete          proc    near
                mov     cl,[si]                 ;Get count
                cmp     cl,cs:[bufferptr]       ;End-of-line?
                jb      del2                    ;Yes, then ignore keypress
                sub     cl,cs:[bufferptr]       ;Calculate number of shifts
                xor     ch,ch                   ;Byte to word in CX
                push    cx                      ;Save shift count
                jcxz    del1                    ;Branch if no shifts
                push    si                      ;Save SI and DI
                push    di
                mov     si,di                   ;Position registers for shift
                inc     si
                rep     movsb                   ;Shift chars right of cursor
                pop     di                      ;Restore registers
                pop     si
del1:
                mov     ah,3                    ;Get cursor position
                int     10h
                jmp     bs2                     ;Exit thru BACKSPACE routine
del2:
                ret
delete          endp

;------------------------------------------------------------------------------
; CTRL_BS deletes the word at the cursor.
;------------------------------------------------------------------------------
lesschars       dw      ?                       ;Count of chars deleted
shiftcount      dw      ?                       ;Count of chars shifted

ctrl_bs         proc    near
                mov     cl,[si]                 ;Exit now if there is nothing
                xor     ch,ch                   ;  on the command line
                or      cx,cx
                jnz     cbs1
cbs_exit:
                ret
cbs1:
                cmp     cs:[bufferptr],1        ;Exit if the cursor is at the
                je      cbs3                    ;  at the end of the command
                cmp     cl,cs:[bufferptr]       ;  line or if it is under a
                jb      cbs_exit                ;  space; otherwise, move to
                cmp     byte ptr [di],32        ;  the beginning of the
                je      cbs_exit                ;  current word
                cmp     byte ptr [di-1],32
                je      cbs3
cbs2:
                push    cx                      ;Save CX
                call    ctrl_left               ;Move to start of word
                pop     cx                      ;Restore CX
cbs3:
                inc     cx                      ;Calculate max number of
                push    cx                      ;  characters to search
                mov     dl,cs:[bufferptr]       ;  looking for the next
                xor     dh,dh                   ;  word or end-of-line
                sub     cx,dx
                push    di                      ;Save DI
cbs4:
                inc     di                      ;Search until DI addresses
                cmp     byte ptr [di],32        ;  either the first character
                je      cbs5                    ;  in the next word or the
                cmp     byte ptr [di-1],32      ;  end of the command line
                je      cbs6
cbs5:
                loop    cbs4
cbs6:
                mov     dx,di                   ;Save final value of DI
                pop     di                      ;Restore DI
                pop     cx                      ;Retrieve count
                mov     cs:[lesschars],dx       ;Calculate number of chars to
                sub     cs:[lesschars],di       ;  be deleted
                sub     cx,dx                   ;Then calculate how many chars
                add     cx,si                   ;  must be shifted
                mov     cs:[shiftcount],cx
                jcxz    cbs7                    ;Branch if no shift
                push    si                      ;Save registers
                push    di
                mov     si,dx                   ;Point DS:SI to next word
                rep     movsb                   ;Delete current word
                pop     di                      ;Restore registers
                pop     si
cbs7:
                mov     cx,cs:[lesschars]       ;Update character counter
                sub     [si],cl                 ;  in input buffer
                mov     ah,3                    ;Save the current cursor
                int     10h                     ;  position on the stack
                push    dx
                push    si                      ;Update the text on the
                mov     si,di                   ;  command line
                mov     cx,cs:[shiftcount]
                call    print_string
                pop     si
                mov     cx,cs:[lesschars]
cbs8:
                mov     ah,2                    ;Print as many spaces as
                mov     dl,32                   ;  there were characters
                int     21h                     ;  deleted
                loop    cbs8
                pop     dx                      ;Restore cursor position
                mov     ah,2                    ;  and exit
                int     10h
                ret
ctrl_bs         endp

;------------------------------------------------------------------------------
; CTRL_END deletes command line text from the cursor to the end of the line.
;------------------------------------------------------------------------------
ctrl_end        proc    near
                mov     cl,[si]                 ;Exit if already at end
                cmp     cl,cs:[bufferptr]       ;  of line
                jb      ce_exit
                sub     cl,cs:[bufferptr]       ;Calculate number of chars
                xor     ch,ch                   ;  to be deleted
                inc     cx
                sub     [si],cl                 ;Update count in input buffer
                push    cx
                mov     ah,3                    ;Get and save cursor position
                int     10h
                pop     cx
                push    dx
ce1:
                mov     ah,2                    ;Print as many spaces as
                mov     dl,32                   ;  there are characters
                int     21h                     ;  to delete
                loop    ce1
                mov     ah,2                    ;Reset cursor position and
                pop     dx                      ;  exit
                int     10h
ce_exit:
                ret
ctrl_end        endp

;------------------------------------------------------------------------------
; MOVE_LEFT moves the cursor one character left.
;------------------------------------------------------------------------------
move_left       proc    near
                cmp     cs:[bufferptr],1        ;At beginning of line?
                je      left2                   ;Yes, then ignore keypress
                mov     ah,3                    ;Get cursor position
                int     10h
                dec     dl                      ;Decrement it by 1
                cmp     dl,0FFh
                jne     left1
                mov     dl,cs:[columns]         ;Decrement row number by 1
                dec     dh                      ;  if cursor wraps around
left1:
                mov     ah,2                    ;Set new position
                int     10h
                dec     di                      ;Decrement pointers
                dec     cs:[bufferptr]
left2:
                ret
move_left       endp

;------------------------------------------------------------------------------
; MOVE_RIGHT moves the cursor one character right.
;------------------------------------------------------------------------------
move_right      proc    near
                mov     cl,[si]                 ;Get count
                cmp     cl,cs:[bufferptr]       ;End-of-line?
                jb      rt2                     ;Yes, then ignore keypress
                mov     ah,3                    ;Get cursor position
                int     10h
                inc     dl                      ;Increment column number
                cmp     dl,cs:[columns]         ;Increment row number if
                jna     rt1                     ;  cursor wraps around
                xor     dl,dl
                inc     dh
rt1:
                mov     ah,2                    ;Position cursor
                int     10h
                inc     di                      ;Advance pointers
                inc     cs:[bufferptr]
rt2:
                ret
move_right      endp

;------------------------------------------------------------------------------
; CTRL_LEFT moves the cursor one word left.
;------------------------------------------------------------------------------
ctrl_left       proc    near
                call    move_left               ;Move one character left
                cmp     cs:[bufferptr],1        ;Beginning of line?
                je      cl_exit                 ;Yes, then exit
                cmp     byte ptr es:[di],32     ;Loop back if current char
                je      ctrl_left               ;  is a space
                cmp     byte ptr es:[di-1],32   ;Loop back if char to the
                jne     ctrl_left               ;  left is not a space
cl_exit:
                ret
ctrl_left       endp

;------------------------------------------------------------------------------
; CTRL_RIGHT moves the cursor one word right.
;------------------------------------------------------------------------------
ctrl_right      proc    near
                call    move_right              ;Move one character right
                mov     cl,[si]                 ;End-of-line?
                cmp     cl,cs:[bufferptr]       ;Yes, then exit
                jb      cr_exit
                cmp     byte ptr es:[di],32     ;Loop back if current char
                je      ctrl_right              ;  is a space
                cmp     byte ptr es:[di-1],32   ;Loop back if char to the
                jne     ctrl_right              ;  left is not a space
cr_exit:
                ret
ctrl_right      endp

;------------------------------------------------------------------------------
; HOME relocates the cursor to the beginning of the command line.
;------------------------------------------------------------------------------
home            proc    near
                mov     cl,cs:[bufferptr]       ;Get position pointer
                dec     cl                      ;Calculate distance from start
                xor     ch,ch
                jcxz    home_exit               ;Exit if already there
home1:
                push    cx                      ;Save count
                call    move_left               ;Move left one space
                pop     cx                      ;Retrieve count
                loop    home1                   ;Loop until done
home_exit:
                ret
home            endp

;------------------------------------------------------------------------------
; EOL advances the cursor to the end of the command line.
;------------------------------------------------------------------------------
eol             proc    near
                mov     cl,[si]                 ;Get count
                cmp     cl,cs:[bufferptr]       ;Already at end?
                jb      eol_exit                ;Yes, then exit
                sub     cl,cs:[bufferptr]       ;Calculate distance from end
                inc     cl
                xor     ch,ch                   ;Byte to word in CX
eol1:
                push    cx                      ;Advance right CX times
                call    move_right
                pop     cx
                loop    eol1
eol_exit:
                ret
eol             endp

;------------------------------------------------------------------------------
; CLEAR_LINE clears the command line.
; Entry:  BH - current video page
;------------------------------------------------------------------------------
clear_line      proc    near
                mov     cl,[si]                 ;Get count
                xor     ch,ch
                jcxz    cline2                  ;Exit if no characters
                push    cx                      ;Save count
                call    home                    ;Home the cursor
                mov     ah,3                    ;Get cursor position
                int     10h
                pop     cx                      ;Restore CX
                push    dx                      ;Save cursor address
                mov     ah,2                    ;Print ASCII spaces
                mov     dl,32
cline1:
                int     21h
                loop    cline1
                mov     ah,2                    ;Home cursor again
                pop     dx
                int     10h
                mov     byte ptr [si],0         ;Reset count
cline2:
                ret
clear_line      endp

;------------------------------------------------------------------------------
; CMD_RECORD records the latest command in the command stack.
; Entry:  DS:DX - input buffer address
;------------------------------------------------------------------------------
cmd_record      proc    near
                mov     si,dx                   ;Point SI to input buffer
                mov     al,[si+1]               ;Get length of input string
                cmp     al,cs:[minlength]       ;Is the length zero?
                jb      cmd_exit                ;Yes, then exit now
                inc     si                      ;Advance SI to count byte
                push    cs                      ;Point ES to command stack
                pop     es                      ;  segment
                mov     di,cs:[next_ptr]        ;Get command stack pointer
                mov     cl,7                    ;Convert it to offset address
                shl     di,cl
                add     di,cs:[cmdstack_base]
                mov     cl,al                   ;Transfer string length to CL
                inc     cl                      ;Increment for count byte
                xor     ch,ch                   ;Byte to word in CX
                cld                             ;Clear DF
                rep     movsb                   ;Copy string to command stack
                inc     cs:[next_ptr]           ;Update pointer
                mov     ax,cs:[cmdstack_size]
                cmp     cs:[next_ptr],ax        ;Wrap around if necessary
                jne     cmdrec1
                mov     cs:[next_ptr],0
cmdrec1:
                mov     cs:[cmd_ptr],0          ;Zero current command pointer
cmd_exit:
                ret
cmd_record      endp

;------------------------------------------------------------------------------
; SET_CURSOR sets the cursor mode based on the state of the insert flag.
;------------------------------------------------------------------------------
set_cursor      proc    near
                cmp     cs:[insert_flag],0      ;Test state of insert flag
                je      setc1                   ;Branch if not set
                mov     cx,cs:[cursor_mode]     ;Raise top by 2 scan lines
                sub     ch,2
                jns     setc2                   ;Exit if it wraps around
                ret
setc1:
                mov     cx,cs:[cursor_mode]     ;Set default cursor
setc2:
                mov     ah,1                    ;Set cursor mode
                int     10h
                ret
set_cursor      endp

;============================================================================
; MUXINT processes calls to interrupt 2Fh
; Entry:  AH - Device ID
; Exit:   AL - 0FFh if AH = Alias device ID. Unchanged otherwise.
;         ES - Code segment if AH = Alias device ID. Unchanged otherwise.
;============================================================================
muxint          proc    far
                assume  cs:code,ds:nothing,es:nothing
                cmp     ah,cs:[multiplex_id]    ;Check for program ID
                je      muxint_1                ;Its us, indicate installed.
                jmp     cs:[int2fh]             ;else pass the call on
muxint_1:
                mov     al,-1                   ;Indicate Alias installed
                push    cs                      ;ES = installed code segment
                pop     es
                iret
muxint          endp

                even                            ;Align stack on word boundry
end_of_resident =       $
;----------------------------------------------------------------------------
; Start of non-resident code.
;----------------------------------------------------------------------------
final_install:
                rep     movsb                   ;Copy alias list
                mov     di,cmdstack_base        ;Initialize command stack
                mov     cx,cmdstack_size        ;  area with leading zeroes
                jcxz    tsr                     ;Branch if size is zero
                xor     al,al
                cld
final_loop:
                stosb
                add     di,127
                loop    final_loop
tsr:
                mov     ax,3100h                ;Terminate and stay resident
                int     21h

;----------------------------------------------------------------------------
; Non-resident data.
;----------------------------------------------------------------------------
alrdy_installed db      0                       ;Installed flag
other_seg       dw      0                       ;Segment of installed code
databuff_seg    dw      0                       ;Segment of data buffer.

alias_buffer    dw      512                     ;Extra buffer for alias list.
aliaslist_end   dw      0                       ;Offset of end of the list.
alias_inlist    db      0                       ;Flag used in alias list append

infomsg1        db      "ALIAS uninstalled$"
infomsg2        db      13,10,9,"Command stack size: $"
infomsg3        db      13,10,9,"Minimum stacked command length: $"
infomsg4        db      13,10,9,"Bytes free in alias buffer: $"
infomsg5        db      13,10,9,"Alias translation is $"
infomsg5d       db      "disabled",13,10,"$"
infomsg5e       db      "enabled",13,10,"$"
infomsg6        db      13,10,9,"FUNCTION KEY DEFINITIONS$"
infomsg7        db      13,10,9,"ALIAS DEFINITIONS$"
infomsg8        db      13,10,"For help type ALIAS ?$"
infomsg9        db      13,10,"Can",39,"t find Master Env. block$"

filemsg1        db      13,10,"Error in line $" ;File identification message.
filemsg2        db      " of file: "
filenam_field   db      78 dup (0)              ;Name of current entry file.

errmsg0         db      "Need DOS 2.0 or greater$"
errmsg1         db      "ALIAS not installed$"

errmsg2         db      13,10,"Syntax: ALIAS [alias [command]] "
                db      "[/F filename] [/S nn] [/U]",13,10
                db      14 dup (" ")
                db      "[/B nn] [/D] [/E] [/L] [/M]",13,10,10
                db      9,"ALIAS alias command",13,10
                db      9,"ALIAS [fn] command",13,10,10
                db      9,"/B = Buffer size",13,10
                db      9,"/D = Disable alias translation",13,10
                db      9,"/E = Enable alias translation",13,10
                db      9,"/F = Filename of command file",13,10
                db      9,"/L = List aliases",13,10
                db      9,"/M = Minimum command length to stack",13,10
                db      9,"/S = Size of command stack",13,10
                db      9,"/U = Uninstall",13,10,"$"

errmsg3         db      "Can",39,"t uninstall$"
errmsg4         db      "Can",39,"t change parameter after installation$"
errmsg5         db      "Illegal number$"
errmsg6         db      "Can",39,"t find alias file$"
errmsg7         db      "Can",39,"t find COMMAND.COM$"
errmsg8         db      "Not enough memory$"
errmsg9         db      "Alias list full$"
errmsg10        db      "List and stack too large$"
errmsg11        db      "Invalid key assignment$"
errmsg12        db      "Number too big$"
errmsg13        db      "Alias not in list$"
errmsg14        db      "Error using Int 2Fh$"
endmsg          db      13,10,"$"

shiftmsg        db      "S-$"
altmsg          db      "A-$"
ctlmsg          db      "C-$"

file_linecount  dw      0                       ;Line number being processed.
caps_flag       db      0
param_found     db      0                       ;Cmd line parameter found flag
append_cr       db      0                       ;Append cr to alias flag
cmdcom_name     db      "COMMAND"               ;Name of command.com

cmd_switches    db      "sfeldbum*/"            ;Letters of valid commands.
cmd_switch_end  =       $
cmd_jmp_tbl     dw      offset setstacksize     ;This jump table is used to
                dw      offset loadaliasfile    ;  call the routines that
                dw      offset enablealias      ;  process the command line
                dw      offset listalias        ;  arguments
                dw      offset disablealias
                dw      offset setlistbuffer
                dw      offset remove
                dw      offset minstacklen
                dw      offset comment_line     ;Comments can be indicated by
                dw      offset comment_line     ;  a /* or a //.

;----------------------------------------------------------------------------
; Initialization routine.
;----------------------------------------------------------------------------
initialize      proc    near
                assume  cs:code,ds:code,es:code
                cld
                mov     ah,30h                  ;Get DOS version
                int     21h
                xchg    al,ah                   ;Swap major, minor numbers
                mov     dx,offset errmsg0       ;Bad DOS version
                cmp     ah,2                    ;Run if DOS 2.0 or greater.
                jb      jmp_disp_error
                mov     dos_version,ax          ;Save version number

                mov     ax,max_code_size        ;Get size of installed code.
                mov     bx,ax
                shl     ax,1                    ;Convert to bytes
                shl     ax,1
                shl     ax,1
                shl     ax,1
                mov     sp,ax                   ;Move stack pointer
                sub     ax,512                  ;Save room for stack
                mov     aliaslist_size,ax       ;Mark end of alias list
                mov     ah,4ah                  ;Reduce memory allocation
                int     21h

                mov     bx,filebuf_size         ;Get size of file buffer,
                mov     ah,48h                  ;Allocate memory block
                int     21h
                mov     dx,offset errmsg8       ;Not enough memory msg
                jc      jmp_disp_error
                mov     databuff_seg,ax         ;Save segment to file buffer.

                mov     ax,offset end_of_code           ;Initialize alias list
                mov     word ptr [aliaslist_ptr],ax     ;  pointers.
                mov     ax,cs
                mov     word ptr [aliaslist_ptr+2],ax
                les     di,aliaslist_ptr                ;Initialize alias list
                mov     ax,-1                           ;  by writing a -1 as
                stosw                                   ;  an end flag.
                mov     aliaslist_end,di
;
;See if a copy is already resident in memory. If > DOS 3.0, use int 2Fh.
;
                mov     byte ptr [main+2],0     ;Initialize fingerprint
                cmp     dos_version,300h        ;See if DOS 3.0 or later
                jb      find_copy1              ;No, search the old way.
                mov     cx,16                   ;Try 16 different IDs.
find_copy:
                xor     ax,ax
                mov     es,ax
                mov     ah,multiplex_id         ;Load ID.  Use Int 2Fh to
                int     2fh                     ;  reach installed code so
                or      al,al                   ;  that we are compatible
                jne     find_copy0              ;  with 386 memory managers.
                push    cs
                pop     es                      ;If AL not changed, ALIAS not
                jmp     short find_copy4        ;  installed.
find_copy0:
                push    cx
                call    cmpheader               ;See if really Alias by
                pop     cx                      ;  comparing file headers.
                je      find_copy3
                inc     multiplex_id            ;ID used by another program.
                loop    find_copy               ;  Change and try again.
                mov     dx,offset errmsg14      ;All IDs taken, print error
jmp_disp_error:
                jmp     disp_error              ;  msg and exit.
;
;For DOS 2.x find the installed code the old fashioned way by scanning
;the memory control blocks.
;
find_copy1:
                xor     bx,bx                   ;zero BX for start
                mov     ax,cs                   ;keep CS value in AX
find_copy2:
                inc     bx                      ;increment search segment value
                mov     es,bx
                assume  es:nothing
                cmp     ax,bx                   ;not installed if current
                je      find_copy4              ;  segment is found.
                call    cmpheader
                jne     find_copy2              ;loop back if not found
find_copy3:
                inc     alrdy_installed         ;Set installed flag
find_copy4:
                mov     other_seg,es            ;Save seg of installed code
;
;Copy the command line to the file buffer to treat as a one line file.
;
                mov     ax,databuff_seg         ;Get segment of file buffer.
                mov     es,ax                   ;Treat the command line as
                xor     di,di                   ;  a 1 line file.
                mov     si,offset command_tail
                xor     cx,cx
                or      cl,[si]                 ;Get command line length
                je      parse_line_end          ;If zero, skip parse routine
                inc     si                      ;Copy command line into file
                inc     cx                      ;  buffer.
                push    cx
                rep     movsb
                pop     cx                      ;CX = file size.
                xor     si,si                   ;Point SI to start of buffer
                push    es
                pop     ds                      ;Set DS to file buffer seg
                assume  ds:nothing
                mov     es,cs:[other_seg]       ;Set ES to installed code
;
;Parse command line and command files.
;
parse_line_loop:
                xor     bl,bl
                call    scanline                ;Get 1st character
                jc      parse_2                 ;If carriage return, skip line
                cmp     al,"/"                  ;Compare character to switch.
                je      parse_switch_found
                cmp     al,"?"                  ;See if help character
                mov     dx,offset errmsg2       ;Command not found msg
                je      disp_error
                call    setkey                  ;Must be alias definition
                jc      disp_error
                mov     param_found,1           ;Set parameter found flag
                jmp     short parse_2           ;Return to parse loop
parse_switch_found:
                mov     param_found,1           ;Set parameter found flag
                lodsb                           ;Get command line switch
                dec     cx
                cmp     al,'A'                  ;Convert to lower case if
                jb      parse_1                 ;  necessary.
                cmp     al,'Z'
                ja      parse_1
                or      al,20h
parse_1:
                push    cx                      ;Scan the list of allowable
                push    es                      ;  command switches. If switch
                push    cs                      ;  found, use its position for
                pop     es                      ;  an index into the list of
                mov     di,offset cmd_switches  ;  routines.
                mov     cx,offset cmd_switch_end - offset cmd_switches
                mov     bx,offset cmd_switch_end - offset cmd_switches - 1
                repne   scasb
                mov     ax,cx                   ;Copy index into list
                pop     es
                pop     cx
                mov     dx,offset errmsg2       ;Command not found msg
                jne     disp_error
                sub     bx,ax                   ;Compute offset into list
                shl     bx,1                    ;Convert to word offset
                add     bx,offset cmd_jmp_tbl   ;Add the start addr of table.
                call    cs:[bx]                 ;Call command routine.
                jc      disp_error              ;If error terminate parse.
parse_2:
                jcxz    parse_line_end          ;If at file end, exit parse.
                jmp     short parse_line_loop
;
;See if installed. If not, install.
;
parse_line_end:
                cmp     cs:[param_found],1      ;See if any parameters found
                je      init2                   ;If already installed and
                cmp     cs:[alrdy_installed],0  ;  no parameters on the
                je      init2                   ;  command line, default to
                mov     es,cs:[other_seg]       ;  showing program information
                call    listalias
init2:
                mov     ah,49h                  ;Release memory block used
                mov     es,cs:[databuff_seg]    ;  for file buffer.
                int     21h
                push    cs
                pop     ds
                assume  ds:code
                mov     word ptr databuff_seg,0 ;Indicate file buff released
                cmp     alrdy_installed,0       ;If not already installed,
                je      install                 ;  jump to install routine.
exit:
                mov     ax,4C00h                ;Terminate with RC = 0
                int     21h
;
;Display error message.
;
                assume  ds:nothing
disp_error:
                xor     ax,ax                   ;If file buffer still
                or      ax,databuff_seg         ;  allocated, deallocate it.
                jz      disp_error0
                mov     ah,49h                  ;Release memory block used
                mov     es,ax                   ;  for file buffer.
                int     21h
disp_error0:
                push    cs
                pop     ds
                assume  ds:code
                cmp     byte ptr filenam_field,0
                je      disp_error1             ;If processing a file, print
                push    dx                      ;  a message informing the
                mov     dx,offset filemsg1      ;  user the filename being
                call    printmsg                ;  processed and the line
                mov     ax,file_linecount       ;  that the error occured.
                call    hex2asc
                mov     dx,offset filemsg2
                call    printmsgcr
                pop     dx
disp_error1:
                call    printmsgcr              ;print string
                mov     ax,4c01h                ;Terminate with RC = 1
                int     21h
;
;Install routine. Find segment of COMMAND.COM, hook into int 21h, 2Fh and TSR.
;
                assume  ds:code
install:
                mov     dx,offset program       ;Display copyright message
                call    printmsg

                push    ds                      ;Save DS
                xor     ax,ax                   ;Then zero it
                mov     ds,ax
                mov     cs:[points],ax          ;Record number of scan lines
                mov     ax,ds:[0460h]           ;  per character and cursor
                mov     cs:[cursor_mode],ax     ;  mode
                pop     ds                      ;Restore DS

                mov     byte ptr filenam_field,0  ;Don't callout file on error
                mov     ah,52h                  ;get address of first MCB
                int     21h
                mov     ax,es:[bx-2]            ;point ES to MCB
                mov     cx,20                   ;Allow only 20 loops.
mcb_loop:
                mov     es,ax
                cmp     byte ptr es:[0],"M"     ;check for mcb signature
                jne     mcb_error
                inc     ax                      ;point AX to memory block
                cmp     ax,es:[1]               ;See if this is a PSP block
                je      psp_found
mcb_continue:
                add     ax,es:[3]               ;Get size of memory block
                loop    mcb_loop
mcb_error:
                mov     dx,offset errmsg7       ;Can't locate command.com PSP
                jmp     disp_error
psp_found:
                cmp     dos_version,0a00h       ;If OS/2, use DOS 3.3 method.
                jae     psp_found_1
                cmp     dos_version,0400h       ;If DOS 4.00 or greater,
                jb      psp_found_1             ;  COMMAND.COM may not be the
                push    ds                      ;  first program loaded.  Look
                mov     si,offset cmdcom_name   ;  at the name of the program
                mov     di,8                    ;  stored in the last 8 bytes
                mov     cx,7                    ;  of the memory control
                repe    cmpsb                   ;  block.  If the string
                pop     ds                      ;  "COMMAND" isn't found, keep
                jne     mcb_continue            ;  looking.
psp_found_1:
                mov     cmdcom_psp,ax           ;Save segment of cmd.com PSP
                mov     es,ax
                mov     bx,es:[2ch]             ;Get seg of master environment
                mov     master_env,bx
                dec     bx
                mov     es,bx
                cmp     byte ptr es:[0],"M"     ;See if valid memory block
                je      psp_found_4
                mov     dx,ax                   ;Save COMMAND PSP segment
                dec     ax                      ;Point back to mcb
                mov     es,ax
psp_found_2:
                add     ax,es:[3]               ;Get size of memory block
                inc     ax
                cmp     es:[1],dx               ;See if owned by CMD.COM
                je      psp_found_3
                mov     es,ax
                loop    psp_found_2
                mov     dx,offset infomsg9      ;Indicate can't find env
                call    printmsgcr
                xor     ax,ax
psp_found_3:
                inc     ax
                mov     master_env,ax
psp_found_4:
;
;Set up pointers to the cmd stack and to move the alias list to the end of the
;command stack so it takes up less space when ALIAS resident.
;
                mov     di,mystack_ptr          ;Get base of internal stack
                mov     work_buff_ptr,di        ;Copy ptr to alias buffer
                add     di,128                  ;Add size of buffer
                mov     cmdstack_base,di        ;Copy for start of cmd stack
                mov     ax,cmdstack_size        ;Get size of command stack
                mov     ah,128                  ;128 bytes for each entry
                mul     ah
                add     di,ax                           ;Add to cmd stack for
                mov     si,word ptr aliaslist_ptr       ;  start of the
                mov     cx,aliaslist_end                ;  alias list.
                mov     word ptr aliaslist_ptr,di       ;Save new pointer.
                sub     cx,si                   ;Compute size of list.
                mov     dx,di                   ;See if we have enough room
                add     dx,cx                   ;  for everything in one seg.
                jnc     install_1
                mov     dx,offset errmsg10      ;Not enough memory, exit.
                jmp     disp_error
install_1:
                add     dx,alias_buffer         ;Add additional space for list
                mov     aliaslist_size,dx       ;Save pointer to end of seg
                add     dx,15                   ;Convert memory needed to
                shr     dx,1                    ;  paragraphs.
                shr     dx,1
                shr     dx,1
                shr     dx,1
                cmp     di,si                   ;Check for overlap in the move
                jb      install_2               ;If overlap, copy list from
                std                             ;  the top down to avoid
                add     di,cx                   ;  overwriting the list.
                add     si,cx
                dec     si
                dec     di
;
;Revector interrupts 21h and 2Fh (if necessary).
;
install_2:
                mov     ax,3521h                ;Get interrupt 21 (DOS)
                int     21h                     ;  vector.
                mov     word ptr [int21h],bx
                mov     word ptr [int21h+2],es
                push    dx                      ;Save memory size parameter
                mov     ax,2521h                ;Point int 21 to internal
                mov     dx,offset dosint        ;  routine.
                int     21h

                cmp     dos_version,300h        ;See if we are using Int 2Fh
                jb      install_3
                mov     ax,352fh                ;Get interrupt 2F (MUX)
                int     21h                     ;  vector.
                mov     word ptr [int2fh],bx
                mov     word ptr [int2fh+2],es
                mov     ax,252fh                ;Point int 2F to internal
                mov     dx,offset muxint        ;  routine.
                int     21h
install_3:
                pop     dx
                push    ds                      ;ES = DS
                pop     es
                jmp     final_install           ;Jump to safe place for move.
initialize      endp

;-----------------------------------------------------------------------------
; CMPHEADER compares the first 16 bytes of this file with the segment
;           pointed to by ES.
; Entry:  DS - code segment
;         ES - pointer to segment to compare
; Exit:   ZF - 0 = segments match.
;-----------------------------------------------------------------------------
cmpheader       proc    near
                mov     si,offset main+2        ;Search this segment for ASCII
                mov     di,si                   ;  fingerprint.
                mov     cx,16
                repe    cmpsb
                ret
cmpheader       endp

;-----------------------------------------------------------------------------
; PRINTMSG prints the message pointed to by DX to the screen.
; Entry:  DX - pointer to ASCII message terminated by $
;-----------------------------------------------------------------------------
printmsg        proc    near
                assume  ds:nothing,es:nothing
                push    ds
                push    cs
                pop     ds
                assume  ds:code
                mov     ah,9                    ;Print message
                int     21h
                pop     ds
                ret
printmsg        endp

;-----------------------------------------------------------------------------
; PRINTMSGCR calls PRINTMSG, then appends a carriage return to the message.
; Entry:  DX - pointer to ASCII message terminated by $
;-----------------------------------------------------------------------------
printmsgcr      proc    near
                assume  ds:nothing,es:nothing
                push    dx
                call    printmsg
                mov     dx,offset endmsg
                call    printmsg
                pop     dx
                ret
printmsgcr      endp

;-----------------------------------------------------------------------------
; SETSTACKSIZE - Sets the size of the command line stack.
; Entry:  DS:SI - points to stack size in ascii
; Exit:      CF - Clear if sucessful, Set if error, DX points to error msg.
;-----------------------------------------------------------------------------
setstacksize    proc    near
                assume  ds:nothing,es:nothing
                xor     bl,bl                   ;Check for installed code
                call    setparameter            ;Get num and convert to binary
                jc      setstack_exit           ;Check for error
                mov     cs:[cmdstack_size],ax   ;Save parameter
setstack_exit:
                ret
setstacksize    endp

;-----------------------------------------------------------------------------
; SETLISTBUFFER - Sets the size of the additional buffer reserved for alias
;                 list expansion.
; Entry:  DS:SI - points to buffer size in ascii
; Exit:      CF - Clear if sucessful, Set if error, DX points to error msg.
;-----------------------------------------------------------------------------
setlistbuffer   proc    near
                assume  ds:nothing,es:nothing
                xor     bl,bl                   ;Check for installed code
                call    setparameter            ;Get num and convert to binary
                jc      setlistbuffer_exit      ;Check for error
                mov     cs:[alias_buffer],ax    ;Save buffer size parameter
setlistbuffer_exit:
                ret
setlistbuffer   endp

;-----------------------------------------------------------------------------
; MINSTACKLEN - sets the minimum legth of a command to stack.
; Entry:     ES - segment of the installed code
;         DS:SI - points to buffer size in ascii
; Exit:      CF - Clear if sucessful, Set if error, DX points to error msg.
;-----------------------------------------------------------------------------
minstacklen     proc    near
                assume  ds:nothing,es:nothing
                mov     bl,1                    ;Don't check for installed code
                call    asc2bin                 ;Get num and convert to binary
                jc      minstacklen_exit        ;Check for error
                cmp     al,126
                jb      minstacklen_1
                mov     dx,offset errmsg12      ;Stack length too big
                stc
                jmp     short minstacklen_exit
minstacklen_1:
                or      al,al                   ;Make sure min length is not
                jne     minstacklen_2           ; specified at 0. If so,
                inc     al                      ; change to 1.
minstacklen_2:
                mov     es:[minlength],al       ;Save minimum length parameter
minstacklen_exit:
                ret
minstacklen     endp

;-----------------------------------------------------------------------------
; SETPARAMETER - Common code used by the set stack and set buffer and set
;                minimum command length routines.
; Entry:  DS:SI - points to ascii number
;            BL - Flag to indicate check for installed code, BL=0, check.
; Exit:      CF - Clear if sucessful, Set if error.
;-----------------------------------------------------------------------------
setparameter    proc    near
                assume  ds:nothing,es:nothing
                mov     dx,offset errmsg4       ;Can't change parameter msg
                cmp     cs:[alrdy_installed],1  ;If already installed don't
                je      setparam_error          ;  change parameter.
                call    asc2bin
setparam_exit:
                ret
setparam_error:
                stc                             ;Set error flag.
                jmp     short setparam_exit
setparameter    endp

;-----------------------------------------------------------------------------
; SETKEY modifies the alias list to add function key definitions.
; Entry:  DS:SI - pointer to string identifing the function key
;            ES - pointer to segment of installed code
; Exit:      CF - clear if successful
;-----------------------------------------------------------------------------
setkey          proc    near
                assume  ds:nothing,es:nothing
                push    es
                cmp     al,"["                  ;Determine alias or key
                je      setkey_1
                dec     si                      ;Backup before last key
                inc     cx
                jmp     short setkey_5
setkey_1:
                lodsb                           ;Get next character
                dec     cx
                jcxz    setkey_badkey
                mov     bx,58                   ;Load default F1 keycode
                or      al,20h                  ;Convert to lower case
                cmp     al,"f"
                je      setkey_3
                add     bx,25                   ;Assume shift F1 keycode
                cmp     al,"s"
                je      setkey_2
                add     bx,10                   ;Assume ctl F1 keycode
                cmp     al,"c"
                je      setkey_2
                add     bx,10                   ;Assume alt F1 keycode
                cmp     al,"a"
                jne     setkey_badkey
setkey_2:
                lodsb                           ;Get next character
                dec     cx
                or      al,20h                  ;Convert to lower case
                cmp     al,"f"                  ;Make sure next key is "f"
                jne     setkey_badkey
setkey_3:
                call    asc2bin
                jc      setkey_badkey           ;If bad number, exit
                or      ax,ax
                je      setkey_badkey           ;Make sure not zero
                cmp     ax,10
                ja      setkey_badkey           ;Make sure less than 10
                cmp     byte ptr [si-1],"]"     ;Check for closing bracket
                jne     setkey_badkey           ;  if not found, error
                add     bx,ax                   ;Add in function key number
                cmp     byte ptr [si]," "       ;Check to see if a character
                jbe     setkey_4                ;  trails the closing bracket.
                cmp     byte ptr [si],"*"       ;Check to see if an * is
                jne     setkey_badkey           ;  appended to the function
                lodsb                           ;  key assignment. If so,
                dec     cx                      ;  remove key and set flag
                mov     cs:[append_cr],1        ;  to append cr to command.
setkey_4:
                sub     si,2                    ;Back up to keycode.
                xor     bh,bh
                mov     ds:[si],bx              ;Save keycode on command line
                add     cx,2
setkey_5:
                call    setalias                ;Use SETALIAS to load
setkey_exit:
                pop     es                      ;Restore ptr to installed seg
                ret
setkey_badkey:
                mov     dx,offset errmsg11      ;Bad key assignment msg
                stc
                jmp     setkey_exit
setkey          endp

;-----------------------------------------------------------------------------
; SETALIAS modifies the alias list according to command line agruments.
; Entry:  DS:SI - pointer to string to be inserted into alias list.
;            ES - pointer to segment of installed code.
; Exit:      CF - clear if successful
;-----------------------------------------------------------------------------
setalias        proc    near
                assume  ds:nothing,es:nothing
                push    es
                xor     bl,bl                   ;Find 1st character on
                call    scanline                ;  command line.
                jnc     setalias_1              ;If at end of line, exit
                jmp     setalias_exit           ;  routine.
setalias_1:
;
;Get length of alias, then search list for matching alias
;
                dec     si                      ;Backup to before 1st char.
                inc     cx
                mov     byte ptr cs:[alias_inlist],0 ;Assume not in list
                call    searchalias             ;Is there already an alias?
                jc      setalias_2              ;No, continue.

                inc     byte ptr cs:[alias_inlist]
                call    delete_aliasent         ;Delete entry from list
setalias_2:
;
;Append new alias to the end of the list.
;
                mov     bp,di                   ;Save ptr to end of list.
                add     di,4                    ;Move past length fields.
                push    es                      ;Get max size of alias list.
                mov     es,cs:[other_seg]
                mov     dx,es:[aliaslist_size]
                pop     es
;
;Append alias to list.
;
                xor     ax,ax                   ;Clear character counter
setalias_3:
                lodsb                           ;Get byte
                dec     cx                      ;Decriment buffer counter.
                jcxz    setalias_4              ;If at end of file, exit.
                cmp     al,13                   ;See if at end of line.
                jne     setalias_6              ;No, continue.
setalias_4:
                cmp     byte ptr cs:[alias_inlist],0
                jne     setalias_5              ;Was alias in list?
                jmp     setalias_notnfil        ;No, incomplete alias specifed
setalias_5:
                jmp     setalias_exit           ;Yes, alias simply erased.
setalias_6:
                cmp     al,' '                  ;See if at end of tag.
                je      setalias_8              ;Yes, exit copy loop
                cmp     al,9                    ;Check for tab
                je      setalias_8
                cmp     di,dx                   ;See if alias list is full
                jbe     setalias_7              ;No, continue
                jmp     setalias_full           ;Yes, exit routine
setalias_7:
                stosb                           ;No, add character to list
                inc     ah                      ;Inc size of tag
                jmp     short setalias_3
setalias_8:
                mov     es:[bp+2],ah            ;Save size of alias
;
;Append command to alias list.
;
                mov     cs:[caps_flag],0        ;Initialize setcaps flag.
                xor     bx,bx                   ;Find 1st character in
                call    scanline                ;  command.
                jc      setalias_exit           ;If no command, exit
                xor     ah,ah                   ;Clear character counter
setalias_9:
                cmp     al,"%"                  ;Check for environment var
                jne     setalias_11             ;  by checking for % signs.
                cmp     cs:[caps_flag],0        ;If caps flag set capitialize
                jne     setalias_10             ;  string before saving.
                mov     bl,ds:[si]              ;Get next character
                cmp     bl,"0"                  ;If numeric, assume this is
                jb      setalias_10             ;  a line parameter, not an
                cmp     bl,"9"                  ;  environment variable so
                jbe     setalias_11             ;  don't set caps flag.
                cmp     bl,"%"                  ;Don't let double % signs
                je      setalias_11             ;  indicate environment var.
setalias_10:
                not     byte ptr cs:[caps_flag] ;Toggle caps flag
setalias_11:
                cmp     cs:[caps_flag],0        ;Capitialize environment
                je      setalias_12             ;  variables so they will
                cmp     al,"a"                  ;  match when searched for in
                jb      setalias_12             ;  the environment block.
                cmp     al,"z"
                ja      setalias_12
                and     al,0dfh                 ;Make character uppercase.
setalias_12:
                cmp     di,dx                   ;See if alias list is full
                ja      setalias_full           ;Yes, exit routine
                stosb                           ;Append character on list
                inc     ah                      ;Inc character counter.
                jcxz    setalias_13             ;Check for end of file.
                lodsb                           ;Get next character
                dec     cx                      ;Dec file counter.
                cmp     al,13                   ;See if carriage return.
                jne     setalias_9              ;If not continue
setalias_13:
                cmp     cs:[append_cr],1        ;If flag set, append carrage
                jne     setalias_14             ;  return to command.
                mov     al,13
                inc     ah
                stosb
                mov     cs:[append_cr],0        ;Clear flag
setalias_14:
                mov     es:[bp+3],ah            ;Save command size
                mov     word ptr es:[di],-1     ;Set new end of list flag
                mov     ax,di                   ;Save end pointer to list
                add     ax,2                    ;Make room for the end flag.
                mov     cs:[aliaslist_end],ax
                sub     di,bp                   ;Compute size of entry
                mov     es:[bp],di              ;Put size over old end flag.
                inc     cs:[file_linecount]     ;Point counter to next line.
setalias_exit:
                clc
setalias_exit1:
                pop     es                      ;Restore ptr to installed seg
                ret
setalias_full:
                mov     dx,offset errmsg9       ;Alias list too large msg.
                stc
                jmp     short setalias_exit1
setalias_notnfil:
                mov     dx,offset errmsg13      ;Alias not in list.
                stc
                jmp     short setalias_exit1
setalias        endp

;-----------------------------------------------------------------------------
; DELALIASENTRY - Deletes an alias entry
; Entry:  DS:SI - pointer to the entry in the alias list to delete
; Exit:      CF - clear if successful
;-----------------------------------------------------------------------------
delete_aliasent proc    near
                push    cx                      ;Save registers
                push    si
                push    ds                      ;Yes, remove entry from list
                push    es                      ;  by moving the remainder of
                pop     ds                      ;  the list over this entry.
                mov     si,es:[di]              ;Point SI to the next list
                add     si,di                   ;  entry.
delent_1:
                cmp     word ptr es:[si],-1     ;Check for the end of the list
                je      delent_2
                mov     cx,es:[si]              ;Get size of entry
                rep     movsb                   ;Copy next entry over current
                jmp     short delent_1          ;  entry.
delent_2:
                mov     word ptr es:[di],-1     ;Set end of list indicator
                pop     ds                      ;Get back file buffer pointer
                pop     si
                pop     cx
                ret
delete_aliasent endp

;-----------------------------------------------------------------------------
; LOADALIASFILE loads a file containing a list of alias commands.
; Entry:  DS:SI - pointer to the name of the file to open
; exit:      CX - size of the file in bytes
;            CF - clear if successful
;-----------------------------------------------------------------------------
loadaliasfile   proc    near
                assume  ds:nothing,es:nothing
                xor     bl,bl
                call    scanline                ;Find 1st char of filename.
                mov     dx,si                   ;Copy filename pointer
                inc     bl                      ;Find end of filename
                call    scanline
                mov     byte ptr [si-1],0       ;Make filename ASCIIZ.
                dec     dx
                mov     ax,3d00h                ;Open file (Read only)
                int     21h
                jc      loadfile_error
                mov     bx,ax                   ;Copy file handle
;
;Save the name of the file for error messages.
;
                push    si
                push    es
                mov     di,offset filenam_field
                push    cs
                pop     es
                mov     si,dx
loadfile_1:
                lodsb
                stosb
                or      al,al
                jne     loadfile_1
                mov     byte ptr es:[di-1],"$"  ;Terminate string with $.
                pop     es
                pop     si
;
;Open file and read contents into file buffer.
;
                mov     ah,3fh                  ;Read alias file
                xor     dx,dx                   ;Point to base of file buffer.
                mov     cx,cs:[filebuf_size]    ;Get size of file buffer.
                shl     cx,1                    ;Convert to bytes
                shl     cx,1
                shl     cx,1
                shl     cx,1
                int     21h
                mov     si,ax
                mov     byte ptr ds:[si],13     ;Append a CR to end of file.
                mov     cx,ax                   ;Save new file size
                xor     si,si                   ;Reset file pointer.
                mov     ah,3eh                  ;Close file.
                int     21h
                mov     cs:[file_linecount],1   ;Reset line counter.
loadfile_exit:
                clc
loadfile_exit1:
                ret
loadfile_error:
                stc
                mov     dx,offset errmsg6       ;Bad filename specified.
                jmp     short loadfile_exit1
loadaliasfile   endp

;-----------------------------------------------------------------------------
; LISTALIAS prints the alias list to screen.
; Entry:  ES - segment of the installed code
;-----------------------------------------------------------------------------
listalias       proc    near
                assume  ds:nothing,es:nothing
                push    ds
;
;Print Command stack size and amount of alias buffer space remaining
;
                mov     dx,offset infomsg2      ;Print size of command stack
                call    printmsg
                mov     ax,es:[cmdstack_size]   ;Get size of command stack
                call    hex2asc                 ;Print size
                mov     dx,offset infomsg3      ;Print min cmd length
                call    printmsg
                xor     ax,ax
                mov     al,es:[minlength]       ;Get min cmd length to stack
                call    hex2asc                 ;Print length
                mov     dx,offset infomsg4      ;Print label to buffer size
                call    printmsg
                lds     si,es:[aliaslist_ptr]   ;Get pointer to alias list
listalias_1:
                cmp     word ptr [si],-1        ;Scan through alias list to
                je      listalias_2             ;  find the end of the list.
                add     si,[si]                 ;Point to next entry
                jmp     short listalias_1
listalias_2:
                mov     ax,es:[aliaslist_size]  ;Get offset to end of buffer
                sub     ax,si                   ;Subtract end of alias list
                sub     ax,2                    ;Correct for end pointer
                call    hex2asc                 ;Print size
                mov     dx,offset infomsg5      ;Indicate if alias translation
                call    printmsg                ;  is enabled or disabled
                mov     dx,offset infomsg5d     ;Disabled message
                cmp     byte ptr es:[chk_alias],0       ;See if alias enabled
                je      listalias_3
                mov     dx,offset infomsg5e     ;Enabled message
listalias_3:
                call    printmsg
;
;Scan through alias list to print function key assignments
;
                xor     dx,dx                   ;Clear alias found flag
                lds     si,es:[aliaslist_ptr]   ;Get pointer to alias list
listalias_4:
                cmp     word ptr [si],-1        ;Check for end of list
                jne     listalias_5
                jmp     listalias_11
listalias_5:
                mov     bx,si                   ;Save pointer to entry.
                cmp     byte ptr [si+5],0       ;See if key assignment
                je      listalias_6             ;If so, skip entry
                or      dh,1                    ;Set alias found flag
                add     si,[bx]                 ;Point to next entry
                jmp     short listalias_4
listalias_6:
;
;Convert scan code into function key label
;
                test    dh,80h                  ;See if first time though
                jne     listalias_7
                push    dx
                mov     dx,offset infomsg6      ;If first time print header
                call    printmsgcr
                pop     dx
                or      dh,80h                  ;Set header printed flag
listalias_7:
                call    printtab
                push    dx
                add     si,4                    ;Point SI to command string
                mov     al,[si]                 ;Get key code
                mov     dh,al
                cmp     al,84                   ;Check for base code
                jb      listalias_9
                sub     dh,25
                mov     di,offset shiftmsg      ;Assume shift prefix
                cmp     al,94                   ;Check for shift
                jb      listalias_8
                sub     dh,10
                mov     di,offset ctlmsg        ;Assume control prefix
                cmp     al,104                  ;Check for ctl
                jb      listalias_8
                mov     di,offset altmsg        ;Assume alt prefix
                sub     dh,10
listalias_8:
                push    dx                      ;Print prefix identifier
                mov     dx,di
                call    printmsg
                pop     dx
listalias_9:
                mov     dl,"F"                  ;Set function key
                mov     ah,2                    ;Character output
                int     21h
                mov     al,dh                   ;Get modified key code
                sub     al,58                   ;Convert from scan code to hex
                xor     ah,ah
                call    hex2asc                 ;Print function key number
                mov     cl,[bx+3]               ;Get length of command
                inc     si                      ;Point SI to command string
                inc     si
                mov     di,bx
                add     di,[bx]
                cmp     byte ptr [di-1],13      ;Check for carrage return
                jne     listalias_10            ;  after command. If found,
                mov     dl,'*'                  ;  indicate immediate command
                mov     ah,2                    ;  by appending an * to the
                int     21h                     ;  end of the function key.
listalias_10:
                call    printtab
                call    printtab
                call    print_string            ;Print command to screen
                mov     dx,offset endmsg        ;Append carriage return to
                call    printmsg                ;  advance to next line. SI
                pop     dx                      ;  points to the next entry.
                jmp     listalias_4
;
;Scan through alias list to print aliases.
;
listalias_11:
                test    dh,1                    ;See if any aliases found
                je      listalias_exit          ;If no aliases, skip remainder
                mov     dx,offset infomsg7      ;Print header
                call    printmsgcr
                xor     cx,cx                   ;Clear CX
                lds     si,es:[aliaslist_ptr]   ;Get pointer to alias list
listalias_12:
                mov     bx,si                   ;Save pointer to entry.
                cmp     byte ptr [si+5],0       ;See if key definition
                jne     listalias_13            ;If so, skip entry
                add     si,[bx]
                jmp     short listalias_14
listalias_13:
                call    printtab
                add     si,4                    ;Point SI to alias string
                mov     cl,[bx+2]               ;Get length of alias
                call    print_string            ;Print alias to screen
                call    printtab
                call    printtab
                mov     cl,[bx+3]               ;Get length of command
                call    print_string            ;Print command to screen
                mov     dx,offset endmsg        ;Append carriage return to
                call    printmsg                ;  advance to next line.
listalias_14:
                cmp     word ptr [si],-1        ;Check for end of list. SI
                jne     listalias_12            ;  points to the next entry.
listalias_exit:
                mov     dx,offset infomsg8      ;Print pointer to help msg
                call    printmsgcr
                pop     ds
                clc
                ret
listalias       endp

;-----------------------------------------------------------------------------
; PRINTTAB prints a TAB character to the screen.
; Entry:  ES - segment of the installed code
;-----------------------------------------------------------------------------
printtab        proc    near
                assume  ds:nothing,es:nothing
                mov     ah,2                    ;Character output
                mov     dl,9                    ;Print TAB character
                int     21h
                ret
printtab        endp

;-----------------------------------------------------------------------------
; ENABLEALIAS enables alias translation.
; Entry:  ES - segment of the installed code
;-----------------------------------------------------------------------------
enablealias     proc    near
                assume  ds:nothing,es:nothing
                mov     byte ptr es:[chk_alias],1       ;Enable alias
                clc
                ret
enablealias     endp

;-----------------------------------------------------------------------------
; DISABLEALIAS disables alias translation.
; Entry:  ES - segment of the installed code
;-----------------------------------------------------------------------------
disablealias    proc    near
                assume  ds:nothing,es:nothing
                mov     byte ptr es:[chk_alias],0       ;Disable alias
                clc
                ret
disablealias    endp

;-----------------------------------------------------------------------------
; REMOVE uninstalls the installed program from memory.
;-----------------------------------------------------------------------------
remove          proc    near
                assume  ds:nothing,es:nothing
                push    ds
                mov     dx,offset errmsg1       ;Not installed msg
                cmp     cs:[alrdy_installed],0  ;See if installed
                je      remove_exit             ;Not installed, error

                mov     cx,cs:[other_seg]       ;Get segment of installed code
                mov     ax,3521h                ;Get DOS vector
                int     21h
                mov     ax,es                   ;Check to make sure DOS
                cmp     ax,cs:[other_seg]       ;  vector not modified.
                jne     remove_error

                lds     dx,es:[int2fh]          ;Get old interrupt 2F vector
                cmp     dx,-1                   ;See if Int used
                je      remove_1                ;No, skip check of Int 2F
                mov     ax,352fh                ;Get MUX vector
                int     21h
                mov     ax,es                   ;Check to make sure MUX
                cmp     ax,cs:[other_seg]       ;  vector not modified.
                jne     remove_error
                mov     ax,252fh                ;Set interrupt
                int     21h
remove_1:
                lds     dx,es:[int21h]          ;Get old interrupt 21 vector
                mov     ax,2521h                ;Set interrupt
                int     21h
                mov     cx,es:[env_segment]
                mov     ah,49h                  ;Free memory block
                int     21h
                mov     es,cx                   ;Free environment block
                mov     ah,49h
                int     21h
                mov     dx,offset infomsg1      ;Indicate uninstalled.
                mov     byte ptr filenam_field,0 ;Clear filename field
remove_exit:
                stc
                pop     ds
remove_exit1:   ret
remove_error:
                mov     dx,offset errmsg3       ;Can't remove error msg
                jmp     short remove_exit

remove          endp

;-----------------------------------------------------------------------------
; COMMENT_LINE allows comments in the alias file by skipping to the next
; carriage return.
; Entry:  SI - pointer to ASCII string
;         CX - file length
;-----------------------------------------------------------------------------
comment_line    proc near
                assume  ds:nothing,es:nothing
comment_loop:
                push    es
                push    ds
                pop     es
                mov     di,si                   ;Copy file pointer
                mov     al,13                   ;Scan for carriage return.
                repne   scasb
                inc     cs:[file_linecount]     ;Inc file line counter.
                mov     si,di                   ;Restore file pointer.
                pop     es
                clc
                ret
comment_line    endp

;-----------------------------------------------------------------------------
; HEX2ASC converts a binary number to ASCII and prints it to the screen.
; Entry:  AX - binary number
;-----------------------------------------------------------------------------
hex2asc         proc near
                assume  ds:nothing,es:nothing
                push    bx
                mov     cx,5                    ;Allow max of five digits
hex_loop1:
                xor     dx,dx                   ;Clear high word
                mov     bx,10                   ;Load number base
                div     bx                      ;Divide by base (10)
                add     dl,30h                  ;Convert to ascii
                push    dx                      ;Save digit on stack
                loop    hex_loop1
                mov     cx,5                    ;Allow max of five digits
                mov     bl,"0"                  ;Set leading zero indicator
hex_loop2:
                pop     dx                      ;Get digit off stack
                or      bl,dl                   ;Don't print leading zeros.
                cmp     bl,"0"                  ;The first non zero will
                je      hex_1                   ;  change bl to non-zero.
                mov     ah,2                    ;DOS character output
                int     21h
hex_1:
                loop    hex_loop2
hex_exit:
                pop     bx
                ret
hex2asc         endp

;-----------------------------------------------------------------------------
; ASC2BIN - converts an ASCII number of the command line to hex.
; Entry:  DS:SI - pointer to ASCII number
;-----------------------------------------------------------------------------
asc2bin         proc    near
                push    bx
                xor     bl,bl
                call    scanline                ;Find next character.
                mov     di,offset errmsg5       ;Bad number message
                jc      asc_error               ;If no number found, error
                mov     bl,al                   ;Copy first digit.
                xor     ax,ax                   ;Clear out sum
                xor     bh,bh                   ;Clear high byte for word adds
asc_loop:
                cmp     bl," "                  ;If space, assume end of
                jbe     asc_exit                ;  number.
                cmp     bl,"]"                  ;Exit if closing bracket
                je      asc_exit                ;  encountered
                sub     bl,"0"                  ;Check for valid number then
                jb      asc_error               ;  convert to binary.
                cmp     bl,9
                ja      asc_error
                mov     dx,10                   ;DX holds base multiplier
                mul     dx                      ;Shift over current number
                jc      asc_overflow            ;If overflow, indicate error
                add     ax,bx                   ;Add new digit to sum.
                jcxz    asc_exit                ;If end of file, exit.
                mov     bl,ds:[si]              ;Get next ASCII character
                inc     si                      ;Point to next character
                dec     cx                      ;Dec file size counter
                jmp     short asc_loop          ;Go back for more
asc_exit:
                clc                             ;Clear error flag.
asc_exit1:
                pop     bx
                ret
asc_overflow:
                mov     di,offset errmsg12      ;Number too large message.
asc_error:
                mov     dx,di                   ;Copy message pointer.
                stc                             ;Set error flag.
                jmp     short asc_exit1
asc2bin         endp
;-----------------------------------------------------------------------------
; SCANLINE performs the same function as SCAN4CHAR but keeps track of the
; carriage returns.
; Entry:  SI - pointer to ASCII string
;         BL - 0 = find next char, 1 = find next space
;         CX - file length
; Exit:   AL - first nonspace character
;         CF - set if carriage return found
;-----------------------------------------------------------------------------
scanline        proc    near
                call    scan4char               ;Find the next char.
                jnc     scanline_exit
                inc     cs:[file_linecount]     ;Point to next line.
                stc
scanline_exit:
                ret
scanline        endp

end_of_code     =       $
code            ends

end             main
------------
