#include "cOSErrLookup.h" #include #include #include #include #include #include #include #include #include // =========================================================================== // cOSErrLookup.cpp Version 1.1 ©1999 Joakim Braun All rights reserved. // =========================================================================== // // CONTENTS: cOSErrLookup: A PowerPlant class for looking up operating system errors // and viewing their text constants and descriptions. // REQUIRES: Several PowerPlant classes. // HOW TO USE: Accompanying this file should be a resource file called "Errors.rsrc". // Include it in your app, and you will be able to use cOSErrLookup's two main static methods: // Boolean GetOSErrDescription(ResIDT inResID, OSErr inErr, Str255 outErrorConstant, Str255 outErrorDescription); // void SignalOSErr(OSErr inErr, ResIDT inResID = eErrorResourceID); // They do what you'd expect them to: Use data in the resource to extract the C constant for // the error (e g, -108 is "memFullErr"), as well as the descriptive text, if any. // SignalOSErr() will signal using these strings if inErr != noErr. // cOSErrLookup also includes methods for compiling your own 'err#' resources from simple text files, // as well as decompiling them and dumping them to a text file. Note that you can't compile // header files directly, see description below under "Compile()". // CAVEAT: The error resource included was compiled using TexEdit, AppleScript and lots of search and replace // on the "Errors.h" header of the Universal Interfaces 3.0.1. "Errors.h" doesn't have very much // in the way of descriptive error strings, and hunting through Inside Mac is something I'll leave // for someone else to do. If you have Resorcerer, you can easily alter or supplement the included // 'err#' resource to suit your needs. // These classes are free for any and all use. // Do not distribute modified source code under my name. // No support promised, no liability accepted. Provided "as is". // That said, I can be reached at braun@swipnet.se. // Change history: // 1.0 January 4, 1999 First release // 1.1 February 10, 1999 Added copying error text to clipboard // No longer requires MoreFiles. //************************************************************************************** // Load the error constant and the error description (if any) into Str255's, // and return true/false according to whether the error was found in the resource Boolean cOSErrLookup::GetOSErrDescription(ResIDT inResID, OSErr inErr, Str255 outErrorConstant, Str255 outErrorDescription){ TArray specArray; ArrayIndexT theIndex = LArray::index_Bad; outErrorConstant[0] = 0; outErrorDescription[0] = 0; ThrowIf_(MakeSpecArray(inResID, specArray) != true); specArray.SetComparator(new cErrorSpecComparator); specArray.Sort(); theIndex = specArray.FetchIndexOfKey(&inErr); if(theIndex != LArray::index_Bad){ ::BlockMove(specArray[theIndex].errorConstant, outErrorConstant, specArray[theIndex].errorConstant[0] + 1); ::BlockMove(specArray[theIndex].errorDescription, outErrorDescription, specArray[theIndex].errorDescription[0] + 1); } return theIndex != LArray::index_Bad; } //************************************************************************************** // Signal if we have an error, using description strings in resource. // Also puts description on clipboard. void cOSErrLookup::SignalOSErr(OSErr inErr, ResIDT inResID){ LStr255 str; Str255 errConstant, errDesc; if(inErr != noErr){ if(GetOSErrDescription(inResID, inErr, errConstant, errDesc)){ str.Append((long)inErr).Append("\p (").Append(errConstant).Append("\p)\r").Append(errDesc); SignalPStr_(str); LClipboard* theClipboard = LClipboard::GetClipboard(); if(theClipboard) theClipboard->SetData('TEXT', (Ptr)&str[1], (Int32)str[0], true); } else SignalPStr_("\p'err#' resource not found!"); } } //************************************************************************************** // Read an 'err#' resource and make an array of errorSpecs out of it // Return true/false according to whether resource was found. Boolean cOSErrLookup::MakeSpecArray(ResIDT inResID, TArray& outArray){ Handle theResource = ::Get1Resource(eErrorResourceType, inResID); errorSpec theSpec; Int16 nSpecs = 0; outArray.RemoveAllItemsAfter(LArray::index_First - 1); if(theResource){ ::DetachResource(theResource); LHandleStream theStream(theResource); theStream >> nSpecs; for(int i = 0; i < nSpecs; i++){ theStream >> theSpec.error; theStream >> theSpec.errorConstant; theStream >> theSpec.errorDescription; outArray.AddItem(theSpec); } } return theResource != NULL; } #pragma mark - //************************************************************************************** // Run a file dialog to compile a text file into an 'err#' resource placed in the current resource file void cOSErrLookup:: CompileTextFile(ResIDT inTgtResID){ // Browse for a document. SFTypeList theTypeList = {'TEXT'}; StandardFileReply theReply; // Deactivate the desktop. StDesktopDeactivator deactivatedDesktop; ::StandardGetFile(NULL, 1, theTypeList, &theReply ); if(theReply.sfGood){ Handle thePath = NULL; short savedVol; ThrowIfOSErr_(::GetVol(0, &savedVol)); ThrowIfOSErr_(::HSetVol(0, theReply.sfFile.vRefNum, theReply.sfFile.parID)); p2cstr(&theReply.sfFile.name[0]); ifstream inFileStream((char*)theReply.sfFile.name); ThrowIfNot_(inFileStream.is_open()); Compile(inFileStream, inTgtResID); ::SetVol(0, savedVol); } } //************************************************************************************** // This will compile an 'err#' resource out of a text file structured like this: // // anErrorConstant [e g memFullErr] // [numeric error value, e g -108] // [optional line with fuller error description, e g Not enough room in heap zone] // // [two carriage returns between error descriptions] // Format of 'err#' resource is: // // Int16 nErrors; // char errorData[1]; // Error data is formatted as: // OSErr error; // Pstring errorConstant; // a string in the form length byte + [length byte] number of bytes // Pstring errorDescription; // a string in the form length byte + [length byte] number of bytes void cOSErrLookup::Compile(ifstream& inFileStream, ResIDT inTgtResID){ TArray specArray; errorSpec theSpec; char errStr[256]; Int16 nSpecs = 0; OSErr osErr = noErr; while(inFileStream.good()) { errStr[0] = 0; theSpec.error = noErr, theSpec.errorConstant[0] = 0, theSpec.errorDescription[0] = 0; inFileStream.getline((char*)&theSpec.errorConstant[1], sizeof(theSpec.errorConstant) - 1); theSpec.errorConstant[0] = strlen((char*)&theSpec.errorConstant[1]); inFileStream.getline(errStr, sizeof(errStr)); theSpec.error = atoi(errStr); inFileStream.getline((char*)&theSpec.errorDescription[1], sizeof(theSpec.errorDescription) - 1); theSpec.errorDescription[0] = strlen((char*)&theSpec.errorDescription[1]); ::UppercaseText((char*)&theSpec.errorDescription[1], 1, smSystemScript); specArray.AddItem(theSpec); // Jump all following carriage returns until we hit text while(inFileStream.good() && inFileStream.peek() == traits::newline()) inFileStream.rdbuf()->pubseekpos(inFileStream.rdbuf()->pubseekoff(0, ios::cur) + 1,ios::in); } // Sort the error spec array specArray.SetComparator(new cErrorSpecComparator); specArray.Sort(); // Write array to stream LHandleStream outResourceStream; outResourceStream << (Int16) specArray.GetCount(); for(int i = 0, j = specArray.GetCount(); i < j; i++){ outResourceStream << specArray[i + 1].error; outResourceStream << specArray[i + 1].errorConstant; outResourceStream << specArray[i + 1].errorDescription; } { // Delete old resource, if any StDeleteResource oldResource(eErrorResourceType, inTgtResID, false, true); } StNewResource theResource(eErrorResourceType, inTgtResID); theResource.mResourceH = outResourceStream.DetachDataHandle(); theResource.Write(true); } #pragma mark - //************************************************************************************** // Run a standard put file dialog, then dump err# data to a text file void cOSErrLookup::DecompileResource(ResIDT inSrcResID){ LStr255 docName("err# "); StandardFileReply theReply; docName.Append((long)inSrcResID).Append("\p dump.txt"); // Deactivate the desktop. StDesktopDeactivator deactivatedDesktop; ::StandardPutFile("\pSave text dump as:", docName, &theReply); if(theReply.sfGood){ Handle theText = Decompile(inSrcResID); LFile theFile(theReply.sfFile); ::HLock(theText); if(!theReply.sfReplacing) theFile.CreateNewFile('ttxt', 'TEXT', smSystemScript); theFile.OpenDataFork(fsWrPerm); theFile.WriteDataFork(&**theText, ::GetHandleSize(theText)); theFile.CloseDataFork(); ::DisposeHandle(theText); } } //************************************************************************************** // Return a handle of decompiled text from 'err#' resource. Throws if resource not found. Handle cOSErrLookup::Decompile(ResIDT inSrcResID){ TArray specArray; LHandleStream outStream; ThrowIf_(MakeSpecArray(inSrcResID, specArray) != true); specArray.SetComparator(new cErrorSpecComparator); specArray.Sort(); TArrayIterator iterator(specArray); errorSpec theSpec; while(iterator.Next(theSpec)){ outStream.WriteData(&theSpec.errorConstant[1], theSpec.errorConstant[0]); outStream << char_Return; ::NumToString(theSpec.error, theSpec.errorConstant); outStream.WriteData(&theSpec.errorConstant[1], theSpec.errorConstant[0]); outStream << char_Return; outStream.WriteData(&theSpec.errorDescription[1], theSpec.errorDescription[0]); if(theSpec.errorDescription[0] > 0) outStream << char_Return; outStream << char_Return; } return outStream.DetachDataHandle(); } #pragma mark - //************************************************************************************** Int32 cErrorSpecComparator::Compare( const void* inItemOne, const void* inItemTwo, Uint32 inSizeOne, Uint32 inSizeTwo) const { return ((Int32)((cOSErrLookup::errorSpec*)inItemOne)->error - (Int32)((cOSErrLookup::errorSpec*)inItemTwo)->error); } //************************************************************************************** Int32 cErrorSpecComparator::CompareToKey( const void* inItem, Uint32 inSize, const void* inKey) const { return ((Int32)((cOSErrLookup::errorSpec*)inItem)->error - (Int32)*(OSErr*)inKey); }