DriverGestaltExplorer.c

/*
    File:       DriverGestaltExplorer.c
    
    Description:DriverGestaltExplorer is both sample code showing how to call Driver
                Gestalt and a useful tool for testing your block device driverÕs support
                of Driver Gestalt.
 
    Author:     Quinn
 
    Copyright:  Copyright: © 1998-1999 by Apple Computer, Inc.
                all rights reserved.
    
    Disclaimer: You may incorporate this sample code into your applications without
                restriction, though the sample code has been provided "AS IS" and the
                responsibility for its operation is 100% yours.  However, what you are
                not permitted to do is to redistribute the source as "DSC Sample Code"
                after having made changes. If you're going to re-distribute the source,
                we require that you make it clear in the source that the code was
                descended from Apple Sample Code, but that you've made changes.
    
    Change History (most recent first):
                6/23/99 Updated for Metrowerks Codewarrior Pro 2.1(KG)
 
*/
 
/////////////////////////////////////////////////////////////////
// Standard Mac OS Interfaces
 
#include <DriverGestalt.h>
 
/////////////////////////////////////////////////////////////////
// Standard C Libraries
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
/////////////////////////////////////////////////////////////////
// We use some of the utility routines from TradDriverLoaderLib,
// which prevents us having to reinvent the wheel.
 
#include "TradDriverLoaderLib.h"
 
/////////////////////////////////////////////////////////////////
#pragma mark ----- Utility Routines -----
 
static void QAssert(Boolean mustBeTrue)
    // Stardard Assert functionality.  How many times have
    // I written this?  How much time would I save if it was
    // built into the OS?
{
    if ( ! mustBeTrue ) {
        DebugStr("\pQAssert: Assertion failure");
    }
}
 
static DriverRefNum MapDriveToRefNum(SInt16 drive)
    // Walks the drive queue looking for the given drive
    // and returns the driver reference number of the driver
    // controlling the drive.
    //
    // I would have used UTFindDrive for this, but calling
    // it from PowerPC is tricky.
{
    DrvQElPtr thisDrv;
    
    thisDrv = (DrvQElPtr) GetDrvQHdr()->qHead;
    while (thisDrv != nil) {
        if (thisDrv->dQDrive == drive) {
            return thisDrv->dQRefNum;
        }
        thisDrv = (DrvQElPtr) thisDrv->qLink;
    }
    return 0;
}
 
static Boolean DriveSupportsDriverGestalt(SInt16 drive)
    // Returns true if the driver controlling the
    // supplied drive supports Driver Gestalt.
{
    OSErr junk;
    DriverRefNum refNum;
    DriverFlags driverFlags;
    
    refNum = MapDriveToRefNum(drive);
    
    junk = TradGetDriverInformation(refNum, nil, &driverFlags, nil, nil);
    QAssert(junk == noErr);
    return TradDriverGestaltIsOn(driverFlags);
}
 
static StringPtr RefNumToName(DriverRefNum refNum)
    // Returns the (Pascal string) name of the driver with the supplied
    // refNum.  Basically a wrapper around TradDriverLoaderLib.
    // 
    // Note that the routine returns the address of a static
    // buffer, so you don't have to dispose it but you must
    // be careful not to call the routine again until you're
    // done with the result.
{
    static Str255 nonReentrantBuffer;
    OSErr junk;
 
    junk = TradGetDriverInformation(refNum, nil, nil, nonReentrantBuffer, nil);
    QAssert(junk == noErr);
    return nonReentrantBuffer;
}
 
static char *OSTypeToString(OSType selector)
    // Returns a C string for the supplied selector.
    // 
    // Note that the routine returns the address of a static
    // buffer, so you don't have to dispose it but you must
    // be careful not to call the routine again until you're
    // done with the result.
{
    static char nonReentrantBuffer[5];
 
    *((OSType*) nonReentrantBuffer) = selector; 
    nonReentrantBuffer[4] = 0;
    return nonReentrantBuffer;
}
 
/////////////////////////////////////////////////////////////////
#pragma mark ----- Data Table Types -----
 
// An array of OSTypeElement's is passed as the argument
// to OSTypeDisplay.  The array is terminated by an element
// whose name is nil.  This array allows OSTypeDisplay to
// map an OSType to its string description.
 
