[comp.sys.ibm.pc] Sherlok, do you recognize this "ls" code?

blumzi@TAURUS.BITNET (01/30/90)

Hi,
        Enclosed is the TASM source code for a UNIX like ls
        command. This code has been presented by a student
        as a project, and a suspicion has been raised that
        the code is copied from one of the many existing
        public domain / shareware or even a commercial ls (MKS ?).
        We would like to clear or verify this suspicion
        by your response. Please check to see if you recognize
        the following code. Remember that superficial changes
        are always possible: Different commenting style,
        different naming conventions. It may be even the case
        that the code was written in a different language,
        and a compiler has been used to generate the assembly.
        The label naming convention suggests this idea, but
        the direct reference to the hardware cannot be explained
        by this method.

        Since you have read so far, it is only fair that the reasons
        for the suspicion will be detailed:

                1) The student suggested the idea for having
                a project instead of all the programming
                exercises in the course.

                2) The project topic was suggested by the student.

                3) The student claimed that he is fluent in 8088
                assembly, and that he had "15 years of experience"
                in the field. However, for various reasons he was
                not able to finish the course in 1988, and
                he had to repeat the course in 1989. The course
                is based mostly on (almost trivial) 8086 assembly language
                programming tasks, no final exams.

                4) The professor of the course is not in the "heart
                of the matter" in PC related general knowledge

                5) The project was finished very very quickly - perhaps
                too quick for that order of magnitude of lines of
                good assembly code. The code looks as one of
                "not so poor" UNIX ls imitations.

        The student knows that an investigation is done about his project,
        but we do not specify his name on purpose. Please do not make
        any attemts to find the name of the student. This posting is
        (very) indirect in purpose.  No need for more than needed publicity
        at this stage. It can very well be that every thing is proper.

        Enclosed below is the manual page for the ls command. The manual
        is very clearly a modified UNIX ls man page. It is known
        (from other investigation) that this is the case indeed, but
        of course - this doesn't proof anuthing.

        After the manual page - you can find the source code. Parts of the
        code are deleted because of two reasons.
                1) If someone will be able to come up with the missing
                parts - this will be the best proof.
                2) If the code was copied from somewhere - it should
                not become public domain after the posting:

        All deletions are clearly marked in the code.


        Many thanks



======================= This is the man page ==========================
NAME
     ls - list and generate statisitcs for files

SYNTAX
     ls [_o_p_t_i_o_n_s] _n_a_m_e...

DESCRIPTION
     For each directory argument, _l_s lists the contents of the
     directory.  For each file argument, _l_s repeats the file name
     and gives any other information you request with the options
     available.  By default, the list is sorted alphabetically.
     When no argument is given, the current directory is listed.
     When several arguments are given, files are listed first,
     followed by directories and the files within each directory.
     Options are listed below.

OPTIONS
     -1 Displays one entry per line.

     -a Displays all entries including Hidden/Sys/Volume and those
        begining with a period (.).

     -A Displays all entries including Hidden/Sys/Volume but not
        those begining with a period.

     -c Sorts entries by date-time.  Default is by name.

     -d Displays names of directories only, not contents. Use
        this option with -l to get the status of a directory.

     -F Marks directories with trailing slash (/), and
        executable files with a trailing asterisk (*).

     -f Displays names in the order they exist in directory.
        all entries are listed.  This option overrides the
        -l, -t, -s, and -r options.

     -l Lists the attributes,  size in bytes, and date
        and time of last modification for each file.

        The attribute field consists of 6 characters.  Dots
        indicate that the attribute is off, A letter indicates on :

        R if the entry is read-only
        H if the entry is hidden
        S if the entry is a system file
        V if the entry is a volume name
        D if the entry is a directory
        A if the archive bit is on

     -R Recursively lists all subdirectories.

     -r Sorts entries in reverse alphabetic or time order.

     -s Displays the size in bytes of each file.  This is the
        first item listed in each entry.

     -t Sorts by time modified (latest first) instead of by name.

     -u Uses the time of last access instead of last modification
        for sorting (with the -t option) or printing (with the -l
        option).

RESTRICTIONS
     The output device is assumed to be 80 columns wide.


======================= This is the assembly code ========================
; $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
; $                                                                $
; $     ls                                                         $
; $                                                                $
; $     implement UNIX 'ls' on MS-DOS, for a complete definition   $
; $     and instructions see manual document.                      $
; $     compatiable with DOS 3.20 .                                $
; $                                                                $
; $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

        title   ls

; *********************** definitions *******************************
;
                        ; *********** constants definitions *********
;       general constants definitions
;
TAB             EQU     09h
LF              EQU     0ah
CR              EQU     0dh
;
ATTD_MASK       EQU     10h     ; mask for dir bit in file attr
ATTALL_MASK     EQU     3fh     ; mask for all bits in attr
;
tbl_size        EQU     128     ; size of work tables, limits # of dirent/dir
en_size         EQU     30h     ; length of each entry, > max(entsize,fnlen)
args_num        EQU     10h     ; max number of command line arguments (ex sw)
;       offsets of fields in a parsed dir ent
ofs_name        EQU     00h
ofs_size        EQU     20h
ofs_attr        EQU     28h     ; (len = 1)
ofs_date        EQU     29h     ; (len = 2+1+1)
ofs_time        EQU     2dh     ; (len = 1+1)
ofs_attd        EQU     10h     ; attr & ATTD_MASK (len = 1)
ofs_nadd        EQU     11h     ; addrs of original name (len = 2)
ofs_mark        EQU     1fh     ; extra mark char for '-F' option (len = 1)
;       lengths of fields in a parsed dir ent
len_name        EQU     14      ; 8+1+3+1+1 (name+dot+ext+mark+space)
len_size        EQU     8       ; > 32MB (DOS max)
;
                        ; ************* macro definitions ***********
