           <<< METOO::SYS$SYSDEVICE:[NOTES$LIBRARY]TOOLSHED.NOTE;1 >>>
                                 -< TOOLSHED >-
================================================================================
Note 932.5                  CONVERT LBN to FILE ? How                     5 of 5
GEC013::OAKLEY                                     1603 lines  22-MAY-1996 10:13
                          -< Better late than never >-
--------------------------------------------------------------------------------
    Below is a description and source for a program that returns the
    filename given an lbn. It works on VAX VMS up through V6.1 (and
    probably later). I have not modified it for Alpha.
    
---------------------- README.TXT -------------------------------------
FINDFILE.EXE is a utility to get the name of a file, given a
disk LBN. This utility is handy when maintenance discovers bad
blocks on the disks, and we need to know which file (and owner
of the file) the bad blocks are allocated to. 
The syntax for FINDFILE is:

	$ FINDFILE/LBN=n  [device]

where "n" is a logical block number and "device" is a disk.
The default for "device" is SYS$DISK. FINDFILE also accepts
the syntax:

	$ FINDFILE/CYL=cyl/TRACK=track/SEC=sec  [device]

where "cyl" is a cylinder number, "track" is a track number,
and "sec" is a sector number. Specifying a physical address
might be useful when working the RM03's or RM05's.

Below is a sample session using FINDFILE.

$ FINDFILE :== $DEV$SSG:[SSG.TOOLS]FINDFILE
$ FINDFILE/LBN=1 

	Disk file is []INDEXF.SYS;1 
	File owner is [000001,000001]    
	File id is (1,1,1)
	Blocks allocated/used: 17868/17869 

$ FINDFILE/LBN=66234 SYS$SYSDEVICE

	Disk file is [SYSBIGCOMMON.ISSCO.TELLAGRAF]FILCON51.EXE;2 
	File owner is [000001,000004]   SYSTEM
	File id is (1791,4,1)
	Blocks allocated/used: 516/515 

$ FINDFILE/LBN=890123

	Disk file is [PADANAL.BHARPER.DISS]GRAPH7.SSD;2 
	File owner is [000105,000007]   SUMANT
	File id is (9773,28,1)
	Blocks allocated/used: 988/987 

$ FINDFILE/LBN=900000
%FFS-F-LBNTOOBIG, LBN is too big for this device

$ FINDFILE/LBN=166124 $1$DUA7
%FFS-F-FILNOTFOU, File not found
---------------------------------------- BLD.COM-------------------------------
$ MACRO/LIST       FINDFILE
$ MESSAGE/LIST     FFERRORS
$ LINK/MAP/NOTRACE FINDFILE,FFERRORS
$ EXIT
---------------------------------------- DESCRIP.MMS --------------------------
FINDFILE.EXE : FINDFILE.OBJ, FFERRORS.OBJ
	LINK/DEBUG  FINDFILE,FFERRORS
	PURGE  *.EXE,*.LIS,*.MAP,*.OBJ,1.0

FINDFILE.OBJ : FINDFILE.MAR
	MACRO/LIST/DEBUG  FINDFILE+DEV$SSG:[SSG.MDO.EXPT.MACROS]PRTMAC/LIB

FFERRORS.OBJ : FFERRORS.MSG
	MESSAGE/LIST FFERRORS

PRINT : 
	PRINT/DELETE/NOTIFY/DEVICE=LPA0 FINDFILE.LIS+FFERRORS.LIS+FINDFILE.MAP
	SHOW QUEUE/ALL SYS$PRINT
	SHOW QUEUE/ALL LPA0
--------------------------------------- FFERRORS.MSG --------------------------
! Error messages for FINDFILE utlitity.

	.FACILITY	FFS,1/PREFIX=FFS_

	.SEVERITY	FATAL

	NO_QUA		<No qualifiers present on command line>
	LBNTOOBIG	<LBN is too big for this device>
	CYLTOOBIG	<Cylinder value is too big for this device>
	TRKTOOBIG	<Track value is too big for this device>
	SECTOOBIG	<Sector value is too big for this device>
	TOOMANYQUA	<Too many qualifiers specified>
	FILNOTFOU	<File not found>

	.END
--------------------------------------- FINDFILE.MAR --------------------------
	.TITLE	FINDFILE Find a File Given a Disk Address
	.IDENT	/2.0/
;++
;
; Title:
;	FINDFILE.MAR - Find a file given a disk address.
;
; Version:
;	1-001
;
; Facility:
;	System management tool.
;
; Abstract:
;	This program returns the name of a file given a disk address
;	(lbn or cylinder/track/sector address) and a device name. This
;	tools is quite useful when you know where a bad spot is on a
;	disk, but you don't know who occupies it.
;
; Environment:
;	Native mode, need access to file header index file.
;
; Author:
;	Mark Oakley	Battelle Columbus Laboratories	28-Mar-1984
;
; Modifications:
;
; 09-Jan-1986	Mark Oakley	Modified to work properly with V4, replace
;				FAB's with FIB's as workaround to RMS bug,
;				supply more file info on output.
;
; 11-Jan-1986	Mark Oakley	Fixed to compute file spec length.
;
;--


	.SBTTL	Definitions and Macros

	.LIBRARY	/SYS$LIBRARY:LIB.MLB/

	$DVIDEF		; Device information symbols.
	$FABDEF		; RMS FAB symbols.
	$FIBDEF		; File information block definitions.
	$FI2DEF		; File header id symbols.
	$FH2DEF		; File header symbols.
	$SECDEF		; Section (mapping) symbols.
	$SSDEF		; Status definitions.
	$TPADEF		; LIB$TPARSE symbols.


FH2$L_ALLOC = ^X18	; Offset to blocks allocated.
FH2$L_USED  = ^X1C	; Offset to blocks used.

;
; Macro to help set up fibs for each volume in a volume set.
;
	.MACRO	MFIB	?DESC,?FIB
DESC:	.LONG	 FIB$K_LENGTH
	.ADDRESS FIB
FIB:
. = . + FIB$W_FID
	.WORD	1			; INDEXF.SYS always has 
	.WORD 	1			; (1,1,n) file id.
. = FIB + FIB$K_LENGTH
	.ENDM	MFIB

;
; Macro to handle return codes.
;
	.MACRO	ON_ERR	THERE,?HERE
	BLBS	R0,HERE
	BRW	THERE
HERE:	.ENDM	ON_ERR

;
; Macro to create table for a SPANC instruction. We
; will need this table to skip over an ascii number
; field.
;
	.MACRO	NUMBER_TABLE
	.BYTE	0[48]
	.BYTE	1[10]		; "0" to "9" are Ascii 48 to 57.
	.BYTE	0[198]
	.ENDM	NUMBER_TABLE


	.SBTTL	Command Line Data Structures
	.PSECT	FINDFILE_DATA,RD,WRT,NOEXE,PAGE,PIC,SHR

COMM_LINE_BUF:				; Store command line here.
	.BLKB	80
