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

1857 lines
28 KiB
NASM

**************************************************************************
*
* The GNO Shell Project
*
* Developed by:
* Jawaid Bazyar
* Tim Meekins
*
* $Id: jobs.asm,v 1.9 1998/12/31 18:29:13 tribby Exp $
*
**************************************************************************
*
* JOBS.ASM
* By Tim Meekins
* Modified by Dave Tribby for GNO 2.0.6
*
* Job control handling routines
*
* Note: text set up for tabs at col 16, 22, 41, 49, 57, 65
* | | | | | |
* ^ ^ ^ ^ ^ ^
**************************************************************************
*
* Interfaces defined in this file:
*
* pwait no parameters wait for forground
*
* jobkiller no parameters kill all jobs
*
* palloc subroutine (2:pid,2:bg,4:cmd) alloc/fill proc struc
*
* pallocpipe subroutine (2:pid,2:bg,4:cmd) palloc+append to pipe
*
* pchild subroutine (2:code,2:signum) handle SIGCHLD
*
* removejentry subroutine (2:pid) Remove a job entry
* Return: A-reg = 1 if found, 0 if not found
*
* mkjobcur jsr with 1 address parameter
*
* jobs subroutine (4:argv,2:argc) built-in command
*
* kill subroutine (4:argv,2:argc) built-in command
*
* fg subroutine (4:argv,2:argc) built-in command
*
* bg subroutine (4:argv,2:argc) built-in command
*
* stop subroutine (4:argv,2:argc) built-in command
*
* pprint subroutine (4:pp,2:idflag,2:signum) print job entry
*
* parsepid subroutine (4:str) parse "%" pid
* return 2:pid
*
**************************************************************************
mcopy /obj/gno/bin/gsh/jobs.mac
dummyjobs start ; ends up in .root
end
setcom 60
WSTOPPED gequ $7F
SIGINT gequ 2
SIGKILL gequ 9
SIGTERM gequ 15
SIGSTOP gequ 17
SIGTSTP gequ 18
SIGCONT gequ 19
SIGCHLD gequ 20
SIGTTIN gequ 21
SIGTTOU gequ 22
;
; process structure used in job control
;
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
;
; p_flags values
;
PRUNNING gequ %0000000000000001 ;running
PSTOPPED gequ %0000000000000010 ;stopped
PNEXITED gequ %0000000000000100 ;normally exited
PAEXITED gequ %0000000000001000 ;abnormally exited
PSIGNALED gequ %0000000000010000 ;terminated by signal != SIGINT
PNOTIFY gequ %0000000000100000 ;notify async when done
PTIME gequ %0000000001000000 ;job times should be printed
PAWAITED gequ %0000000010000000 ;top level is waiting for it
PFOREGND gequ %0000000100000000 ;started in shells pgrp
PDUMPED gequ %0000001000000000 ;process dumped core
PDIAG gequ %0000010000000000 ;diagnostic output also piped out
PPOU gequ %0000100000000000 ;piped output
PREPORTED gequ %0001000000000000 ;status has been reported
PINTERRUPTED gequ %0010000000000000 ;job stopped via interrupt signal
PPTIME gequ %0100000000000000 ;time individual process
PNEEDNOTE gequ %1000000000000000 ;notify as soon as practicle
;====================================================================
;
; pwait - wait for foreground processes to finish up.
;
;====================================================================
pwait START
using pdata
mypid equ 1
oldsig equ mypid+2
p equ oldsig+4
space equ p+4
end equ space+3
; subroutine (0:dummy),space
tsc
sec
sbc #space-1
tcs
phd
tcd
getpid Get process ID.
sta mypid
;
; Start of loop that waits for child processes to complete
;
waitloop anop
; Block signals 15 (SIGTERM), 18 (SIGTSTP), and 20 (SIGCHLD):
; Bit 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
; Num 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1
; X/A 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ldx #%0000000000001010
lda #%0100000000000000
sigblock @xa
sta oldsig Save the previous signal mask.
stx oldsig+2
lock plistmutex Ensure list doesn't change.
lda pjoblist Start pointer at head of job list.
ldx pjoblist+2
loop sta p Save job entry pointer in p.
stx p+2
ora p+2 If pointer is 0,
beq done all done.
ldy #p_flags Get entry's job status flags.
lda [p],y
and #PFOREGND+PRUNNING
cmp #PFOREGND+PRUNNING If running in foreground,
bne getnext look at the next entry.
ldy #p_pid Get entry's process ID.
lda [p],y
cmp mypid If it's this processes' pid,
beq getnext look at the next entry.
; Check if the process is actually running..if it is not, report to the
; user that a stale process was detected and remove it from job control.
unlock plistmutex
sigsetmask oldsig Restore previous signal mask.
sigpause #0 Wait for a signal to arrive.
bra waitloop Start searching the entire list.
getnext ldy #p_next+2 Get pointer to next entry
lda [p],y
tax
ldy #p_next
lda [p],y
bra loop and stay in loop.
;
; Arrive here when p == 0
;
done unlock plistmutex
sigsetmask oldsig Restore previous signal mask.
pld Reset direct page and
tsc stack pointers.
clc
adc #end-4
tcs
rtl Return to caller.
END
;====================================================================
;
; jobkiller - kills all jobs
;
;====================================================================
jobkiller START
using pdata
p equ 0
space equ p+4
subroutine (0:dummy),space
loop lda pjoblist
beq done
ldx pjoblist+2
beq done
sta p
stx p+2
ldy #p_pid
lda [p],y
kill (@a,#SIGKILL)
bra loop
done return
END
;====================================================================
;
; palloc - allocate a process structure and fill it up
;
;====================================================================
palloc START
using pdata
using global
pp equ 1
space equ pp+4
cmd equ space+3
bg equ cmd+4
pid equ bg+2
end equ pid+2
; subroutine (2:pid,2:bg,4:cmd),space
tsc
sec
sbc #space-1
tcs
phd
tcd
lock plistmutex Ensure list doesn't change.
ldx #%0000000000001000 Block SIGCHILD signals.
lda #%0000000000000000
sigblock @xa
phx
pha
ph4 #p_space
~NEW
sta pp
stx pp+2
ldy #p_pid ;set pid
lda pid
sta [pp],y
ldy #p_jobid
sta [pp],y
lda bg ;set running flags
bne bg00
lda #PRUNNING+PFOREGND
bra bg01
bg00 lda #PRUNNING
bg01 ldy #p_flags
sta [pp],y
ldy #p_next
lda pjoblist
sta [pp],y
ldy #p_next+2
lda pjoblist+2
sta [pp],y
ldx pp+2
ldy pp
stx pjoblist+2
sty pjoblist
lda pcurrent
ora pcurrent+2
bne in01
stx pcurrent+2
sty pcurrent
bra in02
in01 lda pprevious
ora pprevious+2
bne in02
stx pprevious+2
sty pprevious
in02 anop
ldy #p_friends
lda #0
sta [pp],y
ldy #p_friends+2
sta [pp],y
inc pmaxindex ;set job number
ldy #p_index
lda pmaxindex
sta [pp],y
pei (cmd+2)
pei (cmd)
jsr cstrlen
inc a
pea 0
pha
~NEW
pei (cmd+2)
pei (cmd)
phx
pha
ldy #p_command ;set command
sta [pp],y
txa
ldy #p_command+2
sta [pp],y
jsr copycstr
unlock plistmutex
lda bg
beq noprint
pei (pp+2)
pei (pp)
pea 1
pea 0
jsl pprint
noprint anop
case on
jsl sigsetmask Restore signal mask.
case off
lda space
sta end-3
lda space+1
sta end-2
pld
tsc
clc
adc #end-4
tcs
rtl
END
;====================================================================
;
; pallocpipe - allocate a process structure and fill it up and append
; to previous command pipeline.
;
;====================================================================
pallocpipe START
using pdata
using global
p equ 1
pp equ p+4
space equ pp+4
cmd equ space+3
bg equ cmd+4
pid equ bg+2
end equ pid+2
; subroutine (2:pid,2:bg,4:cmd),space
tsc
sec
sbc #space-1
tcs
phd
tcd
lock plistmutex Ensure list doesn't change.
ldx #%0000000000001000
lda #%0000000000000000
sigblock @xa
phx
pha
ph4 #p_space
~NEW
sta pp
stx pp+2
ldy #p_pid ;set pid
lda pid
sta [pp],y
ldy #p_jobid
sta [pp],y
lda bg ;set running flags
bne bg00
lda #PRUNNING+PFOREGND
bra bg01
bg00 lda #PRUNNING
bg01 ldy #p_flags
sta [pp],y
ldy #p_next
lda #0
sta [pp],y
ldy #p_next+2
sta [pp],y
ldy #p_friends
sta [pp],y
ldy #p_friends+2
sta [pp],y
pei (cmd+2)
pei (cmd)
jsr cstrlen
inc a
pea 0
pha
~NEW
pei (cmd+2)
pei (cmd)
phx
pha
ldy #p_command ;set command
sta [pp],y
txa
ldy #p_command+2
sta [pp],y
jsr copycstr
;
; update the current pipeline to know about the last pipe.
;
lda pjoblist
ldx pjoblist+2
loop sta p
stx p+2
ldy #p_flags
lda [pp],y
sta [p],y
ldy #p_jobid
lda [pp],y
sta [p],y
ldy #p_friends
lda [p],y
ldy #p_friends+2
ora [p],y
beq addit
lda [p],y
tax
ldy #p_friends
lda [p],y
bra loop
addit ldy #p_friends
lda pp
sta [p],y
ldy #p_friends+2
lda pp+2
sta [p],y
unlock plistmutex
case on
jsl sigsetmask
case off
lda space
sta end-3
lda space+1
sta end-2
pld
tsc
clc
adc #end-4
tcs
rtl
END
;====================================================================
;
; handle a SIGCHLD signal
;
;====================================================================
pchild START
using pdata
using global
waitstatus equ 1
pid equ waitstatus+4 ;just in case
p equ pid+2
space equ p+4
signum equ space+3
code equ signum+2
end equ code+2
; subroutine (2:code,2:signum),space
tsc
sec
sbc #space-1
tcs
phd
tcd
phb
phk
plb
stz signum
;
; get status for the process that just died
;
ldx #0
clc
tdc
adc #waitstatus
wait @xa
sta pid
;
; Search job list for the process that has finished.
;
lda pjoblist
ldx pjoblist+2
lookloop sta p
stx p+2
ora p+2 If at end of job list,
jeq done the process was not found.
ldy #p_jobid
lda [p],y
cmp pid
beq lookfound
ldy #p_next+2
lda [p],y
tax
ldy #p_next
lda [p],y
bra lookloop
;
; See how wait was signaled.
;
lookfound anop
lda waitstatus
and #$FF
cmp #WSTOPPED
jne kill
lda waitstatus
xba
and #$FF ;<- signal number
sta signum
cmp #SIGTSTP
beq stop
cmp #SIGTERM
jeq zap
cmp #SIGINT
beq zap
cmp #SIGSTOP
beq stop
cmp #SIGTTIN
beq stop
cmp #SIGTTOU
bne zap
stop ldy #p_flags
lda [p],y
eor #PRUNNING
ora #PSTOPPED
sta [p],y
pei (p+2)
pei (p)
jsr mkjobcur
ldy #p_flags
lda [p],y
bit #PFOREGND
beq stop2
tctpgrp (gshtty,gshpid)
stop2 pei (p+2)
pei (p)
pea 0
pei (signum)
jsl pprint
bra done
kill ldy #p_flags
lda [p],y
bit #PFOREGND ;only set status variable if in
beq zap0 ; foreground
lda waitstatus
jsr setstatus
bra zap
zap0 inc signalled
zap ldy #p_pid Remove the job entry
lda [p],y by referring to its pid.
pha
jsl removejentry
ldy #p_flags
lda [p],y
bit #PFOREGND
beq done
tctpgrp (gshtty,gshpid)
done anop
plb
lda space
sta end-3
lda space+1
sta end-2
pld
tsc
clc
adc #end-4
tcs
rtl
;--------------------------------------------------------------------
;
; Set $status return variable
;
setstatus ENTRY
xba Isolate status
and #$FF byte.
cmp #10
bcs digits2or3 If < 10,
adc #'0' Convert to single digit
sta valstat_text and store in value string.
ldx #1 Set length of string to 1.
stx valstat
bra set_value
digits2or3 cmp #100 If parameter number
bcs digits3 >= 10 && < 99,
ldx #2 length = 2
bra setit otherwise
digits3 ldx #3 length = 3
;
; Store length (2 or 3) and convert number to text
;
setit stx valstat
Int2Dec (@a,#valstat_text,valstat,#0)
set_value anop
SetGS SetPB
rts
;
; Parameter block for shell SetGS calls (p 427 in ORCA/M manual)
;
SetPB anop
dc i2'3' pCount
dc i4'status' Name (pointer to GS/OS string)
dc i4'valstat' Value (pointer to GS/OS string)
dc i2'0' Export flag
status gsstr 'status' Name of environment variable
;
; Value of status: GS/OS string with one to three digits
;
valstat ds 2 Length word
valstat_text dc c'000' Text (up to three digits)
END
;====================================================================
;
; Remove an entry in the job list, based on process number
; Return with A-reg = 1 if found, 0 if not found
;
;====================================================================
removejentry START
using pdata
p equ 1
prev equ p+4
found equ prev+4
space equ found+2
pid equ space+3 process id
end equ pid+2
; subroutine (2:pid),space
tsc
sec
sbc #space-1
tcs
phd
tcd
stz found
stz prev
stz prev+2
lock plistmutex Ensure list doesn't change.
lda pjoblist
ldx pjoblist+2
loop sta p Get next entry in job list.
stx p+2
ora p+2 If null pointer,
jeq done all done.
ldy #p_pid Get job/pid number in entry.
lda [p],y
cmp pid If it's not the one we're looking for,
beq gotit
lda p Set prev to this entry.
sta prev
stx prev+2
ldy #p_next+2
lda [p],y Set X/A to next entry.
tax
ldy #p_next
lda [p],y
bra loop Check next entry.
;
; Entry found: adjust linked list pointers
;
gotit inc found found = TRUE
ora2 prev,prev+2,@a If prev != NULL,
beq gotit2
ldy #p_next prev->next = p->next
lda [p],y
sta [prev],y
ldy #p_next+2
lda [p],y
sta [prev],y
bra gotit3
gotit2 ldy #p_next else
lda [p],y pjoblist = p->next
sta pjoblist
ldy #p_next+2
lda [p],y
sta pjoblist+2
;
; free the node (may want to check currjob and prevjob pointers)
;
gotit3 anop
ldy #p_flags If PFOREGND status bit is set,
lda [p],y
and #PFOREGND
bne gotit3c
jsr newline Print the job entry
ldy #p_flags
lda #0
sta [p],y
pei (p+2)
pei (p)
pea 0
pea 0
jsl pprint
gotit3c anop
ldy #p_command+2 Free memory used to hold
lda [p],y command string.
pha
dey2
lda [p],y
pha
jsl nullfree
lda pcurrent If pcurrent != p
eor pcurrent+2
eor p
eor p+2
bne gotit3a
mv4 pprevious,pcurrent pcurrent = pprevious
stz pprevious pprevious = NULL
stz pprevious+2
lda prev If prev != pcurrent,
eor prev+2
eor pcurrent
eor pcurrent+2
beq gotit3a
mv4 prev,pprevious pprevious = prev
gotit3a lda pprevious If pprevious != p,
eor pprevious+2
eor p
eor p+2
bne gotit3b
stz pprevious pprevious == NULL
stz pprevious+2
gotit3b anop
gotit4 ldy #p_friends
lda [p],y
pha
ldy #p_friends+2
lda [p],y
pha
pei (p+2) Free memory used to hold entry.
pei (p)
jsl nullfree
pla p = p->p_friends
sta p+2
pla
sta p
ora p+2
beq gotit5 If p != NULL,
ldy #p_command+2 Free memory used to
lda [p],y hold text of command
pha
ldy #p_command
lda [p],y
pha
jsl nullfree
bra gotit4
gotit5 anop
;
; find maximum job number and set pmaxindex
;
stz prev
lda pjoblist
ldx pjoblist+2
fmaxloop sta p
stx p+2
ora p+2
beq gotmax
ldy #p_index
lda [p],y
cmp prev
bcc skipmax
sta prev
skipmax ldy #p_next+2
lda [p],y
tax
ldy #p_next
lda [p],y
bra fmaxloop
gotmax mv2 prev,pmaxindex
done anop
unlock plistmutex
ldy found
lda space+1
sta end-2
lda space
sta end-3
pld
tsc
clc
adc #end-4
tcs
tya Return value = found flag.
rtl
END
;====================================================================
;
; mkjobcur
;
;====================================================================
mkjobcur START
using pdata
space equ 1
p equ space+2
end equ p+4
; subroutine (4:p),space
lda p,s
eor p+2,s
eor pcurrent
eor pcurrent+2
beq done
mv4 pcurrent,pprevious
lda p,s
sta pcurrent
lda p+2,s
sta pcurrent+2
done lda space,s
sta end-2,s
tsc
clc
adc #end-3
tcs
rts
END
**************************************************************************
*
* JOBS: builtin command
* syntax: jobs
*
* displays jobs
*
**************************************************************************
jobs START
using pdata
pidflag equ 0
pp equ pidflag+2
count equ pp+4
status equ count+2
space equ status+2
subroutine (4:argv,2:argc),space
stz pidflag
stz status
lda argc If no argument,
dec a
beq startcmd start processing.
dec a If > 1 argument,
bne prusage print usage string.
;
; Argument provided. It had better be "-l\0"
;
ldy #4
lda [argv],y
sta pp
ldy #4+2
lda [argv],y
sta pp+2
lda [pp]
and #$FF
if2 @a,ne,#'-',prusage
ldy #1
lda [pp],y
if2 @a,eq,#'l',optset
;
; Error with command line
;
prusage ldx #^Usage Print usage string.
lda #Usage
jsr errputs
inc status Return status = 1.
jmp done
;
; "-l" option set
;
optset inc pidflag
;
; Start processing "jobs" command
;
startcmd ld2 1,count count = 1.
;
; Outer loop: scan the job list
;
loop lda pjoblist
ldx pjoblist+2
;
; Inner loop: find entry in job list that matches the count number.
;
loop2 sta pp
stx pp+2
ora pp+2 If at end of list,
beq next there was no entry with this count!
ldy #p_index If p_index field
lda [pp],y in this entry
cmp count matches count,
beq gotit go print the contents.
ldy #p_next+2 Otherwise,
lda [pp],y point to next entry in list.
tax
ldy #p_next
lda [pp],y
bra loop2
gotit pei (pp+2) Push address of entry,
pei (pp)
pei (pidflag) "long" flag,
pea 0 (signum = 0)
jsl pprint and print the entry.
next inc count Bump count.
lda count
cmp pmaxindex If more to be printed,
beq loop scan list for next entry.
bcc loop
done return 2:status
Usage dc c'Usage: jobs [-l]',h'0d00'
END
**************************************************************************
*
* KILL: builtin command
* syntax: kill [-sig] [pid ...]
*
* sends a [kill] signal to a process
*
**************************************************************************
kill START
ptr equ 1
arg equ ptr+4
signum equ arg+4
pid equ signum+2
status equ pid+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
stz status
lda argc
dec a
bne init
ldx #^Usage
lda #Usage
jsr errputs
inc status Return status = 1.
jmp done
init stz pid
ld2 SIGTERM,signum
dec argc
jeq dokill
add2 argv,#4,argv
ldy #2
lda [argv]
sta arg
lda [argv],y
sta arg+2
lda [arg]
and #$FF
cmp #'-'
jne getpid
incad arg
lda [arg]
and #$FF
cmp #'0'
bcc getname
cmp #'9'+1
bcs getname
pei (arg+2)
pei (arg)
jsr cstrlen
tax
Dec2Int (arg,@x,#0),signum
lda signum ;yeah, yeah, I know...
bmi ohshitnum
cmp #1
bcc ohshitnum
cmp #25
bcc next
cmp #30
beq next
cmp #31
beq next
ohshitnum ldx #^err1
lda #err1
jsr errputs
inc status Return status = 1.
jmp done
getname lda [arg]
cmp #'l'
beq lister
pei (arg+2)
pei (arg)
jsr lowercstr
ld2 1,signum
ld4 names,ptr
nameloop pei (arg+2)
pei (arg)
pei (ptr+2)
pei (ptr)
jsr cmpcstr
cmp #0
beq next
inc signum
add2 ptr,#8,ptr
lda signum
cmp #32
bcc nameloop
ldx #^err3
lda #err3
jsr errputs
inc status Return status = 1.
bra done
next dec argc
jeq dokill
add2 argv,#4,argv
ldy #2
lda [argv]
sta arg
lda [argv],y
sta arg+2
getpid pei (arg+2)
pei (arg)
jsl parsepid
sta pid
cmp #0
beq killnull
cmp #-1
bne dokill
ldx #^err2
lda #err2
jsr errputs
inc status Return status = 1.
bra done
lister ldx #^liststr
lda #liststr
jsr puts
bra done
killnull ldx #^err4
lda #err4
jsr errputs
inc status Return status = 1.
bra done
dokill kill (pid,signum)
cmp #0
beq done
ldx #^err2
lda #err2
jsr errputs
inc status Return status = 1.
done ldy status
lda space
sta end-3
lda space+1
sta end-2
pld
tsc
clc
adc #end-4
tcs
tya
rtl
Usage dc c'Usage: kill [-signum | -signame] [pid | %jobid] ...',h'0d00'
err1 dc c'kill: Invalid signal number.',h'0d00'
err2 dc c'kill: No such job or pid.',h'0d00'
err3 dc c'kill: Invalid signal name.',h'0d00'
err4 dc c'kill: Killing the kernel null process isn''t such a good idea.',h'0d00'
liststr dc c'sighup sigint sigquit sigill sigtrap sigabrt sigemt '
dc c'sigfpe sigkill sigbus sigsegv sigsys sigpipe sigalrm '
dc c'sigterm sigurg sigstop sigtstp sigcont sigchld sigttin '
dc c'sigttou sigio sigxcpu sigusr1 sigusr2',h'0d00'
names dc c'sighup',h'0000' ;1
dc c'sigint',h'0000' ;2
dc c'sigquit',h'00' ;3
dc c'sigill',h'0000' ;4
dc c'sigtrap',h'00' ;5
dc c'sigabrt',h'00' ;6
dc c'sigemt',h'0000' ;7
dc c'sigfpe',h'0000' ;8
dc c'sigkill',h'00' ;9
dc c'sigbus',h'0000' ;10
dc c'sigsegv',h'00' ;11
dc c'sigsys',h'0000' ;12
dc c'sigpipe',h'00' ;13
dc c'sigalrm',h'00' ;14
dc c'sigterm',h'00' ;15
dc c'sigurg',h'0000' ;16
dc c'sigstop',h'00' ;17
dc c'sigtstp',h'00' ;18
dc c'sigcont',h'00' ;19
dc c'sigchld',h'00' ;20
dc c'sigttin',h'00' ;21
dc c'sigttou',h'00' ;22
dc c'sigio',h'000000' ;23
dc c'sigxcpu',h'00' ;24
dc h'0000000000000000' ;25
dc h'0000000000000000' ;26
dc h'0000000000000000' ;27
dc h'0000000000000000' ;28
dc h'0000000000000000' ;29
dc c'sigusr1',h'00' ;30
dc c'sigusr2',h'00' ;31
END
**************************************************************************
*
* FG: builtin command
*
**************************************************************************
fg START
using pdata
using global
pid equ 0
p equ pid+2
status equ p+4
space equ status+2
subroutine (4:argv,2:argc),space
stz status
lda argc
dec a
bne getit
ora2 pjoblist,pjoblist+2,@a
jeq whoashit
mv4 pjoblist,p
bra dofg
getit dec a
beq getit2
ldx #^usage
lda #usage
jmp puterr
getit2 ldy #4+2
lda [argv],y
pha
ldy #4
lda [argv],y
pha
jsl parsepid
sta pid
cmp #-1
jeq nojob
mv4 pjoblist,p
loop ora2 p,p+2,@a
jeq nojob
ldy #p_jobid
lda [p],y
cmp pid
beq dofg
ldy #p_next
lda [p],y
tax
ldy #p_next+2
lda [p],y
sta p+2
stx p
bra loop
dofg ldy #p_flags
lda [p],y
and #PFOREGND+PRUNNING
cmp #PFOREGND+PRUNNING
bne dofg1
ldx #^err02
lda #^err01
bra puterr
dofg1 lda [p],y
ora #PRUNNING+PFOREGND
sta [p],y
ldy #p_jobid
lda [p],y
tax
tctpgrp (gshtty,@x)
ldy #p_flags
lda [p],y
bit #PSTOPPED
beq dofg2
eor #PSTOPPED
sta [p],y
pei (p+2)
pei (p)
pea 0
pea 0
jsl pprint
ldy #p_jobid
lda [p],y
_getpgrp @a
eor #$FFFF
inc a
kill (@a,#SIGCONT)
bra dofg3
dofg2 pei (p+2)
pei (p)
pea 0
pea 0
jsl pprint
dofg3 jsl pwait
bra done
whoashit ldx #^err01
lda #err01
bra puterr
nojob ldx #^err03
lda #err03
puterr jsr errputs
inc status Return status = 1.
done return 2:status
usage dc c'Usage: fg [%job | pid]',h'0d00'
err01 dc c'fg: No job to foreground.',h'0d00'
err02 dc c'fg: Gee, this job is already in the foreground.',h'0d00'
err03 dc c'fg: No such job.',h'0d00'
END
**************************************************************************
*
* BG: builtin command
*
**************************************************************************
bg START
using pdata
using global
pid equ 0
p equ pid+2
status equ p+4
space equ status+2
subroutine (4:argv,2:argc),space
stz status
lda argc
dec a
bne getit
ora2 pjoblist,pjoblist+2,@a
jeq whoashit
mv4 pjoblist,p
bra dofg
getit dec a
beq getit2
ldx #^usage
lda #usage
jmp puterr
getit2 ldy #4+2
lda [argv],y
pha
ldy #4
lda [argv],y
pha
jsl parsepid
sta pid
cmp #-1
jeq nojob
mv4 pjoblist,p
loop ora2 p,p+2,@a
jeq nojob
ldy #p_jobid
lda [p],y
cmp pid
beq dofg
ldy #p_next
lda [p],y
tax
ldy #p_next+2
lda [p],y
sta p+2
stx p
bra loop
dofg ldy #p_flags
lda [p],y
bit #PRUNNING
beq dofg1
ldx #^err02
lda #err02
bra puterr
dofg1 anop note: Y = #p_flags, A = [p],y
ora #PRUNNING
sta [p],y
bit #PFOREGND
beq dobg0
eor #PFOREGND
sta [p],y
dobg0 anop
bit #PSTOPPED
beq dofg2
eor #PSTOPPED
sta [p],y
tctpgrp (gshtty,gshpid)
pei (p+2)
pei (p)
pea 0
pea 0
jsl pprint
ldy #p_jobid
lda [p],y
_getpgrp @a
eor #$FFFF
inc a
kill (@a,#SIGCONT)
bra dofg3
dofg2 pei (p+2)
pei (p)
pea 0
pea 0
jsl pprint
dofg3 bra done
whoashit ldx #^err01
lda #err01
bra puterr
nojob ldx #^err03
lda #err03
puterr jsr errputs
inc status Return status = 1
done return 2:status
usage dc c'Usage: bg [%job | pid]',h'0d00'
err01 dc c'bg: No job to background.',h'0d00'
err02 dc c'bg: Gee, this job is already in the background.',h'0d00'
err03 dc c'bg: No such job.',h'0d00'
END
**************************************************************************
*
* STOP: builtin command
*
**************************************************************************
stop START
using pdata
using global
pid equ 0
p equ pid+2
status equ p+4
space equ status+4
subroutine (4:argv,2:argc),space
stz status
lda argc
dec a
bne getit
ora2 pjoblist,pjoblist+2,@a
jeq whoashit
mv4 pjoblist,p
bra dofg
getit dec a
beq getit2
ldx #^usage
lda #usage
jmp puterr
getit2 ldy #4+2
lda [argv],y
pha
ldy #4
lda [argv],y
pha
jsl parsepid
sta pid
cmp #-1
jeq nojob
mv4 pjoblist,p
loop ora2 p,p+2,@a
jeq nojob
ldy #p_jobid
lda [p],y
cmp pid
beq dofg
ldy #p_next
lda [p],y
tax
ldy #p_next+2
lda [p],y
sta p+2
stx p
bra loop
dofg ldy #p_flags
lda [p],y
and #PSTOPPED
beq dofg1
ldx #^err02
lda #err02
bra puterr
dofg1 tctpgrp (gshtty,gshpid)
ldy #p_jobid
lda [p],y
_getpgrp @a
eor #$FFFF
inc a
kill (@a,#SIGSTOP)
bra done
whoashit ldx #^err01
lda #err01
bra puterr
nojob ldx #^err03
lda #err03
puterr jsr errputs
inc status Return status = 1.
done return 2:status
usage dc c'Usage: stop [%job | pid]',h'0d00'
err01 dc c'stop: No job to stop.',h'0d00'
err02 dc c'stop: Gee, this job is already stopped.',h'0d00'
err03 dc c'stop: No such job.',h'0d00'
END
;====================================================================
;
; print job
;
;====================================================================
pprint START
using pdata
p equ 0
count equ p+4
space equ count+2
subroutine (4:pp,2:idflag,2:signum),space
stz count
;
; display job number
;
lda #'['
jsr putchar
ldy #p_index
lda [pp],y
cmp #10
bcs id10
adc #'0'
jsr putchar
lda #']'
jsr putchar
lda #' '
jsr putchar
bra ida
id10 Int2Dec (@a,#dec10,#2,#0)
ldx #^dec10
lda #dec10
jsr puts
ida lda #' '
jsr putchar
;
; display '+' or '-'
;
if2 pp,ne,pcurrent,pcur1
if2 pp+2,ne,pcurrent+2,pcur1
lda #'+'
bra pcur3
pcur1 if2 pp,ne,pprevious,pcur2
if2 pp+2,ne,pprevious+2,pcur2
lda #'-'
bra pcur3
pcur2 lda #' '
pcur3 jsr putchar
lda #' '
jsr putchar
;
; display process id
;
lda idflag
beq stat
ldy #p_jobid
lda [pp],y
Int2Dec (@a,#pidstr,#5,#0)
ldx #^pidstr
lda #pidstr
jsr puts
ld2 6,count
;
; status
;
stat ldy #p_flags
lda [pp],y
bit #PRUNNING
beq stat2
ldx #^statrun
lda #statrun
bra stat0
stat2 bit #PSTOPPED
beq stat3
ldx #^statstop
lda #statstop
bra stat0
stat3 ldx #^statohshit
lda #statohshit
stat0 jsr puts
;
; show signal
;
lda signum
beq sig0
if2 @a,ne,#SIGTTIN,sig2
ldx #^sigttinstr
lda #sigttinstr
bra sig1
sig2 if2 @a,ne,#SIGTTOU,sig3
ldx #^sigttoustr
lda #sigttoustr
bra sig1
sig3 ldx #^sigotherstr
lda #sigotherstr
sig1 phx
pha
jsr puts
jsr cstrlen
clc
adc count
sta count
sig0 anop
;
; display command
;
showcmd ldy #p_command
lda [pp],y
sta p
ldy #p_command+2
lda [pp],y
sta p+2
ldy #0
chloop lda [p],y
and #$FF
beq next
phy
jsr putchar
ply
iny
inc count
if2 count,cc,#60,chloop
showell ldx #^ellipsis
lda #ellipsis
jsr puts
bra cmd01
next ldy #p_friends
lda [pp],y
pha
ldy #p_friends+2
lda [pp],y
ora 1,s
beq donename
lda [pp],y
sta pp+2
plx
stx pp
if2 count,cs,#57,showell
clc
adc #3
sta count
ldx #^pipestr
lda #pipestr
jsr puts
bra showcmd
donename pla
ldy #p_flags
lda [pp],y
and #PFOREGND
bne cmd01
ldx #^ampstr
lda #ampstr
jsr puts
cmd01 anop
jsr newline
return
dec10 dc c'00]',h'00'
pidstr dc c'00000 ',h'00'
statrun dc c'Running ',h'00'
statstop dc c'Stopped ',h'00'
statohshit dc c'Done ',h'00'
ampstr dc c' &',h'00'
ellipsis dc c'...',h'00'
pipestr dc c' | ',h'00'
sigttinstr dc c'(tty input) ',h'00'
sigttoustr dc c'(tty output) ',h'00'
sigotherstr dc c'(signal) ',h'00'
END
;====================================================================
;
; parse process id (if starts with '%', then parse job num and
; concert to process id).
;
;====================================================================
parsepid START
using pdata
p equ 0
len equ p+4
pid equ len+2
space equ pid+2
subroutine (4:str),space
pei (str+2)
pei (str)
jsr cstrlen
sta len
lda [str]
and #$FF
cmp #'%'
beq dojob
cmp #'0'
jcc nojob
cmp #'9'+1
jcs nojob
Dec2Int (str,len,#0),pid
jmp done
dojob ldy #1
lda [str],y
and #$FF
beq docurjob
if2 @a,eq,#'+',docurjob
if2 @a,eq,#'%',docurjob
if2 @a,ne,#'-',dojobnum
lda pprevious
ldx pprevious+2
bra gotjob
docurjob lda pcurrent
ldx pcurrent+2
bra gotjob
dojobnum ldy len
dey
ldx str+2
lda str
incad @xa
Dec2Int (@xa,@y,#0),pid
lda pjoblist
ldx pjoblist+2
sta p
stx p+2
loop ora2 p,p+2,@a
beq nojob
ldy #p_index
lda [p],y
cmp pid
beq gotjob2
ldy #p_next
lda [p],y
tax
ldy #p_next+2
lda [p],y
sta p+2
stx p
bra loop
gotjob sta p
stx p+2
gotjob2 ora2 p,p+2,@a
beq nojob
ldy #p_jobid
lda [p],y
sta pid
bra done
nojob ld2 -1,pid
done return 2:pid
END
;====================================================================
;
; process data
;
;====================================================================
pdata DATA
plistmutex key Mutual exclusion for job list
pwaitsem dc i2'0'
pjoblist dc i4'0' ;job list
pcurrent dc i4'0' ;current job in table
pprevious dc i4'0' ;previous job in table
pmaxindex dc i2'0' ;current maximum job index
END