;
movss   MACRO   sstr,dstr,len   ; copy string
        cld
        lea     si,sstr
        lea     di,dstr
        mov     cx,len
        rep     movsb
ENDM

dosfunc MACRO   funcnum                 ; do a DOS function call
        mov     ah,funcnum
        int     21h
ENDM

dsply   MACRO   strng                   ; dsply a string ending with a '$'
        mov     dx,offset       strng
        dosfunc 09h
ENDM

p1space MACRO                           ; dsply one space
        mov     dl,' '
        dosfunc 02h
ENDM

ptab    MACRO                           ; dsply one tab
        mov     dl,TAB
        dosfunc 02h
ENDM
;
; **************************** stack segment ************************
;
sseg    SEGMENT STACK
        DB      100     dup(?)
sseg    ENDS
;
; **************************** data segment *************************
;
dseg    SEGMENT
                        ; ************** variables definitions ******
;
;       character strings for display and formating templates
;
star2   DB      "\*.*",0
dot1    DB      ".",0
dotexe  DB      ".EXE"
dotcom  DB      ".COM"
errmsg  DB      "ls: $"
crlf    DB      CR,LF,"$"
dots2   DB      ":",CR,LF,"$"
ftotal  DB      "Total $"
attrbs  DB      "RHSVDA"
dplate  DB      "00-00-0000$"
tplate  DB      "00:00$"
nplate  DB      "             $"
;       ls flags   (0=T,other=F)
flgall  DB      'a'
flgtmc  DB      'c'
flgndr  DB      'd'
flgpln  DB      'f'
flglng  DB      'l'
flgrev  DB      'r'
flgsiz  DB      's'
flgtmt  DB      't'
flgtmu  DB      'u'
flg1pl  DB      '1'
flgaex  DB      'A'
flgmkf  DB      'F'
flgrec  DB      'R'
flgxxx  DB      0ffh
;       variables
my_dta  DB      30h   dup(0)    ; dta to accept output of dosfun 4E
;
neword  DW      1               ; flag for command line decoding
numflg  DW      flgxxx - flgall ; number of flags installed
clen    DW      0               ; length of command
cmndln  DB      80    dup('$')  ; internal buffer for command line
;       'sort' procedure arguments and internal vars
srtfld  DW      0               ; arg- offset of key field (rel bx)
srtlen  DW      0               ; arg- length of key field
srtst1  DW      0               ; var- address of one string
srtst2  DW      0               ; var- address of second string
srtyet  DW      0               ; arg- number of recs to sort
srtdrc  DW      0               ; arg- direction of sort
;
dtitle  DW      0               ; dsply title flg (1=T,other=F)
phase   DW      0               ; phase indicator (1,2)
;       'showall' proc internal vars
colms   DW      0               ; # of colums to be in formated output
lines   DW      0               ; # of lines ...
colc    DW      0               ; colums counter (down)
linc    DW      0               ; lines counter (down)
;
ecntr   DW      0               ; number of relevant enteris in ent tbl
ncntr   DW      0               ; number of relevant entries in names tbl
numdirs DW      0               ; number of subdir ent in ent tbl
curnam  DW      0               ; pointer to name being refered
entaddr DW      0               ; pointer to entry now being refered
namaddr DW      0               ; pointer to names being expanded
namelen DW      0               ; length of name being expanded
;       buffers
zero_en DB      en_size        dup(0)   ; used to reset totl_ent
totl_en DB      en_size        dup(0)   ; size field used to count total
ffname  DB      80      dup(0)          ; buffer for name to be expanded
entries DB      en_size * tbl_size  dup(?)      ; tbl for entries found
names   DB      en_size * tbl_size  dup(?)      ; bfr for dir names to expnd
args    DB      en_size * args_num  dup(?)      ; bfr for cmmndln args

;       error msgs tbl
e_01    DB      "Invalid function code$"
e_02    DB      "File not found$"
e_03    DB      "Path not found$"
e_08    DB      "Insufficient memory$"
e_18    DB      "No more files$"
e_87    DB      "Invalid parameter$"
;
e_unkn  DB      "Unknown error$"
;       error codes tbl
err_tbl DW      1       ,e_01
        DW      2       ,e_02
        DW      3       ,e_03
        DW      8       ,e_08
        DW      18      ,e_18
        DW      87      ,e_87
        DW      0       ,e_unkn

dseg    ENDS
;
; ******************************* code segment **********************
;
cseg    SEGMENT
        ASSUME  cs:cseg,ds:dseg,ss:sseg
;
; ############ entry point ##################
;
                        ; **************** initialization ***********
start:  mov     ax,dseg
        mov     ds,ax
        mov     phase,0