COMM_LINE_BUF_SIZ = . - COMM_LINE_BUF

COMM_LINE_DESC:
	.LONG	 COMM_LINE_BUF_SIZ
	.ADDRESS COMM_LINE_BUF

COMM_LINE_LEN:
	.BLKL	1

PARSE_BLK:				; Parse block for LIB$TPARSE.
	.LONG	TPA$K_COUNT0
	.LONG	TPA$M_ABBREV		; Permit abbreviations.
. = PARSE_BLK + TPA$K_LENGTH0


QUAL_MASK:				; Used to determine if logical or
	.LONG	0			; physical address specified.

LBN:					; Desired lbn.
	.BLKL	1

SECTOR:					; Desired sector.
	.BLKL	1

TRACK:					; Desired track.
	.BLKL	1

CYLINDER:				; Desired cylinder.
	.BLKL	1


	.SBTTL	Device Information

DEVICE_ITM_LIST:			; Item list to return:
	.WORD	 4			;   1) Sectors per track.
	.WORD	 DVI$_SECTORS		;   2) Tracks per cylinder.
	.ADDRESS SEC_PER_TRK		;   3) Cylinders on volume.
	.ADDRESS 0			;   4) Maximum files that
	.WORD	 4			;      can be on volume.
	.WORD	 DVI$_TRACKS		;   5) Physical device name.
	.ADDRESS TRK_PER_CYL		;   6) Maximum number of
	.ADDRESS 0			;      blocks on volume.
	.WORD	 4			;   7) Root device name.
	.WORD	 DVI$_CYLINDERS		;   8) Number of volumes in set.
	.ADDRESS CYL_PER_VOL		;   9) Number of this volume.
	.ADDRESS 0
	.WORD	 4
	.WORD	 DVI$_CLUSTER
	.ADDRESS CLUSTER_FACTOR
	.ADDRESS 0
	.WORD	 4
	.WORD	 DVI$_MAXFILES
	.ADDRESS MAX_FILES
	.ADDRESS 0
	.WORD	 DEVICE_BUF_SIZ
	.WORD	 DVI$_DEVNAM
	.ADDRESS DEVICE_BUF
	.ADDRESS DEVICE_BUF_LEN
	.WORD	 4
	.WORD	 DVI$_MAXBLOCK
	.ADDRESS MAX_LBN
	.ADDRESS 0
	.WORD	 NEXT_BUF_SIZ
	.WORD	 DVI$_ROOTDEVNAM
	.ADDRESS NEXT_BUF
	.ADDRESS NEXT_LEN
	.WORD	 4
	.WORD	 DVI$_VOLCOUNT
	.ADDRESS VOLCOUNT
	.ADDRESS 0
	.WORD	 4
	.WORD	 DVI$_VOLNUMBER
	.ADDRESS VOLNUMBER
	.ADDRESS 0
	.LONG	 0				; End of list.


DEFAULT_DEVICE:
	.ASCID	/SYS$DISK/

DEVICE_BUF:					; Physical device name.
	.BLKB	40
DEVICE_BUF_SIZ = . - DEVICE_BUF

DEVICE_DESC:
	.LONG	 DEVICE_BUF_SIZ
	.ADDRESS DEVICE_BUF

DEVICE_BUF_LEN:					; Length of physical device
	.BLKL	1				; name.

SEC_PER_TRK:					; Sectors per track.
	.BLKL	1

TRK_PER_CYL:					; Tracks per cylinder.
	.BLKL	1

CYL_PER_VOL:					; Cylinders on volume
	.BLKL	1

MAX_FILES:					; Maximum number of files that
	.BLKL	1				; can be stored on device.

CLUSTER_FACTOR:					; Allocation unit for device.
	.BLKL	1

HEADER_OFFSET:					; Pointer to start of headers
	.BLKL	10				; INDEXF.SYS.

MAX_LBN:					; Maxmium number of blocks on
	.BLKL	1				; volume.

VOLCOUNT:					; Number of volumes in set.
	.BLKL	1

VOLNUMBER:					; Number of current volume
	.BLKL	1				; in set.

DVI_IOSB:					; Return status.
	.BLKQ	1


	.SBTTL	Mapping Data Structures

	.ALIGN	4				; Long word align for fib.

IDX_FIB:
. = . + FIB$W_FID
	.WORD	1				; INDEXF.SYS always has
	.WORD	1				; (1,1,n) file id.
. = IDX_FIB + FIB$K_LENGTH

IDX_FIB_DESC:					; For an index header file
	.LONG	 FIB$K_LENGTH			; we will map.
	.ADDRESS IDX_FIB

CHANNEL:					; Need channels for mapping,
	.BLKW	10				; and tracing backlinks.

QIO_IOSB:					; Return status.
	.BLKQ	1

INPUT_ADDR:					; Let system service allocate
	.LONG	^X20000				; memory as needed.
	.LONG	^X20000

RETURN_ADDR:					; Need to know exactly where
	.BLKQ	1				; memory was allocated.

MAPPED_PAGES:					; Maximum number of pages to
	.LONG	4000				; map at once.

PF_CLUSTER:					; Number of pages to bring into
	.LONG	128				; physical memory at once.

SECT_FLAG = <SEC$M_CRF!SEC$M_EXPREG>		; Section is copy-on-reference
						; and expand memory allocation
						; as needed.


	.SBTTL	Table of Fib's for Volume Set

	.PSECT	FIB_TABLE,,RD,WRT,NOEXE,PAGE,SHR,PIC

;
; Allow up to 10 volumes in a volume set.
;

INDEXF_FIB_1:	MFIB

INDEXF_FIB_2:	MFIB

INDEXF_FIB_3:	MFIB

INDEXF_FIB_4:	MFIB

INDEXF_FIB_5:	MFIB

INDEXF_FIB_6:	MFIB

INDEXF_FIB_7:	MFIB

INDEXF_FIB_8:	MFIB

INDEXF_FIB_9:	MFIB

INDEXF_FIB_10:	MFIB


FIB_INDEX_TABLE:
	.ADDRESS INDEXF_FIB_1
	.ADDRESS INDEXF_FIB_2
	.ADDRESS INDEXF_FIB_3
	.ADDRESS INDEXF_FIB_4
	.ADDRESS INDEXF_FIB_5
	.ADDRESS INDEXF_FIB_6
	.ADDRESS INDEXF_FIB_7
	.ADDRESS INDEXF_FIB_8
	.ADDRESS INDEXF_FIB_9
	.ADDRESS INDEXF_FIB_10



	.SBTTL	Data Structures for Tracing Backlinks

NEXT_DVI_ITMLST:				; Item list to find:
	.WORD	 NEXT_BUF_SIZ			;   1) Next volume in set.
	.WORD	 DVI$_NEXTDEVNAM		;   2) Cluster factor of
	.ADDRESS NEXT_BUF			;      current volume.
	.ADDRESS NEXT_LEN			;   3) Maximum number of
	.WORD	 4				;      files that can be
	.WORD	 DVI$_CLUSTER			;      stored on current
	.ADDRESS CLUSTER_FACTOR			;      volume.
	.ADDRESS 0
	.WORD	 4
	.WORD	 DVI$_MAXFILES
	.ADDRESS MAX_FILES
	.ADDRESS 0
	.LONG	 0