struct OSTypeElement {
    OSType value;
    char*  name;
};
typedef struct OSTypeElement OSTypeElement;
 
// The address of a BooleanTable is passed as the argument
// to BooleanDisplay.  The table allows BooleanDisplay to
// map a Boolean result to a string description.
 
struct BooleanTable {
    char *trueString;
    char *falseString;
};
typedef struct BooleanTable BooleanTable;
 
// An array of FlagElement's is passed as the argument
// to Flags16Display and Flags32Display, and hence on
// to PrintFlags.  The array is terminated by an element
// whose mask is 0.  This array allows PrintFlags to
// test for the known flags in a flags UInt32 and print
// out the corresponding textual descriptions.
 
struct FlagElement {
    UInt32 mask;
    char *trueString;
    char *falseString;
};
typedef struct FlagElement FlagElement;
 
// A GestaltElement holds information about a single
// DriverGestalt selector, including the selector itself,
// a textual description of the selector (name), the address
// of a routine that can print the response, and the argument
// for that routine.
//
// The primary use of this type is in gGestaltList,
// an array of these elements (terminated by an entry with
// a selector of 0) which describes all the Driver Gestalt
// selectors documented at the time this sample was last updated.
 
typedef void (*ResponseDisplayProc)(DriverGestaltParam *pb, void *arg);
 
struct GestaltElement {
    OSType selector;
    char*  name;
    ResponseDisplayProc responseDisplayRoutine;
    void*  responseDisplayArg;
};
typedef struct GestaltElement GestaltElement;
 
/////////////////////////////////////////////////////////////////
#pragma mark ----- Response Printing -----
 
// The routines in this section are responsible for taking a
// Driver Gestalt response and printing it to stdout.  They
// all conform to the ResponseDisplayProc type and are referenced
// by gGestaltList table.
//
// The routines are given in the order of most generic to most
// specific.
 
static void DefaultDisplay(DriverGestaltParam *pb, void *arg)
    // Prints a Driver Gestalt response for a selector we
    // don't know about.  All we can do is print the four
    // standard response fields as hex.
{
    #pragma unused(arg)
    printf("  driverGestaltResponse  = %08lx\n", pb->driverGestaltResponse);
    printf("  driverGestaltResponse1 = %08lx\n", pb->driverGestaltResponse1);
    printf("  driverGestaltResponse2 = %08lx\n", pb->driverGestaltResponse2);
    printf("  driverGestaltResponse3 = %08lx\n", pb->driverGestaltResponse3);
}
 
static void OSTypeDisplay(DriverGestaltParam *pb, void *arg)
    // Prints a Driver Gestalt response in driverGestaltResponse
    // whose value is an OSType.  arg is a pointer to an array
    // of OSTypeElements that describes the expected values
    // and their textual descriptions.
{
    OSType response;
    OSTypeElement *responseList;
    Boolean found;
    ItemCount thisIndex;
    
    response = (OSType) pb->driverGestaltResponse;
    responseList = (OSTypeElement *) arg;
 
    // Search for response in the responseList.
    
    found = false;
    thisIndex = 0;
    while ( ! found && responseList[thisIndex].name != nil ) {
        found = (responseList[thisIndex].value == response);
        if ( ! found ) {
            thisIndex += 1;
        }
    }
    
    // If we found it, print the response with its textual description,
    // otherwise just print the response.
    
    if (found) {
        printf("  Response = '%s' Ò%sÓ\n", OSTypeToString(response), responseList[thisIndex].name);
    } else {
        printf("  Response = '%s'\n", OSTypeToString(response));
    }
}
 
static void BooleanDisplay(DriverGestaltParam *pb, void *arg)
    // Prints a Driver Gestalt response which is a Boolean in the
    // first byte of driverGestaltResponse.  arg is a pointer to
    // an array of BooleanElements that gives the textual descriptions
    // for true and false.
{
    BooleanTable *booleanTable;
    Boolean value;
    
    booleanTable = (BooleanTable *) arg;
    value = *GetDriverGestaltBooleanResponse(pb);
    switch (value) {
        case true:
            printf("  Response = %d Ò%sÓ\n", value, booleanTable->trueString);
            break;
        case false:
            printf("  Response = %d Ò%sÓ\n", value, booleanTable->falseString);
            break;
        default:
            printf("  Response = %d (Weird)\n", value);
            break;
    }
}
 