start1:                 ; **** copy cmnd line from PSP to data seg *******
        mov     ch,0
        mov     cl,es:80h       ; get length of cmnd line
        mov     bx,cx           ; save length
        mov     clen,cx
        mov     si,cx

        cmp     bx,0
        je      start2          ; no cmnd line
cp1:    mov     al,es:80h[si]   ; copy cmnd line
        mov     ds:cmndln[si] - 1,al     ; to internal buffer
        dec     si
        loopnz  cp1
                        ; **************** init ES ******************
start2: push    ds              ; set ES
        pop     es              ;   to eq DS

        mov     byte ptr ds:cmndln[bx],0
getsw1:                 ; ******** get option switchs ***************
        lea     bx,cmndln[0]
getsw2:
        mov     al,[bx]         ; get char
        cmp     al,0            ; EOL ??
        je      getsw9          ; Y - exit
        cmp     al,' '          ; space ??
        jne     getsw3
        mov     neword,1        ; Y - deli
        jmp     getsw6          ; adv to next chr
getsw3: cmp     neword,0        ; not spc, is first ??
        je      getsw4          ;  middle of word, go process
        cmp     al,'/'          ;  first, is sw ??
        je      getsw5          ; Y - skip slash
        cmp     al,'-'          ; accept '-' as well as '/'
        je      getsw5
        jmp     getsw9          ; N - param found - exit

getsw4: mov     cx,numflg       ; scan flag tbl
        lea     si,flgall       ; from first
gts4:   mov     dl,[si]
        cmp     dl,al
        je      gts44           ; until found
        inc     si
        loop    gts4
gts44:  mov     byte ptr [si],0 ; mark flg with a zero
getsw5: mov     neword,0
getsw6: inc     bx
        dec     clen            ; remaining string length
        jmp     getsw2

getsw9:                 ; ************ get arguments *******************
        lea     si,dot1         ; default parm
        mov     cx,2            ; size of it
        cmp     al,0            ; no parm ??
        je      cont2
gs99:   mov     si,bx           ; copy user parm
cont2:                          ; copy args into args tbl
        lea     bx,args[0]
        mov     ncntr,1
cont5:  mov     di,bx
        mov     cx,0            ; count arg len
cont4:
        mov     al,[si]         ; get chr from argv
        inc     si
        cmp     al,' '          ; is seperator ??
        jne     cont3           ; no
        mov     al,0
        mov     [di],al
        add     bx,en_size
        inc     ncntr
        cmp     ncntr,args_num  ; too many args ??
        ja      error0          ; yes
        jmp     cont5
cont3:  mov     [di],al
        cmp     al,0            ; is end-of-line ??
        je      dep0            ; yes
        inc     di              ; adv addr
        inc     cx              ; adv cntr
        cmp     cx,en_size      ; arg too long ??
        jae     error0          ; yes - abort
        jmp     cont4           ; no - do next char

dep0:                   ; *********** set options dependancies *************
        cmp     flgpln,0
        jne     dep2
dep1:   mov     flgall,0        ; ON
        mov     flglng,0ffh     ; OFF
        mov     flgsiz,0ffh
        mov     flgtmt,0ffh
dep2:   cmp     flglng,0
        jne     dep3
        mov     flg1pl,0
        mov     flgsiz,0
dep3:   cmp     flgall,0
        jne     dep4
        mov     flgaex,0
dep4:   cmp     flgtmc,0
        jne     dep5
        mov     flgtmt,0
dep5:   cmp     flgtmu,0
        jne     dep6
        mov     flgtmt,0
dep6:
                        ; *********** set current dta addr **********
set_dta:mov     dx,offset       my_dta
        dosfunc 1ah

; *******************************************************************
sof0:   jmp     phase1

error0:
        mov     ax,87           ; Invalid parameter
        jmp     errorh
;
; ************ end of init *************************
;
; ************** phase 1 ***************************
;
;       For each argument, find all the dirctory entries that matches it
;       and store them in ent tbl. If no match was found exit with a
;       error message. Sort entries using specified or default criteria
;       with files ent before subdirs ent. Unless flag 'd' is on, push
;       subdir ent into names stack. Show remainig entries.
;
phase1: mov     phase,1

start3:                 ; *************** expand args ***************
        mov     ecntr,0
        mov     numdirs,0       ; no sub-dirs found yet
        lea     bx,entries[0]   ; start of table
        mov     entaddr,bx
        lea     ax,args         ; names in args tbl
        mov     namaddr,ax
start4: call    findall         ; expand argument to entries tbl
        jnc     start6
        cmp     ax,2            ; File not found
        je      start6
        cmp     ax,3            ; Path not found
        je      start6
        cmp     ax,18           ; No more files
        je      start6
        jmp     error1
start6: dec     ncntr
        jz      start5          ; no more names
        mov     ax,namaddr
        add     ax,en_size
        mov     namaddr,ax
        jmp     start4          ; go do next name
start5:
        mov     ax,2            ; no files found !!
        cmp     ecntr,0
        jne     p100
        jmp     error1
                        ; ***** sort ent tbl, files below subdirs ***
p100:   mov     srtfld,ofs_attd         ; sort by file/dir
        mov     srtlen,1                ; 1 byte
        mov     srtdrc,1                ; files first
        mov     ax,ecntr                ; sort all ent
        mov     srtyet,ax               ; sort all ent
        lea     bx,entries[0]           ; from first
        call    far ptr sort
