TweetFollow Us on Twitter

Ultra-Groovy LString Class

Volume Number: 15 (1999)
Issue Number: 1
Column Tag: PowerPlant Workshop

The Ultra-Groovy LString Class

by John C. Daub, Austin, Texas, USA

An introduction to the PowerPlant-way of working with strings

Hello, World!

Most software developers have been working with strings since they wrote their first program - that's what "Hello World!" is all about. To facilitate working with strings many libraries of utility functions have been written. Some people have their home-brewed string libraries, while others utilize third party libraries or use the libraries provided in Standard C and C++. These solutions are well and good, but they do not always translate very effectively into the world of Mac OS. Many of these offerings, like the Standard C and C++ libraries, are written to work with C-style strings - a sequence of characters terminated by a null character. But due to the Mac OS Toolbox's Pascal heritage, strings on the Mac are Pascal-style strings - a sequence of characters preceeded by a length byte which stores the length of the string. Consequently, a string library designed to work with Pascal-style strings is more valuable to the Mac OS software developer. Enter PowerPlant's LString class.

LString is a C++ abstract base class for working with Pascal-style strings. It provides means for converting strings to numbers and numbers to strings; obtain strings from numerous sources (characters, other strings, resources); copy, append, and compare strings; find, insert, remove, and replace substrings; and does so in a manner that respects the boundaries of Pascal-style strings, takes advantage of the C++ language, makes your code easier to read and understand, and most of all is easy and logical to use. The inherited classes LStr255, TString, and LStringRef provide the concrete means for working with strings in a manner very familiar to Mac OS software developers, so the learning curve for LString is not a steep one. And although LString is a PowerPlant class it can be used independent of the rest of PowerPlant; feel free to use it in all of your Mac OS software development efforts.

If LString sounds good to you, then please read on. In this article I hope to provide you with a good overview of what LString provides, and illustrate how it will make your software development efforts easier. Along the way I'll also show you a few of the really cool and groovy power-user features of LString. But don't worry! Those power-user features are so easy to use that you'll be an LString-Warrior before you know it!

First, Some Background

Before I go any further, I want to make sure that we're on the same grounds of understanding just what a string is, and what the differences are between the various types of strings. If you already understand what the different types of strings are, feel free to skip ahead to the next section.

Simply put, a string is a sequence of characters. This sentence is an example of a string.

A C-style string is an array of characters (char*). The internal representation of the string has a null character ('\0') at the end, to signal the end of the string, so the amount of storage required for the string is one more than the number of characters in the string. Due to the use of the null terminator, there is theoretically no limit to the length of a C-style string, but you must scan the entire string to determine the length of the string (the C standard library function strlen() can be used to determine the length of the string). So the string "Hello, World!" contains thirteen characters, but at least fourteen bytes must be allocated to hold that string.

A Pascal-style string is also an array of characters (unsigned char*). Like a C-style string, a Pascal-style string also requires one more than the number of characters in the string for storage. However unlike a C-style string (and this is the defining and differentiating characteristic) there is no null terminator at the end of the string; instead, the first byte of the string is used to store a count of the number of characters in the string. You do not need to scan the entire length of the string to determine its length - you can merely examine the first byte (length byte) of the string. Also unlike a C-style string, a Pascal-style string does have a limit; this limit is determined by the number of bytes allocated in the unsigned char* array (e.g. unsigned char[256] would allow a string up to 255 characters). As a Pascal-style string "Hello, World!" still contains thirteen characters and also needs at least fourteen bytes for storage, but the internal representation of the string is different. Finally, when specifying a Pascal-style string constant, they are typically prefaced by a \p to tell the compiler to treat this string as a Pascal-style string (e.g. "\pHello, World!" would be the actual way to represent our example string as a Pascal-style string). Figure 1 illustrates how the internal storage of the "Hello, World!" string differs as a C-style string and a Pascal-style string.


Figure 1. String internal representations.

