************************************************************************
*
* The GNO Shell Project
*
* Developed by:
*   Jawaid Bazyar
*   Tim Meekins
*
* $Id: builtin.asm,v 1.9 1998/12/31 18:29:11 tribby Exp $
*
**************************************************************************
*
* BUILTIN.ASM
*   By Tim Meekins
*   Modified by Dave Tribby for GNO 2.0.6
*
* Builtin command searching and execution.
*
* Note: text set up for tabs at col 16, 22, 41, 49, 57, 65
*              |     |                  |       |       |       |
*	^	^	^	^	^	^	
**************************************************************************
*
* Interfaces defined in this file:
*
*   builtin	subroutine (2:argc,4:argv)
*	Returns completion status in Accumulator
*		
*   IsBuiltin	subroutine (4:name)
*	return 2:tbl
*
* Remainder are interfaces to builtin commands with interface
*	subroutine (4:argv,2:argc)
*	returns status in accumulator
*   cd	(chdir is entry as an alternate name)
*   clear
*   echo
*   pwd
*   which
*   prefix
*   rehash	(unhash is entry as an alternate name)
*   exit
*   setdebug
*   psbi	(command name is "ps")
*   hashbi	(command name is "hash")
*   source
*   cmdbi	(command name is "commands")
*
**************************************************************************

	mcopy /obj/gno/bin/gsh/builtin.mac

dummybuiltin	start		; ends up in .root
	end

	setcom 60

p_next	gequ	0	;next in global proclist
p_friends	gequ	p_next+4	;next in job list
p_flags	gequ	p_friends+4	;various job status flags
p_reason	gequ	p_flags+2	;reason for entering this state
p_index	gequ	p_reason+2	;job index
p_pid	gequ	p_index+2	;process id
p_jobid	gequ	p_pid+2	;process id of job leader
p_command	gequ	p_jobid+2	;command (how job invoked)
p_space	gequ	p_command+4	;space for structure

**************************************************************************
*
* Find and execute a builtin command
*
**************************************************************************

builtin	START

	using BuiltinData

val	equ	1
file	equ	val+2
tbl	equ	file+4
space	equ	tbl+4
argv	equ	space+3
argc	equ	argv+4
end	equ	argc+2

;	 subroutine (2:argc,4:argv),space

	tsc
	sec
	sbc	#space-1
	tcs
	phd
	tcd

	ldy	#2
	lda	[argv]
	sta	file
	lda	[argv],y
	sta	file+2
	ld4	builtintbl,tbl
	ld2	-1,val
	lda	argc
	beq	done

loop	ldy	#2
	lda	[tbl]
	ora	[tbl],y
	beq	done
	lda	[tbl],y
	pha
	lda	[tbl]
	pha
	pei	(file+2)
	pei	(file)
	jsr	cmpcstr
	beq	foundit
	bpl	done
	add2	tbl,#10,tbl
	bra	loop
;
; Found the command handler address. Since we don't have a "jsl [tbl],y"
; instruction, after pushing parameters on the stack we will push the
; return address, then the command's address - 1 (2 bytes only, since it's
; in the current bank).  An rts will take us to the command handler, and a
; rtl at the end of the handler will bring us back.
;
foundit	anop
	pei	(argv+2)	Push parameters (3 words)
	pei	(argv)
	pei	(argc)
	phk		Push return address (high byte)
	per	return-1	Push return address (low bytes)
	ldy	#4	Get address of builtin handler
	lda	[tbl],y	 (16 bytes; assume in this bank)
	dec	a	  subtract 1 so rts will take us there
	pha
	rts		Go to builtin handler routine.
;
; Return from handler to this  location
;
return	sta	val	Save return status.

;
; Free the argv array
;
	ph4	#0	(no path)
	pei	(argc)
	pei	(argv+2)
	pei	(argv)
	jsl	argfree

done	ldy	val	Y-reg = return value.

	lda	space
	sta	end-3
	lda	space+1
	sta	end-2
	pld
	tsc
	clc
	adc	#end-4
	tcs

	tya		Accumulator = return value.

	rtl

	END

**************************************************************************
*
* Is it a built-in?
*
* Return value is: -1 if not a built-in, 0 if forked built-in,
*	    1 if non-forked built-in.
*
**************************************************************************

IsBuiltin	START

	using	BuiltinData

tbl	equ	0
space	equ	tbl+4

	subroutine (4:name),space

	ld4	builtintbl,tbl
builtinloop	ldy	#2
	lda	[tbl]
	ora	[tbl],y
	beq	nofile
	lda	[tbl],y
	pha
	lda	[tbl]
	pha
	pei	(name+2)
	pei	(name)
	jsr	cmpcstr
	beq	foundit
	bpl	nofile
	add2	tbl,#10,tbl
	bra	builtinloop
foundit	ldy	#8	Get the fork/nofork flag
	lda	[tbl],y	 and use it as return value.
	bra	foundbuiltin

nofile	lda	#-1	Set not-found return value.
foundbuiltin	sta	tbl

	return 2:tbl

	END

**************************************************************************
*
* Builtin data
*
**************************************************************************