p110:                                   ; sort files at lower ent tbl
        mov     ax,ecntr
        sub     ax,numdirs              ; calc num of files
        jz      p130                    ; no files
        mov     srtyet,ax
        mov     srtfld,ofs_name         ; defaul is sort by fname
        mov     srtlen,len_name
        cmp     flgtmt,0
        jne     p120
p118:   mov     srtfld,ofs_date         ; sort by date+time
        mov     srtlen,6
p120:   lea     bx,entries[0]
        mov     al,flgrev
        cbw
        mov     srtdrc,ax
        call    far ptr sort
p130:                                   ; sort subdirs at upper ent tbl
        mov     ax,numdirs
        cmp     ax,0
        je      p145                    ; no subdirs to sort
        mov     srtyet,ax
        mov     srtfld,ofs_name         ; defaul is sort by fname
        mov     srtlen,len_name
        cmp     flgtmc,0
        je      p138
        cmp     flgtmt,0
        jne     p140
p138:   mov     srtfld,ofs_date         ; sort by date+time
        mov     srtlen,6
p140:   lea     bx,entries[0]
        mov     ax,ecntr
        sub     ax,numdirs
        mov     dx,en_size
        mul     dx                      ; dx destroyed
        add     bx,ax                   ; bx points to first dir ent
        mov     al,flgrev
        cbw
        mov     srtdrc,ax
        call    far ptr sort
p145:                   ; ************ process subdir names *********
        cmp     flgndr,0                ; no dir exp ??
        je      p150                    ;  then go show all now
        cmp     numdirs,0
        je      p150                    ; no dirs
        mov     ax,ecntr
        mov     dtitle,ax
        lea     bx,entries[0]
        mov     ax,ecntr
        sub     ax,1
        mov     dx,en_size
        mul     dx                      ; dx destroyed
        add     bx,ax                   ; bx points to last dir ent
        lea     dx,names[0]
        mov     curnam,dx
pcp1:                                   ; mov dir names to names, rev order
        mov     di,curnam
        mov     si,[bx+ofs_nadd]        ; restore addr of original arg
pcp2:   mov     dx,di                   ; save addr of last ok chr + 1
pcp3:   mov     al,byte ptr [si]        ; get chr from arg
        cmp     al,0                    ; end-of-name ??
        je      pcp4                    ; yes
        mov     byte ptr [di],al        ; no - put chr
        inc     si
        inc     di
        cmp     al,'\'                  ; is path sep ??
        je      pcp2                    ; yes - remember loc
        cmp     al,':'                  ; is path sep ??
        je      pcp2                    ; yes - remember loc
        jmp     pcp3

pcp4:   mov     di,dx                   ; restore sep + 1 addr
        mov     si,bx                   ; addr of dir ent name
        add     si,ofs_name
        mov     cx,len_name
        rep     movsb

        inc     ncntr
        dec     ecntr
        dec     numdirs
        jz      p150                    ; no more dir names
        cmp     ncntr,tbl_size  ; too many names ??
        ja      error1          ; yes
        sub     bx,en_size
        add     curnam,en_size
        jmp     pcp1
p150:                   ; ********** show remaining ent tbl *********
        lea     bx,entries[0]   ; start of table
        mov     entaddr,bx
        call    showall

; ******************************************************************
ok1:    jmp     phase2

sof1:   jmp     sof
error1: jmp     errorh
;
; ********************** end of phase 1 *****************************
;
;************************* phase 2 **********************************
;
;       Until names stack is empty, remove first name and process it:
;               find all dir ent in that subdir. Unless flag 'f' is on
;               sort ent tbl. Show entries according to formating flags,
;               If flag 'R' is on, push names of subdirs found to stack.
;
phase2: mov     phase,2
p2:
        cmp     ncntr,0
        jne     p20
        jmp     sof2                    ; no more names to expand
p20:
        cmp     ncntr,tbl_size  ; too many names ??
        jbe     p200            ; no
        jmp     error2          ; yes

p200:                   ; ********** prepare search string **********
        lea     di,ffname
        mov     si,curnam               ; expand current dir name
        mov     cx,en_size
        mov     namelen,0               ; count name len
        mov     dx,0                    ; count spaces
p208:   lodsb                           ; get chr
        cmp     al,' '
        je      p205
        cmp     dx,0
        jne     p206
        inc     namelen                 ; remember name len
        jmp     p206
p205:   inc     dx
p206:   stosb
        loop    p208
p207:
        lea     si,star2                ; concat '\*.*',0 to name
        lea     di,ffname
        add     di,namelen
        mov     cx,5
        rep     movsb
                        ; *************** expand subdir *************
        mov     ecntr,0         ; prepare for findall
        movss   zero_en,totl_en,en_size
        lea     bx,entries[0]   ; start of table
        mov     entaddr,bx
        lea     ax,ffname
        mov     namaddr,ax
        mov     numdirs,0
p210:   call    findall         ; expand argument to entries tbl
        jnc     p220            ; ignore expected errors
        cmp     ax,2            ; File not found
        je      p220
        cmp     ax,3            ; Path not found
        je      p220
        cmp     ax,18           ; No more files
        je      p220
        jmp     error2          ; unexpected error