In Figure 1, each column represents a byte in the array (note we start at zero, like any array). The top row is the byte count, the middle row is the C-style string, and the bottom row is the Pascal-style string. Notice that both string styles require the same amount of storage (fourteen bytes). The C-style string places a null terminator in the last byte (although it looks like two separate characters \0 is just a special single character). When the string is read in, characters are read one by one until the null terminator is reached. If we wanted to find the length of the C-style string we would scan the string, starting from the beginning, counting each character as we go along until we reach the null terminator (this is what strlen does). The Pascal-style string places the length of the string in the first byte of the array (in this case the number thirteen, not "13" as a string or character). When the string is read, the first byte is checked for the length, and then exactly that many characters are read. If we wanted to find the length of the string, we check the first byte (string[0]).

Both styles of strings have their strengths and weaknesses. C-style strings can be of an arbitrary length whereas Pascal-style strings tend to have a more fixed size. C-style strings also have more overhead involved in determining the length of the string (a function call and walking the entire string), but you can find the length of a Pascal-style string with a simple check of the length byte.

On the Mac OS, most strings are Pascal-style strings (since the OS and toolbox were originally written in Pascal). To more easily represent and identify strings, and provide a simple means for defining strings of different lengths, MacTypes.h (formerly Types.h) typedefs some arrays of unsigned char's. The most popular version is a Str255, which is an array of 256 unsigned char's. There are others (Str63, Str32, Str15, StringPtr, ConstStr255Param - see MacTypes.h for a complete listing), and although Str255's are the most commonly used, the discussions in this article apply to any of these type(def)s. The Mac OS can also have raw text runs (e.g. 'TEXT' resources, TERec, etc.), and you can use C-style strings as well. The only time you are "forced" to use a Pascal-style string is when you interact with the Toolbox. If you wish to use C-style strings otherwise, you are very welcome to do so, especially if you are more familiar with the C Standard Library. Furthermore, the Toolbox provides some Pascal to-from C string conversion routines (c2pstr and p2cstr are in the Universal Header TextUtils.h ), and even provides C-glue routines that are glue routines to Toolbox functions that take C-style strings as arguments instead (look for the symbol CGLUESUPPORTED throughout the Universal Headers).

But as most Mac OS software development sooner or later requires you to interact with the Toolbox, most people find it easier to use Pascal-style strings all the time (also avoids the constant overhead of back and forth conversions). Due to this preference is why PowerPlant offers a great solution in LString. Let's now take a look at what exactly LString has to offer.

What's in There?

