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
$99.76
Apple Inc.
+2.09
MSFT
$44.08
Microsoft Corpora
+0.45
GOOG
$520.84
Google Inc.
+9.67

MacTech Search:
Community Search:

Software Updates via MacUpdate

RestoreMeNot 2.0.3 - Disable window rest...
RestoreMeNot provides a simple way to disable the window restoration for individual applications so that you can fine-tune this behavior to suit your needs. Please note that RestoreMeNot is designed... Read more
Macgo Blu-ray Player 2.10.9.1750 - Blu-r...
Macgo Mac Blu-ray Player can bring you the most unforgettable Blu-ray experience on your Mac. Overview Macgo Mac Blu-ray Player can satisfy just about every need you could possibly have in a Blu-ray... Read more
Apple iOS 8.1 - The latest version of Ap...
The latest version of iOS can be downloaded through iTunes. Apple iOS 8 comes with big updates to apps you use every day, like Messages and Photos. A whole new way to share content with your family.... Read more
TechTool Pro 7.0.5 - Hard drive and syst...
TechTool Pro is now 7, and this is the most advanced version of the acclaimed Macintosh troubleshooting utility created in its 20-year history. Micromat has redeveloped TechTool Pro 7 to be fully 64... Read more
PDFKey Pro 4.0.2 - Edit and print passwo...
PDFKey Pro can unlock PDF documents protected for printing and copying when you've forgotten your password. It can now also protect your PDF files with a password to prevent unauthorized access and/... Read more
Yasu 2.9.1 - System maintenance app; per...
Yasu was originally created with System Administrators who service large groups of workstations in mind, Yasu (Yet Another System Utility) was made to do a specific group of maintenance tasks... Read more
Hazel 3.3 - Create rules for organizing...
Hazel is your personal housekeeper, organizing and cleaning folders based on rules you define. Hazel can also manage your trash and uninstall your applications. Organize your files using a... Read more
Autopano Giga 3.7 - Stitch multiple imag...
Autopano Giga allows you to stitch 2, 20, or 2,000 images. Version 3.0 integrates impressive new features that will definitely make you adopt Autopano Pro or Autopano Giga: Choose between 9... Read more
MenuMeters 1.8 - CPU, memory, disk, and...
MenuMeters is a set of CPU, memory, disk, and network monitoring tools for Mac OS X. Although there are numerous other programs which do the same thing, none had quite the feature set I was looking... Read more
Coda 2.5 - One-window Web development su...
Coda is a powerful Web editor that puts everything in one place. An editor. Terminal. CSS. Files. With Coda 2, we went beyond expectations. With loads of new, much-requested features, a few... Read more

Latest Forum Discussions

See All

This Week at 148Apps: October 13-17, 201...
Expert App Reviewers   So little time and so very many apps. What’s a poor iPhone/iPad lover to do? Fortunately, 148Apps is here to give you the rundown on the latest and greatest releases. And we even have a tremendous back catalog of reviews; just... | Read more »
Angry Birds Transformers Review
Angry Birds Transformers Review By Jennifer Allen on October 20th, 2014 Our Rating: :: TRANSFORMED BIRDSUniversal App - Designed for iPhone and iPad Transformed in a way you wouldn’t expect, Angry Birds Transformers is a quite... | Read more »
GAMEVIL Announces the Upcoming Launch of...
GAMEVIL Announces the Upcoming Launch of Mark of the Dragon Posted by Jessica Fisher on October 20th, 2014 [ permalink ] Mark of the Dragon, by GAMEVIL, put | Read more »
Interview With the Angry Birds Transform...
Angry Birds Transformers recently transformed and rolled out worldwide. This run-and-gun title is a hit with young Transformers fans, but the ample references to classic Transformers fandom has also earned it a place in the hearts of long-time... | Read more »
Find Free Food on Campus with Ypay
Find Free Food on Campus with Ypay Posted by Jessica Fisher on October 20th, 2014 [ permalink ] iPhone App - Designed for the iPhone, compatible with the iPad | Read more »
Strung Along Review
Strung Along Review By Jordan Minor on October 20th, 2014 Our Rating: :: GOT NO STRINGSUniversal App - Designed for iPhone and iPad A cool gimmick and a great art style keep Strung Along from completely falling apart.   | Read more »
P2P file transferring app Send Anywhere...
File sharing services like Dropbox have security issues. Email attachments can be problematic when it comes to sharing large files. USB dongles don’t fit into your phone. Send Anywhere, a peer-to-peer file transferring application, solves all of... | Read more »
Zero Age Review
Zero Age Review By Jordan Minor on October 20th, 2014 Our Rating: :: MORE THAN ZEROiPad Only App - Designed for the iPad With its mind-bending puzzles and spellbinding visuals, Zero Age has it all.   | Read more »
Hay Ewe Review
Hay Ewe Review By Campbell Bird on October 20th, 2014 Our Rating: :: SAVE YOUR SHEEPLEUniversal App - Designed for iPhone and iPad Pave the way for your flock in this line drawing puzzle game from the creators of Worms.   | Read more »
My Very Hungry Caterpillar (Education)
My Very Hungry Caterpillar 1.0.0 Device: iOS Universal Category: Education Price: $3.99, Version: 1.0.0 (iTunes) Description: Care for your very own Very Hungry Caterpillar! My Very Hungry Caterpillar will captivate you as he crawls... | Read more »