p220:                   ; **************** sort ent *****************
        cmp     flgpln,0
        je      p250                    ; plain dsply - do not sort
        mov     srtfld,ofs_name         ; defaul is sort by fname
        mov     srtlen,len_name
        cmp     flgtmc,0
        je      sort2
        cmp     flgtmt,0
        jne     sort3
sort2:  mov     srtfld,ofs_date         ; sort by date+time
        mov     srtlen,6
sort3:  lea     bx,entries[0]
        mov     al,flgrev
        cbw
        mov     srtdrc,ax
        mov     dx,ecntr
        cmp     dx,0
        je      p250                    ; no files to sort
        mov     srtyet,dx
        call    far ptr sort
p250:                   ; *********** show entries found ************
        dsply   crlf
        cmp     dtitle,1
        je      p260                    ; only one subdir
        mov     bx,curnam
        add     bx,namelen
        mov     byte ptr [bx],'$'       ; mark end of name for printing
        mov     dx,curnam
        dosfunc 09h                     ; show dir-name
        dsply   dots2
p260:   cmp     flgsiz,0
        jne     p270
        call    ptotal
p270:   lea     bx,entries[0]   ; start of table
        mov     entaddr,bx
        call    showall
        sub     curnam,en_size  ; adjust after showall
        dec     ncntr
        mov     dtitle,2        ; from now on title

precd:                  ; ****** if recursion push subdir names *****
        cmp     flgrec,0        ; sub dir recursion ??
        jne     ok2
        mov     ax,en_size
        mul     ecntr           ; dx destroyed
        mov     bx,ax
        add     bx,entaddr      ; addr of last+1 ent
p280:   cmp     numdirs,0
        je      ok2
        sub     bx,en_size
        mov     al,[bx+ofs_attd]        ; file/dir byte
        cmp     al,0
        je      p280                    ; file - go next
        dec     numdirs                 ; dir ent found
        mov     al,[bx+ofs_name]
        cmp     al,'.'                  ; '.' or '..'
        je      p280
        add     curnam,en_size
                                        ; copy name of current dir
        lea     si,ffname
        mov     di,curnam
        mov     cx,namelen
        rep     movsb
        mov     byte ptr [di],'\'                  ; concat '\'
        inc     di
        mov     si,bx                   ; concat subdir name
        add     si,ofs_name
        mov     cx,len_name
        rep     movsb

        inc     ncntr
        jmp     p280

; ******************************************************************
ok2:    jmp     p2

sof2:   jmp     sof
error2: jmp     errorh
;
; ********************* end of phase 2 *****************************
;
; ********************* error routine ******************************
;
errorh:
        lea     bx,err_tbl
errh1:  mov     cx,[bx]         ; error code in cx
        cmp     cx,0            ; no more codes ??
        je      errhp           ; print unknown
        cmp     cx,ax           ; this code ??
        je      errhp           ; print it
        add     bx,4            ; next entry in err_tbl
        jmp     errh1
errhp:  add     bx,2            ; point to addr of msg str
        dsply   crlf
        dsply   errmsg          ; dsply err prompt
        mov     dx,[bx]         ; point to body of msg
        dosfunc 09h             ; print it
        dsply   crlf
;
; ******************************************************************
;
; ############### exit point ##################
;
sof:    dosfunc 4ch

; *********** end of main *******************************************
;
; ********************** procedures *********************************
;
; NOTES on procedures documentation headers :
;       * Registers listed undel DESTROYed include only registers
;         destroyed directly by the procedure itself or the macros
;         it uses, but NOT registers destroyed by any other
;         procedure that it may call.
;       * Definitions of global variables listed under PARaMeterS,
;         USES, or VARiableS can be found on the original lines
;         where those variables are declared.
;       * 'FLAGS' mean a subset of the flag variables.
;       * The notetion 'DOS(x1,x2,...)' under CALLS means that the
;         procedure or a macro it uses does (a) system call(s) with
;         the functions x1,x2 etc. requested. If xi is a symbol this
;         means that the call is being made from a macro named xi,
;         in this case refer to the macro definition to see what
;         function was called.

findall PROC    NEAR    ; *******************************************
                        ; find all directory entries matching a given
                        ; filename path which may include wildcards.
                        ;
                        ; destroy :  ax,bx,cx,dx,si,di
                        ; prams   :  namaddr    addr of name to expand
                        ;            entaddr    addr of first entry storage loc
                        ; uses    :  FLAGS,phase
                        ; vars    :  None
                        ; effects :  ecntr,numdirs,[entaddr]
                        ; calls   :  DOS(4E,4F,dsply)
                        ; output  :  ax         error code
                        ;            CF         error flag

get_1:  mov     dx,namaddr      ; addr of name in dx
        mov     cx,ATTD_MASK    ; default is only normal files + dirs
        cmp     flgaex,0        ; show all files ??
        jne     get11
        or      cx,ATTALL_MASK  ; yes
get11:  dosfunc 4eh
chk1:   jnc     found1
        jmp     ferror
found1:
        mov     bx,entaddr
        call    parse2
        mov     al,[bx+ofs_attd]
        cmp     al,0            ; is ent a subdir ??
        je      get20           ; no
get15:  mov     si,namaddr
        mov     di,bx
        add     di,ofs_name
        mov     al,0
        cmp     phase,1
        je      get17           ; name is arg
        mov     al,' '          ; seperator