NEXT_DESC:					; Then "NEXT" data structures
	.LONG	 NEXT_BUF_SIZ			; are used to obtain cluster
	.ADDRESS NEXT_BUF			; factors and maximum file
						; information about each
NEXT_BUF:					; volume in the set.
	.BLKB	30
NEXT_BUF_SIZ = . - NEXT_BUF

NEXT_LEN:
	.BLKL	1


	.SBTTL	Data Structures for Printing Results

DIR_DESC:					; Descriptor for directory
	.LONG	 DIR_BUF_SIZ			; spec.
	.ADDRESS DIR_BUF

DIR_BUF:					; Directory spec will be
	.BLKB	255				; stored here.
DIR_BUF_SIZ = . - DIR_BUF

DIR_BUF_LEN:					; Length of directory spec.
	.BLKL	1

FILE_NAME_DESC:
	.LONG	 FILE_NAME_BUF_SIZ
	.ADDRESS FILE_NAME_BUF

FILE_NAME_BUF:					; Store file name here.
	.BLKB	<FI2$S_FILENAME+FI2$S_FILENAMEXT>
FILE_NAME_BUF_SIZ = . - FILE_NAME_BUF

FILE_NAME_LEN:
	.BLKL	1

NUM_TBL:					; Used to find the end of the
	NUMBER_TABLE				; version number field.

DIR_NAME_DESC:
	.LONG	 DIR_NAME_BUF_SIZ
	.ADDRESS DIR_NAME_BUF

DIR_NAME_BUF:					; Store a directory name here.
	.BLKB	<FI2$S_FILENAME+FI2$S_FILENAMEXT>
DIR_NAME_BUF_SIZ = . - DIR_NAME_BUF

DIR_NAME_LEN:
	.BLKL	1

FILE_ID:					; File id, seq are here.
FILE_ID_NUM:
	.BLKW	1
FILE_ID_SEQ:
	.BLKW	1

FILE_UIC:					; Owner of file.
FILE_MEM_UIC:
	.BLKW	1
FILE_GRP_UIC:
	.BLKW	1


NAME_DESC:					; Ascii id owner of file.
	.LONG	 NAME_BUF_SIZ
	.ADDRESS NAME_BUF

NAME_BUF:
	.BLKB	20
NAME_BUF_SIZ = . - NAME_BUF

BLKS_ALLOC:					; Blocks allocated to file.
	.BLKL	1

BLKS_USED:					; Blocks used by file.
	.BLKL	1

CTRL_STG:					; Control string for $FAO.
	.ASCID	#!/!_Disk file is !AS!AS #

CTRL_STG2:
	.ASCID	#!_File owner is [!6OW,!6OW]   !AS#

CTRL_STG3:
	.ASCID	#!_File id is (!ZW,!ZW,!ZW)#

CTRL_STG4:
	.ASCID	#!_Blocks allocated/used: !ZL/!ZL !/#

OUTPUT_DESC:
	.LONG	 OUTPUT_BUF_SIZ
	.ADDRESS OUTPUT_BUF

OUTPUT_BUF:					; Output line will be
	.BLKB	80				; stored here.
OUTPUT_BUF_SIZ = . - OUTPUT_BUF

OUTPUT_LEN:
	.BLKL	1


	.SBTTL	Header Buffers and File ID's.
	.PSECT	BL_HDR_BUF,RD,WRT,NOEXE,PAGE,PIC,SHR

FILE_HDR_BUF:					; File header buffer.
	.BLKB	512

EXT_HDR_BUF:					; Buffer for any file extension
	.BLKB	512				; headers.

DIR_HDR_BUF:					; Buffer for directory file
	.BLKB	512				; headers.

EXT_SEG_NUM:					; Extension number for this
	.BLKW	1				; segment.

FILE_EXT_NUM:					; File extension number.
	.BLKW	1

FILE_EXT_SEQ:					; File extension sequence
	.BLKW	1				; number.

FILE_EXT_RVN:					; File extension relative
	.BLKW	1				; volume number.

FILE_NUM:					; File ID for disk address we
	.BLKW	1				; are looking for.

FILE_SEQ:					; File sequence number for disk
	.BLKW	1				; address we are looking for.

FILE_RVN:					; File relative volume number
	.BLKW	1				; for disk address we are
						; looking for.


	.SBTTL	Parse Intructions
	.PSECT	FINDFILES_PARSE,RD,NOWRT,EXE,PAGE,PIC,SHR

	$INIT_STATE	FILES_STATE,FILES_KEY

	$STATE	START
	$TRAN	TPA$_EOS,TPA$_EXIT	; End of string indicates success.
	$TRAN	'/'			; Start of qualifier.
	$TRAN	TPA$_SYMBOL,TPA$_EXIT,-	; Get device name (if there is one).
		,,DEVICE_DESC

	$STATE
	$TRAN	'LBN',GET_LBN,,<<^B01>>,QUAL_MASK
	$TRAN	'CYLINDER',GET_CYL,,<<^B10>>,QUAL_MASK
	$TRAN	'TRACK',GET_TRK,,<<^B10>>,QUAL_MASK
	$TRAN	'TRK',GET_TRK,,<<^B10>>,QUAL_MASK
	$TRAN	'SECTOR',GET_SEC,,<<^B10>>,QUAL_MASK

	$STATE	GET_LBN			; Get the LBN to look for.
	$TRAN	'='
	$STATE
	$TRAN	TPA$_DECIMAL,START,-
		,,LBN

	$STATE	GET_CYL			; Get the cylinder to look for.
	$TRAN	'='
	$STATE
	$TRAN	TPA$_DECIMAL,START,-
		,,CYLINDER

	$STATE	GET_TRK			; Get the track to look for.
	$TRAN	'='
	$STATE
	$TRAN	TPA$_DECIMAL,START,-
		,,TRACK

	$STATE	GET_SEC			; Get the sector to look for.
	$TRAN	'='
	$STATE
	$TRAN	TPA$_DECIMAL,START,-
		,,SECTOR

	$END_STATE


	.SBTTL	Main Program
	.PSECT	FINDFILES_CODE,RD,NOWRT,EXE,PAGE,SHR,PIC
	.ENTRY	FINDFILES,^M<R2,R3,R4,R5,R6,R7,R8,R9,R10,R11>