static void PrintFlags(UInt32 flags, FlagElement flagList[])
    // A utility routine called by Flags16Display and Flags32Display.
    // flags is the response flags to be printed.  The routine
    // prints both the value of flags and a textual description
    // of the flags based on flagList, an array of FlagElements
    // which describes the known flag masks and the textual
    // descriptions for those masks.
{
    ItemCount thisIndex;
    
    printf("  Response = %08lx\n", flags);
    thisIndex = 0;
    while ( flagList[thisIndex].mask != 0 ) {
        if ( (flags & flagList[thisIndex].mask) != 0 ) {
            printf("    %s\n", flagList[thisIndex].trueString);
        } else {
            printf("    %s\n", flagList[thisIndex].falseString);
        }
        thisIndex += 1;
    }
}
 
static void Flags16Display(DriverGestaltParam *pb, void *arg)
    // Prints a Driver Gestalt response which is a UInt32 in the
    // first two bytes of driverGestaltResponse.  arg is a pointer to
    // an array of FlagElements which describes the known flag masks and
    // the textual descriptions for those masks.
{
    UInt32 flags;
    
    flags = *((UInt16 *) &pb->driverGestaltResponse);
    PrintFlags(flags, arg);
}
 
static void Flags32Display(DriverGestaltParam *pb, void *arg)
    // Prints a Driver Gestalt response which is a UInt32 in the
    // driverGestaltResponse.  arg is a pointer to an array of
    // FlagElements which describes the known flag masks and
    // the textual descriptions for those masks.
{
    UInt32 flags;
    
    flags = *((UInt32 *) &pb->driverGestaltResponse);
    PrintFlags(flags, arg);
}
 
static void PowerDisplay(DriverGestaltParam *pb, void *arg)
    // Prints a Driver Gestalt power response, used by the
    // various power rating selectors to return the
    // power in microWatts.  arg is unused.
{
    #pragma unused(arg)
    DriverGestaltPowerResponse* response;
    
    response = GetDriverGestaltPowerResponse(pb);
    printf("  Response = %ld microWatts\n", response->powerValue);
}
 
static void VersionDisplay(DriverGestaltParam *pb, void *arg)
    // Prints a Driver Gestalt response which is a NumVersion in
    // driverGestaltResponse.  arg is unused.
{
    #pragma unused(arg)
    NumVersion version;
    char versionString[256];
    char tmpString[256];
    char stageChar;
    
    version = *((NumVersion *) &pb->driverGestaltResponse);
    
    sprintf(versionString, "%d.%d", version.majorRev, (version.minorAndBugRev >> 4) & 0x0f);
    if ( (version.minorAndBugRev & 0x0f) != 0 ) {
        sprintf(tmpString, ".%d", version.minorAndBugRev & 0x0f);
        strcat(versionString, tmpString);
    }
    if ( version.stage != finalStage || version.nonRelRev != 0 ) {
        switch (version.stage) {
            case finalStage:
                stageChar = 'f';
                break;
            case betaStage:
                stageChar = 'b';
                break;
            case alphaStage:
                stageChar = 'a';
                break;
            case developStage:
                stageChar = 'd';
                break;
            default:
                stageChar = '?';
                break;
        }
        sprintf(tmpString, "%c%d", stageChar, version.nonRelRev);
        strcat(versionString, tmpString);
    }
 
    printf("  Response = %s (%08lx)\n", versionString, pb->driverGestaltResponse);
}
 
static void BootDisplay(DriverGestaltParam *pb, void *arg)
    // Prints the response to the Driver Gestalt kdgBoot selector,
    // which is a DriverGestaltBootResponse.  arg is unused.
{
    #pragma unused(arg)
    DriverGestaltBootResponse* response;
    
    response = GetDriverGestaltBootResponse(pb);
    printf("  extDev    = %d\n", response->extDev);
    printf("  partition = %d\n", response->partition);
    printf("  SIMSlot   = %d\n", response->SIMSlot);
    printf("  SIMsRSRC  = %d\n", response->SIMsRSRC);
}
 
