	.title	DISKSPACE - Get free space data
	.ident	/V4.01/
	.sbttl	Documentation
;+
;
; For every mounted FILES-11 disk on the system, provide the calling
; program with the logical name, number of free blocks, and maximum
; number of blocks on the disk.
;
; Calling sequence (from FORTRAN):
;
;	status = DISKSPACE (maxnum, number, bufflen, namelen, 
;				%ref(diskname), maxblks, freeblks)
;
; Inputs:
;	maxnum		- maximum number of disk units to be handled
;	bufflen		- length of character array elements to hold log name
;
; Outputs:	(arrays)
;	status		- return status code
;	number		- number of mounted disks found
;	namelen		- lengths of names of disks
;	diskname	- logical names of disks
;	maxblks		- total number of blocks on disk
;	freeblks	- number of free blocks on the disk
;
; Side-effects:
;	none
;
;
;	Heavily based on the DISKSPACE.MAR routine from ZEUS
;
;	Eric F. Richards
;	23-Sep-85
;	Gould OSD VAX/VMS V4.2
;-
	
	.sbttl	Equated Symbols

	.enable suppression
	.disable traceback, debug
	.link	/sys$system:sys.stb/	; use system symbol table
	.library /sys$library:lib/	; use version-sensitive library

	$mtldef				; Mount list offsets
	$ucbdef				; Unit control block offsets
	$vcbdef				; Volume control block offsets
	$devdef				; device characteristic codes
	$lnmstrdef			; logical name table offsets
	$ssdef				; Status code def'ns
	$dcdef				; device class def'ns
	$prdef				; priv'd reg def'ns

	.global	ioc$gq_mountlst		; ptr to system mount list que
	.global	sch$gl_curpcb		; pointer to current PCB
	.global	sch$iolockr		; entry point to read-lock I/O database
	.global	sch$iounlock		; entry point to unlock I/O database

	maxnum	 =	4		; # of elements in return ary
	number	 =	8		; number of disks actually returned
	bufflen  =	12		; size of char ary element
	namelen  =	16		; length of name returned
	diskname =	20		; pointer to char ary space
	maxblks  =	24		; total usable space on disk
	freeblks =	28		; total space free

	.page
	.sbttl	Main routine

	.psect	$$$diskspace_code, page, nowrt, exe, pic, shr
	.entry	diskspace, 0		; Get into kernel mode so we
	$cmkrnl_s routin=scan_data, -	; can do something useful
		arglst=(ap)		; give it the args it needs
	ret				; all done
	.align	quad

scan_data:
	.word	^m<r2, r3, r4, r5, r6, r7, r8, r10>

;
;	Lock I/O data base for read.  IPL goes to 2 when this is done.
;

	movl	g^sch$gl_curpcb, r4		; Get our PCB address
	jsb	g^sch$iolockr			; Lock down the I/O data base

	assume	mtl$l_mtlfl eq 0		; end-of-list chk counts on this

	clrl	r10				; Init disk unit count
	moval g^ioc$gq_mountlst-mtl$l_mtlfl, r8 ; Get adr of 1st mntd vol descr
	movl	r8, r7				; Get modifiable copy of address

;
;	Walk mounted volume list for our information.  End of list
;	is indicated by the address of the pointer to the first
;	mounted volume.
;

10$:	movl	mtl$l_mtlfl(r7), r7	; Get address of next one
	cmpl	r7, r8			; is this the end of the list?
	bneq	15$			; If NEQL, continue
	brw	30$			; otherwise, stop here
15$:	movl	mtl$l_ucb(r7), r6	; Get address of first UCB
	beql	10$			; Skip if no UCB's

;
;	Check to make sure volume is a R/W Files-11 system volume
;

	cmpb	ucb$b_devclass(r6), #dc$_disk	; Is device a disk?
	bneq	10$				; If NEQ no
	movl	ucb$l_devchar(r6), r0		; get device char's, skip if:
	bbc	#dev$v_mnt, r0, 10$		; Device not mounted
	bbs	#dev$v_for, r0, 10$		; Mounted foreign
	bbs	#dev$v_swl, r0, 10$		; or Write Locked

;
;	Get the total block count from UCB
;

	moval	@maxblks(ap)[r10], r2	; Get addr of max blocks storage
	ifnowrt	#4, (r2), 50$		; Check for writability
	movl	ucb$l_maxblock(r6),(r2)	; write out max block count

;
;	Get the number of free blocks from the VCB
;

	movl	ucb$l_vcb(r6), r5	; Get address of VCB
	beql	10$			; If EQL none, dismounted
	moval	@freeblks(ap)[r10], r2	; Get adr of free block storage
	ifnowrt	#4, (r2), 50$		; Check for writability
	movl	vcb$l_free(r5), (r2)	; Get free block count

;
;	Get the logical dev name from the logical name block
;

	moval	@bufflen(ap), r1	; get address of buffer length dsc
	ifnord	#4, (r1), 50$		; error if i can't read this
	movl	(r1), r1		; get buffer length
	mull3	r1, r10, r2		; r2 has offset factor of string buffer
	movab	@diskname(ap)[r2], r2	; r2 now has the address of the buffer
	ifnowrt	r1, (r2), 50$		; bomb if i can't write to the buffer
	movl	mtl$l_logname(r7), r6	; get logical name block for this vol.
	beql	40$			; If none, dismounted
	movzbl	lnmb$t_name(r6), r0	; get length of name
	beql	40$			; again, if none, no such volume
	moval	@namelen(ap)[r10], r3	; get length of return buffer for len
	ifnowrt	#4, (r3), 50$		; check for correct access
	movl	r0, (r3)		; if good, save that value
	movc5	r0,1+lnmb$t_name(r6), -	; move the logical name assoc. with
		  #^a/ /, r1, (r2)	; the device into the user buffer

;
;	Check and see if we have filled the array.  If we have, then
;	don't continue through loop.  Note that this routine assumes
;	that the value of MAXNUM is at least 1.
;

	moval	@maxnum(ap), r2		; Get address of max unit count
	ifnord	#4, (r2), 50$		; Check for readability
	aoblss	(r2), r10, 40$		; Bump number of disks found
30$:	moval	@number(ap), r2		; Get address of disk count
	ifnowrt	#4, (r2), 50$		; Check for writability
	movl	r10, (r2)		; Save count of disks found
	pushl	#ss$_normal		; Set success, save on stack
	brb	60$			; do final cleanup

;
;	IFRD, IFNORD, IFWRT, IFNOWRT failed -- access violation,
;	return error condition - go back to user mode
;

50$:	pushl	#ss$_accvio		; save error status on stack

;
;	Unlock database, pop return status and go back to user mode
;

60$:	movl	g^sch$gl_curpcb, r4	; Get our PCB address
	jsb	g^sch$iounlock		; Unlock the I/O data base
	setipl	#0			; Drop IPL
	popl	r0			; Restore status code
	ret				; ...all done!

40$:	brw	10$			; common jump to top of loop

end_scan_data:

	assume	<end_scan_data - diskspace> le 512
	.end