get17:  cmp     byte ptr [si],'.'        ; is name '.' ??
        jne     getaa           ; no - ok
        cmp     [si+1],al
        jne     get19
        mov     byte ptr my_dta[1eh],'.'        ; yes - restore
        mov     byte ptr my_dta[1eh+1],0
        jmp     repars
get19:  cmp     byte ptr [si+1],'.'      ; is name '..' ??
        jne     getaa
        cmp     [si+2],al
        jne     getaa
        mov     byte ptr my_dta[1eh],'.'        ; yes - restore
        mov     byte ptr my_dta[1eh+1],'.'
        mov     byte ptr my_dta[1eh+2],0
repars: call    parse2
getaa:
        cmp     byte ptr [di],'.'                ; is entry '.' or '..' ??
        jne     getdz                   ; no
        cmp     flgall,0
        je      getdz
        cmp     phase,2
        je      chknxt      ; ignore '.' and '..' in ph2 unless flgall
getdz:
        inc     numdirs         ; count subdirs
get20:
        inc     ecntr
        cmp     ecntr,tbl_size  ; too many entrs ??
        jb      get22           ; no
        mov     ax,8            ; yes - out of memory
        jmp     ferror
get22:  add     bx,en_size
        mov     entaddr,bx      ; store add of next ent

chknxt: dosfunc 4fh
        jc      ferror
        jmp     found1

chklst: cmp     ax,18           ; no more files
        je      fok             ; other error

ferror: stc
        jmp     fexit

fok:    clc
        mov     ax,0
fexit:
        ret

findall ENDP

showall PROC    NEAR    ; *******************************************
                        ; show all directory entries stored at ent tbl
                        ; with formating as requeired.
                        ;
                        ; destroy :  ax,bx,dx
                        ; prams   :  entaddr    addr of first entry storage loc
                        ; uses    :  FLAGS,ecntr,crlf
                        ; vars    :  colms,lines,colc,linc
                        ; effects :  None
                        ; calls   :  psize,pattrb,pdate,ptime,pname2
                        ; output  :  None

        mov     colms,1
        mov     ax,ecntr
        cmp     ax,0
        jne     sh20
        jmp     shexit                 ; no files
sh20:   mov     lines,ax
        cmp     flg1pl,0
        je      cp22                    ; one ent per line
        mov     colms,5                 ; calc num of lines in col format
        cmp     flgsiz,0
        jne     sh01
        mov     colms,3
sh01:   mov     dx,colms
        add     ax,colms
        sub     ax,1
        div     dl                      ; result in al
        cbw
        mov     lines,ax

cp22:   mov     ax,lines
        mov     linc,ax
cp33:   mov     ax,colms
        mov     colc,ax
        jmp     cp55
cp44:   ptab
cp55:
        mov     ax,lines
        mov     dx,colms
        sub     dx,colc
        mul     dl                      ; result in ax, dx destroyed
        add     ax,lines
        sub     ax,linc
        cmp     ax,ecntr
        jae     pr8                     ; shorter line
        mov     dx,en_size
        mul     dl                      ; offset of entry in ax
        add     ax,entaddr
        mov     bx,ax                   ; addr of ent in bx
pr2:
        cmp     flgsiz,0
        jne     pr3                     ; show size ??
        call    psize
        p1space
pr3:
        cmp     flglng,0
        jne     pr7                     ; long format ??
pr4:    call    pattrb
        p1space
pr5:    call    pdate
        p1space
pr6:    call    ptime
        p1space
pr7:    call    pname2

        dec     colc
        jnz     cp44
pr8:    dsply   crlf
        dec     linc
        jnz     cp33

shexit:
        ret

showall ENDP

parse2  PROC    NEAR    ; *******************************************
                        ; parse a directory entry created by DOS func
                        ; 4E and 4F into ent tbl using format defined
                        ; by ofs_ and len_ constants.
                        ;
                        ; destroy :  ax,cx,dx,si,di
                        ; prams   :  bx         pointer to offset 0 of entry
                        ; uses    :  nplate,my_dta,dotexe,dotcom,nameaddr
                        ;            totl_en
                        ; vars    :  None
                        ; effects :  [bx]
                        ; calls   :  None
                        ; output  :  None


        lea     si,nplate               ; cp plate to entry[0]
        mov     di,bx
        mov     cx,len_name             ; length of nplate
        rep     movsb

        lea     si,my_dta[1eh]          ; cp name to entry
        mov     di,bx
        mov     dx,0                    ; no dot found yet

par23:  mov     al,[si]                 ; get chr
        cmp     al,0
        je      par29                   ; end of name
        cmp     al,'.'
        jne     par25
        mov     dx,di                   ; save addr of dot in entry
        jmp     par25 ;;;;;;;;;;;;;;;;;;;
        mov     cl,my_dta[15h]
        and     cl,ATTD_MASK            ; is entry a directory
        jnz     par25                   ; Y - allow dots
        mov     di,bx                   ; jmp dst pointer to dot loc
        add     di,8
par25:  mov     [di],al                 ; store chr
        inc     di
        inc     si
        jmp     par23
par29:
        mov     byte ptr [bx+ofs_mark],' '   ; default mark as plain file
        cmp     dx,0
        je      par45
        mov     di,dx                   ; chk if file is 'exe'
        lea     si,dotexe
        mov     cx,4
        repe    cmpsb
        je      par40
        mov     di,dx                   ; chk if file is 'com'
        lea     si,dotcom
        mov     cx,4
        repe    cmpsb
        jne     par45