static void PurgeDisplay(DriverGestaltParam *pb, void *arg)
    // Prints the response to the Driver Gestalt kdgPurge selector,
    // which is a DriverGestaltBootResponse.  arg is unused.
{
    #pragma unused(arg)
    DriverGestaltPurgeResponse *response;
    
    response = GetDriverGestaltPurgeResponse(pb);
 
    PrintFlags(response->purgePermission, arg);
    printf("  purgeDriverPointer = %08lx\n", response->purgeDriverPointer);
}
 
static void FlushDisplay(DriverGestaltParam *pb, void *arg)
    // Prints the response to the Driver Gestalt kdgFlush selector,
    // which is a GetDriverGestaltFlushResponse.  arg is unused.
{
    #pragma unused(arg)
    DriverGestaltFlushResponse* response;
    char *tmpStr;
    
    response = GetDriverGestaltFlushResponse(pb);
    if (response->canFlush) {
        tmpStr = "Driver supports kdcFlush Driver Configure call";
    } else {
        tmpStr = "Driver does not support kdcFlush Driver Configure call";
    }
    printf("  canFlush = %d Ò%sÓ\n", response->canFlush, tmpStr);
    if (response->needsFlush) {
        tmpStr = "Driver needs flush when volume flushed";
    } else {
        tmpStr = "Driver does not need flush when volume flushed";
    }
    printf("  needsFlush = %d Ò%sÓ\n", response->needsFlush, tmpStr);
}
 
static void MediaInfoDisplay(DriverGestaltParam *pb, void *arg)
    // Prints the response to the Driver Gestalt kdgMediaInfo selector,
    // which is a DriverGestaltMediaInfoResponse.  arg is unused.
{
    #pragma unused(arg)
    DriverGestaltMediaInfoResponse* response;
    
    response = GetDriverGestaltMediaInfoResponse(pb);
    printf("  numberBlocks = %ld\n", response->numberBlocks);
    printf("  blockSize = %ld\n", response->blockSize);
    switch (response->mediaType) {
        case kMediaTypeUnknown:
            printf("  mediaType = ÒunknownÓ\n");
            break;
        case kMediaTypeCDROM:
            printf("  mediaType = ÒCD-ROMÓ\n");
            break;
        case kMediaTypeDVDROM:
            printf("  mediaType = ÒDVD-ROMÓ\n");
            break;
        case kMediaTypeNoMedia:
            printf("  mediaType = Òno mediaÓ\n");
            break;
    }
}
 
/////////////////////////////////////////////////////////////////
#pragma mark ----- Data Tables -----
 
#pragma mark OSType Tables
 
// gInterfaceTypeList describes the expected results
// from the kdgInterface selector.
 
static OSTypeElement gInterfaceTypeList[] = {
    {kdgScsiIntf, "SCSI"},
    {kdgPcmciaIntf, "PC Card"},
    {kdgATAIntf, "ATA"},
    {kdgFireWireIntf, "Firewire"},
    {kdgExtBus, "External Bus"},
    {0, nil}
};
 
// gDeviceTypeList describes the expected results
// from the kdgDeviceType selector.
 
static OSTypeElement gDeviceTypeList[] = {
    {kdgDiskType, "Disk"},
    {kdgTapeType, "Tape"},
    {kdgPrinterType, "Printer"},
    {kdgProcessorType, "Processor"},
    {kdgWormType, "Write-Once, Read Many"},
    {kdgCDType, "CD-ROM"},
    {kdgFloppyType, "Floppy"},
    {kdgScannerType, "Scanner"},
    {kdgFileType, "File (ie disk image)"},
    {kdgRemovableType, "Removable Disk"},
    {0, nil}
};
 
#pragma mark Boolean Tables
 
// Boolean results from various selectors.
 
static BooleanTable gSyncBooleanTable = { "Synchronous", "Asynchronous" };
static BooleanTable gWideBooleanTable = { "Supports wide ioPosMode", "No support for wide ioPosMode" };
static BooleanTable gPowerSwitchBooleanTable = { "Supports power switching", "No support for power switching" };
static BooleanTable gHighPowerBooleanTable = { "In high power mode", "In low power mode" };
static BooleanTable gSupportsPowerControlBooleanTable = { "Supports power control", "No support for power control" };
 
#pragma mark Flag Tables
 
// Flag results from various selectors.
 
