!pr1
Multi-Level ProDOS Catalog.................Bob Sander-Cederlof

Last week I looked through some old piles of papers and came across a program by Greg Seitz, dated Dec 20, 1983.  It was attached to a set of ProDOS Tech Notes, and Greg apparently worked at Apple at that time.

Greg's program lists the filenames of an entire ProDOS directory, showing the whole tree.  It shows directory files by printing a slash in front of the filename, and shows the level by indenting.  For example, a typical listing might look like this:

       PRODOS
       BASIC.SYSTEM
       /UTILITIES
        HELPER
        DOER
        /MORE
         WHATEVER
         AND.ANOTHER
        TEXT.FILE
       ANOTHER

A listing like this can be a big help in finding things on a large hard disk.  The program can also be extended in many ways.  One that comes to mind immediately is to print the rest of the CATALOG information as well as the file names.  Another is to create a complete CATALOG MANAGER utility, which would permit re-arranging the filenames, promoting and demoting files, and so on.

I typed in Greg's program, and then I rewrote it.  The listing that follows bears very little resemblance to his code, but I do thank him for the help in getting started.

The program assumes a prefix has been set.  If there is no prefix, you will get a beep and no listing.  If there is a prefix, and the directory named is online, the listing will begin with that directory.  Another enhancement would be to display the current prefix, and allow accepting it or changing it before starting the filename listing.

If we were always starting with the volume directory, it would be a little easier.  The volume directory always starts in block 2.  However, since we are able to start with any directory, we do not know where it starts.  ProDOS allows you to read a directory, and we can get the first block of any directory by using MLI to open the directory file.

Lines 1100-1120 read the current prefix into a buffer.  The lines 1130-1150 open that file.  Although I have never seen it in the books, apparently OPEN also reads the first block.  After the OPEN call, BUFFER.ONE contains the first block of the directory file.  Unless we are willing to do a complete search without ProDOS's help, this is the only way I know of to find the first block of a directory file (other than the volume directory).

Since the only reason to OPEN the directory file was to read the first block, lines 1180-1200 close it again.  If any of these MLI calls don't go through, line 1210 will ring the alarm and stop.

Lines 1230-1260 start up the directory listing.  The first block ONLY will be in BUFFER.ONE.  All subsequent blocks will be read into BUFFER.TWO. In order to make the LIST.DIRECTORY program completely recursive, it is called with the buffer address in a zero-page pointer.  SETUP.NEXT.BLOCK also gets the next block pointer from the buffer and saves it in NEXT.BLOCK.

LIST.DIRECTORY is really quite simple, in spite of its size.  Its main function is to print a list of filenames.  Each filename is preceded by a number of blanks, determined by NEST.LEVEL.  NEST.LEVEL is incremented at line 1290, each time LIST.DIRECTORY is called.  If a file listed happens to be a directory file, LIST.IDRECTORY saves all the pointers and counters on the stack and then calls itself.  When the subdirectory's files have all been listed, that recursive call of LIST.DIRECTORY will return, the pointers and counters can be unstacked, and the listing can continue.

The format of the information in a directory is detailed quite well in both "Beneath Apple ProDOS" and "Apple ProDOS Advanced Features".  (We recommend and sell both books.)  The first four bytes of each block are two block numbers:  that of the previous block, and that of the next block, in the same directory.  This allows scanning in both forward and reverse directions through a directory.  We will only use the next-block pointers in our program.  After the block numbers there are 13 descriptors of 39 bytes each.  The first descriptor in a directory describes the directory itself, and the rest describe files.

For some reason Apple was not quite sure that it would always use 13 39-byte descriptors, so they stored these two numbers in the directory descriptor.  Anyone who access a directory is supposed to look up these two numbers and use them, just in case Apple decides to change them someday.  The directory descriptor also contains an active file count.  When a file is deleted this count is decremented, but the file descriptor remains.  We use the active file count to determine when we reach the end of a directory.  Lines 1300-1360 pick up the bytes per descriptor, descriptors per block, and active file count and save them.

Lines 1370-1450 set up PNTR to point at the first file descriptor, which follows the directory header.  CURRENT.ENTRY.NUMBER will count up to 13, so we will know when it is time to read another block.  We start at 2, because the first block uses the first descriptor for the header.  We also clear the file count.

Lines 1460-1500 check for the special case of an empty directory.  If there are no active files, we are finished.

Lines 1510-1750 print out the file name from the current file descriptor.  The first byte of a descriptor contains a code for the type of file in the first nybble, and the length of the file name in the second nybble.  If both are zero, the file has been deleted.  The other legal values are $1x, $2x, and $3x to signify a seedling, sapling, or tree file, respectively; and $Dx to signify a directory file.  All we care about is whether is a directory file or not, and how long the file name is.

If it is a directory file, lines 1760-2100 will be executed.  Lines 1760-1860 push the counters and pointers on the stack.  Lines 1870-1930 read in the first block of the sub-directory.  Line 1950 calls LIST.DIRECTORY to list the subdirectory.  After it is finished, line 1960 will decrement the nesting level.  Lines 1970-2060 unstack the pointers and counters.  If we were still in the first block of the highest level directory (where we started), we do not need to read the block again:  it is still in BUFFER.ONE.  Otherwise, lines 2070-2100 read the block back in.  If we did not care how much memory we used, we could make this program a lot faster by using more buffers.  We could have a different buffer for each level, so that blocks would never have to be re-read.

Lines 2110-2210 count the file just listed, and then check to see if our count is the same as the active file count from the directory header.  If so, we are finished.

If we are not finished, lines 2220-2290 bump the pointer into the directory block by the size of a descriptor entry.  If we are still in the same block, that is all that we need to do.  If not, lines 2350-2420 read in the next block and set things up for it.  Then it's back to the top again for the next file name!

We hope some time in the not-so-distant future to be able to write a complete catalog manager program like I started to describe back at the beginning of this article.  Some of you are using Bill Morgan's CATALOG ARRANGER for DOS 3.3, and this would be an equivalent utility for ProDOS.  We're not quite ready yet, but this program is a step in the right direction.


