So here's a new version of OPIND, the RSX11 ODS-1 explore and fixup utility.
Truth to tell, the previous version of OPIND was...not very good. I only worked on it long enough to get it able to patch the disk corruption problem I had at the time. It did the basics, but a lot of the fancier functions had bugs in them, and it was hard to predict what radix particular numeric inputs were expecting. As well, it was unclear whether some commands acted on in-memory copies, or directly on actual blocks in the file. This new version has had a lot of debugging done, and a slug of enhancements made. Now you can specify the input radix - HEXadecimal, DECimal, or OCTal. There are also new features that allow easier access to the Index File extension headers (file IDs 6 and 7).
So let's talk a little bit about how OPIND works and how to use it.
In this version, there are several data buffers where disk blocks live. The main one is the Current buffer. It is where disk blocks are read from and to, and is the default place where commands act unless a different buffer is specified.
The other buffers are HOME (contains the Home block), IFH (Index File Header), IF6 (file ID 6, the first Index File Extension Header) and IF7 (file ID 7, the second Index File Extension Header). These are one block sized headers. There is also the Bitmap buffer, a special case among special cases, which contains the Index File Bitmap, and can be up to 8 blocks long. These all get special locations because they are...special...they impose the ODS structure on the disk.
These special buffers' LOAD commands store them in their special locations, not the Current buffer like LOAD BLOCK and LOAD HEADER do. As a result, they have to be COPYed to the Current buffer, to be modified if necessary. Then they can be written to the file, to their approriate locations by their WRITE commands (their WRITE commands write from the Current Buffer, to their location in the file. If you expect, for instance, WRITE HOME to write from the HOME buffer to the file....well, you'll be disappointed).
So, for example, to patch something in the HOME block, you could LOAD HOME (which is also done automatically at startup BTW), then COPY HOME, which copies it to the Current buffer. You can check the contents of the Current buffer with FETCH commands, and change contents of the Current buffer with DEPOSIT commands. When your changes are complete, you can then choose to WRITE HOME (or IFH, IF6, or IF7{ and the contents of the Current buffer are copied to the approriate place in the file. Note that at that point, the old contents are still in the HOME buffer, so you'd likely want to do a LOAD HOME to refresh it from the file, so it reflects your recent changes.
At startup, HOME, IFH, IF6 IF7 and the Bitmap buffer are loaded from the target disk. That is, if the disk isn't too screwed up. If the Home block or the other special blocks are damaged, lots of the advanced functions that locate by file ID or bitmap bit, or special buffer name aren't going to work, and your first job will be sorting out the damage and fixing it with the simple commands that only take a disk block number as input - LOAD BLOCK, WRITE BLOCK, GET and PUT commands.
GET and PUT deserve some mention. They will read and write the current block. This "cut and paste" buffer survives across invocations of the utility - so, for instance , you could use the utlity on a good disk, to load a block of insterest into the Current buffer. You could save the block with a PUT command, and exit the program. Then you could start it up again, this time targeting a problem disk, and use the GET command to load that previously saved block into the Current buffer. perhaps edit it a bit, and then do a WRITE HOME (or, if that's not working so hot, WRITE BLOCK 2) command, to store in in the problem disk's HOME block location.
Then there's the Bitmap buffer. It's 8 blocks long, so it won't fit in the Current buffer (which is one block long). It gets edited in its special buffer via TEST ID, CLEAR ID and SET ID commands. These edits can then get copied back to the file, direct from the Bitmap buffer, via the WRITE BITMAP command. What could be cimpler?
Lessee....let's explain how DUMP works. DUMP with no arguments dump the Current buffer. DUMP HEADER 123 reads header 123 from the file and dumps it. DUMP BLOCK 123 reads the block 123 from the file and dumps it. Both of those variants expect the specified number in the currently chosen radix. DUMP HOME, IFH, IF6, IF7 and BITMAP dump the contents of those special buffers. They don't read fresh from the file. All variants of the DUMP command respect the output radix switches /HEX /OCT /DEC /ASC /R50.
And a word about the checksum functions. File headers and the Home block contain checksums of their contents. If you edit one of those, you need to update the checksums before you write them back to the file. So, suppose you LOAD HOME, then COPY HOME to copy it to the Current block. You edit it by using FETCH and DEPOSIT. Now you need to CHECKSUM HOME UPDATE, to update the checksum fields. Then you can WRITE HOME, which will write the newly modified and checksummed block into the disk's home block. For a header, you;d read it into the Current block via the LOAD HEADER xyz command (or COPY IFH, IF6, or IF7 for those special headers). modify it in Current, Checksum it with CHECKSUM HEADER UPDATE, and then WRITE to...where it needs to go, depending on what it is. There is also CHECKSUM HEADER CHECK and CHECKSUM HOME CHECK, which will evaluate the Checksum of a Header or Home block, in the CUrrent Buffer.
Numbers entered as input are interpreted according to current radix
The default input radix is hexadecimal, unless changed by radix command
Output radix is ascii unless set by command line switch.
Output radix switches are /HEX /OCT /DEC /ASC /R50 (specified as /<radix> in the cpmmand summary below. /R50 comes in handy when looking at headers, since file names in headers are encoded in RAD50.
radix hex input numbers interpreted as hex
radix oct input numbers interpreted as octal
radix dec input numbers interpreted as decimal
load block 23 read block 23 into current block
load header 23 read header 23 into current block
load bitmap read bitmap into bitmap buffer
load home read home block into home buffer
load ifh load index file header into ifh buffer
load if6 load 1st index file ext header into if6 buffer
load if7 load 2nd index file ext header into if7 buffer
dump dump current block as ascii bytes
dump /<radix> dump current block in specified radix (eg, dump /hex)
dump block 23 dump block 23 as ascii bytes
dump block 23/<radix> dump block 23 as specified radix bytes (eg, dump block 23/oct)
dump header 27 dump header 27 as ascii bytes
dump header 27<radix dump header 27 as selected radix bytes
dump bitmap dump the header bitmap as ascii
dump bitmap/<radix> dump the header as specifed radix bytes
dump home dump home block as ascii bytes
dump home/<radix> dump home block as specified radix bytes
dump ifh dump index file header as ascii bytes
dump ifh/<radix> dump index file header as specified radix bytes
dump if6 dump index file header as ascii bytes
dump if6/<radix> dump index file header as specified radix bytes
dump if7 dump index file header as ascii bytes
dump if7/<radix> dump index file header as specified radix bytes
fetch @200 display byte at loc 200, current radix, in the current block
deposit 10@123 deposit 107,current radix,at location 123,current block
put save current block in file - like a paste buffer
get get saved block from paste file, make it current
write block 123 write current block to block 123, with 123 in current radix
write header 123 write current block to header 123, with 123 in current radix
write bitmap write the bitmap out
write home write current block to home block
write ifh write current block to index file header
write if6 write current block to 1st index file ext heade
write if7 write current block to 2nd index file ext header
test id 123 test bit 123 in current radix in the header bitmap buffer
set id 123 set bit for header 123 in current radix in header bitmap buffer
clear id 123 clear bit for header 123 in current radix in header bitmap buffer
copy home copy home block from its buffer to the current buffer
copy ifh copy index file header from its buffer to the current buffer
copy if6 copy first index file extension header from its buffer to current buffer
copy if7 copy first index file extension header from its buffer to the current buffer
checksum home check check the home block checksums the current block
checksum home update update the home block checksums in current block
checksum header check check checksum in header in current block
checksum header update update checksum in header in current block
So much for Version 2. Version 3 will have search functionality, an ANALYZE command that displays the fields in the special buffers. and the ability to follow and operate on the blocks in a particular file.
But enough of this silly documenting, It's turning into a case of TL;DR. Let's get down to the code.
To build,
>mac opind=opind
>tkb opind/pr:0=opind
It's built /PR:0 so it can do logical block IO to disks, no matter how they are mounted or who has them allocated. LIke I've said - you can do some serious damage with this ultility, so you better understand what you're doing. If you're trying to patch up a corrupted disk, it would be better to make a physical copy of the disk in question, and mount that copy /FOR, and then have at it with OPIND - tht way, if things go bad, you haven't lot any ground.
Please let me know if you find any bus or have any problems.