static FlagElement gPCXFlags[] = {
    { 0x01, "Supports PC Exchange calls", "No support for PC Exchange" },
    { 0, nil, nil}
};
 
static FlagElement gEjectFlags[] = {
    { kRestartDontEject_Mask,   "Don't want eject at Restart",      "Wants eject at Restart" },
    { kShutDownDontEject_Mask,  "Don't want eject at Shutdown",     "Wants eject at Shutdown" },
    { 0, nil, nil}
};
 
static FlagElement gVMOptionsFlags[] = {
    { kAllowVMNoneMask,         "VM should never use this drive",       "VM may use this drive" },
    { kAllowVMReadOnlyMask,     "VM may use this drive read-only",      "VM may not use this drive for read-only" },
    { kAllowVMReadWriteMask,    "VM may use this drive read/write",     "VM may not use this drive for read/write" },
    { 0, nil, nil}
};
 
static FlagElement gPurgeFlags[] = {
    { 1 << kbCloseOk,   "Close OK",         "Close not allowed" },
    { 1 << kbRemoveOk,  "RemoveDriver OK",  "RemoveDriver not allowed" },
    { 1 << kbPurgeOk,   "DisposePtr OK",    "DisposePtr not allowed" },
    { 0, nil, nil}
};
 
#pragma mark gGestaltList
 
// The primary list of Driver Gestalt selectors that we understand.
// This table allows us to map selectors to their textual descriptions,
// and allows us to print a Driver Gestalt response by dispatching
// to the appropriate display routines. 
 
static GestaltElement gGestaltList[] = {
    {kdgVersion,            "Version",              VersionDisplay, nil },
    {kdgDeviceType,         "Device Type",          OSTypeDisplay, gDeviceTypeList },
    {kdgInterface,          "Interface Type",       OSTypeDisplay, gInterfaceTypeList },
    {kdgSync,               "Synchronous",          BooleanDisplay, &gSyncBooleanTable },
    {kdgBoot,               "Boot",                 BootDisplay, nil },
    {kdgWide,               "Wide",                 BooleanDisplay, &gWideBooleanTable },
    {kdgPurge,              "Purge Permission",     PurgeDisplay, gPurgeFlags },
    {kdgSupportsSwitching,  "Power Management",     BooleanDisplay, &gPowerSwitchBooleanTable },
    {kdgMin3VPower,         "Min 3.3 V Power",      PowerDisplay, nil },
    {kdgMin5VPower,         "Min 5 V Power",        PowerDisplay, nil },
    {kdgMax3VPower,         "Max 3.3 V Power",      PowerDisplay, nil },
    {kdgMax5VPower,         "Max 5 V Power",        PowerDisplay, nil },
    {kdgInHighPower,        "High Power Mode",      BooleanDisplay, &gHighPowerBooleanTable },
    {kdgSupportsPowerCtl,   "Power API Support",    BooleanDisplay, &gSupportsPowerControlBooleanTable },
    {kdgAPI,            "PC Exchange API Support",  Flags16Display, gPCXFlags },
    {kdgEject,          "Shutdown Eject Options",   Flags32Display, gEjectFlags },
    {kdgFlush,              "Flush Options",        FlushDisplay, nil },
    {kdgVMOptions,          "VM Options",           Flags32Display, gVMOptionsFlags },
    {kdgMediaInfo,          "Media Information",    MediaInfoDisplay, nil},
    {0,                     nil,                    nil, nil }
};
 
/////////////////////////////////////////////////////////////////
#pragma mark ----- User Interface Code -----
 
static void PrintListOfDrives(void)
    // Prints a nicely formatted list of the drives
    // available on this machine by walking the drive
    // queue.
{
    DrvQElPtr thisDrv;
    
    printf("Drive List\n");
    printf("  0) <<<all drives>>>\n");
    
    thisDrv = (DrvQElPtr) GetDrvQHdr()->qHead;
    while (thisDrv != nil) {
        printf("  %d) with driver %d Ò%#sÓ\n", thisDrv->dQDrive, thisDrv->dQRefNum, RefNumToName(thisDrv->dQRefNum));
        thisDrv = (DrvQElPtr) thisDrv->qLink;
    }
}
 