;
; This main routine invokes routines to get the desired lbn to search
; for, and access the files headers to search for it.
;

	JSB	PARSE_COMM_LINE		; Find what to look for.
	ON_ERR	MAIN_EXIT


	JSB	GET_DEV_INFO		; Get device-specific information.
	ON_ERR	MAIN_EXIT


	JSB	CHECK_QUALS		; Verify disk address is in range.
	ON_ERR	MAIN_EXIT


	JSB	SEARCH_HDRS		; Search file headers for desired lbn.
	ON_ERR	MAIN_EXIT


	JSB	GET_OFFSETS		; Setup for search of backlink
	ON_ERR	MAIN_EXIT		; pointers.


	JSB	FOLLOW_BACKLINKS	; Follow backlink pointers to get
	ON_ERR	MAIN_EXIT		; complete file-spec.


	JSB	OUTPUT_RESULTS		; Report file-spec.
	ON_ERR	MAIN_EXIT


MAIN_EXIT:
	RET


	.SBTTL	Parse Command Line
;++
;
; Functional Description:
;	This routine inputs the command line, and parses it for
;	a disk address and device name.
;
; Calling Sequence:
;	JSB	PARSE_COMM_LINE
;
; Formal Parameters:
;	None.
;
; Implicit Inputs:
;	Command line buffer,
;	Parse block for LIB$TPARSE,
;	Parse tables,
;	Default device name,
;	Device name buffer.
;
; Implicit Outputs:
;	Device name buffer.
;
; Completion Status:
;	Returned in R0.
;
; Side Effects:
;	None.
;
;--

PARSE_COMM_LINE:

	MOVQ	DEFAULT_DEVICE,-		; Assume the default device
		DEVICE_DESC			; until we find otherwise.

	PUSHL	#0				; Read the command line.
	PUSHAL	COMM_LINE_LEN
	PUSHL	#0
	PUSHAL	COMM_LINE_DESC
	CALLS	#4,G^LIB$GET_FOREIGN
	ON_ERR	PARSE_COMM_LINE_EXIT

	MOVL	COMM_LINE_LEN,-			; Setup for LIB$TPARSE.
		PARSE_BLK+TPA$L_STRINGCNT
	MOVAL	COMM_LINE_BUF,-
		PARSE_BLK+TPA$L_STRINGPTR
	PUSHAL	FILES_KEY
	PUSHAL	FILES_STATE
	PUSHAL	PARSE_BLK
	CALLS	#3,G^LIB$TPARSE			; Parse the command line.
	ON_ERR	PARSE_COMM_LINE_EXIT

PARSE_COMM_LINE_EXIT:
	RSB


	.SBTTL	Get Device Information
;++
;
; Functional Description:
;	This routine invokes $GETDVI to get information on device
;	geometry (cylinders, tracks, etc.) and computes the start
;	of the file headers in INDEXF.SYS.
;
; Calling Sequence:
;	JSB	GET_DEV_INFO
;
; Formal Parameters:
;	None.
;
; Implicit Inputs:
;	Device name,
;	Item list for getting disk geometry.
;
; Implicit Outputs:
;	Device geometry (cylinders, tracks, etc.),
;	Offset to start of file headers in INDEXF.SYS,
;	Channel to INDEXF.SYS.
;
; Completion Status:
;	Status returned in R0.
;
; Side Effects:
;	None.
;
;--

GET_DEV_INFO:

	$GETDVI_S -				; Get info on disk geometry,
		DEVNAM=DEVICE_DESC,-		; max files, cluster factor,
		ITMLST=DEVICE_ITM_LIST,-	; etc.
		IOSB=DVI_IOSB
	ON_ERR	GET_DEV_INFO_EXIT
	MOVZWL	DVI_IOSB,R0
	ON_ERR	GET_DEV_INFO_EXIT

	MOVW	DEVICE_BUF_LEN,-		; Must restore what we or
		DEVICE_DESC			; LIB$TPARSE did to this
	MOVAL	DEVICE_BUF,-			; to this descriptor.
		DEVICE_DESC+4

;
; Compute the offset to the first file header as:
;	 (MAX FILES / 4096) + 1 + (4 * CLUSTER FACTOR)
;
	DIVL3	#4096,MAX_FILES,R1
	INCL	R1				; R1 = Bitmap size.
	MULL3	#4,CLUSTER_FACTOR,R2
	ADDL3	R1,R2,HEADER_OFFSET


;
; Get a channel to the index header file stored on the device.
;
	$ASSIGN_S -
		DEVNAM=DEVICE_DESC,-
		CHAN=CHANNEL
	ON_ERR	GET_DEV_INFO_EXIT

	$QIOW_S	FUNC=#<IO$_ACCESS!IO$M_ACCESS>,-	; Channel will be to
		CHAN=CHANNEL,-				; INDEXF.SYS file.
		IOSB=QIO_IOSB,-
		P1=IDX_FIB_DESC
	ON_ERR	GET_DEV_INFO_EXIT
	MOVZWL	QIO_IOSB,R0
	ON_ERR	GET_DEV_INFO_EXIT

GET_DEV_INFO_EXIT:
	RSB


	.SBTTL	Check Qualifiers
;++
;
; Functional Description:
;	This routine validates the disk address specified on the
;	command line to be sure it is in range.
;
; Calling Sequence:
;	JSB	CHECK_QUALS
;
; Formal Parameters:
;	None.
;
; Implicit Inputs:
;	Qualifier mask to determine if logical or physical address specified,
;	Disk address, either LBN or cylinder, track, and sector,
;	Maximum device values for blocks, cylinders, tracks, and sectors.
;
; Implicit Outputs:
;	LBN.
;
; Completion Status:
;	Errors are signaled,
;	Status returned in R0.
;
; Side Effects:
;	None.
;
;--

CHECK_QUALS:

	CASEB	QUAL_MASK,#0,#3		; Determine what qualifiers were
1$:	.WORD	NO_QUAL-1$		; specified.
	.WORD	LOG_QUAL-1$
	.WORD	PHYS_QUAL-1$
	.WORD	BOTH_QUAL-1$


NO_QUAL:				; No qualifier specified, signal
	PUSHAL	FFS_NO_QUA		; error.
	CALLS	#1,G^LIB$SIGNAL

LOG_QUAL:				; Logical disk address specified.
	CMPL	MAX_LBN,LBN		; Is it in range?
	BGEQ	10$
	PUSHAL	FFS_LBNTOOBIG		; No, signal error.
	CALLS	#1,G^LIB$SIGNAL
10$:	MOVL	#SS$_NORMAL,R0		; Yes, indicate success.
	BRW	CHECK_QUALS_EXIT

PHYS_QUAL:				; Physical address specified.
	CMPL	CYL_PER_VOL,CYLINDER	; Is cylinder in range?
	BGEQ	20$
	PUSHAL	FFS_CYLTOOBIG		; No, signal error.
	CALLS	#1,G^LIB$SIGNAL
20$:	
	CMPL	TRK_PER_CYL,TRACK	; Is track in range?
	BGEQ	30$
	PUSHAL	FFS_TRKTOOBIG		; No, signal error.
	CALLS	#1,G^LIB$SIGNAL
30$:
	CMPL	SEC_PER_TRK,SECTOR	; Is sector in range?
	BGEQ	40$
	PUSHAL	FFS_SECTOOBIG		; No, signal error.
	CALLS	#1,G^LIB$SIGNAL