Price Scanner via MacPrices.net

Deals on 2011 13-inch MacBook Airs, from $649
Daily Steals has the Mid-2011 13″ 1.7GHz i5 MacBook Air (4GB/128GB) available for $699 with a 90 day warranty. The Mid-2011 13″ 1.7GHz i5 MacBook Air (4GB/128GB SSD) is available for $649 at Other... Read more
2013 15-inch 2.0GHz Retina MacBook Pro availa...
B&H Photo has leftover previous-generation 15″ 2.0GHz Retina MacBook Pros now available for $1599 including free shipping plus NY sales tax only. Their price is $400 off original MSRP. B&H... Read more
Updated iPad Prices
We’ve updated our iPad Air Price Tracker and our iPad mini Price Tracker with the latest information on prices and availability from Apple and other resellers, including the new iPad Air 2 and the... Read more
Apple Pay Available to Millions of Visa Cardh...
Visa Inc. brings secure, convenient payments to iPad Air 2 and iPad mini 3as well as iPhone 6 and 6 Plus. Starting October 20th, eligible Visa cardholders in the U.S. will be able to use Apple Pay,... Read more
Textkraft Pocket – the missing TextEdit for i...
infovole GmbH has announced the release and immediate availability of Textkraft Pocket 1.0, a professional text editor and note taking app for Apple’s iPhone. In March 2014 rumors were all about... Read more
C Spire to offer iPad Air 2 and iPad mini 3,...
C Spire on Friday announced that it will offer iPad Air 2 and iPad mini 3, both with Wi-Fi + Cellular, on its 4G+ LTE network in the coming weeks. C Spire will offer the new iPads with a range of... Read more
Belkin Announces Full Line of Keyboards and C...
Belkin International has unveiled a new lineup of keyboard cases and accessories for Apple’s newest iPads, featuring three QODE keyboards and a collection of thin, lightweight folios for both the... Read more
Verizon offers new iPad Air 2 preorders for $...
Verizon Wireless is accepting preorders for the new iPad Air 2, cellular models, for $100 off MSRP with a 2-year service agreement: - 16GB iPad Air 2 WiFi + Cellular: $529.99 - 64GB iPad Air 2 WiFi... Read more
Price drops on refurbished Mac minis, now ava...
The Apple Store has dropped prices on Apple Certified Refurbished previous-generation Mac minis, with models now available starting at $419. Apple’s one-year warranty is included with each mini, and... Read more
Apple refurbished 2014 MacBook Airs available...
The Apple Store has Apple Certified Refurbished 2014 MacBook Airs available for up to $180 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is free.... Read more

Jobs Board

Project Manager / Business Analyst, WW *Appl...
…a senior project manager / business analyst to work within our Worldwide Apple Fulfillment Operations and the Business Process Re-engineering team. This role will work Read more
*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** At the Apple Store, you connect business professionals and entrepreneurs with the tools they need in order to put Apple solutions to work in their Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** The Apple Store is a retail environment like no other - uniquely focused on delivering amazing customer experiences. As an Expert, you introduce people Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.