The LString class (and all of the classes and material I'll discuss here) can be found within the PowerPlant folder of the Metrowerks CodeWarrior Professional product. Specifically within the files LString.cp and LString.h. If you own CodeWarrior Professional, go ahead and open up those two files and look over them. I don't expect you to necessarily understand what's in there right now, but give them a look over so you can know what I'm talking about. You might also find it handy to refer to the sources as I refer to various parts of it so you can see how all of this fits together. For reference, I am writing this article using CodeWarrior Pro 4 and a version of LString that should be part of PowerPlant v1.9.3 (which will be released as public netborne update to Pro 4, and should be available by the time you read this article).

LString

LString is an abstract base class for working with Pascal-style strings. It defines almost all of the functionality that one would need for working with Pascal-style strings. LString also works to take advantage of the C++ concept of overloading. First many of the methods are overloaded to work with many data types (all built-in data types, both signed and unsigned, including floating point types; C-style strings; Pascal-style strings; FourCharCode; a Handle to text; other LString objects). Second, many of the methods are also offered as operator overloads, were logical, to make it easier for you to utilize these methods in your code. For example, operator += is the same as the Append() method. LString also defines some public methods as static so that non-LString objects can take advantage of some commonly needed string manipulations like copying and appending.

LString is designed to be clean, efficient, and work within the boundaries of the Mac OS and the Pascal-style strings utilized therein. As noted earlier, MacTypes.h defines different types of Pascal-style strings, like a Str255 or a Str63. Due to this ability to vary in storage length, any time length of string and/or storage is relevant to the functionality, LString always requires this information be provided, but typically also provides a default towards a Str255 as this is the most commonly used type. Furthermore, since Mac OS Pascal-style strings have 255 characters as the upper limit for strings (hence the Str255), LString always enforces this upper boundary where appropriate. If utilized correctly, LString should alleviate fears and eliminate the problem of array boundary overflows.

Although PowerPlant is a Mac OS-only framework, it does try to do what it can to aid those that might be using PowerPlant in a non-Mac context such as porting your Mac OS-based application to Windows. Some LString methods are only available under Mac OS (due to their nature), and techniques are provided for development environments that might not fully understand Mac OS conventions. For example, the traditional way to obtain the length of the string is to directly check the length byte. But since not all development environments support this method of access (of course CodeWarrior does), the LString::Length() method is provided as a certain means of always obtaining the length of the string.

As an implementation note, there is only one data member in LString: mStringPtr. mStringPtr is a StringPtr which points to your Pascal-style string. Note that the actual storage for the string is not part of LString. Without this sort of design, neither TString nor LStringRef could exist (I'll discuss these classes later in the article). So does this mean that you always have to allocate your own storage for your string? Not at all, as this is the purpose of LStr255.

LStr255

LStr255 inherits from LString. You can find the declaration of LStr255 in LString.h and definition in LString.cp. If the name of the class looks familiar to you, this is intended. The design of LStr255 is to replicate the functionality of a Str255, but with lots of extras. The great part is an LStr255 can be used almost anywhere a regular Str255 is used and in the same manner. Furthermore an LStr255 inherits all of the functionality of LString, plus it provides the actual storage for the string itself (the mString data member) so you do not have to.

Listing 1: Use of Str255

Obtain and concatenate two strings
To explain a convention used in all listings: any function call
preceeded by the unary scope resolution operator (::) specifies
this symbol is located in the global namespace - the same place
where the Toolbox symbols are located. These are Toolbox calls.
Feel free to look them up in Inside Macintosh on the Apple WWW
site for more information and detail.

    // Declare a string and initialize it
    Str255    theString = "\pHello, World!";

    // Append another string, obtained from a STR# resource
    Str255 appendString;
    ::GetIndString(appendString, 128, 1);

    // Ensure we don't overrun our boundaries
    SInt16    charsToCopy = appendString[0];
    Size theStringSize = sizeof(theString);

    if ((theString[0] + charsToCopy) > (theStringSize - 1)) {
        charsToCopy = theStringSize - 1 - theString[0];
    }

    // Append the string
    ::BlockMoveData((Ptr)&appendString[1], theString +
                theString[0] + 1, charsToCopy);

    // Reset the length byte
    theString[0] += charsToCopy;

    // Draw the string
    ::DrawString(theString);

Listing 1 demonstrates a typical set of actions that one might perform on a string. All of that work and code just to concatenate two strings. Listing 2 shows the same code but written using LString. Don't worry if you do not understand everything that is going on in Listing 2 because I'll cover it all in coming sections.

Listing 2: Use of LStr255

Obtain and concatenate two strings

    // Declare a string and initialize it
    LStr255    theString( "\pHello World" );

    // Append another string, obtained from a STR# resource
    LStr255    appendString(128, 1);

    theString += appendString;

    // Draw the string
    ::DrawString(theString);

As you can see comparing Listing 2 to Listing 1, the same result is accomplished but Listing 2 contains a lot less code and is much easier to read. This just scratches the surface of what LString can do.

Two usage notes. First, when you are using LStr255 objects and viewing them in a debugger, you will actually see two instances of your string data within the LStr255 object. One of these will be from mStringPtr and the other from mString. Why do you see two strings and why are they always in sync? Why not just have one string? Remember that mStringPtr is just a pointer to your actual storage for your string, mString; rather, mStringPtr points to mString. This is how things are designed and it's all OK, don't worry about it. However if they are not in sync, you might want to investigate as this could be signaling a problem. Second, beginning with the next section and continuing for the remainder of the article, I will be using LStr255 objects in all code listings because LStr255 is the most commonly used type of LString. Do remember that what applies in those listings will generally apply to other LString derivatives as well, like TString and LStringRef.

TString

TString is a templatized version of LString. It allows you to utilize the power and functionality of LString upon any Pascal-style string type (Str255, Str63, Str15, etc.). Use of the class is exactly like using LStr255, except that you instantiate your object through normal C++ template instantiation mechanisms. Listing 3 is a rewrite of Listing 1 using TString. Note as well its similarity to Listing 2.

Listing 3: Use of TString

Obtain and concatenate two strings

    // Declare a string and initialize it
    TString<Str63>        theString("\pHello World");

    // Append another string, obtained from a STR# resource
    TString<Str255>        appendString(128,1);

    theString += appendString;

    // Draw the string
    ::DrawString(theString);

LStringRef

LStringRef is a newcomer to the LString family (it was introduced in PowerPlant v1.9.1, post CodeWarrior Pro 3). LStringRef provides you with a means to obtain the functionality of LString and use it to manipulate a string that you do not have ownership of. Unlike LStr255 and TString, LStringRef does not contain storage for the string itself; mStringPtr points to a string allocated elsewhere. For example, this can be used to change a string that is part of some data structure. Listing 4 demonstrates the use of LStringRef to change the filename in an FSSpec from whatever it originally was to "Hello File! copy".

Listing 4: Use of LStringRef

    // Obtain our file spec
    FSSpec        theFileSpec;
    theLFile->GetSpecifier(theFileSpec);

    // Create the LStringRef and have it point to the filename
    LStringRef        fileName(sizeof(theFileSpec.name),
                      theFileSpec.name);

    // Change the filename entirely
    fileName = "Hello File!";

    // Actually it's a copy
    fileName += " copy";

Basic Features

Now that you've been introduced to the LString family and seen a little of what LString can do, it's now time to dig through the lines of code in LString.cp and LString.h to really see all that LString has to offer. By the way, do make sure you also read through LString.h as many of LString's functions are declared inline in the header file.

Object Construction

If you look through the LString sources, you'll notice that there is only one LString constructor. That's all that LString needs as it performs the core setup of the class's functionality. Where the constructors are really needed is within the subclasses. Look at the number of constructors there are for LStr255! And look what you can do with all of those constructors as well. You can create your LStr255 from: another LStr255, another LString, a Pascal-style string, a C-style string, an arbitrary string of text, a single character, data within a Handle, from a 'STR ' or 'STR#' resource, from a long or short integer, a FourCharCode, and from floating point values (long double). Wow! That covers just about every and any way you might want to create your LStr255 object.

If you look at the implementation of most of those LStr255 constructors, you'll see many of them call the Assign() method. Assign() assigns the given value to the LString object. Just as the constructors use Assign(), you are free to use Assign() as well when you wish to assign a value to your LString object. Furthermore, as a logical use of C++, you can see in LString.h that the assignment operator (operator =) has been overloaded with just as many combinations for those of you that prefer the use of operators in logical situations. Listing 5 demonstrates the many ways Assign() can be used.

Listing 5: The Many Faces of Assign()

Each line group produces the same result of creating an LStr255
object and giving it a string of "Hello, Assign!".

    // Create and assign by initialization
    LSt255        initString("Hello, Assign!");
    // Create and assign by Assign()
    LStr255    assignString;
    assignString.Assign("\pHello, Assign!");
    // Create and assign by assignment
    LStr255    assignmentString = assignString;

String Manipulation

Now that you have created your LString and given it some text, you will probably want to do something with the text in that string. This functionality is what LString tends to be used for most, and within this subset lies the use of Append().

Append() appends a given value to an LString object. What can be appended is again just about anything (see LString.h for a complete list). As with Assign() and operator =, so with Append() and operator +=. You'll probably find yourself using operator += a great deal as it's so simple to type and makes your code so much easier to read. One issue to note with operator += (and perhaps other overloaded functions in LString) is that depending what you are trying to append you may receive compiler errors about an ambiguous access to an overloaded function. For instance, the following line of code will generate this error because the compiler cannot determine if you intend 1 to be treated as char, unsigned char, long, or short:

    theString += 1;

If you receive this compiler error, all you need to do to resolve it is apply a static_cast (or C-style cast) which will tell the compiler explicitly how you wish the value to be treated:

    theString += static_cast<SInt32>(1);

In addition to member functions for appending, LString also provides some global string addition operators (operator +). They are not LString member operators, but do act upon LString objects. These operators concatenate LString objects with: another LString object, a pointer to a string, or a character. The result of the addition is an LStr255 object. You can find these operators declared about mid-way through LString.h and defined near the bottom of LString.cp.

Finally, what would string manipulation be if you could not compare strings! You will find in LString.h a boat-load of global comparison operators that let you do just about any sort of string comparison you can think of: equals (operator ==), not equals (operator !=), greater than (operator >), less than (operator <), greater than or equals (operator >=), and less than or equals (operator <=). Listing 6 demonstrates the many ways you can manipulate strings with LString.

Listing 6: String Manipulation

    // Create our string, "Hi"
    LStr255    theString("Hi");

    // Whoops! We forgot the punctuation
    theString += '!';

    // Create a new string from 2 smaller strings

    LStr255    newString;
    newString = theString + "\p My name is John.";

    // Is our newString the same as our old string?
    // If so, beep.
    if ( newString == theString ) {
        ::SysBeep(2);
    }

Utilities

Rounding out the basic features of LString are some utility functions. They fall into two groups: object-related and public.

The object-related utilities are utilities that are class member functions and only behave in relation to their associated LString object. These are methods like: Length(), which returns the string length as an UInt8; ShortLength() which returns length as an SInt16; LongLength() which returns length as an SInt32 (the different return types are to ease places where you need the string length but do not wish to typecast to match a function parameter or avoid an implicit arithmetic conversion); GetMaxLength(), to return the maximum length the string can be; TextPtr() and ConstTextPtr() to return a Ptr (or const Ptr) to the raw text (avoids those ugly (Ptr)&theString[1] situations, as was used in Listing 1); operator [] to allow access to individual characters in the string, just like you could with a basic array. Listing 7a illustrates the use of these utilities.

Listing 7a: Utilities

    LStr255    theLStr255("\pYea");
    TString<Str255>    theTStr255; // LString::LString() properly
                                         // initializes to null.

    // Manually copy the text out of the LStr255 into a Str255
    ::BlockMoveData(    theLStr255.ConstTextPtr(),
                                    (Ptr)&theTStr255[1],
                                    theLStr255.LongLength()    );
    
    // Reset length byte
    theTStr255[0] = theLStr255.Length();

The public utilities of LString are a small group of static methods, publicly available for calling by pretty much anyone anywhere in your program (within reason, of course). These methods handle the most common string manipulations like: CopyPStr(), to copy one Pascal-style string into another Pascal-style string (e.g. Str255 to Str255); AppendPStr(), for a static version of LString::Append(); CStringLength() for obtaining the length of a C-style string with an upper limit of 255; FourCharCodeToPStr() and PStrToFourCharCode() for converting a FourCharCode (y'know, 'TEXT', 'PICT', 'PPob') to a Pascal-style string and back again; and FetchFloatFormat() and StringToLongDouble() to aid in some quick floating-point conversions. Listing 7b presents a display of these utilities' features.

Listing 7b: Static Utilities

    // Create a Pascal-style string and assign it a value
    Str255                herString;

    LString::CopyPStr("\pMary", herString);

    // Turn it into our app's signature
    FourCharCode    herSig;
    LString::PStrToFourCharCode(herString, herSig);

    // Clear original storage
    herString[0] = 0;

    // And back again
    LString::FourCharCodeToPStr(herSig, herString);

    // Add the rest
    LString::AppendPStr(herString, "\p Victoria");

Power Features

In addition to the basic functionality's discussed above, LString has some features for the power-user that help to round out the practicality of the class. If you consider yourself a beginner to LString, you should still give these power-features a look over as using these power-features is not difficult, and you can be a power-user in no time. The substring manipulations you may or may not use every day, but the "typecasting" abilities you'll find indispensable.

LString's ability to manipulate substrings provides you with the capability to perform substring searches, copies, and changes to that substring. In fact, you might even be able to create your own Find dialog using these features. The search functionality in LString allows you to: Find(), locate where a substring starts within the string; ReverseFind(), locate the position of a substring starting from the end of the string; to see if a string BeginsWith() or EndsWith() a specified string; find where the string starts within another string either from the beginning, FindWithin() or from the end, ReverseFindWithin(). In performing these searches, a comparison of text must of course be done. LString::ToolboxCompareText() is provided as the default mechanism for comparing the text; it uses the Toolbox CompareText() routine. If you do not wish to use the default comparison mechanism, you can specify your own CompareFunc via SetCompareFunc() to change it to whatever you would like. Of course after you have found your substring, you might want to do something with it. If you would like you can Insert() a string into your LString object, Remove() a substring from your object, or Replace() one substring with another. Or if you would just like to make a copy, operator () has been overloaded to provide you with an easy way to perform this copy. Listing 8 illustrates LString's substring manipulation features.

Listing 8: Substring Manipulation

    // Create the base string
    LStr255        theString("Get up. Stand up for your rights.");
    
    // Ensure the compare function is what we need
    theString.SetCompareFunc(LString::ToolboxCompareText);
    
    // Find a target string
    StringPtr    targetString = "\pStand up";
    UInt8            targetLength = targetString[0];
    
    UInt8    index = theString.Find( (Ptr)&targetString[1],
                                     targetLength );
    
    // Make a copy of the target string
    LStr255        targetCopy(theString(index,targetLength));
    targetCopy += "\p. ";

    // Insert the copy into the base string
    theString += char_Space;
    theString.Insert( targetCopy, index );

What I think is one of the niftiest features of LString is, as I like to call them, the typecasting operators. These are a series of operator overloads that fake the appearance of a typecast to allow your LString object to be flexibly used in many situations. You can find the operators listed near the top of the LString declaration in LString.h. These operators include: StringPtr, ConstStringPtr, SInt32, double, long double, and FourCharCode. When one of these operators is applied to the LString object, it converts the string into the "requested type" and returns the conversion. That is, apply operator SInt32 and the string is converted to a long integer and that long integer returned. The way to apply these operators is just like a typecast, but it's not really a typecast; it's technically an application of that operator, but the implementation and application of that operator simulates a typecast for ease of use and improved code readability. Furthermore, when attempting to resolve types, the compiler can implicitly apply these operators to the LString object for you. Look back at Figure 2 and notice that we passed our LStr255 object directly to DrawString? Listing 9 should clarify how all of this works.

Listing 9: Typecasting Operators

    // Create our string (FIXEDDECIMAL comes from fp.h)
    LStr255    theString(3.14, FIXEDDECIMAL, 2);

    // Convert it and do some math (the static_cast technically
    // isn't necessary, but here for illustration. Note that it
    // looks like a typecast, but if you watch the code execute
    // you'll step into operator double().
    double number = static_cast<double>(theString);
    number *= 2;

    // Convert back to a string. Note there is no operator =
    // for floating point variables. This is because of the
    // need for extra information.
    theString.Assign(number, FIXEDDECIMAL,2);

    // Draw onscreen. Note the implicit operator call to
    // operator StringPtr.
    ::DrawString(theString);

PowerPlant uses this technique of typecasting operators throughout the framework as a handy way to allow your objects to be treated as more basic Toolbox types for increased flexibility and seemlessness between PowerPlant, the Toolbox, and your code. See the use of operator Handle and operator Ptr in UMemoryMgr classes for another set of examples. Also, if you have access to the Scott Meyers book More Effective C++, read Item 5: Be wary of user-defined conversion functions. Scott labels the above technique implicit type conversion operators and points out the various strengths and weaknesses of the above technique.

Is There A Downside?

As great as LString is, there are certainly a few downsides. There may be others, but these are the big ones in my book. First, if you don't like Pascal-style strings or perhaps they're not as useful as a C-style string would be in a given situation, then of course LString would fall under the same roof. But if you use and/or like Pascal-style strings then this isn't much of an issue. Second, this code is C++. If you need Pascal-style string helper functions in C or another language then LString will not be of much use to you. Third, LString's are C++ objects. As an object it will have some additional overhead and memory requirements, at least compared to plain arrays (e.g. LStr255 vs. Str255). Furthermore because LString's are objects you cannot place them inside TArray's, except, as with all objects and TArray, as pointers to the object allocated via new. Due to this extra work, it's probably easier to just store plain Str255's in a TArray instead. But do not fret! If you must use Str255's you can still gain the features and power of LString via the LStringRef class applied to your Str255's.

Give It A Try

If you haven't already, give LString a try. The code listings should all function as compilable snippets. And since LString is mostly independent of the rest of PowerPlant (you may need to add the PP_Constants.cp file to your project as well), you can easily drop LString.cp into a basic Toolbox or console stationery and give them a try. Step through each listing in the debugger (make sure inlining is set to "Don't inline") and watch how things work. Watch where you go, how that pertains to the section material being discussed, and how they all fit into the larger picture. Just play around and experiment.

I hope that I've been able to give you a good introduction to the LString class and it's family of subclasses and utilities. It provides the Mac OS software developer with a fairly simple yet powerful class for working with Pascal-style strings. With the ability to create strings from almost any source, manipulate the contents of that string, and convert it for use in just about any situation, LString is a tool worth having in your programmer's toolbox.

I hope you find LString to be as ultra-groovy as I find it to be. But just in case, I'll borrow one from Dennis Miller: "Of course that's just me. I could be wrong."

Bibliography


John C. Daub can't think of anything snazzy to put in his bio other than 311's a great band to listen to while writing articles. If you'd like to contact John, you can do so at hsoi@metrowerks.com.

 
AAPL
$118.93
Apple Inc.
-0.07
MSFT
$47.81
Microsoft Corpora
+0.06
GOOG
$541.83
Google Inc.
+1.46

MacTech Search:
Community Search:

Software Updates via MacUpdate

Adobe Photoshop Elements 13.0 - Consumer...
Adobe Photoshop Elements 12--the #1 selling consumer photo editing software--helps you edit pictures with powerful, easy-to-use options and share them via print, the web, Facebook, and more.Version... Read more
Skype 7.2.0.412 - Voice-over-internet ph...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more
HoudahSpot 3.9.6 - Advanced file search...
HoudahSpot is a powerful file search tool built upon MacOS X Spotlight. Spotlight unleashed Create detailed queries to locate the exact file you need Narrow down searches. Zero in on files Save... Read more
RapidWeaver 6.0.3 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
iPhoto Library Manager 4.1.10 - Manage m...
iPhoto Library Manager lets you organize your photos into multiple iPhoto libraries. Separate your high school and college photos from your latest summer vacation pictures. Or keep some photo... Read more
iExplorer 3.5.1.9 - View and transfer al...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
MacUpdate Desktop 6.0.3 - Discover and i...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
SteerMouse 4.2.2 - Powerful third-party...
SteerMouse is an advanced driver for USB and Bluetooth mice. It also supports Apple Mighty Mouse very well. SteerMouse can assign various functions to buttons that Apple's software does not allow,... Read more
iMazing 1.1 - Complete iOS device manage...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
PopChar X 7.0 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more

Latest Forum Discussions

See All

Mystery Case Files: Dire Grove, Sacred G...
Mystery Case Files: Dire Grove, Sacred Grove HD Review By Jennifer Allen on November 28th, 2014 Our Rating: iPad Only App - Designed for the iPad A decent new installment for the popular Mystery Case Files series.   | Read more »
Castaway Paradise – Tips, Tricks, and St...
Ahoy there, castaways: Were you curious about our own thoughts regarding this pristine shipwreck? Check out our Castaway Paradise review! Castaway Paradise is out for iOS, finally giving mobile gamers the opportunity to enjoy the idyllic lifestyle... | Read more »
Castaway Paradise VIP Subs are on Sale f...
Castaway Paradise VIP Subs are on Sale for a Limited Time, and a Special Holiday Update is Coming Soon Posted by Rob Rich on November 28th, 2014 [ | Read more »
Primitive Review
Primitive Review By Jordan Minor on November 28th, 2014 Our Rating: :: FOLK ARTUniversal App - Designed for iPhone and iPad True to its name, Primitive is about as straightforward as runners get.   | Read more »
7 tips to get ahead of the competition i...
7 tips to get ahead of the competition in Dynasty of Dungeons Posted by Simon Reed on November 28th, 2014 [ permalink ] Playcrab has launched their action-packed new dungeon crawler, Dynasty of Dungeons, today. | Read more »
Master of Tea Kung Fu Review
Master of Tea Kung Fu Review By Jordan Minor on November 28th, 2014 Our Rating: :: ONE DROP RULESUniversal App - Designed for iPhone and iPad Master of Tea Kung Fu is a creative and complex caffeinated brawler.   | Read more »
Monster Strike Review
Monster Strike Review By Campbell Bird on November 28th, 2014 Our Rating: :: BILLIARD STRATEGYUniversal App - Designed for iPhone and iPad Collect monsters and battle by flinging them across the battlefield in this strangely... | Read more »
Proun+ Review
Proun+ Review By Jennifer Allen on November 28th, 2014 Our Rating: :: TWITCHY RACINGUniversal App - Designed for iPhone and iPad Twitchy racing aplenty in Proun+, an enjoyably tricky title.   | Read more »
Lucha Amigos (Games)
Lucha Amigos 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Forget Ninja Turtles, and meet Wrestlers Turtles! Crazier, Spicier and…Bouncier! Sling carapaces of 7 Luchadores to knock all... | Read more »
Record of Agarest War Zero (Games)
Record of Agarest War Zero 1.0 Device: iOS Universal Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: HyperDevbox Holiday Turkey Black Friday Special Pricing! To celebrate the opening of the holiday season HyperDevbox... | Read more »

Price Scanner via MacPrices.net

Best Black Friday Deal: 15-inch Retina MacBoo...
 B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for $300 off MSRP as part of their Black Friday sale. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina... Read more
Up To 75% Off Infovole Text Apps Over Black F...
Infovole’s entire range of apps, including the Textkraft family of word processors for iPads and iPhones, is being offered at 50-75% off over the Black Friday and Cyber Monday weekend. The five-day... Read more
Black Friday: Up to $60 off Mac minis, NY tax...
 B&H Photo has new 2014 Mac minis on sale for up to $60 off MSRP as part of their Black Friday sale. Shipping is free, and B&H charges NY sales tax only: - 1.4GHz Mac mini: $449.99 $50 off... Read more
Black Friday: 27-inch 5K iMac for $2299, save...
 B&H Photo continues to offer Black Friday sale prices on the 27″ 3.5GHz 5K iMac, in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP... Read more
Karalux Announces 24K Gold-Plated iPhone 6
Karalux, a Vietnam-based jewellery firm, has launched a unique 24 karat gold-plated iPhone 6 version with gold-cast monolithic dragon on its back panel. The real 24 karat gold plated enclosure doesn’... Read more
Black Friday: 13-inch 2.6GHz Retina MacBook P...
 B&H Photo has lowered their price for the 13″ 2.6GHz/128GB Retina MacBook Pro to $1159 for Black Friday. That’s $140 off MSRP, and it’s the lowest price for this model (except for Apple’s $1099... Read more
View all the Black Friday sales on our Mac Pr...
We’ve updated our Mac Price Trackers with the latest information on prices, bundles, and availability on systems from Apple’s authorized internet/catalog resellers. View Black Friday sale prices at a... Read more
Black Friday: 11-inch MacBook Air for $779, s...
 Best Buy has lowered their price for the 2014 11″ 1.4GHz/128GB MacBook Air to $779.99 for Black Friday. That’s $120 off MSRP. Choose free shipping or free local store pickup (if available). Sale... Read more
Apple Store Black Friday sale for 2014: $100...
BLACK FRIDAY The Apple Store has posted their Black Friday deals for 2014. Receive a $100 PRODUCT(RED) branded iTunes gift card with the purchase of select Macs, $50 with iPads, and $25 with iPods,... Read more
Black Friday: 15% off iTunes Gift Cards
Staples is offering 15% off $50 and $100 iTunes Gift Cards on their online store as part of their Black Friday sale. Click here for more information. Shipping is free. Best Buy is offering $100... Read more

Jobs Board

Position Opening at *Apple* - Apple (United...
…Summary** As a Specialist, you help create the energy and excitement around Apple products, providing the right solutions and getting products into customers' hands. You Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** Being a Business Manager at an Apple Store means you're the catalyst for businesses to discover and leverage the power, ease, and flexibility of Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.