40$:
	MULL3	TRK_PER_CYL,-		; Compute logical address from
		CYLINDER,R2		; physical address as:
	ADDL2	TRACK,R2		;   LBN = SECTOR + (SEC/TRK) *
	MULL2	SEC_PER_TRK,R2		;        (TRACK + CYLINDER * (TRK/CYL))
	ADDL3	SECTOR,R2,LBN		;       
	MOVL	#SS$_NORMAL,R0		;          
	BRW	CHECK_QUALS_EXIT

BOTH_QUAL:				; Both logical and physical addresses
	PUSHAL	FFS_TOOMANYQUA		; specified, signal error.
	CALLS	#1,G^LIB$SIGNAL

CHECK_QUALS_EXIT:
	RSB


	.SBTTL	Search File Headers
;++
;
; Functional Description:
;	This routine controls the search for a file given a disk address.
;	A linear search is performed, one retrieval pointer at a time,
;	one file header at a time. The file headers are mapped into memory.
;
; Calling Sequence:
;	JSB	SEARCH_HDRS
;
; Formal Parameters:
;	None.
;
; Implicit Inputs:
;	Desired LBN,
;	Channel to file header file,
;	Information to control mapping of file headers.
;
; Implicit Outputs:
;	File id of file with desired LBN.
;
; Completion Status:
;	Status return in R0.
;
; Side Effects:
;	Memory is allocated and deallocated in P0 space,
;	INDEXF.SYS is accessed.
;
; Register Useage:
;	R0  - Scratch and return status
;	R1  - Disk address to start mapping
;	R6  - Desired LBN
;	R7  - File header pointer into header buffer
;	R9  - Pointer to a retrieval poiner in header buffer
;	R10 - File id number
;	R11 - Maximum file id in memory
;--


SEARCH_HDRS:

	CLRL	R11			; Init maximum file id in memory.
	MOVL	LBN,R6			; Remember desired lbn.
	CLRL	R10			; Init file id.

SEARCH_LOOP:
	INCL	R10			; Next header.
	CMPL	R11,R10			; Is it in memory?
	BGEQ	IN_MEMORY		; Yes.

	JSB	MAP_IDX			; No, map file into memory.
	ON_ERR	SEARCH_HDRS_EXIT

IN_MEMORY:
	ADDL2	#512,R7			; Get to next header in buffer.
;
; Do we have a valid header?
;
	TSTW	FH2$W_FID(R7)		; If file number is zero, then
	BNEQ	10$			; header is not valid.
	BRW	SEARCH_LOOP

10$:
	TSTW	FH2$W_CHECKSUM(R7)	; If checksum is zero, then
	BNEQ	20$			; header is not valid.
	BRW	SEARCH_LOOP

20$:
	CMPW	FH2$W_FID_NUM(R7),R10	; If file id field does not equal
	BEQL	30$			; file id, then header is not valid.
	BRW	SEARCH_LOOP

30$:
	MOVZBL	FH2$B_MPOFFSET(R7),R1	; Remember map offset. Offset is in
	ASHL	#1,R1,R1		; words => must convert to bytes.
	ADDL3	R1,R7,R9		; R9 points to first retrieval pointer.

	MOVZBL	FH2$B_MAP_INUSE(R7),R8	; Compute address for end of retrieval
	ASHL	#1,R8,R8		; pointers, and save address in R8.
	ADDL2	R9,R8


RETRIEVAL_LOOP:
	CMPL	R8,R9			; End of retrieval pointers?
	BGTR	GET_ESCAPE_CODE
	BRW	END_OF_RET_PTRS		; Yes

GET_ESCAPE_CODE:			; No, see what kind of retrieval
	EXTZV	#14,#2,(R9),R1		; pointer we have.
	CASEB	R1,#0,#3
1$:	.WORD	000$-1$
	.WORD	100$-1$
	.WORD	200$-1$
	.WORD	300$-1$

;
; Format for placement control data is the following:
;
;	 ----------------
;	|00              |
;	 ----------------
;
000$:					; Placement control data, ignor it
	ADDL2	#2,R9			; and move onto the next retrieval
	BRW	RETRIEVAL_LOOP		; pointer.

;
; Format for this retrieval pointer is the following:
;
;	 ----------------
;	|01 Hi LBN  Count|
;	 ----------------
;	|    Low LBN     |
;	 ----------------
;
100$:
	MOVZWL	2(R9),R2		; Move start lbn into R3.
	EXTZV	#8,#6,(R9),R0
	INSV	R0,#16,#6,R2
	CMPL	R2,R6			; Could the desired lbn be in here?
	BGTR	190$			; No way!
	BEQL	130$			; Bingo!
	MOVZBL	(R9),R3			; Maybe, get count field.
	ADDL2	R3,R2			; Compute end lbn.
	CMPL	R6,R2			; Could the desired lbn be in here?
	BGTR	190$			; No.
130$:	BRW	LBN_FOUND		; Yes.
190$:	ADDL2	#4,R9			; Move on to the next retrieval
	BRW	RETRIEVAL_LOOP		; pointer.


;
; Format for this retrieval pointer is the following:
;
;	 ----------------
;	|10   Count      |
;	 ----------------
;	|   Low LBN      |
;	 ----------------
;	|   Hi LBN       |
;	 ----------------
; 
200$:
	MOVL	2(R9),R2		; Move start lbn to R2.
	CMPL	R2,R6			; Could the desired lbn be in here?
	BGTR	290$			; No way!
	BEQL	230$			; Bingo!
	EXTZV	#0,#14,(R9),R3		; Maybe, get count field.
	ADDL2	R3,R2			; Compute end lbn.
	CMPL	R6,R2			; Could the desired lbn be in here?
	BGTR	290$			; No.
230$:	BRW	LBN_FOUND		; Yes.
290$:	ADDL2	#6,R9			; Move onto the next retrieval
	BRW	RETRIEVAL_LOOP		; pointer.

;
; Format for this retrieval pointer is the following:
;
;	 ----------------
;	|11   Hi Count   |
;	 ----------------
;	|   Low Count    |
;	 ----------------
;	|   Low LBN      |
;	 ----------------
;	|   Hi LBN       |
;	 ----------------
; 
300$:
	MOVL	4(R9),R2		; Move start lbn to R2.
	CMPL	R2,R6			; Could the desired lbn be in here?
	BGTR	390$			; No way!
	BEQL	330$			; Bingo!
	MOVL	(R9),R3			; Maybe, get count field.
	BICL2	#^XC000,R3		; Top 2 bits are escape code.
	ROTL	#16,R3,R3		; Swap low and hi words.
	ADDL2	R3,R2			; Compute end lbn.
	CMPL	R6,R2			; Could the desired lbn be in here?
	BGTR	390$			; No.
330$:	BRW	LBN_FOUND		; Yes.
390$:	ADDL2	#8,R9			; Move onto the next retrieval
	BRW	RETRIEVAL_LOOP		; pointer.