BuiltinData	DATA
;
; First address is a pointer to the name, the second is a pointer to the
; command. Third value is fork flag (0 to fork, 1 for no fork).
; TABLE MUST BE SORTED BY COMMAND NAME.
;
builtintbl	dc	a4'aliasname,alias',i2'0'
	dc	a4'bgname,bg',i2'1'
	dc	a4'bindkeyname,bindkey',i2'0'
	dc	a4'cdname,cd',i2'1'
	dc	a4'chdirname,chdir',i2'1'
	dc	a4'clearname,clear',i2'1'		Changed to unforked
	dc	a4'cmdname,cmdbi',i2'0'
	dc	a4'dirsname,dirs',i2'0'
	dc	a4'echoname,echo',i2'0'
	dc	a4'editname,edit',i2'1'
	dc	a4'exitname,exit',i2'1'
	dc	a4'exportname,export',i2'1'
	dc	a4'fgname,fg',i2'1'
	dc	a4'hashname,hashbi',i2'0'
	dc	a4'hname,PrintHistory',i2'0'
	dc	a4'jobsname,jobs',i2'1'
	dc	a4'killname,kill',i2'1'
	dc	a4'popdname,popd',i2'1'
	dc	a4'pfxname,prefix',i2'1'
	dc	a4'psname,psbi',i2'0'
	dc	a4'pushdname,pushd',i2'1'
	dc	a4'pwdname,pwd',i2'1'
	dc	a4'rehashname,rehash',i2'1'
	dc	a4'setname,set',i2'0'
	dc	a4'setbugname,setdebug',i2'0'
	dc	a4'setenvname,setenv',i2'0'
	dc	a4'sourcename,source',i2'1' 	Changed to unforked
	dc	a4'stopname,stop',i2'1'
	dc	a4'tsetname,tset',i2'1'
	dc	a4'unaliasname,unalias',i2'1'
	dc	a4'unhashname,unhash',i2'1'
	dc	a4'unsetname,unset',i2'1'
	dc	a4'whichname,which',i2'0'
	dc	i4'0,0'

aliasname	dc	c'alias',h'00'
bgname	dc	c'bg',h'00'
bindkeyname	dc	c'bindkey',h'00'
chdirname	dc	c'chdir',h'00'
cdname	dc	c'cd',h'00'
clearname	dc	c'clear',h'00'
cmdname	dc	c'commands',h'00'
dirsname	dc	c'dirs',h'00'
echoname	dc	c'echo',h'00'
editname	dc	c'edit',h'00'
exitname	dc	c'exit',h'00'
exportname	dc	c'export',h'00'
fgname	dc	c'fg',h'00'
hashname	dc	c'hash',h'00'
hname	dc	c'history',h'00'
jobsname	dc	c'jobs',h'00'
killname	dc	c'kill',h'00'
pfxname	dc	c'prefix',h'00'
popdname	dc	c'popd',h'00'
psname	dc	c'ps',h'00'
pushdname	dc	c'pushd',h'00'
pwdname	dc	c'pwd',h'00'
rehashname	dc	c'rehash',h'00'
setbugname	dc	c'setdebug',h'00'
setname	dc	c'set',h'00'
setenvname	dc	c'setenv',h'00'
sourcename	dc	c'source',h'00'
stopname	dc	c'stop',h'00'
tsetname	dc	c'tset',h'00'
unaliasname	dc	c'unalias',h'00'
unhashname	dc	c'unhash',h'00'
unsetname	dc	c'unset',h'00'
whichname	dc	c'which',h'00'

	END

**************************************************************************
*
* CD: builtin command
* syntax: cd [pathname]
*
* Changes the current prefix to pathname. If no pathname then set to $HOME
*
**************************************************************************

cd	START
chdir	ENTRY

dpg	equ	1	Direct page pointer.
buf	equ	dpg+4	Buffer address to be freed.
status	equ	buf+4	Status returned from command.
space	equ	status+2
argc	equ	space+3
argv	equ	argc+2
end	equ	argv+4

;	 subroutine (4:argv,2:argc),space

	tsc
	sec
	sbc	#space-1
	tcs
	phd
	tcd

	lock	cdmutex

	stz	buf	Clear the pointer to
	stz	buf+2	 allocated buffer.

	stz	status	Assume good status.

	lda	argc	Number of parameters
	dec	a	 determines type of cd...
	beq	cdhome	  either to $HOME
	dec	a	   or to the directory
	jeq	paramcd	    on the command line.

;
; Illegal parameters: print usage string
;
showusage	inc	status	Return status = 1.
	lda	[argv]
	tax
	ldy	#2
	lda	[argv],y
	stx	argv
	sta	argv+2
	lda	[argv],y	
	and	#$FF
	beq	cdusage
	ldx	#^Usage2	Print chdir usage
	lda	#Usage2
	jsr	errputs
	jmp	exit
cdusage	ldx	#^Usage	Print cd usage
	lda	#Usage
	jsr	errputs
	jmp	exit

;
; Set prefix to $home
;
cdhome	anop
	ph4	#home	Get value of $HOME
	jsl	getenv
	sta	buf	If GS/OS result buffer
	stx	buf+2	 wasn't allocated,
	ora	buf+2
	jeq	exit	   there's no more to do.

	clc		Calculate address
	lda	buf	 of GS/OS input string
	adc	#2	  (2 bytes from start of
	sta	PRecPath	   result buffer).
	sta	GRecPath
	lda	buf+2
	adc	#0
	sta	PRecPath+2
	sta	GRecPath+2

	bra	getinfo

;
; Set prefix to path specified on command line
;
paramcd	anop

	ldy	#4
	lda	[argv],y
	sta	dpg
	iny2
	lda	[argv],y
	sta	dpg+2 

	lda	[dpg]
	and	#$FF
	if2	@a,ne,#'-',setprefix
	jmp	showusage

setprefix	pei	(dpg+2)
	pei	(dpg)
	jsr	c2gsstr
	sta	PRecPath
	sta	GRecPath
	sta	buf
	stx	PRecPath+2
	stx	GRecPath+2
	stx	buf+2

;
; Get file information to determine whether target is a valid directory
;
getinfo	GetFileInfo GRec
	bcc	ok
ohshit	sta	ErrError
	ErrorGS Err
	inc	status	Return status = 1.
	bra	done

ok	if2	GRecFT,eq,#$F,ok2
	ldx	dpg+2
	lda	dpg
	jsr	errputs
	ldx	#^direrr
	lda	#direrr
	jsr	errputs
	inc	status	Return status = 1.
	bra	done