static void PrintListOfSelectors(void)
    // Prints a nicely formatted list of Driver Gestalt
    // selectors that we know about by walking our
    // gGestaltList table.
{
    ItemCount thisElement;
 
    printf("Selector List\n");
    thisElement = 0;
    while ( gGestaltList[thisElement].selector != 0 ) {
        printf("  '%s' Ò%sÓ\n",
                    OSTypeToString(gGestaltList[thisElement].selector),
                    gGestaltList[thisElement].name
                    );
        thisElement += 1;
    }
}
 
static SInt16 GetDriveNumber(void)
    // Asks the user to enter a drive number, returning
    // a negative value on error, a positive value for a specific
    // drive number, and 0 if the user wants to query all drives.
{
    SInt16 result;
    char driveStr[256];
    
    PrintListOfDrives();
    
    printf("Enter a drive number:\n");
    gets(driveStr);
    if (driveStr[0] == 0) {
        result = -1;
    } else {
        result = atoi(driveStr);
    }
    return result;
}
 
typedef void (*ForEachDriveProc)(UInt32 refcon, SInt16 drive);
 
static void ForEachDriveDo(SInt16 drive, ForEachDriveProc proc, UInt32 refcon)
    // This routine provides support for the users
    // desire to issue a Driver Gestalt query on all drives,
    // as specified by typing "0" in response to GetDriveNumber.
    // That drive number is passed in as the drive parameter
    // to this routine.  If it's non-zero, this routine calls
    // the supplied proc with that drive as a parameter.  If drive
    // is 0, this routine walks the drive queue, repeatedly calling
    // proc, once for each drive.
{
    DrvQElPtr thisDrv;
 
    if (drive != 0) {
        proc(refcon, drive);
    } else {
        thisDrv = (DrvQElPtr) GetDrvQHdr()->qHead;
        while (thisDrv != nil) {
            proc(refcon, thisDrv->dQDrive);
            printf("\n");
            thisDrv = (DrvQElPtr) thisDrv->qLink;
        }
    }
}
 
static void SingleQuery(SInt16 drive, GestaltElement *element)
    // Perform a single Driver Gestalt query on a single
    // drive.  element is typically an entry from gGestaltList,
    // which specifies which selector to query and how to print
    // the result.
{
    OSErr err;
    DriverGestaltParam pb;
 
    // Set up the parameter block.
    
    pb.ioVRefNum = drive;
    pb.ioCRefNum = MapDriveToRefNum(drive);
    pb.csCode = kDriverGestaltCode;
    pb.driverGestaltSelector = element->selector;
 
    // Print out the query we're about to do.
    
    printf("'%s' (%s) on drive %d with driver %d Ò%#sÓ\n", 
                        OSTypeToString(element->selector),
                        element->name,
                        drive,
                        pb.ioCRefNum,
                        RefNumToName(pb.ioCRefNum)
                        );
                        
    // Check that the driver support Driver Gestalt.  If it doesn't,
    // don't send the driver the query otherwise we might accidentally
    // trigger some action we didn't expect.
    
    if ( DriveSupportsDriverGestalt(drive) ) {
    
        // The drive support Driver Gestalt.  Let's do the query
        // and print out the result.
        
        err = PBStatusSync((ParmBlkPtr) &pb);
        if (err == noErr) {
            QAssert(element->responseDisplayRoutine != nil);
            element->responseDisplayRoutine(&pb, element->responseDisplayArg);
        } else {
            printf("  Failed with error %d.\n", err);
        }
    } else {
        printf("  Driver %d does not support Driver Gestalt.\n", pb.ioCRefNum);
    }
    
}
 
static void OneQueryOnOneDrive(OSType selector, SInt16 drive)
    // This routine issues one specific Drive Gestalt query,
    // specified by selector, on one specific drive.  The basic
    // operation is to search the gGestaltList array for the
    // selector.  If we find it, we can pass the GestaltElement
    // to SingleQuery, which uses it to format the response.
    // If we can't find the query, we cook up our own local
    // GestaltElement (which indicates that the response should
    // be displayed by the DefaultDisplay routine) and use
    // that instead.
{
    ItemCount thisElement;
    Boolean found;
    GestaltElement localElement;
    
    // Search gGestaltList for selector.
    
    thisElement = 0;
    found = false;
    while ( gGestaltList[thisElement].selector != 0 && ! found ) {
        found = (gGestaltList[thisElement].selector == selector);
        if ( ! found ) {
            thisElement += 1;
        }
    }
    
    // If we found it, use it to issue the query, otherwise
    // build our own local GestaltElement and issue the query
    // with that.
    
    if (found) {
        SingleQuery(drive, &gGestaltList[thisElement]);
    } else {
        localElement.selector = selector;
        localElement.name = "Unknown";
        localElement.responseDisplayRoutine = DefaultDisplay;
        localElement.responseDisplayArg = nil;
 
        SingleQuery(drive, &localElement);
    }
}
 