END_OF_RET_PTRS:
	BRW	SEARCH_LOOP

LBN_FOUND:
	MOVW	FH2$W_SEG_NUM(R7),-	; See if we have an extension header,
		EXT_SEG_NUM		; or the file header.
	BEQL	20$

	MOVC3	#512,(R7),EXT_HDR_BUF	; Got extension.
	BRB	40$

20$:
	MOVC3	#512,(R7),FILE_HDR_BUF	; Got file header.

40$:
	$DELTVA_S -			; Get rid of memory we no longer need.
		INADR=RETURN_ADDR
	ON_ERR	SEARCH_HDRS_EXIT

SEARCH_HDRS_EXIT:
	RSB


	.SBTTL	Map File Headers
;++
;
; Functional Description:
;	This routine maps all or a portion of the INDEXF.SYS
;	file into memory. Any sections no longer used are
;	"unmapped".
;
; Calling Sequence:
;	JSB	IDX
;
; Formal Parameters:
;	None.
;
; Implicit Inputs:
;	Header offset into INDEXF.SYS,
;	Channel to INDEXF.SYS,
;	Number of pages to map,
;	Page fault cluster,
;	Memory address of where to map in INDEXF.SYS,
;	Flags to describe section,
;	File id needed to be mapped,
;	Maximum file id already in memory
;
; Implicit Outputs:
;	Mapped file headers + 1 extra block at end,
;	Addresses for mapped area.
;
; Completion Status:
;	Status returned in R0.
;
; Side Effects:
;	Virtual memory exapanded,
;	INDEXF.SYS accessed.
;
;--

MAP_IDX:

	TSTL	R11		; Must we perform any "unmapping" first?
	BEQL	40$
	$DELTVA_S -		; Yes.
		INADR=RETURN_ADDR
	ON_ERR	MAP_IDX_EXIT


40$:
	ADDL3	HEADER_OFFSET,R10,R1	; Determine where to start mapping.
	$CRMPSC_S -			; Bring the pages in.
		INADR=INPUT_ADDR,-
		RETADR=RETURN_ADDR,-
		FLAGS=#SECT_FLAG,-
		CHAN=CHANNEL,-
		PAGCNT=MAPPED_PAGES,-
		VBN=R1,-
		PFC=PF_CLUSTER

	CMPL	#SS$_ENDOFFILE,R0	; Did we reach the end of the headers?
	BNEQ	60$
	PUSHAL	FFS_FILNOTFOU		; Yes, signal file not found.
	CALLS	#1,G^LIB$SIGNAL

60$:	ON_ERR	MAP_IDX_EXIT		; No, check for any other errors.

	SUBL3	RETURN_ADDR,-		; R11 has highest numbered file number
		RETURN_ADDR+4,R11	; in memory.
	ASHL	#-9,R11,R11
	ADDL2	R10,R11
	DECL	R11
	SUBL3	#512,RETURN_ADDR,R7	; R7 is pointer to header in buffer.

MAP_IDX_EXIT:
	RSB


	.SBTTL	Get Offset Information
;++
;
; Functional Description:
;	This routine is invoked because a file has been found which contains
;	the desired disk address. This routine is a setup for a subsequent
;	routine that will follow backlink pointers to determine the complete
;	file-spec. To perform this backlink search, this routine determines
;	the offset to the first file header in INDEXF.SYS for each volume in
;	the volume set. A channel is established to each INDEXF.SYS also.
;
; Calling Sequence:
;	JSB	GET_OFFSETS
;
; Formal Parameters:
;	None.
;
; Implicit Inputs:
;	File ID (file number, sequence number, relative volume number),
;	Root device name.
;
; Implicit Outputs:
;	Table of offsets into each INDEXF.SYS for start of file headers,
;	Table of channel numbers for each INDEXF.SYS.
;
; Completion Status:
;	Returned in R0.
;
; Side Effects:
;	INDEXF.SYS files are open for reading.
;
;--

GET_OFFSETS:

	CLRL	R5			; R5 holds volume number.


NEXT_VOLUME:
	MOVL	R5,R4			; Determine next fib address and
	INCL	R5			; store it in R4.
	MOVL	FIB_INDEX_TABLE[R4],R4
	MOVL	NEXT_LEN,NEXT_DESC	; Set device name length.

	$ASSIGN_S -
		DEVNAM=NEXT_DESC,-	; Get a channel to the device.
		CHAN=CHANNEL[R5]
	ON_ERR	GET_OFFSETS_EXIT
	$QIOW_S	FUNC=#<IO$_ACCESS!IO$M_ACCESS>,-  ; Associate the channel
		CHAN=CHANNEL[R5],-		  ; with INDEXF.SYS.
		IOSB=QIO_IOSB,-
		P1=(R4)
	ON_ERR	GET_OFFSETS_EXIT
	MOVZWL	QIO_IOSB,R0
	ON_ERR	GET_OFFSETS_EXIT

	MOVW	NEXT_LEN,NEXT_DESC	; Fix descriptor for proper length.
	$GETDVI_S -			; Get information on cluster size,
		DEVNAM=NEXT_DESC,-	; maximum files that can be stored
		ITMLST=NEXT_DVI_ITMLST	; on volume, and next volume name.
	ON_ERR	GET_OFFSETS_EXIT

	DIVL3	#4096,MAX_FILES,R2	; Compute where the file headers
	INCL	R2			; start for this volume, and
	MULL3	#4,CLUSTER_FACTOR,R3	; remember this value.
	ADDL3	R2,R3,HEADER_OFFSET[R5]

	CMPL	R5,VOLCOUNT		; Any more volumes to process?
	BGEQ	10$
	BRW	NEXT_VOLUME		; Yes, loop again.

10$:
;
; Take care of case where we are processing just one device
; (that is, not a volume set).
;
	MOVL	HEADER_OFFSET+4,HEADER_OFFSET
	MOVW	CHANNEL+2,CHANNEL

GET_OFFSETS_EXIT:
	RSB


	.SBTTL	Follow Backlinks
;++
;
; Functional Description:
;	This routine follows the backlink pointers of the file extension
;	headers (if this file has any file extension headers), file header,
;	and directory headers to determine the complete file specification.
;
; Calling Sequence:
;	JSB	FOLLOW_BACKLINKS
;
; Formal Parameters:
;	None.
;
; Implicit Inputs:
;	File backlink pointers (number, sequence, volume),
;	Table of Fibs to each INDEXF.SYS in volume set,
;	Table of file header offsets to each INDEXF.SYS in volume set.
;
; Implicit Outputs:
;	Complete directory specification.
;
; Completion Status:
;	Returned in R0.
;
; Side Effects:
;	INDEXF.SYS file(s) are read.
;
;--

FOLLOW_BACKLINKS:

	TSTW	EXT_SEG_NUM		; Do we have a file header with
	BEQL	10$			; directory pointers?
	JSB	EXT_LINKS		; No, we have an extension header,
	ON_ERR	FOLLOW_BACKLINKS_EXIT	; get the file header.