;
; Everything looks OK. Set prefix 0 to the indicated value
;
ok2	SetPrefix PRec
	bcs	ohshit

;
; Deallocate buffer (if necessary), unlock mutex, cleanup stack, and leave
;
done	ph4	buf
	jsl	nullfree

exit	unlock cdmutex

	ldy	status	Put return status in Y-reg

	lda	space
	sta	end-3
	lda	space+1
	sta	end-2
	pld
	tsc
	clc
	adc	#end-4
	tcs

	tya		Put return status in Accumulator.

	rtl

cdmutex	key		Mutual exclusion key

; Parameter block for GS/OS SetPrefix call
PRec	dc	i'2'	pCount
	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)

home	gsstr	'home'	Env variable name

; Parameter block for shell ErrorGS call (p 393 in ORCA/M manual)
Err	dc	i2'1'	pCount
ErrError	ds	2	Error number

Usage	dc	c'Usage: cd [pathname]',h'0d00'
Usage2	dc	c'Usage: chdir [pathname]',h'0d00'
dirErr	dc	c': Not a directory',h'0d00'

	END

**************************************************************************
*
* CLEAR: builtin command
* syntax: clear
*
* clears the screen
*
**************************************************************************

clear	START

space	equ	1
argc	equ	space+3
argv	equ	argc+2
end	equ	argv+4

;	 subroutine (4:argv,2:argc),space

	tsc
	phd
	tcd

	lda	argc
	dec	a
	beq	clearit

	ldx	#^Usage
	lda	#Usage
	jsr	errputs
	ldy	#1	Return status = 1.
	bra	exit

clearit	jsr	clearscrn
	jsr	flush
	ldy	#0	Return status = 0.

exit	lda	space
	sta	end-3
	lda	space+1
	sta	end-2
	pld
	tsc
	clc
	adc	#end-4
	tcs

	tya

	rtl	  

Usage	dc	c'Usage: clear',h'0d00'

	END

**************************************************************************
*
* ECHO: builtin command
* syntax: echo [-n] [text][...]
*
* Echo displays to stdout what is on the command (except -n).
*
* If '-n' specified then don't print newline.
*
**************************************************************************

echo	START

val	equ	1
nl	equ	val+2	flag: was -n option set?
ptr	equ	nl+2
status	equ	ptr+4
space	equ	status+2
argc	equ	space+3
argv	equ	argc+2
end	equ	argv+4

;	 subroutine (4:argv,2:argc),space

* Add space on stack for local variables

	tsc
	sec
	sbc	#space-1
	tcs
	phd
	tcd

	stz	status	Clear return status.
	stz	nl	Clear the -n flag.
	dec	argc	Decrement argument counter.
	jeq	done	Done if no more arguments.

	ldy	#4
	lda	[argv],y	Set ptr to
	sta	ptr	 point to the
	iny2		  text of the
	lda	[argv],y	   first
	sta	ptr+2	    argument.
	ldy	#1	
	lda	[ptr]	Get first
	and	#$FF	  character.
	if2	@a,ne,#'-',loop	If != '-', handle as regular param.


; First argument begins with "-"; only legal value is -n

	lda	[ptr],y	Get second
	and	#$FF	  character.
	if2	@a,eq,#'n',gotn	If != 'n', it's a bad one.

showusage	ldx	#^Usage	Incorrect parameter usage:
	lda	#Usage	 display the usage string.
	jsr	errputs
	inc	status	Return status = 1.
	jmp	exit

gotn	iny
	lda	[ptr],y	Get third
	and	#$FF	  character.
	bne	showusage	If != 0, it's a bad one.
	inc	nl	Set the -n flag.
	add2	argv,#4,argv	Bump argument pointer.
	dec	argc	Decrement argument counter.
	jeq	done	Done if no more arguments.

;
; Beginning of main processing loop of echo parameters.
;
loop	add2	argv,#4,argv	Bump argument pointer.
	ldy	#2
	lda	[argv],y	Set ptr to argv (next argument)
	sta	ptr+2
	lda	[argv]
	sta	ptr
putloop	lda	[ptr]	Get first
	and	#$FF	 character.
	cmp	#0	If 0,
	jeq	doneput	  done with this argument.
	cmp	#'\'	If != "\"
	jne	putit	  go save in print buffer.
	incad	ptr	Escape character found; point
	lda	[ptr]	 to the next
	and	#$FF	  character.
	beq	doneput	If 0, done with this argument.
	if2	@a,ne,#'b',esc02	Check for escape codes: "b"
	ldx	#1
	jsr	moveleft			moveleft
	bra	didit
esc02	if2	@a,ne,#'f',esc03		"f"
	jsr	clearscrn			clearscreen
	bra	didit
esc03	if2	@a,ne,#'n',esc04		"n"
	lda	#13			print newline
	bra	putit
esc04	if2	@a,ne,#'r',esc05		"r"
	lda	#13			print newline
	bra	putit
esc05	if2	@a,ne,#'t',esc06		"t"
	lda	#9			print tab
	bra	putit
esc06	if2	@a,ne,#'0',putit		"0"
	stz	val			decode numeric value
	ldy	#1
escloop	lda	[ptr],y
	and	#$FF
	beq	putval
	if2	@a,cc,#'0',putval
	if2	@a,cs,#'9'+1,putval
	sub2	@a,#'0',@a
	pha
	lda	val
	asl2	a
	adc	val
	asl	a
	adc	1,s
	sta	val
	pla
	incad	ptr
	bra	escloop

putval	lda	val	Get numeric escape code.

putit	jsr	putchar	Save character in accumulator.
didit	incad	ptr	Point to next char in arg
	jmp	putloop	 and go process it.

