mirror of
https://github.com/GnoConsortium/gno.git
synced 2024-11-18 19:09:31 +00:00
6269a8ca25
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)
723 lines
14 KiB
NASM
723 lines
14 KiB
NASM
**************************************************************************
|
|
*
|
|
* The GNO Shell Project
|
|
*
|
|
* Developed by:
|
|
* Jawaid Bazyar
|
|
* Tim Meekins
|
|
*
|
|
* $Id: expand.asm,v 1.7 1998/12/31 18:29:13 tribby Exp $
|
|
*
|
|
**************************************************************************
|
|
*
|
|
* EXPAND.ASM
|
|
* By Tim Meekins
|
|
* Modified by Dave Tribby for GNO 2.0.6
|
|
*
|
|
* Command line expansion routines for wildcards and env vars
|
|
*
|
|
* Note: text set up for tabs at col 16, 22, 41, 49, 57, 65
|
|
* | | | | | |
|
|
* ^ ^ ^ ^ ^ ^
|
|
**************************************************************************
|
|
|
|
mcopy /obj/gno/bin/gsh/expand.mac
|
|
|
|
dummyexpand start ; ends up in .root
|
|
end
|
|
|
|
setcom 60
|
|
|
|
**************************************************************************
|
|
*
|
|
* glob the command line and expand filename wildcard characters
|
|
*
|
|
**************************************************************************
|
|
|
|
glob START
|
|
|
|
using vardata
|
|
|
|
count equ 0
|
|
gname equ count+2
|
|
sepptr equ gname+4
|
|
eptr equ sepptr+4
|
|
exppath equ eptr+4
|
|
filesep equ exppath+4
|
|
shallweglob equ filesep+2
|
|
wordbuf equ shallweglob+2
|
|
ptr equ wordbuf+4
|
|
buf equ ptr+4
|
|
globflag equ buf+4
|
|
space equ globflag+2
|
|
|
|
subroutine (4:cmd),space
|
|
|
|
; Allocate buffer to hold result
|
|
jsl alloc1024
|
|
sta buf
|
|
stx buf+2
|
|
|
|
; Check for noglob variable and exit if it's set to something.
|
|
ldy varnoglob
|
|
beq doglob
|
|
|
|
; Allocate a buffer, copy the command line into it, and return.
|
|
pei (cmd+2)
|
|
pei (cmd)
|
|
pei (buf+2)
|
|
pei (buf)
|
|
jsr copycstr
|
|
jmp bye
|
|
|
|
;
|
|
; noglob isn't set, so we can start globbing.
|
|
;
|
|
doglob sta ptr ptr points to next
|
|
stx ptr+2 position in buf.
|
|
|
|
jsl alloc1024 Create a word buffer.
|
|
sta wordbuf
|
|
stx wordbuf+2
|
|
|
|
stz globflag globflag = no globbing done (yet).
|
|
|
|
;
|
|
; Find the beginning of the next word
|
|
;
|
|
findword jsr g_getbyte Get character from command line.
|
|
jeq alldone
|
|
if2 @a,eq,#' ',passthru
|
|
if2 @a,eq,#009,passthru
|
|
if2 @a,eq,#013,passthru
|
|
if2 @a,eq,#010,passthru
|
|
if2 @a,eq,#';',passthru
|
|
if2 @a,eq,#'&',passthru
|
|
if2 @a,eq,#'|',passthru
|
|
if2 @a,eq,#'>',passthru
|
|
if2 @a,eq,#'<',passthru
|
|
|
|
; It's not a simple pass-through character. See what needs to happen.
|
|
|
|
stz shallweglob
|
|
ldy #0
|
|
bra grabbingword
|
|
|
|
; For pass-through characters, just copy to output buffer.
|
|
|
|
passthru jsr g_putbyte
|
|
bra findword
|
|
|
|
|
|
;
|
|
; single out the next word [y is initialized above]
|
|
;
|
|
grabword jsr g_getbyte
|
|
grabbingword if2 @a,eq,#"'",grabsingle
|
|
if2 @a,eq,#'"',grabdouble
|
|
if2 @a,eq,#'\',grabslash
|
|
if2 @a,eq,#' ',procword
|
|
if2 @a,eq,#009,procword
|
|
if2 @a,eq,#013,procword
|
|
if2 @a,eq,#010,procword
|
|
if2 @a,eq,#000,procword
|
|
if2 @a,eq,#';',procword
|
|
if2 @a,eq,#'&',procword
|
|
if2 @a,eq,#'|',procword
|
|
if2 @a,eq,#'>',procword
|
|
if2 @a,eq,#'<',procword
|
|
if2 @a,eq,#'[',grabglob
|
|
if2 @a,eq,#']',grabglob
|
|
if2 @a,eq,#'*',grabglob
|
|
if2 @a,eq,#'?',grabglob
|
|
|
|
; Default action (also completion of some of the other special cases)
|
|
grabnext sta [wordbuf],y Save character in word buffer
|
|
iny and bump its index.
|
|
bra grabword Get next character of word.
|
|
|
|
; "[", "]", "*", "?"
|
|
grabglob ldx #1 Set "shallweglob"
|
|
stx shallweglob flag.
|
|
bra grabnext Store char in word buf & get next.
|
|
|
|
; "\"
|
|
grabslash sta [wordbuf],y Save "\" in word buffer
|
|
iny and bump its index.
|
|
jsr g_getbyte Get next character in cmd line.
|
|
beq procword If null byte, word is terminated.
|
|
bra grabnext Store char in word buf & get next.
|
|
|
|
; '"'
|
|
grabsingle sta [wordbuf],y Save char in word buffer
|
|
iny and bump its index.
|
|
jsr g_getbyte Get next character in cmd line.
|
|
beq procword If null byte, word is terminated.
|
|
if2 @a,eq,#"'",grabnext If "'", store and grab next char.
|
|
bra grabsingle Save new char and stay in this loop.
|
|
|
|
; "'"
|
|
grabdouble sta [wordbuf],y Save char in word buffer
|
|
iny and bump its index.
|
|
jsr g_getbyte Get next character in cmd line.
|
|
beq procword If null byte, word is terminated.
|
|
if2 @a,eq,#'"',grabnext If '"', store and grab next char.
|
|
bra grabdouble Save new char and stay in this loop.
|
|
|
|
|
|
;
|
|
; The complete word is in the buffer. Time to process it.
|
|
;
|
|
procword dec cmd Decrement cmd line pointer.
|
|
lda #0 Terminate word buffer with
|
|
sta [wordbuf],y a null byte.
|
|
|
|
;
|
|
; Shall we glob? Shall we scream? What happened, to our postwar dream?
|
|
;
|
|
lda [wordbuf]
|
|
and #$FF
|
|
if2 @a,eq,#'-',skipdeglob ;This allows '-?' option.
|
|
lda shallweglob
|
|
bne globword
|
|
;
|
|
; we didn't glob this word, so copy the word buffer to the output buffer
|
|
;
|
|
skipdeglob ldy #0
|
|
flushloop lda [wordbuf],y
|
|
and #$FF
|
|
beq doneflush
|
|
jsr g_putbyte
|
|
iny
|
|
bra flushloop
|
|
doneflush jmp findword
|
|
|
|
;
|
|
; Hello, boys and goils, velcome to Tim's Magik Shoppe
|
|
;
|
|
; Ok, here's the plan:
|
|
; 1. We give InitWildcardGS a PATHNAME.
|
|
; 2. NextWildcardGS returns a FILENAME.
|
|
; 3. We need to expand to the command-line the full pathname.
|
|
; 4. Therefore, we must put aside the prefix, and cat each file returned
|
|
; from NextWildcardGS to the saved prefix, but, we must still pass
|
|
; the entire path to InitWildcardGS.
|
|
; 5. This solves our problem with quoting. Expand the quotes before
|
|
; passing along to InitWildcardGS, BUT the saved prefix we saved to cat
|
|
; to will still have the quotes in it, so that the tokenizer can deal
|
|
; with it. Whew!
|
|
;
|
|
; Well, here goes nuthin'.... [Ya know, and I'm reading Levy's book
|
|
; 'Hackers' right now...]
|
|
;
|
|
;
|
|
;
|
|
; Expand out the quoted stuff, and keep an eye out for that ubiquitous last
|
|
; filename separator... then we can isolate him!
|
|
;
|
|
globword stz filesep
|
|
jsl alloc1024 Alloc buffer for wildcard pattern.
|
|
sta exppath
|
|
stx exppath+2
|
|
|
|
incad @xa Leave room for length word
|
|
incad @xa
|
|
sta eptr
|
|
stx eptr+2
|
|
sta sepptr
|
|
stx sepptr+2
|
|
|
|
ldy #0
|
|
exploop lda [wordbuf],y
|
|
and #$FF
|
|
beq endexp
|
|
iny
|
|
if2 @a,eq,#'\',expslash
|
|
if2 @a,eq,#"'",expsingle
|
|
if2 @a,eq,#'"',expdouble
|
|
if2 @a,eq,#'/',expsep
|
|
if2 @a,eq,#':',expsep
|
|
expput sta [eptr]
|
|
incad eptr
|
|
bra exploop
|
|
expsep sty filesep
|
|
sta [eptr]
|
|
incad eptr
|
|
mv4 eptr,sepptr
|
|
bra exploop
|
|
expslash lda [wordbuf],y
|
|
iny
|
|
and #$FF
|
|
beq endexp
|
|
bra expput
|
|
expsingle lda [wordbuf],y
|
|
iny
|
|
and #$FF
|
|
beq endexp
|
|
if2 @a,eq,#"'",exploop
|
|
sta [eptr]
|
|
incad eptr
|
|
bra expsingle
|
|
expdouble lda [wordbuf],y
|
|
iny
|
|
and #$FF
|
|
beq endexp
|
|
if2 @a,eq,#'"',exploop
|
|
sta [eptr]
|
|
incad eptr
|
|
bra expdouble
|
|
;
|
|
; We really didn't mean to expand the filename, so, copy it back again..
|
|
;
|
|
endexp ldy filesep
|
|
copyback lda [wordbuf],y
|
|
iny
|
|
and #$FF
|
|
sta [sepptr]
|
|
incad sepptr
|
|
cmp #0
|
|
bne copyback
|
|
;
|
|
; Calculate length by subtracting sepptr from starting address
|
|
;
|
|
sub2 sepptr,exppath,@a
|
|
dec2 a Don't count length word or \0
|
|
dec a
|
|
sta [exppath]
|
|
;
|
|
; We now have enough to call InitWildCardGS!!!
|
|
; [ let's mutex the rest so we don't have to fix InitWC and NextWC ;-) ]
|
|
;
|
|
lock glob_mutex
|
|
;
|
|
; Call shell routine InitWildcardGS to initialize the filename pattern
|
|
;
|
|
stz count
|
|
mv4 exppath,initWCpath
|
|
InitWildcardGS initWCparm
|
|
|
|
ph4 #65 Allocate memory for
|
|
~NEW expanded name.
|
|
sta gname Store in direct
|
|
stx gname+2 page pointer
|
|
sta nWCname and in NextWildcardGS
|
|
stx nWCname+2 parameter block.
|
|
lda #65 Store maximum length at
|
|
sta [gname] beginning of result buf.
|
|
;
|
|
; Call shell routine NextWildcardGS to get the next name that matches.
|
|
;
|
|
WCloop NextWildcardGS nWCparm
|
|
ldy #2
|
|
lda [gname],y
|
|
beq nomore
|
|
;
|
|
; Keep count of how many paths are expanded
|
|
;
|
|
inc count
|
|
|
|
;
|
|
; Copy the original path (up to file separator) to output buffer
|
|
;
|
|
ldy #0
|
|
outtahere if2 @y,eq,filesep,globout
|
|
lda [wordbuf],y
|
|
jsr g_putspecial
|
|
iny
|
|
bra outtahere
|
|
;
|
|
; Copy the expanded filename to output buffer
|
|
;
|
|
globout ldy #2 Get returned length
|
|
lda [gname],y from GS/OS buffer.
|
|
tax
|
|
ldy #4
|
|
globoutta lda [gname],y Copy next character
|
|
jsr g_putspecial into buffer.
|
|
iny
|
|
dex
|
|
bne globoutta
|
|
;
|
|
; Place blank as separator after name and see if more are expanded.
|
|
;
|
|
lda #' '
|
|
jsr g_putbyte
|
|
bra WCloop
|
|
|
|
;
|
|
; All of the names (if any) from this pattern have been expanded.
|
|
;
|
|
nomore anop
|
|
|
|
unlock glob_mutex
|
|
;
|
|
; Deallocate path buffers (we should probably alloc once, not each word... )
|
|
;
|
|
pei (gname+2)
|
|
pei (gname)
|
|
jsl nullfree
|
|
ldx exppath+2
|
|
lda exppath
|
|
jsl free1024
|
|
|
|
lda count If something was expanded,
|
|
beq nothingfound
|
|
|
|
lda globflag Set "globbed, something found"
|
|
ora #$8000 bit in globflag.
|
|
sta globflag
|
|
jmp findword Go find the next word.
|
|
|
|
; Nothing was expanded from the wildcard. If we wanted to act like
|
|
; ksh, we could pass the original text by doing a "jmp skipdeglob".
|
|
|
|
nothingfound anop
|
|
lda globflag Set "globbed, nothing found"
|
|
ora #$4000 bit in globflag.
|
|
sta globflag
|
|
|
|
jmp findword Go find the next word.
|
|
|
|
;
|
|
; Goodbye, cruel world, I'm leaving you today, Goodbye, goodbye.
|
|
;
|
|
alldone jsr g_putbyte Store null byte at end of string.
|
|
|
|
;
|
|
; Check globflag for no valid matches found in any pattern
|
|
;
|
|
lda globflag
|
|
cmp #$4000
|
|
bne alldone2
|
|
|
|
ldx #^nomatch
|
|
lda #nomatch
|
|
jsr errputs
|
|
|
|
ldx buf+2
|
|
lda buf
|
|
jsl free1024
|
|
|
|
stz buf+2
|
|
stz buf
|
|
|
|
alldone2 ldx wordbuf+2
|
|
lda wordbuf
|
|
jsl free1024
|
|
|
|
bye return 4:buf
|
|
|
|
|
|
;
|
|
; Subroutine of glob: get a byte from the original command-line
|
|
;
|
|
g_getbyte lda [cmd]
|
|
incad cmd
|
|
and #$FF
|
|
rts
|
|
|
|
;
|
|
; Subroutine of glob: put special characters. Same as g_putbyte, but
|
|
; if it is a special shell character then quote it.
|
|
;
|
|
g_putspecial and #$7F
|
|
if2 @a,eq,#' ',special
|
|
if2 @a,eq,#'.',special
|
|
if2 @a,eq,#013,special
|
|
if2 @a,eq,#009,special
|
|
if2 @a,eq,#';',special
|
|
if2 @a,eq,#'&',special
|
|
if2 @a,eq,#'<',special
|
|
if2 @a,eq,#'>',special
|
|
if2 @a,eq,#'|',special
|
|
bra g_putbyte
|
|
special pha
|
|
lda #'\'
|
|
jsr g_putbyte
|
|
pla ; fall through to g_putbyte...
|
|
;
|
|
; Subroutine of glob: store a byte into the new command-line
|
|
;
|
|
g_putbyte short a
|
|
sta [ptr]
|
|
long a
|
|
incad ptr
|
|
rts
|
|
|
|
|
|
glob_mutex key
|
|
|
|
;
|
|
; Parameter block for shell InitWildcardGS call (p 414 in ORCA/M manual)
|
|
;
|
|
InitWCParm dc i2'2' pCount
|
|
InitWCPath ds 4 Path name, with wildcard
|
|
dc i2'%00000001' Flags (this bit not documented!!!)
|
|
|
|
;
|
|
; Parameter block for shell NextWildcardGS call (p 414 in ORCA/M manual)
|
|
;
|
|
nWCparm dc i2'1' pCount
|
|
nWCname ds 4 Pointer to returned path name
|
|
|
|
nomatch dc c'No match',h'0d00'
|
|
|
|
END
|
|
|
|
**************************************************************************
|
|
*
|
|
* Expand $variables and tildes not in single quotes
|
|
*
|
|
**************************************************************************
|
|
|
|
expandvars START
|
|
|
|
; Maximum number of characters in an environment variable or $<
|
|
MAXVAL equ 512
|
|
|
|
|
|
ptr equ 1
|
|
buf equ ptr+4
|
|
dflag equ buf+4
|
|
space equ dflag+2
|
|
cmd equ space+3
|
|
end equ cmd+4
|
|
|
|
; subroutine (4:cmd),space
|
|
|
|
tsc
|
|
sec
|
|
sbc #space-1
|
|
tcs
|
|
phd
|
|
tcd
|
|
|
|
stz dflag Delimiter flag = FALSE.
|
|
|
|
jsl alloc1024
|
|
sta buf
|
|
sta ptr
|
|
stx buf+2
|
|
stx ptr+2
|
|
|
|
loop jsr e_getbyte
|
|
jeq done
|
|
if2 @a,eq,#"'",quote
|
|
if2 @a,eq,#'$',expand
|
|
if2 @a,eq,#'~',tilde
|
|
if2 @a,eq,#'\',slasher
|
|
jsr e_putbyte
|
|
bra loop
|
|
|
|
slasher jsr e_putbyte
|
|
jsr e_getbyte
|
|
jsr e_putbyte
|
|
bra loop
|
|
|
|
quote jsr e_putbyte
|
|
jsr e_getbyte
|
|
jeq done
|
|
if2 @a,ne,#"'",quote
|
|
jsr e_putbyte
|
|
bra loop
|
|
|
|
;
|
|
; Tilde expansion: use the contents of $home, but make sure the
|
|
; path delimiter(s) match what the user wants (either "/" or ":")
|
|
;
|
|
tilde anop
|
|
lock exp_mutex
|
|
|
|
lda #"oh" Strangely enough,
|
|
sta name this spells "home"!
|
|
lda #"em"
|
|
sta name+2
|
|
sta dflag Delimiter flag = TRUE.
|
|
ldx #4
|
|
jmp getval
|
|
|
|
;
|
|
; Expand an environment variable since a '$' was encountered.
|
|
;
|
|
expand anop
|
|
lock exp_mutex
|
|
|
|
lda #0
|
|
sta name
|
|
lda [cmd]
|
|
and #$FF
|
|
if2 @a,eq,#'{',braceexpand
|
|
if2 @a,eq,#'<',stdinexpand
|
|
ldx #0
|
|
nameloop lda [cmd]
|
|
and #$FF
|
|
beq getval
|
|
if2 @a,cc,#'0',getval
|
|
if2 @a,cc,#'9'+1,inname
|
|
if2 @a,cc,#'A',getval
|
|
if2 @a,cc,#'Z'+1,inname
|
|
if2 @a,eq,#'_',inname
|
|
if2 @a,cc,#'a',getval
|
|
if2 @a,cc,#'z'+1,inname
|
|
bra getval
|
|
inname jsr e_getbyte
|
|
cpx #255 Only the first 255 characters
|
|
beq nameloop are significant.
|
|
sta name,x
|
|
inx
|
|
bra nameloop
|
|
|
|
;
|
|
; expand in braces {}
|
|
;
|
|
braceexpand jsr e_getbyte
|
|
ldx #0
|
|
braceloop lda [cmd]
|
|
and #$FF
|
|
beq getval
|
|
jsr e_getbyte
|
|
if2 @a,eq,#'}',getval
|
|
cpx #255 Only the first 255 characters
|
|
beq braceloop are significant.
|
|
sta name,x
|
|
inx
|
|
bra braceloop
|
|
|
|
;
|
|
; get text from standard input
|
|
;
|
|
stdinexpand jsr e_getbyte
|
|
ReadLine (#value,#MAXVAL,#13,#0),@a
|
|
sta valueln
|
|
bra chklen
|
|
|
|
;
|
|
; Get a value for this variable
|
|
;
|
|
getval stx nameln Save length of name.
|
|
ReadVariableGS ReadVarPB Read its value.
|
|
|
|
lda valueln Get value length.
|
|
cmp #MAXVAL+1 If > maximum allowed length,
|
|
bcs expanded we didn't get anything.
|
|
cmp #0
|
|
chklen anop
|
|
beq expanded If 0, nothing to do
|
|
tax Save length in X-reg.
|
|
lda dflag If delimiter flag isn't set,
|
|
beq storeval go store the variable value
|
|
|
|
; Check to see if delimiters in the variable need to be switched
|
|
lda valueln Set up length in
|
|
tax X-reg and get
|
|
lda [cmd] next command line
|
|
and #$FF character.
|
|
cmp #"/" If it's a slash, see if
|
|
beq chkvarslash variable needs to convert to slash.
|
|
cmp #":" If it's not a colon,
|
|
bne storeval no need to convert.
|
|
lda value Get first character of value.
|
|
and #$FF
|
|
cmp #"/" If it's not a slash,
|
|
bne storeval no need to convert.
|
|
|
|
; Convert variable from "/" to ":" delimiter
|
|
short m
|
|
chk_s lda value-1,x
|
|
cmp #"/"
|
|
bne bump_s
|
|
lda #":"
|
|
sta value-1,x
|
|
bump_s dex
|
|
bpl chk_s
|
|
long m
|
|
bra storeval
|
|
|
|
chkvarslash anop
|
|
lda value Get first character of value.
|
|
and #$FF
|
|
cmp #":" If it's not a colon,
|
|
bne storeval no need to convert.
|
|
|
|
; Convert variable from ":" to "/" delimiter
|
|
short m
|
|
chk_c lda value-1,x
|
|
cmp #":"
|
|
bne bump_c
|
|
lda #"/"
|
|
sta value-1,x
|
|
bump_c dex
|
|
bpl chk_c
|
|
long m
|
|
|
|
;
|
|
; Store the variable's value in the out buffer
|
|
;
|
|
storeval anop
|
|
lda valueln Get length.
|
|
tay Save length in Y-reg.
|
|
ldx #0 Use X-reg in index value.
|
|
putval lda value,x
|
|
jsr e_putbyte
|
|
inx
|
|
dey
|
|
bne putval
|
|
|
|
expanded unlock exp_mutex
|
|
stz dflag Delimiter flag = FALSE.
|
|
jmp loop
|
|
|
|
done jsr e_putbyte
|
|
|
|
ldx buf+2
|
|
ldy buf
|
|
|
|
lda space
|
|
sta end-3
|
|
lda space+1
|
|
sta end-2
|
|
pld
|
|
tsc
|
|
clc
|
|
adc #end-4
|
|
tcs
|
|
|
|
tya
|
|
rtl
|
|
|
|
e_getbyte lda [cmd]
|
|
incad cmd
|
|
and #$FF
|
|
rts
|
|
|
|
e_putbyte short a
|
|
sta [ptr]
|
|
long a
|
|
incad ptr
|
|
rts
|
|
|
|
exp_mutex key
|
|
|
|
|
|
; GS/OS string to hold variable name
|
|
namestr anop
|
|
nameln ds 2 Length of name
|
|
name ds 256 Room for 255 chars + 0.
|
|
|
|
|
|
; GS/OS result buffer to hold up to MAXVAL bytes of value
|
|
valueresult anop
|
|
dc i2'MAXVAL+4' Length of result buffer
|
|
valueln ds 2 Length of value
|
|
value ds MAXVAL Room for MAXVAL chars
|
|
|
|
|
|
; Parameter block for shell ReadVariableGS calls
|
|
ReadVarPB anop
|
|
dc i2'3' pCount
|
|
RVname dc a4'namestr' Name (pointer to GS/OS string)
|
|
RVvalue dc a4'valueresult' Value (ptr to result buf or string)
|
|
RVexport ds 2 Export flag
|
|
|
|
END
|