10$:
	MOVAL	FILE_HDR_BUF,R6		; Remember the file name.
	MOVZBL	FH2$B_IDOFFSET(R6),R7	; Determine start of file name in
	MULL2	#2,R7			; file header.
	ADDL2	R6,R7
	MOVC3	#FI2$S_FILENAME,-	; Save name in buffer.
		FI2$T_FILENAME(R7),-
		FILE_NAME_BUF
	MOVC3	#FI2$S_FILENAMEXT,-
		FI2$T_FILENAMEXT(R7),-
		(R3)


	LOCC	#^A/;/,-		; Need to figure out the length of
		#FILE_NAME_BUF_SIZ,-	; file name. Find the start of the
		FILE_NAME_BUF		; version number.
	INCL	R1			; Start of version number (past ";").
	DECL	R0
	SPANC	R0,(R1),NUM_TBL,#1	; Skip over version number.
	SUBL3	R0,#FILE_NAME_BUF_SIZ,-	; Compute length.
		FILE_NAME_DESC
	
	MOVL	R6,R2			; R2 points to desired file header.
	MOVZWL	FH2$W_BK_FIDRVN(R2),R10	; Get backlink rel. vol. number.
	TSTL	R10			; If it is zero, use volume number
	BGTR	20$			; of volume we specified on command.
	MOVL	VOLNUMBER,R10

20$:
	MOVL	FH2$W_FID(R2),FILE_ID	; Save the file id.
	MOVL	FH2$L_FILEOWNER(R2),-	; Save the owner field.
		FILE_UIC
	$IDTOASC_S -			; See if we have an Ascii
		ID=FILE_UIC,-		; id for this file.
		NAMLEN=NAME_DESC,-
		NAMBUF=NAME_DESC
	BLBS	R0,25$
	MOVL	#1,NAME_DESC		; Set id to a blank if none found.
	MOVB	#^A/ /,NAME_BUF
25$:
	ROTL	#16,FH2$L_ALLOC(R2),-	; Save blocks allocated and used.
		BLKS_ALLOC
	ROTL	#16,FH2$L_USED(R2),-
		BLKS_USED

	MOVAL	DIR_HDR_BUF,R8		; R8 has addr of buf to hold headers.
	MOVW	#1,DIR_DESC		; Prefix dir-spec with "[" and adjust
	MOVB	#^A/[/,DIR_BUF		; descriptor.
	MOVAL	DIR_DESC,R7		; R7 has dir-spec address.
	ADDL2	#1,4(R7)
	MOVZWL	FH2$W_BACKLINK(R2),-(SP)
	CALLS	#1,G^BACK_LINK		; Routine to recursively follow
	ON_ERR	FOLLOW_BACKLINKS_EXIT	; backlinks.

	MOVAL	DIR_BUF,4(R7)		; If file was not in MFD, then there
	ADDL3	(R7),4(R7),R6		; will be a period at end of directory
	CMPW	DIR_DESC,#1		; spec. Adjust descriptor to get rid
	BGTR	30$			; of period.
	INCL	R6
	INCW	DIR_DESC

30$:
	DECL	R6			; Suffix directory-spec with "]".
	MOVB	#^A/]/,(R6)
	MOVL	#SS$_NORMAL,R0

FOLLOW_BACKLINKS_EXIT:
	RSB


	.SBTTL	Trace File Extension Links
;++
;
; Functional Description:
;	This routine locates and reads the "0th" file extension
;	header given the "nth". We need the "0th" for directory
;	tracing and to get the correct file name.
;
; Calling Sequence:
;	JSB	EXT_LINKS
;
; Formal Parameters:
;	None.
;
; Implicit Inputs:
;	File extension buffer of "nth" extension,
;	Channel number table,
;	File header offset table,
;	Number of volumes in set,
;	Volume number for "nth" file extension header.
;
; Implicit Outputs:
;	"0th" file extension header.
;
; Completion Status:
;	Returned in R0.
;
; Side Effects:
;	INDEXF.SYS file(s) are accessed.
;
;--

EXT_LINKS:

	MOVAL	EXT_HDR_BUF,R7			; Remember file ID of "nth"
	MOVW	FH2$W_FID_NUM(R7),FILE_EXT_NUM	; file extension header.
	MOVW	FH2$W_FID_SEQ(R7),FILE_EXT_SEQ
	MOVW	VOLNUMBER,FILE_EXT_RVN

	MOVW	FH2$W_BK_FIDNUM(R7),FILE_NUM	; Remember file ID of "0th"
	MOVW	FH2$W_BK_FIDSEQ(R7),FILE_SEQ	; file extension header.

	CLRL	R5


NEXT_VOL:
	INCL	R5				; R5 is volume counter for
	MOVAL	FILE_HDR_BUF,R8			; volume loop.
	MOVZWL	FILE_NUM,R9
	ADDL2	HEADER_OFFSET[R5],R9		; Get offset into INDEXF.SYS.
	$QIOW_S	CHAN=CHANNEL[R5],-		; Read file header.
		FUNC=#IO$_READVBLK,-
		P1=(R8),-
		P2=#512,-
		P3=R9
	ON_ERR	EXT_LINKS_EXIT

	CMPL	VOLCOUNT,#1			; If only 1 volume is in set,
	BGTR	20$				; then we must have just read
	BRW	GOT_0TH_HDR			; the "0th" file ext header.

20$:
	MOVZWL	EXT_SEG_NUM,R11			; R11 is ext segment count.

NEXT_EXT:
	TSTW	FH2$W_EX_FIDNUM(R8)		; See if this header has any
	BGTR	40$				; extensions.
	BRW	NEXT_VOL

40$:
	MOVW	FH2$W_EX_FIDRVN(R8),R2		; Get file id to next extension
	MOVZWL	FH2$W_EX_FIDNUM(R8),R9		; header.
	ADDL2	HEADER_OFFSET[R2],R9
	$QIOW_S	CHAN=CHANNEL[R2],-		; Read this header.
		FUNC=#IO$_READVBLK,-
		P1=(R7),-
		P2=#512,-
		P3=R9
	ON_ERR	EXT_LINKS_EXIT

	MOVAL	EXT_HDR_BUF,R8
	DECL	R11				; Have we reached the "nth"
	BEQL	GOT_NTH_HDR			; extension header?
	BRW	NEXT_EXT			; No, read next extension.


GOT_NTH_HDR:					; We have reached an "nth"
	CMPW	FILE_EXT_NUM,FH2$W_FID_NUM(R8)	; header. If file id number,
	BEQL	10$				; sequence number, and relative
	BRW	NEXT_VOL			; number match, then we know
						; we have the "0th" header.
10$:
	CMPW	FILE_EXT_SEQ,FH2$W_FID_SEQ(R8)
	BEQL	20$
	BRW	NEXT_VOL

20$:
	CMPW	FILE_EXT_RVN,R2
	BEQL	GOT_0TH_HDR
	BRW	NEXT_VOL

