gno/bin/gsh/dir.asm
tribby 6269a8ca25 Changes for gsh version 2.0d8:
Fixed several mutual exclusion problems, including a particularly nasty
one that would cause gsh to loop forever inside the memory manager. (You
could identify this one by the "BRA (-23)" at the bottom of the infinite
loop.)

Fixed the string vector print routine to properly handle all numbers of
entries in the hash table.  Previously, it would always print duplicate
entries if there were < 6 commands hashed, and would sometimes print
duplicates (depending on previous contents of an internal table) for
other numbers of commands.

gsh would wait on background processes started from an exec file
(executed, not sourced). Now the exec file does not wait on the process,
but the background process is not associated with the parent shell after
the exec file terminates.

Made gsh globbing work more like csh: if none of the requested patterns
are found, print "No match" and exit.

At startup, if /etc/glogin, $HOME/glogin, or $HOME/gshrc does not exist,
don't report a "file not found" error message. (PR#100)
1998-12-31 18:29:14 +00:00

909 lines
16 KiB
NASM

**************************************************************************
*
* The GNO Shell Project
*
* Developed by:
* Jawaid Bazyar
* Tim Meekins
*
* $Id: dir.asm,v 1.9 1998/12/31 18:29:12 tribby Exp $
*
**************************************************************************
*
* DIR.ASM
* By Tim Meekins
* Modified by Dave Tribby for GNO 2.0.6
*
* Directory stack management
*
* Note: text set up for tabs at col 16, 22, 41, 49, 57, 65
* | | | | | |
* ^ ^ ^ ^ ^ ^
**************************************************************************
*
* Interfaces defined in this file:
*
* InitDStack
*
* dirs built-in command
*
* pushd built-in command
*
* popd built-in command
*
* path2tilde
*
* getpfxstr
*
**************************************************************************
mcopy /obj/gno/bin/gsh/dir.mac
dummydir start ; ends up in .root
end
setcom 60
MAXD gequ 50
**************************************************************************
*
* Initialize directory stack
*
**************************************************************************
InitDStack START
using DirData
stz tods
stz dirstack
stz dirstack+2
rts
END
**************************************************************************
*
* DIRS: builtin command
* syntax: dirs [-l]
*
* display the directory stack
*
**************************************************************************
dirs START
using DirData
arg equ 0
status equ arg+4
space equ status+2
subroutine (4:argv,2:argc),space
lock DirMutex
stz status
lda argc
dec a
beq showshort
dec a
bne using
ldy #4
lda [argv],y
sta arg
ldy #6
lda [argv],y
sta arg+2
lda [arg]
and #$FF
cmp #'-'
bne using
ldy #1
lda [arg],y
cmp #'l'
beq showlong
using ldx #^usingstr
lda #usingstr
jsr errputs
inc status Return status = 1.
bra exit
showlong jsl dotods Set top of stack to current directory.
pea 0
jsl showdir
bra exit
showshort jsl dotods Set top of stack to current directory.
pea 1
jsl showdir
exit unlock DirMutex
return 2:status
usingstr dc c'usage: dirs [-l]',h'0d00'
END
**************************************************************************
*
* PUSHD: builtin command
* syntax: pushd [+n | dir]
*
* change directory and push
*
**************************************************************************
pushd START
using DirData
using vardata
count equ 0
p equ count+2
arg equ p+4
status equ arg+4
space equ status+2
subroutine (4:argv,2:argc),space
lock DirMutex
stz status
lda argc Get number of arguments.
dec a If no parameters,
beq xchange exchange top two dirs on stack.
dec a If > 1 parameter,
bne usage print usage string.
ldy #4 Move parameter pointer from
lda [argv],y argv array to direct
sta arg page variable "arg".
ldy #6
lda [argv],y
sta arg+2
lda [arg] If first character
and #$FF of parameter is
cmp #'+' "+",
beq rotate do the rotate;
jmp godir else push the directory.
;
; More than one parameter provided; print usage string and exit.
;
usage ldx #^usagestr
lda #usagestr
jmp prerrmsg
;
; Either no parameter or "+" provided; exchange directory stack entries.
;
xchange lda tods Get index to top of dir stack.
bne xgoodie If 0,
ldx #^err1 print error message
lda #err1 "No other directory"
jmp prerrmsg
xgoodie jsl dotods Set top of stack to current directory.
lda tods
dec a
asl a
asl a
tax X = offset to tos-1.
lda tods
asl a
asl a
tay Y = offset to tos.
lda dirstack,x Swap tos-1 and tos.
pha
lda dirstack,y
sta dirstack,x
pla
sta dirstack,y
lda dirstack+2,x
pha
lda dirstack+2,y
sta dirstack+2,x
pla
sta dirstack+2,y
jmp gototop chdir to the new tos.
;
; Parameter = +n; do the rotate
;
rotate add4 arg,#1,p
pei (p+2)
pei (p)
jsr cstrlen
tax
Dec2Int (p,@x,#0),@a Convert parameter to decimal
sta count and store in count.
cmp #0 If parameter is 0
beq badnum or negative,
bmi badnum report "invalid number".
cmp tods If count >= tos,
beq rotloop
bcc rotloop
roterr ldx #^err2 Print error message:
lda #err2 Directory stack not that deep
jmp prerrmsg
badnum ldx #^errbadnum Print error message:
lda #errbadnum Invalid number
jmp prerrmsg
;
; Loop to rotate entries in directory stack
;
rotloop lda tods
dec a
asl a
asl a
tay
lda dirstack+6,y
pha
lda dirstack+4,y
pha
rotloop2 lda dirstack,y
sta dirstack+4,y
lda dirstack+2,y
sta dirstack+6,y
cpy #0
beq nextrot
dey
dey
dey
dey
bra rotloop2
nextrot pla
sta dirstack
pla
sta dirstack+2
dec count
bne rotloop
;
; chdir to the top-of-stack directory
;
gototop lda tods
asl a
asl a
tay
lda dirstack+2,y
pha
lda dirstack,y
pha
jsl gotodir
bra done All done.
;
; Parameter = directory name; add it to the stack
;
godir anop
lda tods
cmp #MAXD-1 If index >= maximum,
bcc stackok
ldx #^errfull print error message.
lda #errfull
prerrmsg jsr errputs
inc status Return status = 1.
bra exit
stackok anop
jsl dotods Set top of stack to current directory.
pei (arg+2)
pei (arg)
jsl gotodir chdir to the parameter directory.
bne exit
inc tods Bump the top of stack pointer.
lda tods
asl a
asl a
tay
lda #0
sta dirstack,y
sta dirstack+2,y
jsl dotods Set top of stack to current directory.
done lda varpushdsil If $PUSHDSILENT not defined,
bne exit
pea 1
jsl showdir show the directory stack.
exit unlock DirMutex
return 2:status
usagestr dc c'usage: pushd [+n | dir]',h'0d00'
err1 dc c'pushd: No other directory',h'0d00'
err2 dc c'pushd: Directory stack not that deep',h'0d00'
errfull dc c'pushd: Directory stack full',h'0d00'
errbadnum dc c'pushd: Invalid number',h'0d00'
END
**************************************************************************
*
* POPD: builtin command
* syntax: popd [+n]
*
* pop a directory from stack and cd to it
*
**************************************************************************
popd START
using DirData
using vardata
count equ 0
arg equ count+2
status equ arg+4
space equ status+2
subroutine (4:argv,2:argc),space
lock DirMutex
stz status
lda argc
dec a
jeq noarg
dec a
bne using
ldy #4
lda [argv],y
sta arg
ldy #6
lda [argv],y
sta arg+2
lda [arg]
and #$FF
cmp #'+'
beq plus
using ldx #^usingstr
lda #usingstr
jsr errputs
inc status Return status = 1.
jmp exit
plus add4 arg,#1,arg
pei (arg+2)
pei (arg)
jsr cstrlen
tax
Dec2Int (arg,@x,#0),@a
sta count
cmp #0
beq noarg
lda tods
beq pluserr
lda count
cmp tods
beq doplus
bcc doplus
pluserr ldx #^err2
lda #err2
jsr errputs
inc status Return status = 1.
bra exit
doplus jsl dotods Set top of stack to current directory.
sub2 tods,count,@a
asl a
asl a
tax
phx
lda dirstack+2,x
pha
lda dirstack,x
pha
jsl nullfree
plx
plusloop lda dirstack+4,x
sta dirstack,x
lda dirstack+6,x
sta dirstack+2,x
inx4
dec count
bne plusloop
dec tods
bra gototop
noarg lda tods
bne noarg0
ldx #^err1
lda #err1
jsr errputs
inc status Return status = 1.
bra exit
noarg0 lda tods
asl a
asl a
tay
lda dirstack+2,y
pha
lda dirstack,y
pha
jsl nullfree
dec tods
gototop lda tods
asl a
asl a
tay
lda dirstack+2,y
pha
lda dirstack,y
pha
jsl gotodir
lda varpushdsil
bne exit
pea 1
jsl showdir
exit unlock DirMutex
return 2:status
usingstr dc c'Usage: popd [+n]',h'0d00'
err1 dc c'popd: Directory stack empty',h'0d00'
err2 dc c'popd: Directory stack not that deep',h'0d00'
END
**************************************************************************
*
* Set prefix 0 to the passed c string
*
**************************************************************************
gotodir PRIVATE
retval equ 0
space equ retval+2
subroutine (4:dir),space
stz retval
pei (dir+2)
pei (dir)
jsr c2gsstr
sta PRecPath
sta GRecPath
stx PRecPath+2
stx GRecPath+2
lock mutex
GetFileInfo GRec
bcc ok
ohshit sta ErrError
ErrorGS Err
inc retval
bra done
ok if2 GRecFT,eq,#$F,ok2
ldx dir+2
lda dir
jsr errputs
ldx #^direrr
lda #direrr
jsr errputs
inc retval
bra done
ok2 SetPrefix PRec
bcs ohshit
done ph4 PRecPath
jsl nullfree
unlock mutex
return 2:retval
mutex key
; Parameter block for GS/OS SetPrefix call
PRec dc i'2' pCount
PRecNum dc i'0' prefixNum (0 = current directory)
PRecPath ds 4 Pointer to input prefix path
; Parameter block for GS/OS GetFileInfo call
GRec dc i'3' pCount
GRecPath ds 4 Pointer to input pathname
GRecAcc ds 2 access (result)
GRecFT ds 2 fileType (result)
; Parameter block for shell ErrorGS call (p 393 in ORCA/M manual)
Err dc i2'1' pCount
ErrError ds 2 Error number
dirErr dc c': Not a directory',h'0d00'
END
**************************************************************************
*
* Display the directory stack
*
**************************************************************************
showdir PRIVATE
using DirData
idx equ 0
space equ idx+2
subroutine (2:flag),space
lda tods Get directory stack index.
asl a Multiply by four to
asl a get byte offset (idx).
sta idx
loop lda flag If parameter == 1,
beq long
ldy idx
lda dirstack+2,y print entry
pha
lda dirstack,y
pha
jsl path2tilde but first substitute "~"
phx for home directory.
pha
jsr puts
jsl nullfree
bra next
long ldy idx else,
lda dirstack+2,y print full entry.
tax
lda dirstack,y
jsr puts
next lda #' ' Print a space.
jsr putchar
lda idx If idx != 0,
beq done
sub2 idx,#4,idx idx = idx -4
bra loop handle next entry.
done jsr newline
return
END
**************************************************************************
*
* Set the top of the stack to the current directory
*
**************************************************************************
dotods PRIVATE
using DIRDATA
p equ 0
idx equ p+4
space equ idx+2
subroutine (0:dummy),space
lda tods Get index number.
asl a Multiply index by four
asl a to get byte offset.
sta idx Store in idx
tay and Y-register.
lda dirstack,y If there is an address
ora dirstack+2,y in this position,
beq setit
lda dirstack+2,y
pha
lda dirstack,y
pha
jsl nullfree free it.
setit pea 0
jsl getpfxstr Get value of prefix 0.
sta p
stx p+2
ora p+2 If NULL pointer returned,
beq done an error was reported.
ldy #2 If length of returned
lda [p],y GS/OS string is 0,
bne ok an error was reported.
ph4 p Free the buffer.
jsl nullfree
bra done
;
; Move text in GS/OS result buffer to beginning of buffer
; (overwritting the two length words).
;
ok clc Source is result
lda p buffer plus
adc #4 four bytes.
tay
lda p+2
adc #0
pha
phy
pei (p+2) Destination is first
pei (p) byte of buffer.
jsr copycstr
ldy idx Store address of string
lda p in current position
sta dirstack,y of directory stack.
lda p+2
sta dirstack+2,y
done return
END
**************************************************************************
*
* Directory stack data
*
**************************************************************************
DirData DATA
dirstack ds MAXD*4
tods dc i'0'
DirMutex key
END
**************************************************************************
*
* Replace $HOME with a '~' in string
*
**************************************************************************
path2tilde START
ptr equ 0
newpath equ ptr+4
home equ newpath+4
space equ home+4
subroutine (4:path),space
pei (path+2) Get length of
pei (path) path string
jsr cstrlen parameter.
inc2 a Add 2, and allocate
pea 0 memory for result string.
pha
~NEW
sta newpath
stx newpath+2
sta ptr
stx ptr+2
ph4 #homename Get $HOME environment variable.
jsl getenv
sta home
stx home+2
ora home+2 If buffer wasn't allocated
jeq notfound2 cannot search for $HOME.
ldy #2 Get result length word.
lda [home],y
beq notfound2 If 0, just copy the rest.
tax Use X to count down HOME chars.
ldy #0 path index is based from 0.
checkhome lda [path],y
and #$FF Isolate character in parameter,
beq notfound2 checking for end of string,
jsr tolower converting to lower-case
jsr toslash and changing ":" to "/".
pha Hold on stack for comparison.
iny4 $home has 4 bytes of length info
lda [home],y that need to be indexed over.
dey2 Take back 3 of the offset,
dey nudging Y ahead by 1.
and #$FF Isolate $home character,
jsr tolower converting to lower-case
jsr toslash and changing ":" to "/".
cmp 1,s If the parameter character !=,
bne notfound there is no match.
pla Pop the parameter character off stack.
dex Decrement $home length counter.
bne checkhome If more, stay in loop.
;
; First part of parameter matched $HOME
;
cmp #'/' This char = "/"?
beq found yes -- it's a match.
lda [path],y If the following character
and #$FF is zero (end of string),
beq found
jsr toslash '/', or ':', we have a match.
cmp #'/'
bne notfound2
found lda #'~' Store '~' as first character
sta [ptr] in result buffer, and bump
incad ptr result pointer.
bra copyrest
;
; First part of parameter does not match $HOME
;
notfound pla Get rid of comparison value on stack.
notfound2 ldy #0 Not found: copy from beginning.
;
; Copy remainder of parameter (Y-reg marks start) to destination string
;
copyrest short a
copyloop lda [path],y
beq endcopy
cmp #':'
bne copyput
lda #'/'
copyput sta [ptr]
long a
incad ptr
short a
iny
bra copyloop
endcopy sta [ptr]
long a
dec ptr If final character
lda [ptr] was "/",
cmp #'/'
bne skipshorten
lda #0 obliterate it.
sta [ptr]
skipshorten pei (home+2) Free memory allocated
pei (home) for the value of $HOME.
jsl nullfree
return 4:newpath
homename gsstr 'home' Env variable name
END
**************************************************************************
*
* Return pointer to current directory (\0) GS/OS string in a/x
*
**************************************************************************
getpfxstr START
p equ 0
space equ p+4
subroutine (2:pnum),space
lock mutex
; Use dummy GS/OS result buf to get length of pathname
;
lda pnum Put prefix num into
cmp #$FFFF If it's $FFFF,
beq doboot use GetBootVol, not GetPrefix.
sta gpnum Store prefix num in parameter block.
ld4 TempResultBuf,gppath
GetPrefix gpparm
bra chklen
doboot ld4 TempResultBuf,gbpath
GetBootVol gbparm
chklen lda TempRBlen Use that length
clc plus five (for
adc #5 len words and terminator)
pea 0 to allocate memory
pha that holds the string.
~NEW
sta p Store result in
stx p+2 direct page pointer.
ora p+2 If memory was not available,
bne memok
lda #0201 report memory error and
bra rpterr return NULL to user.
memok lda TempRBlen Store result buf
inc2 a length at start
inc2 a of buffer.
sta [p]
tay Store a null byte
short a at the end of the
lda #0 string so it can
sta [p],y be used as a c-string.
long a
;
; Get the prefix string into the newly allocated buffer.
;
lda pnum Prefix number tells
cmp #$FFFF whether to use
beq doboot2 GetPrefix or GetBootVol.
mv4 p,gppath
GetPrefix gpparm
bcs rpterr
bra done
doboot2 mv4 p,gbpath
GetBootVol gbparm
bcc done If there was an error,
rpterr sta errError Save the value
ErrorGS err and report it.
ldy #2 Set length of returned
lda #0 string to 0 so caller
sta [p],y can detect error condition.
done unlock mutex
;
; Return pointer to caller. (Caller has responsibility to deallocate.)
;
return 4:p
mutex key
;
; Parameter block for GetPrefix GS/OS call
;
gpparm dc i2'2' pCount
gpnum dc i2'0' prefixNum (from parameter)
gppath dc i4'0' prefix returned to this GS/OS buffer.
;
; Parameter block for GetBootVol GS/OS call
;
gbparm dc i2'1' pCount
gbpath dc i4'0' prefix returned to this GS/OS buffer.
;
; GS/OS result buffer for getting the full length of the prefix string
;
TempResultBuf dc i2'5' Only five bytes total.
TempRBlen ds 2 String's length returned here.
ds 1 Only 1 byte for value.
;
; Parameter block for shell ErrorGS call (p 393 in ORCA/M manual)
;
err dc i2'1' pCount
errError ds 2 Error number
END