static void AllQueriesOnOneDrive(UInt32 refcon, SInt16 drive)
    // This routine queries all known Driver Gestalt selectors
    // on a specific drive.  The refcon parameter is unused
    // but it's required because this routine is a callback
    // ForEachDriveDo.
{
    #pragma unused(refcon)
    UInt32 thisElement;
    DriverRefNum refNum;
    
    if ( DriveSupportsDriverGestalt(drive) ) {
    
        // Walk our global list of known Driver Gestalt selectors
        // (gGestaltList) and call SingleQuery on each one.
        
        thisElement = 0;
        while ( gGestaltList[thisElement].selector != 0 ) {
            SingleQuery(drive, &gGestaltList[thisElement]);
            thisElement += 1;
        }
    } else {
    
        // The driver does not support Driver Gestalt.  Stop now before
        // generating a huge cascade of "Driver does not support Driver Gestalt"
        // messages.
        
        refNum = MapDriveToRefNum(drive);
        printf("Drive %d with driver %d Ò%#sÓ does not support Driver Gestalt.\n", 
                    drive,
                    refNum,
                    RefNumToName(refNum)
                    );
    }
}
 
static void DoOneQuery(void)
    // This is a primary user interface entry point.  Ask
    // the user for a drive and selector and then issue
    // the query.
{
    SInt16 drive;
    char   selectorStr[256];
    OSType selector;
    
    drive = GetDriveNumber();
    if (drive >= 0) {
        do {
            printf("Enter a selector (Ò?Ó for a list):\n");
            gets(selectorStr);
            if ( strcmp(selectorStr, "?") == 0 ) {
                PrintListOfSelectors();
            }
        } while ( strcmp(selectorStr, "?") == 0 );
        if (selectorStr[0] != 0) {
            selector = *((OSType *) &selectorStr[0]);
            ForEachDriveDo(drive, OneQueryOnOneDrive, selector);
        }
    }
}
 
static void DoAllQueries(void)
    // This is a primary user interface entry point.
    // Ask the user for a drive and then query all
    // known Driver Gestalt selectors on that drive.
{
    SInt16 drive;
    char   selectorStr[256];
    OSType selector;
    
    drive = GetDriveNumber();
    if (drive >= 0) {
        selector = *((OSType *) &selectorStr[0]);
        ForEachDriveDo(drive, AllQueriesOnOneDrive, 0);
    }
}
 
static void PrintHelp(void)
    // This is a primary user interface entry point.
    // Print the list of supported commands.
{
    printf("g) Issue a Driver Gestalt query\n");
    printf("G) Query all known Driver Gestalt selectors\n");
    printf("?) Help\n");
    printf("q) Quit\n");
}
 
void main(void)
    // The main entry point.  A trivial console application
    // that waits for a command to be typed and then performs
    // the command.
{
    Boolean quitNow;
    char    command[256];
    
    printf("Hello Cruel World!\n");
    printf("\n");
    printf("Driver Gestalt Explorer\n");
    printf("-- A simple command line program for exploring\n");
    printf("-- the Drive Gestalt values returned by block\n");
    printf("-- device drivers\n");
    printf("\n");
    
    PrintHelp();
    quitNow = false;
    do {
        printf("\n");
        printf("Enter a command:\n");
        gets(command);
        switch (command[0]) {
            case 'g':
                DoOneQuery();
                break;
            case 'G':
                DoAllQueries();
                break;
            case '?':
                PrintHelp();
                break;
            case 'q':
                quitNow = true;
                break;
            default:
                printf("Huh?\n");
                break;
        }
    } while ( ! quitNow );
    
    printf("Done.  Press command-Q to Quit.\n");
}