GOT_0TH_HDR:
	MOVL	R5,VOLNUMBER		; Need volume number for file id.
	MOVL	#SS$_NORMAL,R0

EXT_LINKS_EXIT:
	RSB


	.SBTTL	Recursively Follow Backlinks
;++
;
; Functional Description:
;	This routine performs the actual following of backlinks to
;	get a complete directory specification.
;
; Calling Sequence:
;	CALLS	#1,BACK_LINK
;
; Formal Parameters:
;	Backlink file number.
;
; Implicit Inputs:
;	Backlink relative volume number (in R10),
;	Buffer to store file headers (address in R8),
;	Directory buffer (address in R7).
;
; Implicit Outputs:
;	Complete directory spec.
;
; Completion Status:
;	Returned in R0.
;
; Side Effects:
;	Performs QIO virtual reads on INDEXF.SYS.
;
; Register Usage:
;	R0  - Return status.
;	R6  - Directory name length saved here.
;	R7  - Address of directory descriptor.
;	R8  - Address of header buffer.
;	R9  - Specifies which vbn in INDESF.SYS to read.
;	R10 - Volume number, used as index into header offset and
;	      channel tables.
;--

	.ENTRY	BACK_LINK,^M<R2,R3,R4,R5,R6,R7,R8,R9,R10>

	ADDL3	HEADER_OFFSET[R10],-		; Compute block which contains
		4(AP),R9			; the file header for the dir.
	$QIOW_S	CHAN=CHANNEL[R10],-		; Read a block.
		FUNC=#IO$_READVBLK,-
		P1=(R8),-
		P2=#512,-
		P3=R9
	ON_ERR	BACK_LINK_EXIT


	MOVZBL	FH2$B_IDOFFSET(R8),R11		; Get word offset to id.
	MULL2	#2,R11				; Compute to bytes.
	ADDL2	R8,R11				; R11 points to start of id.
	MOVC3	#FI2$S_FILENAME,-		; Move first part of directory
		FI2$T_FILENAME(R11),-		; filename into buffer.
		DIR_NAME_BUF
	MOVC3	#FI2$S_FILENAMEXT,-		; Move last part of filename,
		FI2$T_FILENAMEXT(R11),-		; this instruction MUST 
		(R3)				; IMMEDIATELY follow prev instr.

	LOCC	#^A/./,#<FI2$S_FILENAME+FI2$S_FILENAMEXT>,-
		DIR_NAME_BUF			; Compute length of this
	SUBL2	#<FI2$S_FILENAME+FI2$S_FILENAMEXT+1>,-
		R0				; portion of directory
	MNEGL	R0,R6				; string.

	CMPW	FH2$W_BACKLINK(R8),4(AP)	; Are we at [000000] ?
	BEQL	20$

	SUBL2	R6,SP				; Make room for directory
	MOVC3	R6,DIR_NAME_BUF,(SP)		; name and length.
	PUSHL	R6
	MOVZWL	FH2$W_BACKLINK(R8),-(SP)	; No, keep tracing.
	TSTW	FH2$W_BK_FIDRVN(R8)
	BEQL	10$
	MOVZWL	FH2$W_BK_FIDRVN(R8),R10

10$:
	CALLS	#1,G^BACK_LINK
	ON_ERR	BACK_LINK_EXIT

20$:
	MOVL	(SP)+,R6
	MOVC3	R6,(SP),@4(R7)			; Adjust descriptor.
	ADDL2	R6,SP
	ADDL2	R6,(R7)
	ADDL2	R6,4(R7)

	MOVL	#SS$_NORMAL,R0			; Indicate success.

BACK_LINK_EXIT:
	RET



	.SBTTL	Output Results
;++
;
; Functional Description:
;	This routine formats and outputs the file-spec and
;	directory spec.
;
; Calling Sequence:
;	JSB	OUTPUT_RESULTS
;
; Formal Parameters:
;	None.
;
; Implicit Inputs:
;	Directory name,
;	File name,
;	Control (format) string.
;
; Implicit Outputs:
;	None.
;
; Completion Status:
;	Returned in R0.
;
; Side Effects:
;	Writes to SYS$OUTPUT.
;
;--

OUTPUT_RESULTS:

	$FAO_S	CTRSTR=CTRL_STG,-		; Format the string to
		OUTLEN=OUTPUT_LEN,-		; include directory-spec
		OUTBUF=OUTPUT_DESC,-		; and file name.
		P1=#DIR_DESC,-
		P2=#FILE_NAME_DESC
	ON_ERR	OUTPUT_RESULTS_EXIT

	MOVW	OUTPUT_LEN,OUTPUT_DESC		; Fix up the descriptor.
	PUSHAL	OUTPUT_DESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	ON_ERR	OUTPUT_RESULTS_EXIT


	MOVL	#OUTPUT_BUF_SIZ,OUTPUT_DESC
	$FAO_S	CTRSTR=CTRL_STG2,-		; Format the string to
		OUTLEN=OUTPUT_LEN,-		; include owner.
		OUTBUF=OUTPUT_DESC,-
		P1=FILE_GRP_UIC,-
		P2=FILE_MEM_UIC,-
		P3=#NAME_DESC
	ON_ERR	OUTPUT_RESULTS_EXIT

	MOVW	OUTPUT_LEN,OUTPUT_DESC		; Fix up the descriptor.
	PUSHAL	OUTPUT_DESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	ON_ERR	OUTPUT_RESULTS_EXIT

	MOVL	#OUTPUT_BUF_SIZ,OUTPUT_DESC
	$FAO_S	CTRSTR=CTRL_STG3,-		; Format the string to
		OUTLEN=OUTPUT_LEN,-		; include file-id.
		OUTBUF=OUTPUT_DESC,-
		P1=FILE_ID_NUM,-
		P2=FILE_ID_SEQ,-
		P3=VOLNUMBER
	ON_ERR	OUTPUT_RESULTS_EXIT

	MOVW	OUTPUT_LEN,OUTPUT_DESC		; Fix up the descriptor.
	PUSHAL	OUTPUT_DESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	ON_ERR	OUTPUT_RESULTS_EXIT

	MOVL	#OUTPUT_BUF_SIZ,OUTPUT_DESC
	$FAO_S	CTRSTR=CTRL_STG4,-		; Format the string to
		OUTLEN=OUTPUT_LEN,-		; include blocks allocated
		OUTBUF=OUTPUT_DESC,-		; and used.
		P1=BLKS_ALLOC,-
		P2=BLKS_USED
	ON_ERR	OUTPUT_RESULTS_EXIT

	MOVW	OUTPUT_LEN,OUTPUT_DESC		; Fix up the descriptor.
	PUSHAL	OUTPUT_DESC
	CALLS	#1,G^LIB$PUT_OUTPUT
	ON_ERR	OUTPUT_RESULTS_EXIT

OUTPUT_RESULTS_EXIT:
	RSB

	.END	FINDFILES
    
