gno/bin/gsh/expand.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

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