/* File: DcmdShell.c Contains: This is the DcmdShell dcmd. This shell gives you a good start for a new Dcmd with a command line parser. Version: 1.0 Written by: Jim Luther Copyright: © 1998-1999 by Apple Computer, Inc., All Rights Reserved. File Ownership: DRI: Jim Luther Other Contact: Dave Lyons or Jim Murphy Technology: MacsBug Dcmds Writers: (JM3) Jim Murphy (JL) Jim Luther Change History (most recent first): <2> 10/12/99 JM3 Added some unsigneds to make CodeWarrior builds happy. <1> 10/11/99 JL first checked in <0> 10/15/98 JL New today. To do: */ //******************************************************************************************************* #include #include #include "dcmd.h" #include "put.h" //******************************************************************************************************* // // Constants and defines // // The Dcmd's version number #define kDcmdVersionNumber \ { \ 0x01, /* 1st part of version number in BCD */ \ 0x00, /* 2nd & 3rd part of version number share a byte */ \ betaStage, /* stage code: developStage, alphaStage, betaStage, finalStage */ \ 0x01 /* revision level of non-released version */ \ } // The Dcmd's usage string (displayed by "DCMD DcmdShell" or by "? dcmds" or "help dcmds") #define kDcmdUsageStr "\p[expression1 [expression2]] [-X] [-Y value] [-Z ON|OFF]" // the Dcmd's error prefix string #define kDcmdErrPrefixStr "\pDcmdShell - Error: " // The maximum number of numeric expressions to parse from command line enum { kMaxExpressions = 2 // maximum number of expressions to parse from command line }; // StringsEqual - a case-insensitive, diacritical-sensitive string compare that's safe to // call at interrupt time that the parser uses for comparing parameter strings #define StringsEqual(p1, p2) EqualString(p1, p2, false, true) //******************************************************************************************************* // // Globals // static const unsigned char gUsageStr[] = kDcmdUsageStr; static const unsigned char gDcmdErrPrefixStr[] = kDcmdErrPrefixStr; static NumVersionVariant gDcmdVersionNumber = kDcmdVersionNumber; //******************************************************************************************************* // // Prototypes // Boolean ParseParameters(UInt32 *expressionCount, UInt32 *expressions, Boolean *optionX, UInt32 *optionY, Boolean *optionZ); pascal void DcmdCore(dcmdBlockPtr paramPtr); pascal void CommandEntry(dcmdBlockPtr paramPtr); //******************************************************************************************************* // // ParseParameters // // Scan through the entire command line to validate all the parameters. // Returns true if the line parsed, false if it didnŐt. // // Command line syntax: // DcmdShell [expression1 [expression2]] [-X] [-Y value] [-Z ON|OFF] // Boolean ParseParameters(UInt32 *expressionCount, UInt32 *expressions, Boolean *optionX, UInt32 *optionY, Boolean *optionZ) { Boolean parseOK; short position; short delimiter; Str255 parameterStr; Boolean okExpression; long value; UInt32 i; // Default settings for parameters *expressionCount = 0; for ( i = 0; i < kMaxExpressions; ++i ) { expressions[i] = 0; } *optionX = false; *optionY = 0; *optionZ = false; // The parser first looks for string parameters. // If the current parameter doesn't match any strings, then it is evaluated as an expression. // By getting string parameters first, we can ensure that an dash option like "-A" is not seen as the value #-10. parseOK = true; do { position = dcmdGetPosition(); // Save current position in case we need to back up and try again as an expression delimiter = dcmdGetNextParameter(parameterStr); // Get the next parameter (if any) and the delimiter if ( parameterStr[0] != 0 ) { // Is it a '-' or a string parameter? if ( StringsEqual(parameterStr, "\p-X") ) { // -X has no additional parameters // change the setting to the non-default value *optionX = !*optionX; } else if ( StringsEqual(parameterStr, "\p-Y") ) { // -Y has a expression parameter delimiter = dcmdGetNextExpression(&value, &okExpression); if (okExpression) { // return the expression passed for the -Y option *optionY = value; } else { // The expression was bad PutPStr(gDcmdErrPrefixStr); PutPStr("\p-Y expression was invalid"); PutLine(); parseOK = false; } } else if ( StringsEqual(parameterStr, "\p-Z") ) { // -Z has a string parameter delimiter = dcmdGetNextParameter(parameterStr); if ( StringsEqual(parameterStr, "\pON") ) { *optionZ = true; } else if ( StringsEqual(parameterStr, "\pOFF") ) { *optionZ = false; } else { // It wasn't a parameter we expected PutPStr(gDcmdErrPrefixStr); PutPStr("\p-Z parameter was invalid"); PutLine(); parseOK = false; } } else { // Restore position if parameter not handled as string dcmdSetPosition(position); // and then try it as an expression delimiter = dcmdGetNextExpression(&value, &okExpression); if (okExpression) { // Return the expression if we haven't reached the maximum if ( *expressionCount < kMaxExpressions ) { expressions[*expressionCount] = value; ++(*expressionCount); } else { // Too many expression parameters PutPStr(gDcmdErrPrefixStr); PutPStr("\pToo many expression parameters"); PutLine(); parseOK = false; } } else { // Bad comand line PutPStr(gDcmdErrPrefixStr); PutPStr("\pParameter could not be parsed"); PutLine(); parseOK = false; } } } } while ( (delimiter != 0x0d) && parseOK ); // look until the end of the line or until there's a problem if ( !parseOK ) { // Burn the rest of the command line so MacsBug won't complain // that "Command did not use all parameters" do { delimiter = dcmdGetNextParameter(parameterStr); } while ( delimiter != 0x0d ); } return ( parseOK ); } // ParseParameters //******************************************************************************************************* // // DcmdCore // // The main "do it" code // pascal void DcmdCore(dcmdBlockPtr paramPtr) { // This sample doesn't use the dcmdBlockPtr parameter, but go look at all the cool stuff // your Dcmd is passed (dcmd.h). // // In particular, if your Dcmd is going to do any time-comsuming tasks (like displaying large // amounts of information) and you want the user to be able to abort, your Dcmd should watch // the "aborted" field and exit if it changes to TRUE. #pragma unused(paramPtr) UInt32 expressionCount; UInt32 expressions[kMaxExpressions]; Boolean optionX; UInt32 optionY; Boolean optionZ; UInt32 i; // Get command line parameters (if any) if ( ParseParameters(&expressionCount, expressions, &optionX, &optionY, &optionZ) == true ) { PutPStr("\pDcmdShell:"); PutLine(); // Display the parsed parameters // Display the expression parameters (if any) if ( expressionCount == 0 ) { PutPStr("\pNo expression parameters"); PutLine(); } else { for ( i = 0; i < expressionCount; ++i ) { PutPStr("\pExpression "); PutUDec(i + 1); PutPStr("\p = $"); PutUHexZ(expressions[i], 8); PutLine(); } } // Display the string parameters PutPStr("\pOptionX = "); PutPStr(optionX ? "\pTRUE" : "\pFALSE"); PutLine(); PutPStr("\pOptionY = $"); PutUHexZ(optionY, 8); PutLine(); PutPStr("\pOptionZ = "); PutPStr(optionZ ? "\pON" : "\pOFF"); PutLine(); } } // DcmdCore //******************************************************************************************************* // // CommandEntry // // The main entry point for this dcmd // pascal void CommandEntry(dcmdBlockPtr paramPtr) { switch ( paramPtr->request ) { case dcmdInit: // Initialize the Dcmd // (called when MacsBug is loaded) break; case dcmdDoIt: // Normal Dcmd execution // (called when MacsBug wants us to do our job) dcmdSwapWorlds(); DcmdCore(paramPtr); dcmdSwapWorlds(); break; case dcmdHelp: // Display help for Dcmd dcmdDrawLine("\pA sample dcmd shell"); break; case dcmdSecondaryInit: // Second time to init after all System patches have been loaded // (called just before extensions are executed) break; case dcmdShutdown: // Dcmd should remove or disable its patches (if any). break; case dcmdGetInfo: // Return Dcmd version and usage string dcmdFillVersion(paramPtr, gDcmdVersionNumber.whole); dcmdFillString(paramPtr, usageStr, gUsageStr); break; default: // Version 3 and later Dcmds must quietly ignore unrecognized requests break; } } // CommandEntry //*******************************************************************************************************