par40:
        mov     byte ptr [bx+ofs_mark],'*'

par45:  mov     cx,len_size             ; cnvrt fsize to unpacked bcd
        mov     ax,word ptr my_dta[1ah] ; low word in ax
        mov     dx,word ptr my_dta[1ch] ; high word in dx
        mov     si,10                   ; div by 10d
cvlp1:  idiv    si                      ; result in ax
        mov     di,cx                   ;     remainder in dx
        add     di,bx                   ; calc reslt byte addr
        add     di,ofs_size - 1
        mov     [di],dl                 ; sto res
        sub     dx,dx                   ; reset dx to zero
        loop    cvlp1                   ; do len times

tl1:    mov     cx,len_size             ; add size to total
        mov     di,bx
        add     di,ofs_size+len_size
        lea     si,totl_en
        add     si,ofs_size+len_size
        mov     ah,0                    ; carry digit
        mov     dl,10                   ; modulo ten
tl2:    dec     si
        dec     di
tl3:    mov     al,byte ptr [si]        ; get total digit
        add     al,byte ptr [di]
        add     al,ah                   ; add carry
        cbw
        div     dl                      ; new carry now in al
        xchg    al,ah                   ;       back to ah
        mov     byte ptr [si],al        ; result in al, update total
        loop    tl2

        mov     al,my_dta[15h]          ; get attrib byte
        mov     [bx+ofs_attr],al

        and     al,10h                  ; calc direct att
        mov     [bx+ofs_attd],al        ; store
        cmp     al,0
        je      par50                   ; not a subdir
        mov     byte ptr [bx+ofs_mark],'/'

par50:  mov     ax,namaddr              ; save src name addr
        mov     [bx+ofs_nadd],ax

        mov     dx,word ptr my_dta[18h] ; get date
        mov     ax,dx
        mov     cl,9
        shr     ax,cl
        mov     [bx+ofs_date],ax             ; store year word
        mov     ax,dx
        and     ax,111100000b
        mov     cl,5
        shr     ax,cl
        mov     [bx+ofs_date+2],al             ; store month byte
        mov     ax,dx
        and     ax,11111b
        mov     [bx+ofs_date+3],al             ; store day byte

        mov     dx,word ptr my_dta[16h]        ; get time
        mov     ax,dx
        mov     cl,11
        shr     ax,cl
        mov     [bx+ofs_time],al                ; store hours
        mov     ax,dx
        and     ax,11111100000b
        mov     cl,5
        shr     ax,cl
        mov     [bx+ofs_time+1],al              ; store mons

        ret

parse2  ENDP

pname2  PROC    NEAR    ; *******************************************
                        ; print a file name from ent tbl and optionaly
                        ; add file type mark.
                        ;
                        ; destroy :  ax,cx,dx,si,di
                        ; prams   :  bx         pointer to offset 0 of entry
                        ; uses    :  FLAGS
                        ; vars    :  None
                        ; effects :  None
                        ; calls   :  DOS(02)
                        ; output  :  None

        cmp     flgmkf,0
        jne     pna55
        mov     di,bx
        add     di,ofs_name
        mov     cx,len_name
        mov     al,' '
        repne   scasb
        mov     al,[bx+ofs_mark]
        mov     [di-1],al

pna55:  mov     si,bx                   ; file names may include '$'
        add     si,ofs_name
        mov     cx,len_name
        dec     cx
pna66:  mov     dl,[si]
        dosfunc 02h
        inc     si
        loop    pna66

pna99:  ret

pname2  ENDP

psize   PROC    NEAR    ; *******************************************
                        ; print unpacked bcd number stored in 'size'
                        ; field of a entry.
                        ;
                        ; destroy :  ax,cx,dx,si,di
                        ; prams   :  bx         pointer to offset 0 of entry
                        ; uses    :  None
                        ; vars    :  None
                        ; effects :  None
                        ; calls   :  DOS(02)
                        ; output  :  None

        mov     cx,len_size     ; do len times
        mov     si,0            ; reset leading zero flag
        mov     di,ofs_size     ; start offset of size
pslp1:  mov     dl,[bx+di]      ; get digit
        cmp     dl,0            ; is digit zero ?
        jne     not0            ;  no - mark and prnt
        cmp     si,0            ;  yes - is leading ?
        jne     ps2             ;   no - normal
        mov     dl,' '          ;   yes  prnt blank
        jmp     ps3
not0:   mov     si,1
ps2:    add     dl,30h          ; cvrt bcd to ascii
ps3:    dosfunc 02h             ; output char
cont:   inc     di
        loop    pslp1           ; next car

        ret

psize   ENDP

pattrb  PROC    NEAR    ; *******************************************
                        ; print file attributes from an entry, if att
                        ; is on then print the att letter, otherwise
                        ; print a dot.
                        ;
                        ; destroy :  ax,cx,dx,si,di
                        ; prams   :  bx         pointer to offset 0 of entry
                        ; uses    :  attrbs
                        ; vars    :  None
                        ; effects :  None
                        ; calls   :  DOS(02)
                        ; output  :  None

        mov     cl,[bx+ofs_attr]       ; get attrib byte
        mov     si,0