doneput	dec	argc	Decrement argument counter.
	beq	done	Done if no more arguments.
	bmi	done	 (or if there were no arguments!)
	lda	#' '	Add a blank
	jsr	putchar	 between arguments.
	jmp	loop	Get next argument.

done	lda	nl	If "-n" flag isn't set,
	bne	exit
	jsr	newline	  add a newline.

exit	jsr	flush	Print the buffer.
	ldy	status

* Clear parameters from stack and return from subroutine.

	lda	space
	sta	end-3
	lda	space+1
	sta	end-2
	pld
	tsc
	clc
	adc	#end-4
	tcs

	tya

	rtl	  

Usage	dc	c'Usage: echo [-n] [strings...]',h'0d00'

	END

**************************************************************************
*
* PWD: builtin command
* syntax: pwd
*
* print the working directory.
*
**************************************************************************

pwd	START

ptr	equ	1
space	equ	ptr+4
argc	equ	space+3
argv	equ	argc+2
end	equ	argv+4

;	 subroutine (4:argv,2:argc),space

	tsc
	sec
	sbc	#space-1
	tcs
	phd
	tcd

	dec	argc	If an argument was provided,
	beq	wait

	ldx	#^Usage	  print the usage string.
	lda	#Usage
	jsr	errputs
	ldy	#1	Return status = 1.
	bra	exit

wait	pea	0
	jsl	getpfxstr	Get value of prefix 0.
	sta	ptr
	stx	ptr+2

	ora	ptr+2	If NULL pointer returned,
	beq	done	 an error was reported.

	ldy	#2	If length of returned
	lda	[ptr],y	 GS/OS string is 0,
	beq	freebuf	  an error was reported.

	lda	ptr	X/A = address of
	clc		 text (four bytes
	adc	#4	  beyond start).
	bcc	doputs
	inx
doputs	jsr	puts	Print the c-string
	jsr	newline	 and add a newline.

freebuf	ph4	ptr	Free the buffer.
	jsl	nullfree

done	ldy	#0	Return status = 0.

exit	lda	space	Deallocate stack space
	sta	end-3	 and return to the caller.
	lda	space+1
	sta	end-2
	pld
	tsc
	clc
	adc	#end-4
	tcs

	tya		Return status.

	rtl	  

Usage	dc	c'Usage: pwd',h'0d00'

	END

**************************************************************************
*
* WHICH: builtin command
* syntax: which [command ...]
*
* displays the location of command
*
**************************************************************************

which	START

	using hashdata

ptr	equ	1
sptr	equ	ptr+4
file	equ	sptr+4
status	equ	file+4
space	equ	status+2
argc	equ	space+3
argv	equ	argc+2
end	equ	argv+4

;	 subroutine (4:argv,2:argc),space

	tsc
	sec
	sbc	#space-1
	tcs
	phd
	tcd

	stz	status	Clear return status.
;
; Display usage if no argument
;
	if2	argc,ge,#2,loop
	ldx	#^whicherr
	lda	#whicherr
	jsr	errputs
	inc	status	Return status = 1
	jmp  exit
;
; loop through each argument
;
loop	add2	argv,#4,argv
	dec	argc
	jeq	exit

	lda	[argv]
	sta	file
	ldy	#2
	lda	[argv],y
	sta	file+2

;
; see if it's an alias
;
	pei	(file+2)
	pei	(file)
	jsl	findalias
	sta	ptr
	stx	ptr+2
	ora	ptr+2
	beq	chkbuiltin
	ldx	#^aliasstr
	lda	#aliasstr
	jsr	puts
	ldx	ptr+2
	lda	ptr
	jsr	puts
	jmp	nextarg
;
; was it a built-in?
;
chkbuiltin	pei	(file+2)
	pei	(file)
	jsl	IsBuiltin	
	cmp	#-1
	beq	tryhash
foundbuiltin	ldx	#^builtstr
	lda	#builtstr
	jsr	puts
	jmp	nextarg
;
; See if it was hashed
;
tryhash	pei	(file+2)
	pei	(file)
	ph4	hash_table
	ph4	#hash_paths
	jsl	search
	cmp	#0
	bne	foundhash
	cpx	#0
	beq	thispfx
;
; It was hashed, so say so.
;
foundhash	sta	sptr
	stx	sptr+2
	jsr	puts
	pei	(sptr+2)	Free memory allocated
	pei	(sptr)	 by search to hold full path.
	jsl	nullfree
	jmp	nextarg
;
; It must be in the current prefix, so check it out.
;
thispfx	lock	whichmutex
;
; check for existence of file
;
	pei	(file+2)
	pei	(file)
	jsr	c2gsstr
	sta	GRecPath
	stx	GRecPath+2
	sta	ptr
	stx	ptr+2

	GetFileInfo GRec
	bcs	nofile
;
; we found the file, so display the cwd
;
showcwd	pei	(ptr+2)
	pei	(ptr)
	jsl	nullfree

	pea	0
	jsl	getpfxstr	Get value of prefix 0.
	sta	ptr
	stx	ptr+2

	ora	ptr+2	If NULL pointer returned,
	beq	donecwd	 an error was reported.

	ldy	#2	If length of returned
	lda	[ptr],y	 GS/OS string is 0,
	beq	freebuf	  an error was reported.

	lda	ptr	X/A = address of
	clc		 text (four bytes
	adc	#4	  beyond start).
	bcc	doputs
	inx
doputs	jsr	puts	Print the directory name.

	ldx	file+2	Print the file name.
	lda	file
	jsr	puts

freebuf	ph4	ptr	Free the buffer.
	jsl	nullfree
	stz	ptr
	stz	ptr+2
	bra	donecwd
;
; No such command
;
nofile	ldx	#^cantdoit
	lda	#cantdoit
	jsr	puts
donecwd	unlock whichmutex
	pei	(ptr+2)
	pei	(ptr)
	jsl	nullfree
;
; Go try the next file.
;
nextarg	jsr	newline
	jmp	loop

exit	ldy	status
	lda	space
	sta	end-3
	lda	space+1
	sta	end-2
	pld
	tsc
	clc
	adc	#end-4
	tcs

	tya

	rtl	  

whicherr	dc	c'Usage: which [file ...]',h'0d00'
builtstr	dc	c'Shell Built-in Command',h'00'
cantdoit	dc	c'Command Not Found',h'00'
aliasstr	dc	c'Aliased as ',h'00'

whichmutex	key

GRec	dc	i'4'
GRecPath	ds	4
	ds	2
GRecFileType	ds	2
GRecAuxType	ds	4

	END

**************************************************************************
*
* PREFIX: builtin command
* syntax: prefix [num [prefix]]
*
* sets prefix number num to prefix or print list of all prefixes
*
**************************************************************************

prefix	START

dir	equ	1
numstr	equ	dir+4
pfxnum	equ	numstr+4
status	equ	pfxnum+2
space	equ	status+2
argc	equ	space+3
argv	equ	argc+2
end	equ	argv+4

;	 subroutine (4:argv,2:argc),space

	tsc
	sec
	sbc	#space-1
	tcs
	phd
	tcd

	lock	prefixmutex

	stz	status	Clear return status.
	lda	argc	Get number of arguments.
	dec	a
	beq	showall	If no parameters, show all prefixes.
	dec	a
	jeq	showone	If one, show one.
	dec	a
	jeq	setprefix	If two, set a prefix.

	ldx	#^usage
	lda	#usage
	jsr	errputs
	jmp	badstat

; -------------------------------------------------------------------
;
; No parameters provided: show all the prefixes
;
showall	anop
	lda	#$FFFF
	sta	pfxnum	First prefix # will be 0.

	pha		Get the boot volume string.
	jsl	getpfxstr
	sta	dir
	stx	dir+2

	ora	dir+2	If NULL pointer returned,
	beq	bumppfx	 an error was reported.

	ldx	#^bootstr
	lda	#bootstr
	bra	printid	Jump into the loop

allloop	lda	pfxnum
	pha
	jsl	getpfxstr
	sta	dir
	stx	dir+2

	ora	dir+2	If NULL pointer returned,
	beq	bumppfx	 an error was reported.

	ldy	#2	Get length word.
	lda	[dir],y
	beq	nextall	If zero, do the next prefix.
	Int2Dec (pfxnum,#pfxstr,#2,#0)
	ldx	#^pfxstr
	lda	#pfxstr
printid	jsr	puts
	ldx	dir+2
	lda	dir	X/A = address of
	clc		 text (four bytes
	adc	#4	  beyond start).
	bcc	doputs2
	inx
doputs2	jsr	puts	Print the directory name
	jsr	newline

nextall	ph4	dir	Free the GS/OS result buffer
	jsl	nullfree	 allocated for pathname.
bumppfx	inc	pfxnum	Bump the prefix number.
	if2	pfxnum,cc,#32,allloop
	jmp	done

; -------------------------------------------------------------------
;
; One parameter provided: show a single prefix
;    
showone	ldy	#1*4+2	Put pointer to
	lda	[argv],y	 first command
	sta	numstr+2	  argument in
	pha		   numstr, and
	dey2		    also on stack
	lda	[argv],y	     as parameter.
	sta	numstr
	pha
	jsr	cstrlen	Get length of argument.
	tax

	Dec2Int (numstr,@x,#0),@a 	Convert to integer.
	cmp	#32	If prefix num >= 32,
	bcc	getpfx
	jsr	newline		just print blank line.
	jmp	done

getpfx	pha		Get that prefix value.
	jsl	getpfxstr
	sta	dir
	stx	dir+2

	ora	dir+2	If NULL pointer returned,
	jeq	done	 an error was reported.

	Int2Dec (pfxnum,#pfxstr,#2,#0)

	ldy	#2	Get length word.
	lda	[dir],y
	beq	donewline	If zero, just print newline.
	ldx	dir+2
	lda	dir	X/A = address of
	clc		 text (four bytes
	adc	#4	  beyond start).
	bcc	doputs3
	inx
doputs3	jsr	puts	Print the directory name
donewline	jsr	newline
	ph4	dir	Free the GS/OS result buffer
	jsl	nullfree	 allocated for pathname.
	jmp	done

; -------------------------------------------------------------------
;
; Two parameters provided: set a prefix
;
setprefix	ldy	#1*4+2	Put pointer to
	lda	[argv],y	 first command
	sta	numstr+2	  argument (prefix
	pha		   num) in numstr, and
	dey2		    also on stack
	lda	[argv],y	     as parameter.
	sta	numstr
	pha
	jsr	cstrlen	Get length of argument.
	tax

	Dec2Int (numstr,@x,#0),PRecNum	Convert to integer string.

	lda	PRecNum
	cmp	#32	If prefix num >= 32,
	bcs	done		nothing to do.

	ldy	#2*4+2	Put pointer to
	lda	[argv],y	 second command
	pha		  argument (value)
	dey2		   on stack as parameter.
	lda	[argv],y
	pha
	jsr	c2gsstr	Convert to GS string.

	sta	GRecPath	Store in GetFileInfo
	stx	GRecPath+2	 parameter block and
	sta	PRecPath	  SetPrefix p.b.
	stx	PRecPath+2

;
; Get file information to determine whether target is a valid directory
;
	GetFileInfo GRec
	bcc	ok
	sta	ErrError
	ErrorGS Err
	bra	done

ok	if2	GRecFT,eq,#$F,ok2	If filetype != $F,
	ldx	#^direrr		print error message
	lda	#direrr		 'Not a directory'
	jsr	errputs
badstat	inc	status	Return status = 1.
	bra	done

ok2	SetPrefix PRec	Set the prefix.
	bcc	finish	If error flag set,
	ldx	#^errorstr		print error message
	lda	#errorstr		 'could not set prefix,
	jsr	errputs		   pathname may not exist.'
	inc	status	Return status = 1.

; -------------------------------------------------------------------
;
; All done: cleanup and return
;
finish	ph4	PRecPath	Free the name string buffer.
	jsl	nullfree

;
; Exit through here if PRecPath wasn't used
;
done	unlock prefixmutex

	ldy	status

	lda	space
	sta	end-3
	lda	space+1
	sta	end-2
	pld
	tsc
	clc
	adc	#end-4
	tcs

	tya

	rtl	  

prefixmutex	key

errorstr	dc	c'prefix: could not set prefix, pathname may not exist.'
	dc	h'0d00'
usage	dc	c'Usage: prefix prefixnum prefixname',h'0d00'
bootstr	dc	c' *: ',h'00'
pfxstr	dc	c'00: ',h'00'
dirErr	dc	c'prefix: Not a directory',h'0d00'
		  
;
; 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 buffer for SetPrefix GS/OS call
;
PRec	dc	i'2'	pCount
PRecNum	dc	i'0'	prefix number
PRecPath	ds	4	pointer to GS/OS string with value

;
; Parameter block for shell ErrorGS call (p 393 in ORCA/M manual)
;
Err	dc	i2'1'	pCount
ErrError	ds	2	Error number

	END

**************************************************************************
*
* REHASH: builtin command
* syntax: rehash
*
* rehashes the path
*
**************************************************************************
*
* UNHASH: builtin command
* syntax: unhash
*
* turns off command hashing
*
**************************************************************************

rehash	START
unhash	ENTRY

status	equ	0
space	equ	status+2

	subroutine (4:argv,2:argc),space

	stz	status

	lda	argc
	dec	a
	beq	doit

	ldx	#^Usage
	lda	#Usage	
	jsr	errputs
	ldy	#2
	lda	[argv],y
	tax
	lda	[argv]
	jsr	errputs
	lda	#13
	jsr	errputchar
	inc	status	Return status = 1.
	bra	exit

doit	jsr	dispose_hash	;remove old table
	lda	[argv]
	tax
	ldy	#2
	lda	[argv],y
	sta	argv+2
	stx	argv
	lda	[argv]
	and	#$FF
	jsr	tolower
	if2	@a,eq,#'u',exit	;if 'rehash' do the hashing.
	jsl	hashpath	;hash the path

exit	return 2:status

Usage	dc	c'Usage: ',h'00'

	END


**************************************************************************
*
* EXIT: builtin command
* syntax: exit
*
* exit gsh
*
**************************************************************************

exit	START

	using	global

space	equ	0

	subroutine (4:argv,2:argc),space

	inc	exit_requested

	return 2:#0

	END

**************************************************************************
*
* SETDEBUG: builtin command
* syntax: setdebug (val | [+|-]flag)
*
* sets debugging in kernel
*
**************************************************************************

setdebug	START

arg	equ	0
newdebug	equ	arg+4
mode	equ	newdebug+2
status	equ	mode+2
space	equ	status+2

	subroutine (4:argv,2:argc),space

	stz	status

	lda	argc
	dec	a
	bne	ok
showusage	ldx	#^usage
	lda	#usage
	jsr	errputs
	inc	status	Return status = 1.
	jmp	return

ok	stz	mode
	mv2	globaldebug,newdebug
	dec	argc
	add2	argv,#4,argv
loop	lda	[argv]
	sta	arg
	ldy	#2
	lda	[argv],y
	sta	arg+2
	lda	[arg]
	and	#$FF
	if2	@a,eq,#'-',turnoff
	if2	@a,eq,#'+',turnon
	ldx	mode
	bne	showusage
	ldx	argc
	dex	
	bne	showusage
	if2	@a,cc,#'0',showusage
	if2	@a,cs,#'9'+1,showusage
	pei	(arg+2)
	pei	(arg)
	jsr	cstrlen
	tax
	Dec2Int (arg,@x,#0),@a
	sta	newdebug
	jmp	done

turnoff	jsr	findflag
	eor	#$FFFF
	and	newdebug
	bra	turnnext
turnon	jsr	findflag
	ora	newdebug
turnnext	sta	newdebug
	ld2	1,mode
	add2	argv,#4,argv
	dec	argc
	beq	done
	jmp	loop

done	setdebug newdebug
	mv2	newdebug,globaldebug
return	return 2:status

findflag	incad	arg
	ldy	#0
findloop	phy
	lda	nametbl,y
	ora	nametbl+2,y
	beq	nofind
	lda	nametbl+2,y
	pha
	lda	nametbl,y
	pha
	pei	(arg+2)
	pei	(arg)
	jsr	cmpcstr
	beq	foundit
	pla
	add2	@a,#4,@y
	bra	findloop

foundit	pla
	lsr	a
	tax
	lda	bittbl,x
	rts

nofind	pla
	ldx	arg+2
	lda	arg
	jsr	errputs
	ldx	#^errstr
	lda	#errstr
	jsr	errputs
	lda	#-1
	pla		;rts address
	jmp	showusage

usage	dc	c'Usage: setdebug (value | [+|-]flag ... )',h'0d0d'
	dc	c'Flags: gsostrace  - Trace GS/OS calls',h'0d'
	dc	c'       gsosblocks - Trace GS/OS parameter blocks',h'0d'
	dc	c'       gsoserrors - Trace GS/OS errors',h'0d'
	dc	c'       pathtrace  - Trace GS/OS pathnames',h'0d'
	dc	c'       sigtrace   - Trace signals',h'0d'
	dc	c'       systrace   - Trace system calls',h'0d'
* >> Next line is temporary
	dc	c'       breakpoint - Coded brk instructions',h'0d'
	dc	h'00'

errstr	dc	c': Unknown flag',h'0d0d00'

nametbl	dc	a4'str01,str02,str03,str04,str05,str06,str07,0'
str01	dc	c'gsostrace',h'00'
str02	dc	c'pathtrace',h'00'
str03	dc	c'gsoserrors',h'00'
str04	dc	c'sigtrace',h'00'
str05	dc	c'systrace',h'00'
str06	dc	c'gsosblocks',h'00'
* >> Next line is temporary; Also: remove str07 in nametbl
str07	dc	c'breakpoint',h'00'

bittbl	dc	i2'%000001'
	dc	i2'%000010'
	dc	i2'%000100'
	dc	i2'%001000'
	dc	i2'%010000'
	dc	i2'%100000'
* >> Next line is temporary
	dc	i2'%10000000'

* >> Next line is temporary
check4debug	ENTRY

globaldebug	dc	i2'0'

	END

**************************************************************************
*
* PS: builtin command
* syntax: ps
*
* display process status
*
**************************************************************************

psbi	START

	using	pdata

myuid	equ	0
t	equ	myuid+2
ps2	equ	t+4
pr2	equ	ps2+4
pr	equ	pr2+4
ps	equ	pr+4
status	equ	ps+4
space	equ	status+2

	subroutine (4:argv,2:argc),space

	stz	status

	lda	argc
	dec	a
	beq	ok
showusage	ldx	#^usage
	lda	#usage
	jsr	errputs
	inc	status	Return status = 1.
	jmp	return

ok	getuid
	sta	myuid
	kvm_open             
	sta	ps
	stx	ps+2
	ora2	@a,ps+2,@a
	bne	ok2
	ldx	#^kvmerrstr
	lda	#kvmerrstr
	jsr	errputs
	inc	status	Return status = 1.
	jmp	done

ok2	ldx	#^header
	lda	#header
	jsr	puts

loop	kvmnextproc ps
	sta	pr
	stx	pr+2
	ora2	@a,pr+2,@a
	jeq	done

	ldy	#94	;ps->p_uid
	lda	[pr],y
	cmp	myuid
	jne	skip

	ldy	#2
	lda	[ps],y	;ps->pid
	Int2Dec (@a,#pidstr,#4,#0)
	ldx	#^pidstr
	lda	#pidstr
	jsr	puts

	ldy	#2	
	lda	[pr],y	;pr->processState
	cmp	#9
	bcc	okstate
	lda	#9
okstate	asl	a
	asl	a
	asl	a
	tax
	ldy	#8
putstate	lda	statetbl,x
	phx
	phy
	jsr	putchar
	ply
	plx
	inx
	dey
	bne	putstate

	ldy	#6
	lda	[pr],y	;pr->ttyID
	beq	ttnul
	cmp	#3
	bne	ttnum
	lda	#'c'
	jsr	putchar
	lda	#'o'
	bra	showuser
ttnul	lda	#'n'
	jsr	putchar
	lda	#'u'
	bra	showuser
ttnum	ldy	#0
ttnumlup	cmp	#10
	bcc	ttnum0
	sec
	sbc	#10
	iny
	bra	ttnumlup
ttnum0	pha
	tya
	clc
	adc	#'0'
	jsr	putchar
	pla
	clc
	adc	#'0'
showuser	jsr	putchar

	ldy	#4
	lda	[pr],y	;pr->userID
	Int2Hex (@a,#userstr+1,#4)
	ldx	#^userstr
	lda	#userstr
	jsr	puts

	ldy	#94
	lda	[pr],y	;pr->puid
	Int2Hex (@a,#puidstr,#4)
	ldx	#^puidstr
	lda	#puidstr
	jsr	puts

	ldy	#50            
	lda	[pr],y	;pr->ticks
	tax
	iny2
	lda	[pr],y
	LongDivide (@ax,#60),(@ax,@yy)
	LongDivide (@xa,#60),(t,@ax)
	Long2Dec (@xa,#timestr+4,#2,#0)
	Long2Dec (t,#timestr,#3,#0)
	lda	timestr+4
	and	#$FF
	cmp	#' '
	bne	time0
	short	a
	lda	#'0'
	sta	timestr+4
	long	a
time0	ldx	#^timestr
	lda	#timestr
	jsr	puts

	ldy	#34
	lda	[pr],y	;pr->args
	tax
	iny2
	lda	[pr],y
	jne	goodcmd
	cpx	#0
	jne	goodcmd

	lda	pjoblist	;check for name in job list
	ldx	pjoblist+2

jobloop	sta	pr2
	stx	pr2+2

	ora	pr2+2
	beq	childof
	ldy	#p_pid
	lda	[pr2],y
	ldy	#2
	cmp	[ps],y	;ps->pid
	bne	loop2

	ldy	#p_command+2
	lda	[pr2],y
	tax
	ldy	#p_command
	lda	[pr2],y
	jsr	puts
	bra	next

loop2	ldy	#p_next+2
	lda	[pr2],y
	tax
	ldy	#p_next
	lda	[pr2],y
	bra	jobloop

childof	ldx	#^forkstr
	lda	#forkstr
	jsr	puts
	kvm_open
	sta	ps2
	stx	ps2+2
	lda	[pr]
	tay
	kvmgetproc (ps2,@y)
	sta	pr2
	stx	pr2+2
	kvm_close ps2
	ldy	#34
	lda	[pr2],y	;pr2->args
	tax
	iny2
	lda	[pr2],y
	bne	goodcmd
	cpx	#0
	bne	goodcmd

	ldx	#^forkstr2
	lda	#forkstr2
	jsr	puts
	bra	next              

goodcmd	tay
	txa
	tyx
	clc
	adc	#8
	jsr	puts

next	jsr	newline
skip	jmp	loop

done	kvm_close ps

return	return 2:status

usage	dc	c'Usage: ps',h'0d00'
kvmerrstr	dc	c'ps: error in kvm_open()',h'0d00'
header	dc	c'  ID  STATE   TT MMID  UID   TIME COMMAND',h'0d00'
pidstr	dc	c'0000  ',h'00'
userstr	dc	c' 0000 ',h'00'
puidstr	dc	c'0000 ',h'00'
timestr	dc	c'000:00 ',h'00'
forkstr	dc	c'forked child of ',h'00'
forkstr2	dc	c'an unknown process',h'00'

test1	dc	c'getuid = $'
test1a	dc	c'0000',h'0d00'

statetbl	dc	c'defunct '
	dc	c'running '
	dc	c'ready   '
	dc	c'blocked '
	dc	c'ready   '
	dc	c'suspend '
	dc	c'waiting '
	dc	c'waiting '
	dc	c'paused  '
	dc	c'unknown '
		           
	END

**************************************************************************
*
* HASH: builtin command
* syntax: hash
*
* display hashed commands
*
**************************************************************************

hashbi	START

	using	hashdata

sv	equ	0
q	equ	sv+4
p	equ	q+4
status	equ	p+4
space	equ	status+2

	subroutine (4:argv,2:argc),space

	stz	status

	lda	argc
	dec	a
	beq	dohash

	ldx	#^Usage
	lda	#Usage
	jsr	errputs
	inc	status	Return status = 1.
	bra	exit

dohash	ph2	t_size	Get size of hash table.
	jsl	sv_alloc	Allocate a string vector array.
	sta	sv
	stx	sv+2

	lda	hash_table	If no hash table
	ora	hash_table+2	 has been allocated,
	beq	exit	  exit.

	mv4	hash_table,p	Move address to dir pg variable.
	lda	hash_numexe	Get the number of executable files.
	beq	doneadd	Done if 0.
; 
; loop through every hashed file and add it the string vector
;
	ldy	#0	Y is index into the next entry.
	ldx	t_size	X is the number of entries left.
	beq	doneadd
addloop	lda	[p],y	Get next hash table entry.
	sta	q	
	iny
	iny
	lda	[p],y
	sta	q+2
	iny
	iny
	ora	q	If this entry isn't used,
	beq	skip	 skip to the next one.

	phy		Hold the Y and X regs on stack.
	phx	
	pei	(sv+2)	Insert string in table entry
	pei	(sv)	 into the string vector.
	clc
	lda	q
	adc	#2	(Note: tn_name in hash.asm == 2)
	tax
	lda	q+2
	adc	#0
	pha
	phx
	pea	1	(allocflag: 1 = allocate memory)
	jsl	sv_add
	plx		Restore X and Y regs from stack.
	ply
skip	dex
	bne	addloop
;
; Files have all been added to the string vector
;
doneadd	anop

	pei	(sv+2)
	pei	(sv)
	jsl	sv_sort	Sort the string vector.

	pei	(sv+2)
	pei	(sv)
	jsl	sv_colprint	Print the string vector in columns.

	pei	(sv+2)
	pei	(sv)
	jsl	sv_dispose	Dispose of the string vector memory.

exit	return 2:status

usage	dc	c'Usage: hash',h'0d00'

	END

**************************************************************************
*
* SOURCE: builtin command
* syntax: source file [arguments...]
*
* executes a shell file w/o pushing environment
*
**************************************************************************

source	START

retval	equ	0
space	equ	retval+2

	subroutine (4:argv,2:argc),space

	dec	argc	If no filename was provided,
	bne	ok

	ldx	#^usage	  Print usage string.
	lda	#usage
	jsr	errputs
	lda	#1	  Return error status.
	sta	retval
	bra	exit

ok	stz	retval

	add2	argv,#4,argv

*   ShellExec	subroutine (4:path,2:argc,4:argv,2:jobflag)

	ldy	#2	path is filename argument
	lda	[argv],y
	pha
	lda	[argv]
	pha
	pei	(argc)	reuse argc
	pei	(argv+2)	reuse argv
	pei	(argv)
	pea	0	jobflag = 0
	jsl	ShellExec
	sta	retval

exit	return 2:retval

usage	dc	c'usage: source file [arguments...]',h'0d00'

	END
	   
**************************************************************************
*
* COMMANDS: builtin command
* syntax: commands
*
* display builtin commands
*
**************************************************************************

cmdbi	START

	using	BuiltinData

sv	equ	0
status	equ	sv+4
space	equ	status+2

	subroutine (4:argv,2:argc),space

	stz	status

	lda	argc
	dec	a
	beq	docmds

	ldx	#^Usage
	lda	#Usage
	jsr	errputs
	inc	status	Return status = 1.
	bra	exit

docmds	ph2	#50
	jsl	sv_alloc

	sta	sv
	stx	sv+2

	ldx	#0
; 
; loop through every hashed file and add it the string vector
;
addloop	lda	builtintbl,x
	ora	builtintbl+2,x
	beq	doneadd
	phx	
	pei	(sv+2)
	pei	(sv)
	lda	builtintbl+2,x
	pha
	lda	builtintbl,x
	pha
	pea	1
	jsl	sv_add
	pla
	clc
	adc	#10
	tax
	bra	addloop
doneadd	anop

	pei	(sv+2)
	pei	(sv)
	jsl	sv_sort
	pei	(sv+2)
	pei	(sv)
	jsl	sv_colprint			

	pei	(sv+2)
	pei	(sv)
	jsl	sv_dispose

exit	return 2:status

usage	dc	c'Usage: commands',h'0d00'

	END