pat4:   mov     dl,'.'
        rcr     cl,1
        jnc     pat5
        mov     dl,attrbs[si]
pat5:   dosfunc 02h
        inc     si
        cmp     si,6                    ; no more attribs
        jb      pat4

        ret

pattrb  ENDP

pdate   PROC    NEAR    ; *******************************************
                        ; print a date from an entry.
                        ;
                        ; destroy :  ax,cx,dx
                        ; prams   :  bx         pointer to offset 0 of entry
                        ; uses    :  dplate
                        ; vars    :  None
                        ; effects :  dplate
                        ; calls   :  wtoasc,DOS(dsply)
                        ; output  :  None

;;;;;;;;;;;;;;;;;;;;;;;;;;; CODE DELETED ;;;;;;;;;;;;;;;;;;;;;;;

pdate   ENDP

ptime   PROC    NEAR    ; *******************************************
                        ; print a time field from an entry.
                        ;
                        ; destroy :  ax,cx,dx
                        ; prams   :  bx         pointer to offset 0 of entry
                        ; uses    :  tplate
                        ; vars    :  None
                        ; effects :  tplate
                        ; calls   :  wtoasc,DOS(dsply)
                        ; output  :  None

        mov     ax,0
        mov     al,byte ptr [bx+ofs_time]             ; get hours
        push    bx
        lea     bx,tplate[1]
        mov     cx,2
        call    wtoasc
        pop     bx

        mov     ax,0
        mov     al,[bx+ofs_time+1]             ; get mins
        push    bx
        lea     bx,tplate[4]
        mov     cx,2
        call    wtoasc
        pop     bx

        dsply   tplate
        ret

ptime   ENDP

wtoasc  PROC    NEAR    ; *******************************************
                        ; convert binary word to ascii string.
                        ;
                        ; destroy :  bx,dx,si
                        ; prams   :  bx         pointer to last ascii digit
                        ;            cx         length of output string
                        ; uses    :  None
                        ; vars    :  None
                        ; effects :  [bx]
                        ; calls   :  None
                        ; output  :  none

        mov     si,10
wtoa1:  sub     dx,dx
        idiv    si
        add     dl,'0'
        mov     [bx],dl
        dec     bx
        loop    wtoa1

        ret

wtoasc  ENDP

ptotal  PROC    NEAR    ; *******************************************
                        ; print size field of the 'totl_en' entry with
                        ; a title.
                        ;
                        ; destroy :  ax,dx
                        ; prams   :  None
                        ; uses    :  ftotal,totl_en,crlf
                        ; vars    :  None
                        ; effects :  None
                        ; calls   :  DOS(dsply),psize
                        ; output  :  None

;;;;;;;;;;;;;;;;;;; CODE DELETED ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ptotal  ENDP

sort    PROC    FAR     ; *******************************************
                        ; do a key sort on records in ent tbl.
                        ;
                        ; destroy :  ax,cx,dx,di,si
                        ; prams   :  bx      pointer to start of table
                        ;            srtdrc  direction of sort (normal/reverse)
                        ;            srtfld  start pos of key field
                        ;            srtlen  length of key
                        ;            srtyet  number of recs yet to sort
                        ; uses    :  None
                        ; vars    :  srtst1,srtst2
                        ; effects :  [bx],stryet
                        ; calls   :  swaps
                        ; output  :  None

        mov     srtst1,bx

biglup: dec     srtyet
        jz      srt999
        mov     dx,srtyet
        mov     ax,srtst1
        add     ax,en_size
        mov     srtst2,ax               ; set upper to bottm+1

smalup:
scmp1:  mov     si,srtst1
        add     si,srtfld               ; point to key fld
        mov     di,srtst2
        add     di,srtfld
srt1:   mov     cx,srtlen
        repe    cmpsb                   ; scan key until diff
        rcl     al,1                    ; save carry
        cmp     srtdrc,0
        jne     srt11                   ; normal
        not     al                      ; reverce sort
srt11:  rcr     al,1
        jc      srt2                    ; str2 is bigger
        mov     si,srtst1
        mov     di,srtst2
        mov     cx,en_size
        call    swaps                   ; swap entries
srt2:   dec     dx
        jnz     srt3                    ; small loop not finished
        mov     ax,srtst1               ; prepare for big loop
        add     ax,en_size
        mov     srtst1,ax
        jmp     biglup
srt3:
        mov     ax,srtst2               ; prepare for small loop
        add     ax,en_size
        mov     srtst2,ax
        jmp     smalup
srt999:
        ret

sort    ENDP

swaps   PROC    NEAR    ; *******************************************
                        ; swap records (strings).
                        ;
                        ; destroy :  ax,cx,di,si
                        ; prams   :  si    pntr to one rec
                        ;            di    pntr to second rec
                        ;            cx    length of rec
                        ; uses    : None
                        ; vars    : None
                        ; effects : [si],[di]
                        ; calls   : None
                        ; output  : None

swp1:   mov     al,[si]
        mov     ah,[di]
        mov     [si],ah
        mov     [di],al
        inc     si
        inc     di
        loop    swp1
        ret

swaps   ENDP

cseg    ENDS
;
; ********************* end of procedures **********************
;
        END     start






======================== END OF CODE =======================================